summaryrefslogtreecommitdiff
path: root/compiler/Methods/EnvVar.cs
blob: cc1a1eaddba04ce708aee42341ffc7fa348e71dc (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
using AtLangCompiler.ILEmitter;
using System.Reflection;
using System.Reflection.Emit;

namespace AtLangCompiler.Methods;

internal class GetEnvToken : ILexerTokenConfig
{
    public IReadOnlyDictionary<string, TokenType> TokenStrings => new Dictionary<string, TokenType>
    {
        { "@getEnv", TokenType.GETENV }
    };
}

internal class EnvVarAssignment : ASTNode
{
    public string VarName { get; }
    public string? EnvVarName { get; }
    public string? StrValue { get; }

    public EnvVarAssignment(string varName, string? envVarName, string? strValue)
    {
        VarName = varName;
        EnvVarName = envVarName;
        StrValue = strValue;
    }
}

[ParserFor(TokenType.GETENV, inputTokens: 1)]
internal class EnvironmentStatementParser : IStatementParser
{
    public ASTNode ParseStatement(Parser parser)
    {
        parser.Eat(TokenType.GETENV);
        parser.Eat(TokenType.LPAREN);
        parser.Eat(TokenType.AT);
        string envName = parser.current.Text;
        parser.Eat(TokenType.IDENT);
        parser.Eat(TokenType.RPAREN);
        return new EnvVarAssignment(parser.varName, envName, null);
    }
}

[EmitterFor(typeof(EnvVarAssignment))]
internal class EnvVar : IMethodEmitter<EnvVarAssignment>
{
    private readonly ILGenerator il;
    private readonly LocalBuilder dictLocal;

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

    public void EmitIL(EnvVarAssignment node)
    {
        // dict[varName] = ...
        il.Emit(OpCodes.Ldloc, dictLocal);
        il.Emit(OpCodes.Ldstr, node.VarName);

        if (node.EnvVarName != null)
        {
            // Environment.GetEnvironmentVariable(...) ?? ""
            System.Reflection.MethodInfo getEnvMI = typeof(Environment).GetMethod(nameof(Environment.GetEnvironmentVariable), [typeof(string)])!;
            il.Emit(OpCodes.Ldstr, node.EnvVarName);
            il.Emit(OpCodes.Call, getEnvMI);

            // if null => ""
            il.Emit(OpCodes.Dup);
            Label labelNotNull = il.DefineLabel();
            Label labelEnd = il.DefineLabel();
            il.Emit(OpCodes.Brtrue_S, labelNotNull);
            il.Emit(OpCodes.Pop);
            il.Emit(OpCodes.Ldstr, string.Empty);
            il.MarkLabel(labelNotNull);
            il.MarkLabel(labelEnd);
        }
        else
        {
            il.Emit(OpCodes.Ldstr, node.StrValue ?? string.Empty);
        }

        MethodInfo dictSetItem = typeof(Dictionary<string, object>)
            .GetProperty("Item")!
            .GetSetMethod()!;
        il.Emit(OpCodes.Isinst, typeof(string));
        il.Emit(OpCodes.Callvirt, dictSetItem);
    }
}