diff --git a/HW2/HW2.sln b/HW2/HW2.sln
new file mode 100644
index 0000000..c5d8b5c
--- /dev/null
+++ b/HW2/HW2.sln
@@ -0,0 +1,22 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SyntaxTree", "SyntaxTree\SyntaxTree.csproj", "{6C1B7790-DBF9-4146-A87B-83D95A0D5C54}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SyntaxTree.Tests", "SyntaxTree.Tests\SyntaxTree.Tests.csproj", "{29D5F19D-35BC-49BE-8724-369258B5717D}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {6C1B7790-DBF9-4146-A87B-83D95A0D5C54}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6C1B7790-DBF9-4146-A87B-83D95A0D5C54}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6C1B7790-DBF9-4146-A87B-83D95A0D5C54}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6C1B7790-DBF9-4146-A87B-83D95A0D5C54}.Release|Any CPU.Build.0 = Release|Any CPU
+ {29D5F19D-35BC-49BE-8724-369258B5717D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {29D5F19D-35BC-49BE-8724-369258B5717D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {29D5F19D-35BC-49BE-8724-369258B5717D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {29D5F19D-35BC-49BE-8724-369258B5717D}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+EndGlobal
diff --git a/HW2/SyntaxTree.Tests/SyntaxTree.Tests.csproj b/HW2/SyntaxTree.Tests/SyntaxTree.Tests.csproj
new file mode 100644
index 0000000..699997e
--- /dev/null
+++ b/HW2/SyntaxTree.Tests/SyntaxTree.Tests.csproj
@@ -0,0 +1,44 @@
+
+
+
+ net9.0
+ enable
+ enable
+
+ false
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Always
+
+
+
diff --git a/HW2/SyntaxTree.Tests/SyntaxTreeTests.cs b/HW2/SyntaxTree.Tests/SyntaxTreeTests.cs
new file mode 100644
index 0000000..b92e73f
--- /dev/null
+++ b/HW2/SyntaxTree.Tests/SyntaxTreeTests.cs
@@ -0,0 +1,155 @@
+//
+// Copyright (c) khusainovilas. All rights reserved.
+//
+
+namespace SyntaxTree.Tests;
+
+///
+/// tests for syntax tree.
+///
+public class SyntaxTreeTests
+{
+ private StringWriter consoleOutput;
+
+ [SetUp]
+ public void SetUp()
+ {
+ this.consoleOutput = new StringWriter();
+ Console.SetOut(this.consoleOutput);
+ }
+
+ [TearDown]
+ public void TearDown()
+ {
+ this.consoleOutput.Dispose();
+ Console.SetOut(new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = true });
+ }
+
+ ///
+ /// Test parsing a valid expression with multiplication and addition.
+ ///
+ [Test]
+ public void Parser_Parse_ValidExpression_ReturnsCorrectTreeAndResult()
+ {
+ const string input = "(* (+ 1 1) 2)";
+ var tree = Parser.Parse(input);
+ Assert.Multiple(() =>
+ {
+ Assert.That(tree.ToStringRepresentation(), Is.EqualTo("(* (+ 1 1) 2)"));
+ Assert.That(tree.Calculate(), Is.EqualTo(4));
+ });
+ }
+
+ ///
+ /// Test parsing an expression with negative numbers.
+ ///
+ [Test]
+ public void Parser_Parse_NegativeNumber_ReturnsCorrectResult()
+ {
+ const string input = "(+ -5 3)";
+ var tree = Parser.Parse(input);
+ Assert.Multiple(() =>
+ {
+ Assert.That(tree.ToStringRepresentation(), Is.EqualTo("(+ -5 3)"));
+ Assert.That(tree.Calculate(), Is.EqualTo(-2));
+ });
+ }
+
+ ///
+ /// Test parsing an empty input throws ArgumentException.
+ ///
+ [Test]
+ public void Parser_Parse_EmptyInput_ThrowsArgumentException()
+ {
+ Assert.Throws(() => Parser.Parse(string.Empty));
+ }
+
+ ///
+ /// Test parsing an invalid token throws ArgumentException.
+ ///
+ [Test]
+ public void Parser_Parse_InvalidToken_ThrowsArgumentException()
+ {
+ Assert.Throws(() => Parser.Parse("(+ 1 a)"));
+ }
+
+ ///
+ /// Test parsing unbalanced parentheses throws ArgumentException.
+ ///
+ [Test]
+ public void Parser_Parse_UnbalancedParentheses_ThrowsArgumentException()
+ {
+ Assert.Throws(() => Parser.Parse("(( 1 2)"));
+ }
+
+ ///
+ /// Test parsing division by zero throws DivideByZeroException in Calculate.
+ ///
+ [Test]
+ public void Parser_Parse_DivisionByZero_ThrowsDivideByZeroException()
+ {
+ const string input = "(/ 10 0)";
+ var tree = Parser.Parse(input);
+ Assert.That(tree.ToStringRepresentation(), Is.EqualTo("(/ 10 0)"));
+ Assert.Throws(() => tree.Calculate());
+ }
+
+ ///
+ /// Test NumberNode calculation and string representation.
+ ///
+ [Test]
+ public void NumberNode_CalculateAndToString_ReturnsCorrectValues()
+ {
+ var node = new NumberNode(-5);
+ Assert.Multiple(() =>
+ {
+ Assert.That(node.Calculate(), Is.EqualTo(-5));
+ Assert.That(node.ToStringRepresentation(), Is.EqualTo("-5"));
+ });
+ }
+
+ ///
+ /// Test that Add node calculates and prints correctly.
+ ///
+ [Test]
+ public void Add_CalculateAndToString_ReturnsCorrectValues()
+ {
+ var node = new Add(new NumberNode(1), new NumberNode(2));
+
+ Assert.Multiple(() =>
+ {
+ Assert.That(node.Calculate(), Is.EqualTo(3));
+ Assert.That(node.ToStringRepresentation(), Is.EqualTo("(+ 1 2)"));
+ });
+ }
+
+ ///
+ /// Test subtraction (non-commutative operation) works correctly.
+ ///
+ [Test]
+ public void Parser_Parse_Subtraction_ReturnsCorrectResult()
+ {
+ const string input = "(- 10 4)";
+ var tree = Parser.Parse(input);
+ Assert.Multiple(() =>
+ {
+ Assert.That(tree.ToStringRepresentation(), Is.EqualTo("(- 10 4)"));
+ Assert.That(tree.Calculate(), Is.EqualTo(6));
+ });
+ }
+
+ ///
+ /// Test division (non-commutative operation) works correctly and doesn't swap operands.
+ ///
+ [Test]
+ public void Parser_Parse_Division_ReturnsCorrectResult()
+ {
+ const string input = "(/ 10 2)";
+ var tree = Parser.Parse(input);
+ Assert.Multiple(() =>
+ {
+ Assert.That(tree.ToStringRepresentation(), Is.EqualTo("(/ 10 2)"));
+ Assert.That(tree.Calculate(), Is.EqualTo(5));
+ });
+ }
+}
\ No newline at end of file
diff --git a/HW2/SyntaxTree.Tests/stylecop.json b/HW2/SyntaxTree.Tests/stylecop.json
new file mode 100644
index 0000000..76c8e76
--- /dev/null
+++ b/HW2/SyntaxTree.Tests/stylecop.json
@@ -0,0 +1,9 @@
+{
+ "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json",
+ "settings": {
+ "documentationRules": {
+ "companyName": "khusainovilas",
+ "copyrightText": "Copyright (c) {companyName}. All rights reserved."
+ }
+ }
+}
\ No newline at end of file
diff --git a/HW2/SyntaxTree.Tests/testFile.txt b/HW2/SyntaxTree.Tests/testFile.txt
new file mode 100644
index 0000000..fbd556d
--- /dev/null
+++ b/HW2/SyntaxTree.Tests/testFile.txt
@@ -0,0 +1 @@
+(+ 1 2)
\ No newline at end of file
diff --git a/HW2/SyntaxTree/Add.cs b/HW2/SyntaxTree/Add.cs
new file mode 100644
index 0000000..61fb0f2
--- /dev/null
+++ b/HW2/SyntaxTree/Add.cs
@@ -0,0 +1,27 @@
+//
+// Copyright (c) khusainovilas. All rights reserved.
+//
+
+namespace SyntaxTree;
+
+///
+/// Add operation.
+///
+public class Add : Operation
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Left operand of the operation.
+ /// Right operand of the operation.
+ public Add(IAbstractNode left, IAbstractNode right)
+ : base(left, right)
+ {
+ }
+
+ ///
+ protected override char Symbol => '+';
+
+ ///
+ public override int Calculate() => this.Left.Calculate() + this.Right.Calculate();
+}
\ No newline at end of file
diff --git a/HW2/SyntaxTree/Divide.cs b/HW2/SyntaxTree/Divide.cs
new file mode 100644
index 0000000..41bb302
--- /dev/null
+++ b/HW2/SyntaxTree/Divide.cs
@@ -0,0 +1,36 @@
+//
+// Copyright (c) khusainovilas. All rights reserved.
+//
+
+namespace SyntaxTree;
+
+///
+/// Divide operation.
+///
+public class Divide : Operation
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Left operand of the operation.
+ /// Right operand of the operation.
+ public Divide(IAbstractNode left, IAbstractNode right)
+ : base(left, right)
+ {
+ }
+
+ ///
+ protected override char Symbol => '/';
+
+ ///
+ public override int Calculate()
+ {
+ var divisor = this.Right.Calculate();
+ if (divisor == 0)
+ {
+ throw new DivideByZeroException("Division by zero.");
+ }
+
+ return this.Left.Calculate() / divisor;
+ }
+}
\ No newline at end of file
diff --git a/HW2/SyntaxTree/IAbstractNode.cs b/HW2/SyntaxTree/IAbstractNode.cs
new file mode 100644
index 0000000..e84656f
--- /dev/null
+++ b/HW2/SyntaxTree/IAbstractNode.cs
@@ -0,0 +1,23 @@
+//
+// Copyright (c) khusainovilas. All rights reserved.
+//
+
+namespace SyntaxTree;
+
+///
+/// Represents a node in the syntax tree.
+///
+public interface IAbstractNode
+{
+ ///
+ /// Calculates the value of the node.
+ ///
+ /// The calculated result.
+ int Calculate();
+
+ ///
+ /// Returns the string representation of the node.
+ ///
+ /// String representation of the node.
+ string ToStringRepresentation();
+}
\ No newline at end of file
diff --git a/HW2/SyntaxTree/Multiply.cs b/HW2/SyntaxTree/Multiply.cs
new file mode 100644
index 0000000..af84374
--- /dev/null
+++ b/HW2/SyntaxTree/Multiply.cs
@@ -0,0 +1,27 @@
+//
+// Copyright (c) khusainovilas. All rights reserved.
+//
+
+namespace SyntaxTree;
+
+///
+/// Multiply operation.
+///
+public class Multiply : Operation
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Left operand of the operation.
+ /// Right operand of the operation.
+ public Multiply(IAbstractNode left, IAbstractNode right)
+ : base(left, right)
+ {
+ }
+
+ ///
+ protected override char Symbol => '*';
+
+ ///
+ public override int Calculate() => this.Left.Calculate() * this.Right.Calculate();
+}
\ No newline at end of file
diff --git a/HW2/SyntaxTree/NumberNode.cs b/HW2/SyntaxTree/NumberNode.cs
new file mode 100644
index 0000000..2a9586e
--- /dev/null
+++ b/HW2/SyntaxTree/NumberNode.cs
@@ -0,0 +1,17 @@
+//
+// Copyright (c) khusainovilas. All rights reserved.
+//
+
+namespace SyntaxTree;
+
+///
+/// Represents a number node in the syntax tree.
+///
+public class NumberNode(int value) : IAbstractNode
+{
+ ///
+ public int Calculate() => value;
+
+ ///
+ public string ToStringRepresentation() => value.ToString();
+}
\ No newline at end of file
diff --git a/HW2/SyntaxTree/Operation.cs b/HW2/SyntaxTree/Operation.cs
new file mode 100644
index 0000000..efae6d9
--- /dev/null
+++ b/HW2/SyntaxTree/Operation.cs
@@ -0,0 +1,44 @@
+//
+// Copyright (c) khusainovilas. All rights reserved.
+//
+
+namespace SyntaxTree;
+
+///
+/// Base class for all binary operations.
+///
+public abstract class Operation : IAbstractNode
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Left operand of the operation.
+ /// Right operand of the operation.
+ protected Operation(IAbstractNode left, IAbstractNode right)
+ {
+ this.Left = left;
+ this.Right = right;
+ }
+
+ ///
+ /// Gets left child of operator.
+ ///
+ protected IAbstractNode Left { get; }
+
+ ///
+ /// Gets right child of operator.
+ ///
+ protected IAbstractNode Right { get; }
+
+ ///
+ /// Gets symbol of operation.
+ ///
+ protected abstract char Symbol { get; }
+
+ ///
+ public abstract int Calculate();
+
+ ///
+ public string ToStringRepresentation()
+ => $"({this.Symbol} {this.Left.ToStringRepresentation()} {this.Right.ToStringRepresentation()})";
+}
\ No newline at end of file
diff --git a/HW2/SyntaxTree/Parser.cs b/HW2/SyntaxTree/Parser.cs
new file mode 100644
index 0000000..075b079
--- /dev/null
+++ b/HW2/SyntaxTree/Parser.cs
@@ -0,0 +1,100 @@
+//
+// Copyright (c) khusainovilas. All rights reserved.
+//
+
+namespace SyntaxTree;
+
+///
+/// Parser for building the syntax tree from a string representation.
+///
+public static class Parser
+{
+ ///
+ /// Parses the input string into a syntax tree node.
+ ///
+ /// The input string in prefix notation.
+ /// The root node of the syntax tree.
+ public static IAbstractNode Parse(string input)
+ {
+ if (string.IsNullOrWhiteSpace(input))
+ {
+ throw new ArgumentException("Input cannot be empty or whitespace.");
+ }
+
+ if (!AreParenthesesBalanced(input))
+ {
+ throw new ArgumentException("Unbalanced parentheses.");
+ }
+
+ var tokens = input
+ .Replace('(', ' ')
+ .Replace(')', ' ')
+ .Split(' ', StringSplitOptions.RemoveEmptyEntries);
+
+ var pos = 0;
+ var node = ParseExpression(tokens, ref pos);
+
+ if (pos != tokens.Length)
+ {
+ throw new ArgumentException("Extra tokens after parsing complete expression.");
+ }
+
+ return node;
+ }
+
+ private static IAbstractNode ParseExpression(string[] tokens, ref int pos)
+ {
+ if (pos >= tokens.Length)
+ {
+ throw new ArgumentException("Unexpected end of input.");
+ }
+
+ var token = tokens[pos++];
+ if (int.TryParse(token, out var value))
+ {
+ return new NumberNode(value);
+ }
+
+ if (token.Length != 1 || !"+-*/".Contains(token[0]))
+ {
+ throw new ArgumentException($"Invalid operator: {token}");
+ }
+
+ var op = token[0];
+ var left = ParseExpression(tokens, ref pos);
+ var right = ParseExpression(tokens, ref pos);
+
+ return op switch
+ {
+ '+' => new Add(left, right),
+ '-' => new Subtract(left, right),
+ '*' => new Multiply(left, right),
+ '/' => new Divide(left, right),
+ _ => throw new ArgumentException($"Unknown operator: {op}"),
+ };
+ }
+
+ private static bool AreParenthesesBalanced(string s)
+ {
+ var balance = 0;
+ foreach (var c in s)
+ {
+ if (c == '(')
+ {
+ balance++;
+ }
+
+ if (c == ')')
+ {
+ balance--;
+ }
+
+ if (balance < 0)
+ {
+ return false;
+ }
+ }
+
+ return balance == 0;
+ }
+}
diff --git a/HW2/SyntaxTree/Program.cs b/HW2/SyntaxTree/Program.cs
new file mode 100644
index 0000000..71cd4be
--- /dev/null
+++ b/HW2/SyntaxTree/Program.cs
@@ -0,0 +1,42 @@
+//
+// Copyright (c) khusainovilas. All rights reserved.
+//
+
+using SyntaxTree;
+
+try
+{
+ if (args.Length != 1)
+ {
+ throw new ArgumentException("Exactly one argument is expected - the file path to the file.");
+ }
+
+ var filePath = args[0];
+
+ if (!File.Exists(filePath))
+ {
+ throw new FileNotFoundException("File not found.", filePath);
+ }
+
+ var input = File.ReadAllText(filePath).Trim();
+ var tree = Parser.Parse(input);
+
+ Console.WriteLine($"The tree: {tree.ToStringRepresentation()}");
+ Console.WriteLine($"The result: {tree.Calculate()}");
+}
+catch (FileNotFoundException ex)
+{
+ Console.WriteLine($"Error: {ex.Message}");
+}
+catch (ArgumentException ex)
+{
+ Console.WriteLine($"Error: Incorrect expression. {ex.Message}");
+}
+catch (DivideByZeroException)
+{
+ Console.WriteLine("Mistake: Division by zero.");
+}
+catch (Exception)
+{
+ Console.WriteLine("Error: An unknown error has occurred.");
+}
\ No newline at end of file
diff --git a/HW2/SyntaxTree/Subtract.cs b/HW2/SyntaxTree/Subtract.cs
new file mode 100644
index 0000000..4c01f0c
--- /dev/null
+++ b/HW2/SyntaxTree/Subtract.cs
@@ -0,0 +1,27 @@
+//
+// Copyright (c) khusainovilas. All rights reserved.
+//
+
+namespace SyntaxTree;
+
+///
+/// Subtract operation.
+///
+public class Subtract : Operation
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Left operand of the operation.
+ /// Right operand of the operation.
+ public Subtract(IAbstractNode left, IAbstractNode right)
+ : base(left, right)
+ {
+ }
+
+ ///
+ protected override char Symbol => '-';
+
+ ///
+ public override int Calculate() => this.Left.Calculate() - this.Right.Calculate();
+}
\ No newline at end of file
diff --git a/HW2/SyntaxTree/SyntaxTree.csproj b/HW2/SyntaxTree/SyntaxTree.csproj
new file mode 100644
index 0000000..f3becf9
--- /dev/null
+++ b/HW2/SyntaxTree/SyntaxTree.csproj
@@ -0,0 +1,22 @@
+
+
+
+ Exe
+ net9.0
+ enable
+ enable
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
+
diff --git a/HW2/SyntaxTree/stylecop.json b/HW2/SyntaxTree/stylecop.json
new file mode 100644
index 0000000..76c8e76
--- /dev/null
+++ b/HW2/SyntaxTree/stylecop.json
@@ -0,0 +1,9 @@
+{
+ "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json",
+ "settings": {
+ "documentationRules": {
+ "companyName": "khusainovilas",
+ "copyrightText": "Copyright (c) {companyName}. All rights reserved."
+ }
+ }
+}
\ No newline at end of file