summaryrefslogtreecommitdiff
path: root/compiler/Methods/Expression.cs
blob: 6114cfc93bd5c8f9e4ce0d2dd9c85bb13416e899 (plain)
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
using AtLangCompiler.ILEmitter;
using System.Reflection.Emit;

namespace AtLangCompiler.Methods;

internal class BinaryExpression : ASTNode
{
    public ASTNode Left { get; }
    public string Op { get; }
    public ASTNode Right { get; }
    public BinaryExpression(ASTNode left, string op, ASTNode right)
    {
        Left = left; Op = op; Right = right;
    }
}

internal class VarReference : ASTNode
{
    public string Name { get; }
    public VarReference(string name) { Name = name; }
}

internal class StringLiteral : ASTNode
{
    public string Value { get; }
    public StringLiteral(string value) { Value = value; }
}

[EmitterFor(typeof(VarReference))]
[EmitterFor(typeof(StringLiteral))]
[EmitterFor(typeof(BinaryExpression))]
internal class Expression : IMethodEmitter<ASTNode>
{
    private readonly ILGenerator il;
    private readonly LocalBuilder dictLocal;
    private readonly ILMethodEmitterManager emitter;

    public Expression(ILGenerator il, LocalBuilder dictLocal, ILMethodEmitterManager emitter)
    {
        this.il = il ?? throw new ArgumentNullException(nameof(il));
        this.dictLocal = dictLocal ?? throw new ArgumentNullException(nameof(dictLocal));
        this.emitter = emitter ?? throw new ArgumentNullException(nameof(emitter));
    }

    public void EmitIL(ASTNode expr)
    {
        // Evaluate VarReference, StringLiteral, or BinaryExpression
        if (expr is VarReference vr)
        {
            // dict[varName]
            il.Emit(OpCodes.Ldloc, dictLocal);
            il.Emit(OpCodes.Ldstr, vr.Name);

            System.Reflection.MethodInfo dictGetItem = typeof(Dictionary<string, object>)
                .GetProperty("Item")!
                .GetGetMethod()!;
            il.Emit(OpCodes.Callvirt, dictGetItem);
            il.Emit(OpCodes.Isinst, typeof(string));

            // if null => ""
            Label labelNotNull = il.DefineLabel();
            Label labelEnd = il.DefineLabel();
            il.Emit(OpCodes.Dup);
            il.Emit(OpCodes.Brtrue_S, labelNotNull);
            il.Emit(OpCodes.Pop);
            il.Emit(OpCodes.Ldstr, string.Empty);
            il.MarkLabel(labelNotNull);
            il.MarkLabel(labelEnd);
        }
        else if (expr is StringLiteral sl)
        {
            il.Emit(OpCodes.Ldstr, sl.Value);
        }
        else if (expr is BinaryExpression be)
        {
            // left + right
            EmitIL(be.Left);
            EmitIL(be.Right);
            if (be.Op == "+")
            {
                System.Reflection.MethodInfo concat = typeof(string).GetMethod(nameof(string.Concat), [typeof(string), typeof(string)])!;
                il.Emit(OpCodes.Call, concat);
            }
            else
            {
                throw new Exception($"Unsupported operator: {be.Op}");
            }
        }
        else
        {
            throw new Exception($"Unknown expression type: {expr.GetType().Name}");
        }
    }
}