1 /** 
2  * Copyright: Enalye
3  * License: Zlib
4  * Authors: Enalye
5  */
6 module grimoire.compiler.compiler;
7 
8 import std.stdio, std..string, std.array, std.conv, std.math, std.file;
9 import grimoire.runtime, grimoire.assembly;
10 import grimoire.compiler.util, grimoire.compiler.lexer, grimoire.compiler.parser,
11 	grimoire.compiler.primitive, grimoire.compiler.type, grimoire.compiler.data,
12 	grimoire.compiler.error, grimoire.compiler.mangle,
13 	grimoire.compiler.library, grimoire.compiler.pretty;
14 
15 /// Compiler class, generate bytecode and hold errors.
16 final class GrCompiler {
17 	private {
18 		GrData _data;
19 		GrError _error;
20 	}
21 
22 	/// Ctor
23 	this() {
24 		_data = new GrData;
25 	}
26 
27 	/// Add types and primitives defined in the library
28 	void addLibrary(GrLibrary library) {
29 		_data.addLibrary(library);
30 	}
31 
32 	/** 
33 	 * Compile a source file into bytecode
34 	 * Params:
35 	 *	The bytecode struct passed by ref
36 	 *  fileName = Path to script file to compile
37 	 * Returns:
38 	 *  True if compilation was successful, otherwise check `getError()`
39 	 */
40 	GrBytecode compileFile(string fileName, int options = GrOption.none) {
41 		_error = null;
42 		try {
43 			GrLexer lexer = new GrLexer;
44 			lexer.scanFile(fileName);
45 
46 			GrParser parser = new GrParser;
47 			parser.parseScript(_data, lexer, options);
48 
49 			return generate(lexer, parser, options);
50 		}
51 		catch (GrLexerException e) {
52 			_error = e.error;
53 			return null;
54 		}
55 		catch (GrParserException e) {
56 			_error = e.error;
57 			return null;
58 		}
59 	}
60 
61 	/// If an error occurred, fetch it here.
62 	GrError getError() {
63 		return _error;
64 	}
65 
66 	private uint makeOpcode(uint instr, uint value) {
67 		return ((value << 8u) & 0xffffff00) | (instr & 0xff);
68 	}
69 
70 	private GrBytecode generate(GrLexer lexer, GrParser parser, int options) {
71 		uint nbOpcodes, lastOpcodeCount;
72 
73 		GrBytecode bytecode = new GrBytecode;
74 
75 		foreach (func; parser.functions)
76 			nbOpcodes += cast(uint) func.instructions.length;
77 
78 		foreach (func; parser.anonymousFunctions)
79 			nbOpcodes += cast(uint) func.instructions.length;
80 
81 		foreach (func; parser.events)
82 			nbOpcodes += cast(uint) func.instructions.length;
83 
84 		//We leave space for one kill instruction at the end.
85 		nbOpcodes++;
86 
87 		//Without "main", we put a kill instruction instead.
88 		if (parser.getFunction("main") is null)
89 			nbOpcodes++;
90 
91 		//Opcodes
92 		uint[] opcodes = new uint[nbOpcodes];
93 
94 		//Start with the global initializations
95 		auto globalScope = parser.getFunction("@global");
96 		if (globalScope) {
97 			if (options & GrOption.symbols) {
98 				auto debugSymbol = new GrFunctionSymbol;
99 				debugSymbol.start = lastOpcodeCount;
100 				debugSymbol.name = grGetPrettyFunction(globalScope);
101 				debugSymbol.length = cast(uint) globalScope.instructions.length;
102 				debugSymbol.file = lexer.getFile(globalScope.fileId);
103 				foreach(ref position; globalScope.debugSymbol) {
104 					GrFunctionSymbol.Position pos;
105 					pos.line = position.line;
106 					pos.column = position.column;
107 					debugSymbol.positions ~= pos;
108 				}
109 				bytecode.symbols ~= debugSymbol;
110 			}
111 
112 			foreach (size_t i, instruction; globalScope.instructions)
113 				opcodes[lastOpcodeCount + i] = makeOpcode(cast(uint) instruction.opcode,
114 						instruction.value);
115 			lastOpcodeCount += cast(uint) globalScope.instructions.length;
116 			parser.removeFunction("@global");
117 		}
118 
119 		//Then write the main function (not callable).
120 		auto mainFunc = parser.getFunction("main");
121 		if (mainFunc) {
122 			if (options & GrOption.symbols) {
123 				auto debugSymbol = new GrFunctionSymbol();
124 				debugSymbol.start = lastOpcodeCount;
125 				debugSymbol.name = grGetPrettyFunction(mainFunc);
126 				debugSymbol.length = cast(uint) mainFunc.instructions.length;
127 				debugSymbol.file = lexer.getFile(mainFunc.fileId);
128 				foreach(ref position; mainFunc.debugSymbol) {
129 					GrFunctionSymbol.Position pos;
130 					pos.line = position.line;
131 					pos.column = position.column;
132 					debugSymbol.positions ~= pos;
133 				}
134 				bytecode.symbols ~= debugSymbol;
135 			}
136 
137 			mainFunc.position = lastOpcodeCount;
138 			foreach (size_t i, instruction; mainFunc.instructions)
139 				opcodes[lastOpcodeCount + i] = makeOpcode(cast(uint) instruction.opcode,
140 						instruction.value);
141 			foreach (call; mainFunc.functionCalls)
142 				call.position += lastOpcodeCount;
143 			lastOpcodeCount += cast(uint) mainFunc.instructions.length;
144 			parser.removeFunction("main");
145 		}
146 		else {
147 			opcodes[lastOpcodeCount] = makeOpcode(cast(uint) GrOpcode.kill_, 0u);
148 			lastOpcodeCount++;
149 		}
150 
151 		//Every other functions.
152 		uint[string] events;
153 		foreach (GrFunction func; parser.events) {
154 			if (options & GrOption.symbols) {
155 				auto debugSymbol = new GrFunctionSymbol();
156 				debugSymbol.start = lastOpcodeCount;
157 				debugSymbol.name = grGetPrettyFunction(func);
158 				debugSymbol.length = cast(uint) func.instructions.length;
159 				debugSymbol.file = lexer.getFile(func.fileId);
160 				foreach(ref position; func.debugSymbol) {
161 					GrFunctionSymbol.Position pos;
162 					pos.line = position.line;
163 					pos.column = position.column;
164 					debugSymbol.positions ~= pos;
165 				}
166 				bytecode.symbols ~= debugSymbol;
167 			}
168 
169 			foreach (size_t i, instruction; func.instructions)
170 				opcodes[lastOpcodeCount + i] = makeOpcode(cast(uint) instruction.opcode,
171 						instruction.value);
172 			foreach (call; func.functionCalls)
173 				call.position += lastOpcodeCount;
174 			func.position = lastOpcodeCount;
175 			lastOpcodeCount += cast(uint) func.instructions.length;
176 			events[func.mangledName] = func.position;
177 		}
178 		foreach (func; parser.anonymousFunctions) {
179 			if (options & GrOption.symbols) {
180 				auto debugSymbol = new GrFunctionSymbol();
181 				debugSymbol.start = lastOpcodeCount;
182 				debugSymbol.name = grGetPrettyFunction(func);
183 				debugSymbol.length = cast(uint) func.instructions.length;
184 				debugSymbol.file = lexer.getFile(func.fileId);
185 				foreach(ref position; func.debugSymbol) {
186 					GrFunctionSymbol.Position pos;
187 					pos.line = position.line;
188 					pos.column = position.column;
189 					debugSymbol.positions ~= pos;
190 				}
191 				bytecode.symbols ~= debugSymbol;
192 			}
193 			foreach (size_t i, instruction; func.instructions)
194 				opcodes[lastOpcodeCount + i] = makeOpcode(cast(uint) instruction.opcode,
195 						instruction.value);
196 			foreach (call; func.functionCalls)
197 				call.position += lastOpcodeCount;
198 			func.position = lastOpcodeCount;
199 			lastOpcodeCount += func.instructions.length;
200 		}
201 		foreach (func; parser.functions) {
202 			if (options & GrOption.symbols) {
203 				auto debugSymbol = new GrFunctionSymbol();
204 				debugSymbol.start = lastOpcodeCount;
205 				debugSymbol.name = grGetPrettyFunction(func);
206 				debugSymbol.length = cast(uint) func.instructions.length;
207 				debugSymbol.file = lexer.getFile(func.fileId);
208 				foreach(ref position; func.debugSymbol) {
209 					GrFunctionSymbol.Position pos;
210 					pos.line = position.line;
211 					pos.column = position.column;
212 					debugSymbol.positions ~= pos;
213 				}
214 				bytecode.symbols ~= debugSymbol;
215 			}
216 			foreach (size_t i, instruction; func.instructions)
217 				opcodes[lastOpcodeCount + i] = makeOpcode(cast(uint) instruction.opcode,
218 						instruction.value);
219 			foreach (call; func.functionCalls)
220 				call.position += lastOpcodeCount;
221 			func.position = lastOpcodeCount;
222 			lastOpcodeCount += func.instructions.length;
223 		}
224 		parser.solveFunctionCalls(opcodes);
225 
226 		//The contexts will jump here if the VM is panicking.
227 		opcodes[$ - 1] = makeOpcode(cast(uint) GrOpcode.unwind, 0);
228 
229 		//Constants.
230 		bytecode.iconsts = parser.iconsts;
231 		bytecode.fconsts = parser.fconsts;
232 		bytecode.sconsts = parser.sconsts;
233 
234 		//Global variables.
235 		bytecode.iglobalsCount = parser.iglobalsCount;
236 		bytecode.fglobalsCount = parser.fglobalsCount;
237 		bytecode.sglobalsCount = parser.sglobalsCount;
238 		bytecode.oglobalsCount = parser.oglobalsCount;
239 
240 		foreach (variableDef; _data._variableDefinitions) {
241 			GrBytecode.Variable variable;
242 			variable.index = variableDef.register;
243 			final switch (variableDef.type.baseType) with (GrBaseType) {
244 			case bool_:
245 			case int_:
246 			case function_:
247 			case task:
248 			case enum_:
249 			case chan:
250 				variable.typeMask = 0x1;
251 				variable.ivalue = variableDef.isInitialized ? variableDef.ivalue : 0;
252 				break;
253 			case float_:
254 				variable.typeMask = 0x2;
255 				variable.fvalue = variableDef.isInitialized ? variableDef.fvalue : 0f;
256 				break;
257 			case string_:
258 				variable.typeMask = 0x4;
259 				variable.svalue = variableDef.isInitialized ? variableDef.svalue : "";
260 				break;
261 			case array_:
262 			case class_:
263 			case foreign:
264 				variable.typeMask = 0x8;
265 				break;
266 			case void_:
267 			case internalTuple:
268 			case reference:
269 			case null_:
270 				throw new Exception(
271 						"invalid global variable type, the type cannot be " ~ grGetPrettyType(
272 						variableDef.type));
273 			}
274 			bytecode.variables[variableDef.name] = variable;
275 		}
276 
277 		//Instuctions.
278 		bytecode.opcodes = opcodes;
279 
280 		//Global events.
281 		bytecode.events = events;
282 
283 		//Initialize every primitives.
284 		bytecode.primitives.length = _data._primitives.length;
285 		for (size_t id; id < bytecode.primitives.length; ++id) {
286 			bytecode.primitives[id].index = _data._primitives[id].callbackId;
287 			GrType[] inSignature = _data._primitives[id].inSignature;
288 			if (_data._primitives[id].name == "@as")
289 				inSignature.length = 1;
290 			for (size_t i; i < inSignature.length; ++i) {
291 				const GrType type = inSignature[i];
292 				final switch (type.baseType) with (GrBaseType) {
293 				case bool_:
294 				case int_:
295 				case function_:
296 				case task:
297 				case enum_:
298 				case chan:
299 					bytecode.primitives[id].parameters ~= 0x10000 | (
300 							bytecode.primitives[id].iparams & 0xFFFF);
301 					bytecode.primitives[id].iparams++;
302 					break;
303 				case float_:
304 					bytecode.primitives[id].parameters ~= 0x20000 | (
305 							bytecode.primitives[id].fparams & 0xFFFF);
306 					bytecode.primitives[id].fparams++;
307 					break;
308 				case string_:
309 					bytecode.primitives[id].parameters ~= 0x40000 | (
310 							bytecode.primitives[id].sparams & 0xFFFF);
311 					bytecode.primitives[id].sparams++;
312 					break;
313 				case array_:
314 				case class_:
315 				case foreign:
316 					bytecode.primitives[id].parameters ~= 0x80000 | (
317 							bytecode.primitives[id].oparams & 0xFFFF);
318 					bytecode.primitives[id].oparams++;
319 					break;
320 				case void_:
321 				case internalTuple:
322 				case reference:
323 				case null_:
324 					throw new Exception("invalid parameter type in " ~ grGetPrettyFunctionCall(
325 							_data._primitives[id].name,
326 							inSignature) ~ ", the type cannot be " ~ grGetPrettyType(type));
327 				}
328 			}
329 		}
330 
331 		/// Fill in class information
332 		for (size_t i; i < _data._classDefinitions.length; ++i) {
333 			GrClassBuilder class_ = new GrClassBuilder;
334 			class_.name = _data._classDefinitions[i].name;
335 			class_.fields = _data._classDefinitions[i].fields;
336 			bytecode.classes ~= class_;
337 		}
338 
339 		return bytecode;
340 	}
341 }