1 module vayne.compiler;
2 
3 
4 import std.algorithm;
5 import std.array;
6 import std.file;
7 import std.path;
8 import std.stdio;
9 import std.string;
10 
11 import vayne.ast.node;
12 import vayne.ast.printer;
13 import vayne.code.emitter;
14 import vayne.op;
15 import vayne.serializer;
16 import vayne.source.compress;
17 import vayne.source.parser;
18 import vayne.source.preparser;
19 import vayne.source.source;
20 
21 
22 struct CompilerOptions {
23 	auto preparsePrint = false;
24 	auto astPrint = false;
25 	auto instrPrint = false;
26 	auto byteCodePrint = false;
27 	auto constPrint = false;
28 	auto lineNumbers = true;
29 	auto compress = false;
30 	auto verboseIncludes = false;
31 
32 	string[] search;
33 	string ext = ".html";
34 }
35 
36 
37 enum ConstantType : ubyte {
38 	Null,
39 	Boolean,
40 	String,
41 	Integer,
42 	Float,
43 }
44 
45 
46 struct CompiledCode {
47 	uint registerCount;
48 
49 	const(Instr)[] instrs;
50 	const(SourceLoc)[] locs;
51 	const(string)[] sources;
52 	const(Constant)[] constants;
53 	const(string)[] dependencies;
54 
55 	static struct Constant {
56 		ConstantType type;
57 		string value;
58 	}
59 }
60 
61 
62 class CompileErrorsException : Exception {
63 	this(string[] errors) {
64 		assert(!errors.empty);
65 		super(errors[0]);
66 
67 		this.errors = errors;
68 	}
69 
70 	string[] errors;
71 }
72 
73 
74 CompiledCode compile(string fileName, CompilerOptions options) {
75 	try {
76 		SourceManagerOptions mgrOptions;
77 		mgrOptions.search ~= options.search;
78 		mgrOptions.extension = options.ext;
79 
80 		auto mgr = SourceManager(mgrOptions);
81 		auto src = mgr.open(fileName);
82 
83 		PreParserOptions preOptions;
84 		preOptions.lineNumbers = options.lineNumbers;
85 		preOptions.verboseIncludes = options.verboseIncludes;
86 		mgr.set(src.id, preparse(mgr, src.id, preOptions));
87 
88 		if (options.preparsePrint)
89 			writeln(mgr.get(src.id).buffer);
90 
91 		ParserOptions parserOptions;
92 		parserOptions.compress = options.compress ? CompressOptions.defaults : CompressOptions.none;
93 
94 		auto ast = parse(mgr, src.id, parserOptions);
95 		if (options.astPrint)
96 			ast.print.writeln;
97 
98 		Emitter emitter;
99 		emitter.emitModule(cast(Module)ast);
100 
101 		if (options.constPrint) {
102 			foreach (i, k; emitter.constants)
103 				writeln(format("%5d %-16s %s", i, k.type, k.value));
104 		}
105 
106 		if (options.instrPrint || options.byteCodePrint) {
107 			auto fmt = "%5d " ~ (options.byteCodePrint ? "%-58s" : "%-24s");
108 
109 			if (options.lineNumbers)
110 				fmt ~= " ; %s";
111 
112 			if (options.lineNumbers) {
113 				foreach (ip, instr; emitter.instrs)
114 					writeln(format(fmt, ip, options.byteCodePrint ? instr.toStringFull : instr.toString, mgr.loc(emitter.locs[ip])));
115 			} else {
116 				foreach (ip, instr; emitter.instrs)
117 					writeln(format(fmt, ip, options.byteCodePrint ? instr.toStringFull : instr.toString));
118 			}
119 		}
120 
121 		CompiledCode result;
122 		with (result) {
123 			registerCount = emitter.registerCount;
124 
125 			instrs = emitter.instrs;
126 			locs = emitter.locs;
127 			sources = mgr.sourceNames;
128 			dependencies = mgr.dependencies.map!(x => mgr.fileNames[x]).array;
129 
130 			constants.reserve(emitter.constants.length);
131 			foreach (i, k; emitter.constants) {
132 				final switch (k.type) with (Emitter.ConstantSlot.Type) {
133 				case Null:
134 					constants ~= CompiledCode.Constant(ConstantType.Null, k.value);
135 					break;
136 				case Boolean:
137 					constants ~= CompiledCode.Constant(ConstantType.Boolean, k.value);
138 					break;
139 				case Integer:
140 					constants ~= CompiledCode.Constant(ConstantType.Integer, k.value);
141 					break;
142 				case Float:
143 					constants ~= CompiledCode.Constant(ConstantType.Float, k.value);
144 					break;
145 				case String:
146 					constants ~= CompiledCode.Constant(ConstantType.String, k.value);
147 					break;
148 				}
149 			}
150 		}
151 
152 		return result;
153 	} catch (Exception error) {
154 		string[] errors;
155 
156 		if (auto parserErrors = cast(ParserErrorsException)error) {
157 			errors = parserErrors.errors;
158 		} else {
159 			errors ~= error.msg;
160 		}
161 
162 		throw new CompileErrorsException(errors);
163 	}
164 }