-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathOperatorSet.cs
More file actions
186 lines (155 loc) · 8.04 KB
/
Copy pathOperatorSet.cs
File metadata and controls
186 lines (155 loc) · 8.04 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
using PolishForm.Expressions;
using System;
using System.Collections;
using System.Collections.Generic;
namespace PolishForm
{
public class OperatorSet : ICollection<Operator>
{
private readonly Dictionary<char, Operator> operators;
public char[] Symbols => [.. operators.Keys];
static internal UnaryOperator NegationOperator;
public OperatorSet()
{
operators = new();
}
public OperatorSet(int capacity)
{
operators = new(capacity);
}
public OperatorSet(ICollection<Operator> operators)
{
this.operators = new(operators.Count);
foreach (Operator op in operators)
{
Add(op);
}
}
public void Add(Operator op) => operators.Add(op.symbol, op);
public bool TryAdd(Operator op) => operators.TryAdd(op.symbol, op);
public bool IsOperator(char symbol) => operators.ContainsKey(symbol);
public bool TryGetOperator(char symbol, out Operator? op) => operators.TryGetValue(symbol, out op);
/// <summary>
/// Compares the precedence of two operators represented by their symbols and returns an indication of their
/// relative precedence.
/// </summary>
/// <param name="symbol1">The first operator symbol to compare. Must be a valid operator in the current set.</param>
/// <param name="symbol2">The second operator symbol to compare. Must be a valid operator in the current set.</param>
/// <returns><b>Less than zero</b> - first operator has lower precedence.
/// <b>Zero</b> - equal precedence.
/// <b>Greater than zero</b> - first operator has higher precedence.</returns>
/// <exception cref="ArgumentException">Thrown if either <paramref name="symbol1"/> or <paramref name="symbol2"/> is not a valid operator in the current set.</exception>
public int ComparePrecedence(char symbol1, char symbol2)
{
if (!operators.TryGetValue(symbol1, out Operator? op1))
throw new ArgumentException($"Symbol '{symbol1}' is not an operator in this set.", nameof(symbol1));
if (!operators.TryGetValue(symbol2, out Operator? op2))
throw new ArgumentException($"Symbol '{symbol2}' is not an operator in this set.", nameof(symbol2));
return op1.CompareTo(op2);
}
public bool TryGetAssociativity(char symbol, out Operator.Associativity? associativity)
{
if (operators.TryGetValue(symbol, out Operator? op))
{
associativity = op.associativity;
return true;
}
associativity = null;
return false;
}
public static OperatorSet Default => CreateDefault();
public int Count => operators.Count;
public bool IsReadOnly => throw new NotImplementedException();
static OperatorSet CreateDefault()
{
OperatorSet set = new();
// Equality
BinaryOperator eq = new BinaryOperator('=', 0, Operator.Associativity.Right, true);
eq.AddImplementation((Operand left, Operand right) => new LogicLiteral(left.Equals(right)));
set.Add(eq);
// Logical OR
BinaryOperator or = new BinaryOperator('|', 1, Operator.Associativity.Left, true);
or.AddImplementation((LogicLiteral left, LogicLiteral right) => new LogicLiteral(left.Value || right.Value));
set.Add(or);
// Logical AND
BinaryOperator and = new BinaryOperator('&', 2, Operator.Associativity.Left, true);
and.AddImplementation((LogicLiteral left, LogicLiteral right) => new LogicLiteral(left.Value && right.Value));
set.Add(and);
// Comparison < and >
BinaryOperator lt = new BinaryOperator('<', 3, Operator.Associativity.Left, false);
lt.AddImplementation((NumberLiteral left, NumberLiteral right) => new LogicLiteral(left.Value < right.Value));
set.Add(lt);
BinaryOperator gt = new BinaryOperator('>', 3, Operator.Associativity.Left, false);
gt.AddImplementation((NumberLiteral left, NumberLiteral right) => new LogicLiteral(left.Value > right.Value));
set.Add(gt);
// Arithmetic + - * / %
BinaryOperator plus = new BinaryOperator('+', 4, Operator.Associativity.Left, true);
plus.AddImplementation<Operand,Operand,IOperand>((left, right) =>
{
if (left is NumberLiteral ln && ln.Value == 0)
return right;
if (right is NumberLiteral rn && rn.Value == 0)
return left;
if (left is NumberLiteral l && right is NumberLiteral r)
return new NumberLiteral(l.Value + r.Value);
else
return new BinaryExpression(plus, left, right);
});
set.Add(plus);
BinaryOperator minus = new BinaryOperator('-', 4, Operator.Associativity.Left, false);
minus.AddImplementation<Operand, Operand, IOperand>((left, right) =>
{
if (left.Equals(right))
return new NumberLiteral(0);
if (left is NumberLiteral ln && ln.Value == 0)
return new UnaryExpression(OperatorSet.NegationOperator, right).Evaluate();
if (right is NumberLiteral rn && rn.Value == 0)
return left;
if (left is NumberLiteral l && right is NumberLiteral r)
return new NumberLiteral(l.Value - r.Value);
else
return new BinaryExpression(minus, left, right);
});
set.Add(minus);
BinaryOperator times = new BinaryOperator('*', 5, Operator.Associativity.Left, true);
times.AddImplementation((NumberLiteral left, NumberLiteral right) => new NumberLiteral(left.Value * right.Value));
set.Add(times);
BinaryOperator div = new BinaryOperator('/', 5, Operator.Associativity.Left, false);
div.AddImplementation((NumberLiteral left, NumberLiteral right) =>
{
if (right.Value == 0) throw new DivideByZeroException("Division by zero is not allowed.");
return new NumberLiteral(left.Value / right.Value);
});
set.Add(div);
BinaryOperator mod = new BinaryOperator('%', 5, Operator.Associativity.Left, false);
mod.AddImplementation((NumberLiteral left, NumberLiteral right) =>
{
if (right.Value == 0) throw new DivideByZeroException("Modulo by zero is not allowed.");
return new NumberLiteral(left.Value % right.Value);
});
set.Add(mod);
// Exponentiation
BinaryOperator pow = new BinaryOperator('^', 7, Operator.Associativity.Right, false);
pow.AddImplementation((NumberLiteral left, NumberLiteral right) => new NumberLiteral((int)Math.Pow(left.Value, right.Value)));
set.Add(pow);
// Unary operators
UnaryOperator u = new UnaryOperator('~', 6, Operator.Associativity.Right);
u.AddImplementation((NumberLiteral op) => new NumberLiteral(-op.Value));
set.Add(u);
NegationOperator ??= u;
u = new UnaryOperator('!', 8, Operator.Associativity.Right);
u.AddImplementation((LogicLiteral op) => new LogicLiteral(!op.Value));
set.Add(u);
return set;
}
public void Clear() => operators.Clear();
public bool Contains(Operator item) => operators.ContainsValue(item);
public void CopyTo(Operator[] array, int arrayIndex)
{
throw new NotImplementedException();
}
public bool Remove(Operator item) => operators.Remove(item.symbol);
public IEnumerator<Operator> GetEnumerator() => operators.Values.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
}