diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000..e7e7cb3 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,6 @@ +image: Visual Studio 2019 + +build_script: + - For /R %%I in (*.sln) do dotnet test %%I + +test: off \ No newline at end of file diff --git a/hw4ParseTree/hw4ParseTree.Test/hw4ParseTree.Test.csproj b/hw4ParseTree/hw4ParseTree.Test/hw4ParseTree.Test.csproj new file mode 100644 index 0000000..3c475f6 --- /dev/null +++ b/hw4ParseTree/hw4ParseTree.Test/hw4ParseTree.Test.csproj @@ -0,0 +1,19 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + + + + diff --git a/hw4ParseTree/hw4ParseTree.Test/hw4ParseTreeTest.cs b/hw4ParseTree/hw4ParseTree.Test/hw4ParseTreeTest.cs new file mode 100644 index 0000000..9a56c96 --- /dev/null +++ b/hw4ParseTree/hw4ParseTree.Test/hw4ParseTreeTest.cs @@ -0,0 +1,87 @@ +using NUnit.Framework; +using System; + +namespace Hw4ParseTree.Test +{ + public class Tests + { + private ParseTree tree; + + [SetUp] + public void Setup() + { + tree = new ParseTree(); + } + + [TestCase] + public void TestAddition() + { + var str = "( + 2 3 )"; + tree.BuildTree(str); + Assert.AreEqual(5, tree.Calculate()); + } + + [TestCase] + public void TestSubtraction() + { + var str = "( - 2 3 )"; + tree.BuildTree(str); + Assert.AreEqual(-1, tree.Calculate()); + } + + [TestCase] + public void TestMultiplication() + { + var str = "( * 2 3 )"; + tree.BuildTree(str); + Assert.AreEqual(6, tree.Calculate()); + } + + [TestCase] + public void TestDivision() + { + var str = "( / 8 4 )"; + tree.BuildTree(str); + Assert.AreEqual(2, tree.Calculate()); + } + + [TestCase] + public void TestDoubleDivision() + { + var str = "( / 3 2 )"; + tree.BuildTree(str); + Assert.AreEqual(1.5, tree.Calculate(), 0.000001); + } + + [TestCase] + public void TestWithNegativeNumber() + { + var str = "( + 3 -2 )"; + tree.BuildTree(str); + Assert.AreEqual(1, tree.Calculate()); + } + + [TestCase] + public void TestDivisionByZero() + { + var str = "( / 8 0 )"; + tree.BuildTree(str); + Assert.Throws(() => tree.Calculate()); + } + + [TestCase] + public void TestNotCorrectExpression() + { + var str = "( / 8 )"; + Assert.Throws(() => tree.BuildTree(str)); + } + + [TestCase] + public void TestTaskExpression() + { + var str = "( * ( + 1 1 ) 2 )"; + tree.BuildTree(str); + Assert.AreEqual(4, tree.Calculate()); + } + } +} \ No newline at end of file diff --git a/hw4ParseTree/hw4ParseTree.sln b/hw4ParseTree/hw4ParseTree.sln new file mode 100644 index 0000000..adf64c1 --- /dev/null +++ b/hw4ParseTree/hw4ParseTree.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31005.135 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hw4ParseTree", "hw4ParseTree\Hw4ParseTree.csproj", "{70C27B8B-C63D-4F52-B8C7-B9561C911D17}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "hw4ParseTree.Test", "hw4ParseTree.Test\hw4ParseTree.Test.csproj", "{E5953ED4-DAAC-461D-AF86-3DE600136A14}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {70C27B8B-C63D-4F52-B8C7-B9561C911D17}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {70C27B8B-C63D-4F52-B8C7-B9561C911D17}.Debug|Any CPU.Build.0 = Debug|Any CPU + {70C27B8B-C63D-4F52-B8C7-B9561C911D17}.Release|Any CPU.ActiveCfg = Release|Any CPU + {70C27B8B-C63D-4F52-B8C7-B9561C911D17}.Release|Any CPU.Build.0 = Release|Any CPU + {E5953ED4-DAAC-461D-AF86-3DE600136A14}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E5953ED4-DAAC-461D-AF86-3DE600136A14}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E5953ED4-DAAC-461D-AF86-3DE600136A14}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E5953ED4-DAAC-461D-AF86-3DE600136A14}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {E315057C-F86D-4CC5-95B3-11FF0F5A2F77} + EndGlobalSection +EndGlobal diff --git a/hw4ParseTree/hw4ParseTree/Addition.cs b/hw4ParseTree/hw4ParseTree/Addition.cs new file mode 100644 index 0000000..6d51dce --- /dev/null +++ b/hw4ParseTree/hw4ParseTree/Addition.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Hw4ParseTree +{ + /// + /// класс для сложения + /// + public class Addition : Operator + { + public override char Sign => '+'; + + public Addition(INode leftChild, INode rightChild) + { + LeftChild = leftChild; + RightChild = rightChild; + } + + public override double Calculate() + => LeftChild.Calculate() + RightChild.Calculate(); + } +} \ No newline at end of file diff --git a/hw4ParseTree/hw4ParseTree/Division.cs b/hw4ParseTree/hw4ParseTree/Division.cs new file mode 100644 index 0000000..78239d9 --- /dev/null +++ b/hw4ParseTree/hw4ParseTree/Division.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Hw4ParseTree +{ + /// + /// класс для деления + /// + public class Division : Operator + { + public override char Sign => '/'; + + public Division(INode leftChild, INode rightChild) + { + LeftChild = leftChild; + RightChild = rightChild; + } + + public override double Calculate() + { + if (Math.Abs(RightChild.Calculate()) < 0.000001) + { + throw new DivideByZeroException(); + } + return LeftChild.Calculate() / RightChild.Calculate(); + } + } +} \ No newline at end of file diff --git a/hw4ParseTree/hw4ParseTree/Expression.txt b/hw4ParseTree/hw4ParseTree/Expression.txt new file mode 100644 index 0000000..c45d6d9 --- /dev/null +++ b/hw4ParseTree/hw4ParseTree/Expression.txt @@ -0,0 +1 @@ +(* (+ 1 1) 2) \ No newline at end of file diff --git a/hw4ParseTree/hw4ParseTree/INode.cs b/hw4ParseTree/hw4ParseTree/INode.cs new file mode 100644 index 0000000..c766d19 --- /dev/null +++ b/hw4ParseTree/hw4ParseTree/INode.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Hw4ParseTree +{ + /// + /// интерфейс узла в дереве разбора + /// + public interface INode + { + /// + /// печатает, что находится в узле + /// + public void Print(); + + /// + /// возвращает значение элемента, лежащего в узле + /// + public double Calculate(); + } +} \ No newline at end of file diff --git a/hw4ParseTree/hw4ParseTree/InvalidExpressionException.cs b/hw4ParseTree/hw4ParseTree/InvalidExpressionException.cs new file mode 100644 index 0000000..4007e9c --- /dev/null +++ b/hw4ParseTree/hw4ParseTree/InvalidExpressionException.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Hw4ParseTree +{ + /// + /// исключение для неправильных выражений. + /// + public class InvalidExpressionException : Exception + { + public InvalidExpressionException() + { + } + + public InvalidExpressionException(string message) + : base(message) + { + } + } +} \ No newline at end of file diff --git a/hw4ParseTree/hw4ParseTree/Multiplication.cs b/hw4ParseTree/hw4ParseTree/Multiplication.cs new file mode 100644 index 0000000..2a80ced --- /dev/null +++ b/hw4ParseTree/hw4ParseTree/Multiplication.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Hw4ParseTree +{ + /// + /// класс для умножения + /// + public class Multiplication : Operator + { + public override char Sign => '*'; + + public Multiplication(INode leftChild, INode rightChild) + { + LeftChild = leftChild; + RightChild = rightChild; + } + + public override double Calculate() + => LeftChild.Calculate() * RightChild.Calculate(); + } +} \ No newline at end of file diff --git a/hw4ParseTree/hw4ParseTree/Operand.cs b/hw4ParseTree/hw4ParseTree/Operand.cs new file mode 100644 index 0000000..0d54399 --- /dev/null +++ b/hw4ParseTree/hw4ParseTree/Operand.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Hw4ParseTree +{ + /// + /// класс синтаксического дерева, который представляет число + /// + class Operand : INode + { + private double Number; + + public Operand(double number) + => Number = number; + + /// + /// выводит число + /// + public void Print() + => Console.Write($" {Number} "); + + /// + /// считает значение + /// + public double Calculate() + => Number; + } +} \ No newline at end of file diff --git a/hw4ParseTree/hw4ParseTree/Operator.cs b/hw4ParseTree/hw4ParseTree/Operator.cs new file mode 100644 index 0000000..643611a --- /dev/null +++ b/hw4ParseTree/hw4ParseTree/Operator.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Hw4ParseTree +{ + /// + /// Класс синтаксического дерва который представляет оператор + /// + public abstract class Operator : INode + { + public INode LeftChild { get; set; } + + public INode RightChild { get; set; } + + public virtual char Sign { get; } + + /// + /// выводит участок выражения + /// + public void Print() + { + Console.Write("("); + LeftChild.Print(); + Console.Write(Sign); + RightChild.Print(); + Console.Write(")"); + } + + /// + /// считает значение + /// + public abstract double Calculate(); + } +} diff --git a/hw4ParseTree/hw4ParseTree/ParseTree.cs b/hw4ParseTree/hw4ParseTree/ParseTree.cs new file mode 100644 index 0000000..09e324d --- /dev/null +++ b/hw4ParseTree/hw4ParseTree/ParseTree.cs @@ -0,0 +1,148 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Hw4ParseTree +{ + /// + /// дерево разбора + /// + public class ParseTree + { + private INode root; + + /// + /// Функция построения дерева разбора + /// + public void BuildTree(string expression) + { + var index = 0; + if (CheckExpression(expression)) + { + root = Build(expression, ref index); + } + else + { + throw new InvalidExpressionException(); + } + } + + private bool IsCorrectSymbol(char symbol) + => symbol == '(' || symbol == ')'; + + private bool IsCorrect(char symbol) + => symbol == '(' || IsOperator(symbol); + + private bool IsOperator(char symbol) + => symbol == '+' || symbol == '-' || symbol == '*' || symbol == '/'; + + private double ReadNumber(string line, ref int index) + { + var number = ""; + if (line[index] == '-') + { + number += line[index]; + index++; + } + while (char.IsDigit(line[index])) + { + number += line[index]; + index++; + } + if (!double.TryParse(number, out var value)) + { + throw new InvalidExpressionException(); + } + return value; + } + + private bool CheckExpression(string line) + { + int index = 0; + int countNumber = 0; + int countBrackets = 0; + while (index != line.Length) + { + if (line[index] == '(') + { + countBrackets++; + } + else if (line[index] == ' ') + { + index++; + continue; + } + else if (line[index] == ')') + { + countBrackets--; + if (countBrackets < 0) + { + return false; + } + } + else if (line[index] == '-' && char.IsDigit(line[index + 1]) || char.IsDigit(line[index])) + { + var value = ReadNumber(line, ref index); + countNumber++; + continue; + } + else if (IsOperator(line[index])) + { + countNumber--; + } + else + { + return false; + } + index++; + } + return countBrackets == 0 && countNumber == 1; + } + + private INode Build(string line, ref int index) + { + for (; index < line.Length; ++index) + { + if (line[index] == ' ') + { + continue; + } + if (line[index] == '-' && char.IsDigit(line[index + 1]) || char.IsDigit(line[index])) + { + var value = ReadNumber(line, ref index); + return new Operand(value); + } + else if (IsOperator(line[index])) + { + index++; + return line[index - 1] switch + { + '+' => new Addition(Build(line, ref index), Build(line, ref index)), + '-' => new Subtraction(Build(line, ref index), Build(line, ref index)), + '/' => new Division(Build(line, ref index), Build(line, ref index)), + '*' => new Multiplication(Build(line, ref index), Build(line, ref index)), + _ => throw new InvalidExpressionException(), + }; + } + else if (!IsCorrectSymbol(line[index])) + { + throw new InvalidExpressionException(); + } + } + return root; + } + + /// + /// функция печати дерева + /// + public void PrintTree() + => root.Print(); + + /// + /// Считает значение выражения + /// + /// возвращает ответ + public double Calculate() + => root.Calculate(); + } +} \ No newline at end of file diff --git a/hw4ParseTree/hw4ParseTree/Program.cs b/hw4ParseTree/hw4ParseTree/Program.cs new file mode 100644 index 0000000..b19ba91 --- /dev/null +++ b/hw4ParseTree/hw4ParseTree/Program.cs @@ -0,0 +1,29 @@ +using System; +using System.IO; + +namespace Hw4ParseTree +{ + class Program + { + static void Main(string[] args) + { + var tree = new ParseTree(); + var file = new StreamReader("..\\..\\..\\Expression.txt"); + var expression = file.ReadLine(); + Console.WriteLine($"Выражение - {expression}"); + try + { + tree.BuildTree(expression); + } + catch (InvalidExpressionException) + { + Console.WriteLine("Ошибка! Некоректный ввод выражения!"); + return; + } + + Console.Write("Печать выражения: "); + tree.PrintTree(); + Console.WriteLine($"\nОтвет = {tree.Calculate()}"); + } + } +} diff --git a/hw4ParseTree/hw4ParseTree/Subtraction.cs b/hw4ParseTree/hw4ParseTree/Subtraction.cs new file mode 100644 index 0000000..329f910 --- /dev/null +++ b/hw4ParseTree/hw4ParseTree/Subtraction.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Hw4ParseTree +{ + /// + /// класс для вычитания + /// + public class Subtraction : Operator + { + public override char Sign => '-'; + + public Subtraction(INode leftChild, INode rightChild) + { + LeftChild = leftChild; + RightChild = rightChild; + } + + public override double Calculate() + => LeftChild.Calculate() - RightChild.Calculate(); + } +} diff --git a/hw4ParseTree/hw4ParseTree/hw4ParseTree.csproj b/hw4ParseTree/hw4ParseTree/hw4ParseTree.csproj new file mode 100644 index 0000000..c73e0d1 --- /dev/null +++ b/hw4ParseTree/hw4ParseTree/hw4ParseTree.csproj @@ -0,0 +1,8 @@ + + + + Exe + netcoreapp3.1 + + +