summaryrefslogtreecommitdiff
path: root/compiler/Compiler/Compiler.cs
blob: 4e540d6d339e26344b907cf07457aeb1df036649 (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
using AtLangCompiler.ILEmitter;
using System.Reflection;
using System.Reflection.Emit;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Reflection.PortableExecutable;

namespace AtLangCompiler;

public static class Compiler
{
    public static void CompileToIL(string source, string outputPath)
    {
        if (Directory.Exists(outputPath))
        {
            throw new ArgumentOutOfRangeException($"{nameof(outputPath)} should be a filename and not a directory.");
        }

        string? directory = Path.GetDirectoryName(outputPath);
        if (!Directory.Exists(directory))
        {
            throw new ArgumentOutOfRangeException($"The parent directory of {nameof(outputPath)} ({directory}) was not found.");
        }

        // Parse
        Lexer lexer = new Lexer(source);
        Parser parser = new Parser(lexer);
        IList<ASTNode> ast = parser.ParseProgram();

        string assemblyName = Path.GetFileNameWithoutExtension(outputPath);
        PersistedAssemblyBuilder ab = new PersistedAssemblyBuilder(
            new AssemblyName(assemblyName),
            typeof(object).Assembly
        );

        TypeBuilder tb = ab.DefineDynamicModule("MainModule")
            .DefineType("AtLangProgram", TypeAttributes.Public | TypeAttributes.Class);

        MethodBuilder entryPoint = tb.DefineMethod(
            "Main",
            MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.Static,
            typeof(int),
            null
        );

        ILGenerator il = entryPoint.GetILGenerator();
        LocalBuilder dictLocal = il.DeclareLocal(typeof(Dictionary<string, object>));
        ILMethodEmitterManager ilEmitter = new ILMethodEmitterManager(il, dictLocal);

        // We'll store variables in a Dictionary<string,object>
        il.Emit(OpCodes.Newobj, typeof(Dictionary<string, object>).GetConstructor(Type.EmptyTypes)!);
        il.Emit(OpCodes.Stloc, dictLocal);

        // Emit IL for each statement
        foreach (ASTNode node in ast)
        {
            ilEmitter.EmitStatement(node);
        }

        // Return 0 from Main (success)
        il.Emit(OpCodes.Ldc_I4_0);
        il.Emit(OpCodes.Ret);

        tb.CreateType();

        MetadataBuilder metadataBuilder = ab.GenerateMetadata(
            out BlobBuilder ilStream,
            out BlobBuilder fieldData);

        PEHeaderBuilder peHeaderBuilder = new PEHeaderBuilder(
            imageCharacteristics: Characteristics.ExecutableImage);

        ManagedPEBuilder peBuilder = new ManagedPEBuilder(
            header: peHeaderBuilder,
            metadataRootBuilder: new MetadataRootBuilder(metadataBuilder),
            ilStream: ilStream,
            mappedFieldData: fieldData,
            entryPoint: MetadataTokens.MethodDefinitionHandle(entryPoint.MetadataToken));

        BlobBuilder peBlob = new BlobBuilder();
        peBuilder.Serialize(peBlob);

        using FileStream fileStream = new FileStream(outputPath, FileMode.Create, FileAccess.Write);
        peBlob.WriteContentTo(fileStream);

        // Required until this is self-contained
        File.Copy("AtLangCompiler.runtimeconfig.json", $"{assemblyName}.runtimeconfig.json", true);
    }
}