Skip to content

Commit d38cbf5

Browse files
committed
Rewrote Tokeniser with polymorphism
1 parent 9e0ffcc commit d38cbf5

File tree

15 files changed

+106
-93
lines changed

15 files changed

+106
-93
lines changed

v2/BooleanExpressionParser/.vscode/launch.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@
99
"program": "${workspaceFolder}/bin/Debug/net6.0/BooleanExpressionParser.dll",
1010
"args": [
1111
"A.B",
12-
"A+B"
12+
"A+B",
13+
"!(A+B+C)",
14+
"T+F+F.F",
15+
"!A+B"
1316
],
1417
"cwd": "${workspaceFolder}",
1518
"console": "integratedTerminal", // enables input (instead of internalConsole)

v2/BooleanExpressionParser/Evaluator.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,13 @@ private bool Evaluate(Node node, bool[] values)
3636
else if (node is OperatorNode op)
3737
{
3838
var left = Evaluate(op.Left, values);
39+
40+
if (node is NotOperatorNode) return !left;
41+
3942
var right = Evaluate(op.Right!, values);
4043

4144
if (node is AndOperatorNode) return left && right;
4245
if (node is OrOperatorNode) return left || right;
43-
if (node is NotOperatorNode) return !left;
4446
}
4547
else
4648
{

v2/BooleanExpressionParser/Formatter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public static string FormatPrefixTokens(IEnumerable<Token> tokens)
5555

5656
foreach (var token in tokens)
5757
{
58-
sb.Append(token.Value);
58+
sb.Append(token.ToString());
5959
sb.Append(" ");
6060
}
6161

v2/BooleanExpressionParser/Parser.cs

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -13,35 +13,36 @@ public IEnumerable<Token> ParseTokens(IEnumerable<Token> tokens)
1313

1414
foreach (var token in tokens)
1515
{
16-
switch (token.Type)
16+
switch (token)
1717
{
18-
case Token.TokenType.IDENTIFIER:
18+
case VariableToken:
1919
output.Enqueue(token);
2020
break;
2121

22-
case Token.TokenType.BINARY_OPERATOR:
23-
case Token.TokenType.UNARY_OPERATOR:
24-
while ((stack.Count > 0 && stack.Peek().Type != Token.TokenType.OPEN_PAREN) /* && CHECK PRECEDENCE HERE */)
22+
case AndOperatorToken:
23+
case OrOperatorToken:
24+
case NotOperatorToken:
25+
while ((stack.Count > 0 && stack.Peek() is OperatorToken && stack.Peek() is not OpenParenToken) && ((stack.Peek() as OperatorToken)!.Precedence >= (token as OperatorToken)!.Precedence))
2526
{
2627
output.Enqueue(stack.Pop());
2728
}
2829
stack.Push(token);
2930
break;
3031

31-
case Token.TokenType.OPEN_PAREN:
32+
case OpenParenToken:
3233
stack.Push(token);
3334
break;
3435

35-
case Token.TokenType.CLOSE_PAREN:
36-
while (stack.Count > 0 && stack.Peek().Type != Token.TokenType.OPEN_PAREN)
36+
case CloseParenToken:
37+
while (stack.Count > 0 && stack.Peek() is not OpenParenToken)
3738
{
3839
output.Enqueue(stack.Pop());
3940
}
4041

41-
if (stack.Peek().Type == Token.TokenType.OPEN_PAREN)
42+
if (stack.Peek() is OpenParenToken)
4243
{
4344
stack.Pop();
44-
if (stack.Count > 0 && (stack.Peek().Type == Token.TokenType.UNARY_OPERATOR || stack.Peek().Type == Token.TokenType.BINARY_OPERATOR))
45+
if (stack.Count > 0 && (stack.Peek() is AndOperatorToken or OrOperatorToken or NotOperatorToken))
4546
{
4647
output.Enqueue(stack.Pop());
4748
}
@@ -52,7 +53,7 @@ public IEnumerable<Token> ParseTokens(IEnumerable<Token> tokens)
5253

5354
while (stack.Count > 0)
5455
{
55-
if (stack.Peek().Type == Token.TokenType.OPEN_PAREN)
56+
if (stack.Peek() is OpenParenToken)
5657
{
5758
throw new Exception("OPEN_PAREN on operator stack.");
5859
}
@@ -69,27 +70,26 @@ public Ast GrowAst(IEnumerable<Token> tokens)
6970

7071
foreach (var token in tokens)
7172
{
72-
switch (token.Type)
73+
switch (token)
7374
{
74-
case Token.TokenType.IDENTIFIER:
75-
stack.Push(new VariableNode(token.Value));
76-
if (!variables.Contains(token.Value)) variables.Add(token.Value);
75+
case VariableToken var:
76+
stack.Push(new VariableNode(var.Name));
77+
if (!variables.Contains(var.Name)) variables.Add(var.Name);
7778
break;
7879

79-
case Token.TokenType.BINARY_OPERATOR:
80-
if (stack.Count < 2) throw new Exception($"2 parameters needed for operator ${token.Type}");
80+
case AndOperatorToken:
81+
case OrOperatorToken:
82+
if (stack.Count < 2) throw new Exception($"2 parameters needed for operator ${token}");
8183

82-
if (token.Value == "AND")
84+
if (token is AndOperatorToken)
8385
stack.Push(new AndOperatorNode(stack.Pop(), stack.Pop()));
84-
else
86+
else if (token is OrOperatorToken)
8587
stack.Push(new OrOperatorNode(stack.Pop(), stack.Pop()));
8688
break;
8789

88-
case Token.TokenType.UNARY_OPERATOR:
89-
if (stack.Count < 1) throw new Exception($"1 parameter needed for operator ${token.Type}");
90-
91-
if (token.Value == "NOT")
92-
stack.Push(new NotOperatorNode(stack.Pop()));
90+
case NotOperatorToken:
91+
if (stack.Count < 1) throw new Exception($"1 parameter needed for operator ${token}");
92+
stack.Push(new NotOperatorNode(stack.Pop()));
9393
break;
9494
}
9595
}

v2/BooleanExpressionParser/Program.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ static void Main(params string[] args)
2020
var prefixTokens = parser.ParseTokens(infixTokens);
2121
var ast = parser.GrowAst(prefixTokens);
2222

23+
Console.WriteLine($"Expression: {expression}");
24+
Console.WriteLine($"Infix: {Formatter.FormatAstAsInfix(ast.Root)}");
25+
Console.WriteLine($"Prefix: {Formatter.FormatPrefixTokens(prefixTokens)}");
26+
2327
var evaluator = new Evaluator(ast);
2428
var table = evaluator.EvaluateAll();
2529

v2/BooleanExpressionParser/Token.cs

Lines changed: 0 additions & 62 deletions
This file was deleted.
Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1+
using System.Text.RegularExpressions;
2+
13
namespace BooleanExpressionParser;
24

35
class Tokeniser
46
{
7+
private readonly Regex regex = new Regex(@"(\(|\)|\w+|[.&+!¬])\s*");
58
private readonly string input;
69

710
public Tokeniser(string input)
@@ -11,11 +14,26 @@ public Tokeniser(string input)
1114

1215
public IEnumerable<Token> Tokenise()
1316
{
14-
var reader = new StringReader(input);
15-
while (reader.Peek() != -1)
17+
int i = 0;
18+
19+
while (i < input.Length)
1620
{
17-
var token = new Token(reader);
18-
yield return token;
21+
var match = regex.Match(input, i);
22+
if (!match.Success) throw new Exception("Invalid token (no match found) at position " + i);
23+
if (match.Index != i) throw new Exception("Invalid token (match found at wrong position) at position " + i);
24+
25+
string token = match.Groups[1].Value;
26+
i += match.Length;
27+
28+
yield return token switch
29+
{
30+
"(" => new OpenParenToken(),
31+
")" => new CloseParenToken(),
32+
"AND" or "." or "&" => new AndOperatorToken(),
33+
"OR" or "+" or "|" => new OrOperatorToken(),
34+
"NOT" or "!" or "¬" => new NotOperatorToken(),
35+
_ => new VariableToken(token)
36+
};
1937
}
2038
}
2139
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
class AndOperatorToken : OperatorToken
2+
{
3+
public AndOperatorToken() : base(2) { }
4+
5+
public override string ToString() => ".";
6+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
class CloseParenToken : Token
2+
{
3+
public override string ToString() => ")";
4+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
class NotOperatorToken : OperatorToken
2+
{
3+
public NotOperatorToken() : base(3) { }
4+
public override string ToString() => "!";
5+
6+
}

0 commit comments

Comments
 (0)