Skip to content

Commit

Permalink
Switch statement
Browse files Browse the repository at this point in the history
A lot of noise comes from fact that I distiguish between constant literals and constant expressions. We incorrectly name ConstantExpresion the expression which works with constants, but that's more broad concept.
In the spec names are `constant` and `constant-expression` and because we have suffix Expression to all expression we have a clash.

Closes #74 and superseeds #139
  • Loading branch information
kant2002 committed Oct 13, 2022
1 parent 83bda75 commit ca8d640
Show file tree
Hide file tree
Showing 88 changed files with 1,069 additions and 243 deletions.
5 changes: 4 additions & 1 deletion Cesium.Ast/Expressions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public abstract record Expression;

// 6.5.1 Primary expressions
public record IdentifierExpression(string Identifier) : Expression;
public record ConstantExpression(IToken<CTokenType> Constant) : Expression;
public record ConstantLiteralExpression(IToken<CTokenType> Constant) : Expression;

// 6.5.2 Postfix operators
public record SubscriptingExpression(Expression Base, Expression Index) : Expression;
Expand Down Expand Up @@ -42,3 +42,6 @@ public record AssignmentExpression(Expression Left, string Operator, Expression

// 6.5.17 Comma operator
public record CommaExpression(Expression Left, Expression Right) : Expression;

// 6.6 Constant expressions
public record ConstantExpression(Expression Expression) : Expression;
4 changes: 4 additions & 0 deletions Cesium.Ast/Statements.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ public abstract record Statement : IBlockItem;
// 6.8.1 Labeled statement
public record LabelStatement(string Identifier, Statement Body) : Statement;

public record CaseStatement(ConstantExpression? Constant, Statement Body) : Statement;

// 6.8.2 Compound statement
public record CompoundStatement(ImmutableArray<IBlockItem> Block) : Statement;

Expand All @@ -27,6 +29,8 @@ public record AmbiguousBlockItem(string Item1, string Item2) : IBlockItem;
// 6.8.4 Selection statements
public record IfElseStatement(Expression Expression, Statement TrueBranch, Statement? FalseBranch) : Statement;

public record SwitchStatement(Expression Expression, Statement Body) : Statement;

// 6.8.5 Iteration statements
public record WhileStatement(
Expression TestExpression,
Expand Down
63 changes: 63 additions & 0 deletions Cesium.CodeGen.Tests/CodeGenSwitchTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using JetBrains.Annotations;

namespace Cesium.CodeGen.Tests;

public class CodeGenSwitchTests : CodeGenTestBase
{
[MustUseReturnValue]
private static Task DoTest(string source)
{
var assembly = GenerateAssembly(default, source);

var moduleType = assembly.Modules.Single().GetType("<Module>");
return VerifyMethods(moduleType);
}

[Fact]
public Task Empty() => DoTest(@"int main()
{
int x = 0;
switch(x) { };
return 1;
}");

[Fact]
public Task OneCase() => DoTest(@"int main()
{
int x = 0;
switch(x) { case 0: break; };
return 1;
}");

[Fact]
public Task MultiCases() => DoTest(@"int main()
{
int x = 0;
switch(x) {
case 0: break;
case 1: break;
};
}");

[Fact]
public Task MultiCasesWithDefault() => DoTest(@"int main()
{
int x = 0;
switch(x) {
case 0: break;
case 1: break;
default: break;
}
}");

[Fact]
public Task FallthroughCase() => DoTest(@"int main()
{
int x = 0;
switch(x) {
case 0: break;
case 1:
default: break;
}
}");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
System.Int32 <Module>::main()
Locals:
System.Int32 V_0
System.Int32 V_1
IL_0000: ldc.i4.0
IL_0001: stloc.0
IL_0002: ldloc.0
IL_0003: stloc V_1
IL_0007: br IL_0000
IL_000c: nop
IL_000d: ldc.i4.1
IL_000e: ret

System.Int32 <Module>::<SyntheticEntrypoint>()
Locals:
System.Int32 V_0
IL_0000: call System.Int32 <Module>::main()
IL_0005: stloc.s V_0
IL_0007: ldloc.s V_0
IL_0009: call System.Void Cesium.Runtime.RuntimeHelpers::Exit(System.Int32)
IL_000e: ldloc.s V_0
IL_0010: ret
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
System.Int32 <Module>::main()
Locals:
System.Int32 V_0
System.Int32 V_1
IL_0000: ldc.i4.0
IL_0001: stloc.0
IL_0002: ldloc.0
IL_0003: stloc V_1
IL_0007: ldloc V_1
IL_000b: ldc.i4.0
IL_000c: ceq
IL_000e: brtrue IL_0024
IL_0013: ldloc V_1
IL_0017: ldc.i4.1
IL_0018: ceq
IL_001a: brtrue IL_002a
IL_001f: br IL_002b
IL_0024: nop
IL_0025: br IL_0031
IL_002a: nop
IL_002b: nop
IL_002c: br IL_0031
IL_0031: nop
IL_0032: ldc.i4.0
IL_0033: ret

System.Int32 <Module>::<SyntheticEntrypoint>()
Locals:
System.Int32 V_0
IL_0000: call System.Int32 <Module>::main()
IL_0005: stloc.s V_0
IL_0007: ldloc.s V_0
IL_0009: call System.Void Cesium.Runtime.RuntimeHelpers::Exit(System.Int32)
IL_000e: ldloc.s V_0
IL_0010: ret
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
System.Int32 <Module>::main()
Locals:
System.Int32 V_0
System.Int32 V_1
IL_0000: ldc.i4.0
IL_0001: stloc.0
IL_0002: ldloc.0
IL_0003: stloc V_1
IL_0007: ldloc V_1
IL_000b: ldc.i4.0
IL_000c: ceq
IL_000e: brtrue IL_0024
IL_0013: ldloc V_1
IL_0017: ldc.i4.1
IL_0018: ceq
IL_001a: brtrue IL_002a
IL_001f: br IL_0000
IL_0024: nop
IL_0025: br IL_0030
IL_002a: nop
IL_002b: br IL_0030
IL_0030: nop
IL_0031: ldc.i4.0
IL_0032: ret

System.Int32 <Module>::<SyntheticEntrypoint>()
Locals:
System.Int32 V_0
IL_0000: call System.Int32 <Module>::main()
IL_0005: stloc.s V_0
IL_0007: ldloc.s V_0
IL_0009: call System.Void Cesium.Runtime.RuntimeHelpers::Exit(System.Int32)
IL_000e: ldloc.s V_0
IL_0010: ret
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
System.Int32 <Module>::main()
Locals:
System.Int32 V_0
System.Int32 V_1
IL_0000: ldc.i4.0
IL_0001: stloc.0
IL_0002: ldloc.0
IL_0003: stloc V_1
IL_0007: ldloc V_1
IL_000b: ldc.i4.0
IL_000c: ceq
IL_000e: brtrue IL_0024
IL_0013: ldloc V_1
IL_0017: ldc.i4.1
IL_0018: ceq
IL_001a: brtrue IL_002a
IL_001f: br IL_0030
IL_0024: nop
IL_0025: br IL_0036
IL_002a: nop
IL_002b: br IL_0036
IL_0030: nop
IL_0031: br IL_0036
IL_0036: nop
IL_0037: ldc.i4.0
IL_0038: ret

System.Int32 <Module>::<SyntheticEntrypoint>()
Locals:
System.Int32 V_0
IL_0000: call System.Int32 <Module>::main()
IL_0005: stloc.s V_0
IL_0007: ldloc.s V_0
IL_0009: call System.Void Cesium.Runtime.RuntimeHelpers::Exit(System.Int32)
IL_000e: ldloc.s V_0
IL_0010: ret
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
System.Int32 <Module>::main()
Locals:
System.Int32 V_0
System.Int32 V_1
IL_0000: ldc.i4.0
IL_0001: stloc.0
IL_0002: ldloc.0
IL_0003: stloc V_1
IL_0007: ldloc V_1
IL_000b: ldc.i4.0
IL_000c: ceq
IL_000e: brtrue IL_0018
IL_0013: br IL_0000
IL_0018: nop
IL_0019: br IL_001e
IL_001e: nop
IL_001f: ldc.i4.1
IL_0020: ret

System.Int32 <Module>::<SyntheticEntrypoint>()
Locals:
System.Int32 V_0
IL_0000: call System.Int32 <Module>::main()
IL_0005: stloc.s V_0
IL_0007: ldloc.s V_0
IL_0009: call System.Void Cesium.Runtime.RuntimeHelpers::Exit(System.Int32)
IL_000e: ldloc.s V_0
IL_0010: ret
31 changes: 31 additions & 0 deletions Cesium.CodeGen/ConstantEvaluator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using Cesium.CodeGen.Ir.Expressions;
using Cesium.CodeGen.Ir.Expressions.Constants;
using Cesium.Core;

namespace Cesium.CodeGen;

internal class ConstantEvaluator
{
private readonly IExpression _expression;

public ConstantEvaluator(IExpression expression)
{
_expression = expression;
}

public IConstant GetConstantValue()
{
var expression = _expression;
if (expression is ConstantExpression constantExpression)
{
expression = constantExpression.Expression;
}

if (expression is not ConstantLiteralExpression literalExpression)
{
throw new AssertException($"Expression {expression} cannot be evaluated as constant expression.");
}

return literalExpression.Constant;
}
}
55 changes: 55 additions & 0 deletions Cesium.CodeGen/Contexts/SwitchScope.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using Cesium.CodeGen.Contexts.Meta;
using Cesium.CodeGen.Ir;
using Cesium.CodeGen.Ir.Types;
using Cesium.Core;
using Mono.Cecil;
using Mono.Cecil.Cil;

namespace Cesium.CodeGen.Contexts;

internal record SwitchScope(IEmitScope Parent) : IEmitScope, IDeclarationScope
{
public AssemblyContext AssemblyContext => Parent.AssemblyContext;
public ModuleDefinition Module => Parent.Module;
public CTypeSystem CTypeSystem => Parent.CTypeSystem;
public FunctionInfo? GetFunctionInfo(string identifier)
=> ((IDeclarationScope)Parent).GetFunctionInfo(identifier);
public TranslationUnitContext Context => Parent.Context;
public MethodDefinition Method => Parent.Method;

public IType? GetVariable(string identifier)
{
return ((IDeclarationScope)Parent).GetVariable(identifier);
}
public IReadOnlyDictionary<string, IType> GlobalFields => ((IDeclarationScope)Parent).GlobalFields;
public void AddVariable(string identifier, IType variable) =>
throw new WipException(205, "Variable addition into a switch scope is not implemented, yet.");
public VariableDefinition ResolveVariable(string identifier) => Parent.ResolveVariable(identifier); // no declarations for `for` now, so pass parent variables

public ParameterDefinition ResolveParameter(string name) => Parent.ResolveParameter(name);
public ParameterInfo? GetParameterInfo(string name) => ((IDeclarationScope)Parent).GetParameterInfo(name);

/// <inheritdoc />
public IType ResolveType(IType type) => Context.ResolveType(type);
public void AddTypeDefinition(string identifier, IType type) => throw new AssertException("Not supported");

/// <inheritdoc />
public void AddLabel(string identifier)
{
((IDeclarationScope)Parent).AddLabel(identifier);
}

/// <inheritdoc />
public Instruction ResolveLabel(string label)
{
return Parent.ResolveLabel(label);
}

private string _breakLabel = $"switch_{Guid.NewGuid()}";

/// <inheritdoc />
public string GetBreakLabel()
{
return _breakLabel;
}
}
2 changes: 2 additions & 0 deletions Cesium.CodeGen/Extensions/BlockItemEx.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ internal static class BlockItemEx
Ast.ForStatement s => new ForStatement(s),
Ast.WhileStatement s => new WhileStatement(s),
Ast.DoWhileStatement s => new DoWhileStatement(s),
Ast.SwitchStatement s => new SwitchStatement(s),
Ast.CaseStatement s => new CaseStatement(s),
Ast.BreakStatement => new BreakStatement(),
Ast.GoToStatement s => new GoToStatement(s),
Ast.AmbiguousBlockItem a => new AmbiguousBlockItem(a),
Expand Down
3 changes: 2 additions & 1 deletion Cesium.CodeGen/Extensions/ExpressionEx.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ internal static class ExpressionEx
public static IExpression ToIntermediate(this Ast.Expression ex) => ex switch
{
Ast.IdentifierExpression e => new IdentifierExpression(e),
Ast.ConstantExpression { Constant.Kind: CTokenType.Identifier } e => new IdentifierExpression(e),
Ast.ConstantLiteralExpression { Constant.Kind: CTokenType.Identifier } e => new IdentifierExpression(e),
Ast.ConstantLiteralExpression e => new ConstantLiteralExpression(e),
Ast.ConstantExpression e => new ConstantExpression(e),

Ast.FunctionCallExpression e => new FunctionCallExpression(e),
Expand Down
4 changes: 2 additions & 2 deletions Cesium.CodeGen/Ir/BlockItems/AmbiguousBlockItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ private IBlockItem CreateFuctionCallStatement(IDeclarationScope scope)
var argumentToken = CreateFakeToken(_item2);

var functionCallExpression = new Expressions.FunctionCallExpression(new FunctionCallExpression(
new ConstantExpression(functionNameToken),
ImmutableArray.Create<Expression>(new ConstantExpression(argumentToken))
new ConstantLiteralExpression(functionNameToken),
ImmutableArray.Create<Expression>(new ConstantLiteralExpression(argumentToken))
));
var realNode = new ExpressionStatement(functionCallExpression);
return realNode.Lower(scope);
Expand Down
Loading

0 comments on commit ca8d640

Please sign in to comment.