Skip to content

Commit 01f71d1

Browse files
committed
Implemented operator precedence.
1 parent 724abac commit 01f71d1

2 files changed

Lines changed: 47 additions & 13 deletions

File tree

SqlServerSimulator.Tests/SelectTests.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ public void SelectParameterValue(string commandText, string name, object value)
6161
[DataRow("1 & 3", 1)]
6262
[DataRow("1 | 3", 3)]
6363
[DataRow("1 ^ 3", 2)]
64+
[DataRow("1 + 2 * 3", 7)]
65+
[DataRow("3 * 2 + 1", 7)]
6466
public void Expression(string commandText, object value)
6567
{
6668
using var reader = new Simulation().ExecuteReader($"select {commandText}");

SqlServerSimulator/Parser/Expression.cs

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,13 @@ private protected Expression()
1717
/// </summary>
1818
public virtual string Name => string.Empty;
1919

20+
/// <summary>
21+
/// The relative precedence of an expression.
22+
/// When two are in scope, the higher one runs first, otherwise they run left-to-right.
23+
/// </summary>
24+
/// <remarks>Reference: https://learn.microsoft.com/en-us/sql/t-sql/language-elements/operator-precedence-transact-sql</remarks>
25+
public virtual byte Precedence => 0;
26+
2027
/// <summary>
2128
/// Converts the tokens from a command into a single expression.
2229
/// </summary>
@@ -120,7 +127,7 @@ public static Expression Parse(ParserContext context)
120127
}
121128
}
122129

123-
return expression;
130+
return expression is TwoSidedExpression twoSided ? twoSided.AdjustForPrecedence() : expression;
124131
}
125132
}
126133

@@ -188,6 +195,8 @@ private sealed class NamedExpression(Expression expression, string name) : Expre
188195

189196
public override string Name => this.name;
190197

198+
public override byte Precedence => expression.Precedence;
199+
191200
public override object? Run(Func<List<string>, object?> getColumnValue) => this.expression.Run(getColumnValue);
192201

193202
#if DEBUG
@@ -237,9 +246,18 @@ public Value(DoubleAtPrefixedString doubleAtPrefixedString)
237246
#endif
238247
}
239248

240-
public abstract class TwoSidedExpression(Expression left, Expression right) : Expression
249+
private abstract class TwoSidedExpression(Expression left, Expression right) : Expression
241250
{
242-
private readonly Expression left = left, right = right;
251+
private Expression right = right, left = left;
252+
253+
public TwoSidedExpression AdjustForPrecedence()
254+
{
255+
if (this.right is not TwoSidedExpression rightTwo || rightTwo.Precedence < this.Precedence)
256+
return this;
257+
258+
(rightTwo.left, this.right) = (this, rightTwo.left);
259+
return rightTwo;
260+
}
243261

244262
public sealed override object? Run(Func<List<string>, object?> getColumnValue)
245263
=> Run(left.Run(getColumnValue), right.Run(getColumnValue));
@@ -253,70 +271,84 @@ public abstract class TwoSidedExpression(Expression left, Expression right) : Ex
253271
#endif
254272
}
255273

256-
public sealed class Add(Expression left, Expression right) : TwoSidedExpression(left, right)
274+
private sealed class Add(Expression left, Expression right) : TwoSidedExpression(left, right)
257275
{
276+
public override byte Precedence => 3;
277+
258278
protected override object? Run(object? left, object? right) => (int)left! + (int)right!;
259279

260280
#if DEBUG
261281
protected override char Operator => '+';
262282
#endif
263283
}
264284

265-
public sealed class Subtract(Expression left, Expression right) : TwoSidedExpression(left, right)
285+
private sealed class Subtract(Expression left, Expression right) : TwoSidedExpression(left, right)
266286
{
287+
public override byte Precedence => 3;
288+
267289
protected override object? Run(object? left, object? right) => (int)left! - (int)right!;
268290

269291
#if DEBUG
270292
protected override char Operator => '-';
271293
#endif
272294
}
273295

274-
public sealed class Multiply(Expression left, Expression right) : TwoSidedExpression(left, right)
296+
private sealed class Multiply(Expression left, Expression right) : TwoSidedExpression(left, right)
275297
{
298+
public override byte Precedence => 2;
299+
276300
protected override object? Run(object? left, object? right) => (int)left! * (int)right!;
277301

278302
#if DEBUG
279303
protected override char Operator => '*';
280304
#endif
281305
}
282306

283-
public sealed class Divide(Expression left, Expression right) : TwoSidedExpression(left, right)
307+
private sealed class Divide(Expression left, Expression right) : TwoSidedExpression(left, right)
284308
{
309+
public override byte Precedence => 2;
310+
285311
protected override object? Run(object? left, object? right) => (int)left! / (int)right!;
286312

287313
#if DEBUG
288314
protected override char Operator => '/';
289315
#endif
290316
}
291317

292-
public sealed class BitwiseAnd(Expression left, Expression right) : TwoSidedExpression(left, right)
318+
private sealed class BitwiseAnd(Expression left, Expression right) : TwoSidedExpression(left, right)
293319
{
320+
public override byte Precedence => 3;
321+
294322
protected override object? Run(object? left, object? right) => (int)left! & (int)right!;
295323

296324
#if DEBUG
297325
protected override char Operator => '&';
298326
#endif
299327
}
300328

301-
public sealed class BitwiseOr(Expression left, Expression right) : TwoSidedExpression(left, right)
329+
private sealed class BitwiseOr(Expression left, Expression right) : TwoSidedExpression(left, right)
302330
{
331+
public override byte Precedence => 3;
332+
303333
protected override object? Run(object? left, object? right) => (int)left! | (int)right!;
304334

305335
#if DEBUG
306336
protected override char Operator => '|';
307337
#endif
308338
}
309339

310-
public sealed class BitwiseExclusiveOr(Expression left, Expression right) : TwoSidedExpression(left, right)
340+
private sealed class BitwiseExclusiveOr(Expression left, Expression right) : TwoSidedExpression(left, right)
311341
{
342+
public override byte Precedence => 3;
343+
312344
protected override object? Run(object? left, object? right) => (int)left! ^ (int)right!;
313345

314346
#if DEBUG
315347
protected override char Operator => '^';
316348
#endif
317349
}
318350

319-
public sealed class Reference(Name name) : Expression
351+
private sealed class Reference(Name name) : Expression
320352
{
321353
private readonly List<string> name = [name.Value];
322354

@@ -334,7 +366,7 @@ public sealed class Reference(Name name) : Expression
334366
/// <summary>
335367
/// Encapsulates the SQL DATALENGTH command: https://learn.microsoft.com/en-us/sql/t-sql/functions/datalength-transact-sql
336368
/// </summary>
337-
public sealed class DataLength(ParserContext context) : Expression
369+
private sealed class DataLength(ParserContext context) : Expression
338370
{
339371
private readonly Expression source = Parse(context);
340372

@@ -353,7 +385,7 @@ public sealed class DataLength(ParserContext context) : Expression
353385
/// <summary>
354386
/// Encapsulates the SQL ABS command: https://learn.microsoft.com/en-us/sql/t-sql/functions/abs-transact-sql
355387
/// </summary>
356-
public sealed class AbsoluteValue(ParserContext context) : Expression
388+
private sealed class AbsoluteValue(ParserContext context) : Expression
357389
{
358390
private readonly Expression source = Parse(context);
359391

0 commit comments

Comments
 (0)