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);
}
}
|