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 + + + + + + + + + + + + + +