diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml
index 1e6a924..94ac0b1 100644
--- a/.github/workflows/dotnet.yml
+++ b/.github/workflows/dotnet.yml
@@ -3,7 +3,7 @@ name: Build
on: [push]
jobs:
- build:
+ build-Windows:
runs-on: windows-latest
@@ -20,4 +20,19 @@ jobs:
- name: Test
run: $slnList = Get-ChildItem $foo.FullName -Recurse -Filter '*.sln'; foreach ($file in $slnList) {dotnet test $file.FullName}
+ build-Ubuntu:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: Build
+ uses: actions/setup-dotnet@v1
+ with:
+ dotnet-version: '6.x'
+ - name: Restore
+ run: for f in $(find . -name "*.sln"); do dotnet restore $f; done
+ - name: Build
+ run: for f in $(find . -name "*.sln"); do dotnet build $f; done
+ - name: Test
+ run: for f in $(find . -name "*.sln"); do dotnet test $f; done
\ No newline at end of file
diff --git a/Homework4/ParsingTree/ParsingTree.sln b/Homework4/ParsingTree/ParsingTree.sln
new file mode 100644
index 0000000..69baee1
--- /dev/null
+++ b/Homework4/ParsingTree/ParsingTree.sln
@@ -0,0 +1,31 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.1.32228.430
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ParsingTree", "ParsingTree\ParsingTree.csproj", "{3A685608-3E27-4534-942C-AE951EF76977}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestParsingTree", "TestParsingTree\TestParsingTree.csproj", "{921D199E-504F-47CD-AC97-9C93A98C8B0D}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {3A685608-3E27-4534-942C-AE951EF76977}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {3A685608-3E27-4534-942C-AE951EF76977}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {3A685608-3E27-4534-942C-AE951EF76977}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {3A685608-3E27-4534-942C-AE951EF76977}.Release|Any CPU.Build.0 = Release|Any CPU
+ {921D199E-504F-47CD-AC97-9C93A98C8B0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {921D199E-504F-47CD-AC97-9C93A98C8B0D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {921D199E-504F-47CD-AC97-9C93A98C8B0D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {921D199E-504F-47CD-AC97-9C93A98C8B0D}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {DD0BE32A-1D93-45E3-ACD3-92A75D23D824}
+ EndGlobalSection
+EndGlobal
diff --git a/Homework4/ParsingTree/ParsingTree/ParsingTree.cs b/Homework4/ParsingTree/ParsingTree/ParsingTree.cs
new file mode 100644
index 0000000..362784b
--- /dev/null
+++ b/Homework4/ParsingTree/ParsingTree/ParsingTree.cs
@@ -0,0 +1,306 @@
+namespace ParsingTree;
+
+using System;
+
+///
+/// Class representing the parse tree
+///
+public class ParsingTree
+{
+ private abstract class Node
+ {
+ ///
+ /// Abstract method for counting each operator or operand
+ ///
+ /// Operator or operand value
+ public abstract float Count();
+
+ ///
+ /// Abstract method for printing each operator or operand
+ ///
+ public abstract void Print();
+ }
+
+ ///
+ /// A class representing operands
+ ///
+ private class Operand : Node
+ {
+ private readonly string Value;
+
+ public Operand(string element)
+ {
+ Value = element;
+ }
+
+ ///
+ /// The operand class calculates the value for them and returns it
+ ///
+ /// Operand value
+ public override float Count() => float.Parse(Value);
+
+ ///
+ /// The operand class can print the values of operands
+ ///
+ public override void Print()
+ {
+ Console.Write(Value);
+ Console.Write(" ");
+ }
+ }
+
+ ///
+ /// A class representing operators
+ ///
+ private abstract class Operator : Node
+ {
+ public Node? LeftSon;
+ public Node? RightSon;
+
+
+ public abstract char Symbol { get; set; }
+
+ public override void Print()
+ {
+ Console.Write("(");
+ Console.Write(Symbol);
+ LeftSon?.Print();
+ RightSon?.Print();
+ Console.Write(")");
+ }
+ }
+
+ ///
+ /// A class representing the operator +
+ ///
+ private class Plus : Operator
+ {
+ public Plus()
+ {
+ Symbol = '+';
+ }
+
+ public override float Count()
+ {
+ if (LeftSon == null || RightSon == null)
+ {
+ throw new InvalidOperationException();
+ }
+
+ return LeftSon.Count() + RightSon.Count();
+ }
+
+ public override char Symbol { get; set; }
+ }
+
+ ///
+ /// A class representing the operator -
+ ///
+ private class Minus : Operator
+ {
+ public Minus()
+ {
+ Symbol = '-';
+ }
+
+ public override float Count()
+ {
+ // But if we consider the input file to be correct, then such a situation should not arise
+ if (LeftSon == null || RightSon == null)
+ {
+ throw new InvalidOperationException();
+ }
+
+ return LeftSon.Count() - RightSon.Count();
+ }
+
+ public override char Symbol { get; set; }
+ }
+
+ ///
+ /// A class representing the operator /
+ ///
+ private class Divide : Operator
+ {
+ public Divide()
+ {
+ Symbol = '/';
+ }
+
+ public override float Count()
+ {
+ if (LeftSon == null || RightSon == null)
+ {
+ throw new InvalidOperationException();
+ }
+
+ float rightSonValue = RightSon.Count();
+ if (Math.Abs(rightSonValue - 0) < 0.0000000000000000000000000001)
+ {
+ throw new DivideByZeroException();
+ }
+
+ return LeftSon.Count() / rightSonValue;
+ }
+
+ public override char Symbol { get; set; }
+ }
+
+ ///
+ /// A class representing the operator *
+ ///
+ private class Multiplication : Operator
+ {
+ public Multiplication()
+ {
+ Symbol = '*';
+ }
+
+ public override float Count()
+ {
+ if (LeftSon == null || RightSon == null)
+ {
+ throw new InvalidOperationException();
+ }
+ return LeftSon.Count() * RightSon.Count();
+ }
+
+ public override char Symbol { get; set; }
+ }
+
+ private Node? treeRoot;
+
+ ///
+ /// Function for building a tree
+ ///
+ /// The expression that needs to be calculated
+ public void BuildTree(string expression)
+ {
+ int index = 0;
+ Node? node = null;
+ treeRoot = PrivateBuildTree(expression, ref index, node);
+ }
+
+ ///
+ /// Auxiliary function for building a tree
+ ///
+ private Node? PrivateBuildTree(string expression, ref int index, Node? node)
+ {
+ if (index >= expression.Length)
+ {
+ return node;
+ }
+
+ // Skip the characters we don't need
+ while (expression[index] == '(' || expression[index] == ')' || expression[index] == ' ' && index < expression.Length)
+ {
+ index++;
+ }
+
+ // The condition in order to avoid confusion, for example, with 4 -5 and 4 - 5
+ if (index < expression.Length - 1 && !IsOperand(expression[index + 1]) && IsOperator(expression[index]))
+ {
+ InitializeNode(expression, ref index, ref node);
+ return node;
+ }
+
+ // The number could be negative
+ int newIndex = expression[index] == '-' ? index + 1 : index;
+ string nodeValue = "";
+ while (newIndex < expression.Length && IsOperand(expression[newIndex]))
+ {
+ nodeValue += expression[newIndex];
+ newIndex++;
+ }
+ Node? newNode = null;
+
+ // This unused variable x is needed in order to call the function,
+ // And it is the operand that is initialized,
+ // Because the last character of the number cannot be an operator (the file is considered correct
+ int x = nodeValue.Length - 1;
+
+ if (expression[index] == '-')
+ {
+ InitializeNode("-" + nodeValue, ref x, ref newNode);
+ }
+ else
+ {
+ InitializeNode(nodeValue, ref x, ref newNode);
+ }
+
+ index = newIndex;
+ return newNode;
+ }
+
+ ///
+ /// A function for initializing nodes depending on which operator or operator is a string
+ ///
+ private void InitializeNode(string expression, ref int index, ref Node? node)
+ {
+ switch (expression[index])
+ {
+ case '+':
+ {
+ node = new Plus();
+ index++;
+ ((Plus)node).LeftSon = PrivateBuildTree(expression, ref index, ((Plus)node).LeftSon);
+ ((Plus)node).RightSon = PrivateBuildTree(expression, ref index, ((Plus)node).RightSon);
+ return;
+ }
+ case '-':
+ {
+ node = new Minus();
+ index++;
+ ((Minus)node).LeftSon = PrivateBuildTree(expression, ref index, ((Minus)node).LeftSon);
+ ((Minus)node).RightSon = PrivateBuildTree(expression, ref index, ((Minus)node).RightSon);
+ return;
+ }
+ case '*':
+ {
+ node = new Multiplication();
+ index++;
+ ((Multiplication)node).LeftSon = PrivateBuildTree(expression, ref index, ((Multiplication)node).LeftSon);
+ ((Multiplication)node).RightSon = PrivateBuildTree(expression, ref index, ((Multiplication)node).RightSon);
+ return;
+ }
+ case '/':
+ {
+ node = new Divide();
+ index++;
+ ((Divide)node).LeftSon = PrivateBuildTree(expression, ref index, ((Divide)node).LeftSon);
+ ((Divide)node).RightSon = PrivateBuildTree(expression, ref index, ((Divide)node).RightSon);
+ return;
+ }
+ default:
+ {
+ node = new Operand(expression);
+ index++;
+ return;
+ }
+ }
+ }
+
+
+ ///
+ /// Function for printing a tree
+ ///
+ public void Print() => treeRoot?.Print();
+
+ ///
+ /// Function for calculating the value of an expression
+ ///
+ /// value of an expression
+ public float Count()
+ {
+ if (treeRoot == null)
+ {
+ throw new InvalidOperationException();
+ }
+
+ return treeRoot.Count();
+ }
+
+ private static bool IsOperator(char element) => element == '+' || element == '-' || element == '*' || element == '/';
+
+ private static bool IsOperand(char element) => element <= '9' && element >= '0';
+}
\ No newline at end of file
diff --git a/Homework4/ParsingTree/ParsingTree/ParsingTree.csproj b/Homework4/ParsingTree/ParsingTree/ParsingTree.csproj
new file mode 100644
index 0000000..644c780
--- /dev/null
+++ b/Homework4/ParsingTree/ParsingTree/ParsingTree.csproj
@@ -0,0 +1,10 @@
+
+
+
+ Library
+ net6.0
+ enable
+ enable
+
+
+
diff --git a/Homework4/ParsingTree/TestParsingTree/FirstTest.txt b/Homework4/ParsingTree/TestParsingTree/FirstTest.txt
new file mode 100644
index 0000000..40914e1
--- /dev/null
+++ b/Homework4/ParsingTree/TestParsingTree/FirstTest.txt
@@ -0,0 +1 @@
+(* (+ 1 1) 2)
\ No newline at end of file
diff --git a/Homework4/ParsingTree/TestParsingTree/TestParsingTree.cs b/Homework4/ParsingTree/TestParsingTree/TestParsingTree.cs
new file mode 100644
index 0000000..0e4ed91
--- /dev/null
+++ b/Homework4/ParsingTree/TestParsingTree/TestParsingTree.cs
@@ -0,0 +1,52 @@
+namespace TestParsingTree;
+
+using NUnit.Framework;
+using ParsingTree;
+using System.IO;
+using System;
+
+///
+/// A class for testing a parsing tree
+///
+public class TestParsingTree
+{
+ private ParsingTree tree = new();
+
+ [SetUp]
+ public void Setup()
+ {
+ tree = new();
+ }
+
+ [Test]
+ public void ShouldExpected4WhenExpressionEqual4()
+ {
+ string stringToConvert = File.ReadAllText("..//..//..//FirstTest.txt");
+ tree.BuildTree(stringToConvert);
+ Assert.AreEqual(4 , tree.Count());
+ }
+
+ [Test]
+ public void ShouldExpectedOneNumberWhenExpressionContainsOnlyOneNumber()
+ {
+ string stringToConvert = "12";
+ tree.BuildTree(stringToConvert);
+ Assert.AreEqual(12, tree.Count());
+ }
+
+ [Test]
+ public void ShouldThrowsNullReferenceExceptionWhenExpressionIsEmptyString()
+ {
+ string stringToConvert = "";
+ tree.BuildTree(stringToConvert);
+ Assert.Throws(() => tree.Count());
+ }
+
+ [Test]
+ public void ThrowsDivideByZeroExceptionWhenExpressionContainsDivisionByZero()
+ {
+ string stringToConvert = "/ 1 (- 2 2)";
+ tree.BuildTree(stringToConvert);
+ Assert.Throws(() => tree.Count());
+ }
+}
\ No newline at end of file
diff --git a/Homework4/ParsingTree/TestParsingTree/TestParsingTree.csproj b/Homework4/ParsingTree/TestParsingTree/TestParsingTree.csproj
new file mode 100644
index 0000000..18963a6
--- /dev/null
+++ b/Homework4/ParsingTree/TestParsingTree/TestParsingTree.csproj
@@ -0,0 +1,21 @@
+
+
+
+ net6.0
+ enable
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+