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 }