1 /++
2 All the compiler modules packaged into one, this one module should be used to compile scripts.
3 +/
4 module qscript.compiler.compiler;
5 
6 import qscript.compiler.tokengen;
7 import qscript.compiler.ast;
8 import qscript.compiler.astgen;
9 import qscript.compiler.astcheck;
10 import qscript.compiler.codegen;
11 import qscript.compiler.astreadable;
12 import qscript.qscript : QScriptBytecode;
13 
14 import navm.bytecode;
15 
16 import std.json;
17 import std.range;
18 import std.traits;
19 import std.conv : to;
20 
21 import utils.misc;
22 
23 /// An array containing all chars that an identifier can contain
24 package const char[] IDENT_CHARS = iota('a', 'z'+1).array~iota('A', 'Z'+1).array~iota('0', '9'+1).array~[cast(int)'_'];
25 /// An array containing all keywords
26 package const string[] KEYWORDS = [
27 	"import",
28 	"function",
29 	"struct",
30 	"enum",
31 	"var",
32 	"return",
33 	"if",
34 	"else",
35 	"while",
36 	"for",
37 	"do",
38 	"null",
39 	"true",
40 	"false"
41 ] ~ DATA_TYPES ~ VISIBILITY_SPECIFIERS;
42 /// Visibility Specifier keywords
43 package const string[] VISIBILITY_SPECIFIERS = ["public", "private"];
44 /// data types
45 package const string[] DATA_TYPES = ["void", "int", "double", "char", "bool"];
46 /// An array containing double-operand operators
47 package const string[] OPERATORS = [".", "/", "*", "+", "-", "%", "~", "<", ">", ">=", "<=", "==", "=", "&&", "||"];
48 /// single-operand operators
49 package const string[] SOPERATORS = ["!", "@"];
50 /// An array containing all bool-operators (operators that return true/false)
51 package const string[] BOOL_OPERATORS = ["<", ">", ">=", "<=", "==", "&&", "||"];
52 /// function names corresponding to operators
53 package string[string] OPERATOR_FUNCTIONS;
54 static this(){
55 	OPERATOR_FUNCTIONS = [
56 		"." : "opMemberSelect",
57 		"/" : "opDivide",
58 		"*" : "opMultiply",
59 		"+" : "opAdd",
60 		"-" : "opSubtract",
61 		"%" : "opMod",
62 		"~" : "opConcat",
63 		"<" : "opLesser",
64 		">" : "opGreater",
65 		"<=" : "opLesserSame",
66 		">=" : "opGreaterSame",
67 		"==" : "opSame",
68 		"=" : "opAssign",
69 		"&&" : "opAndBool",
70 		"||" : "opOrBool",
71 		"!" : "opNot",
72 		"@" : "opRef",
73 	];
74 }
75 /// Stores what types can be converted to what other types implicitly.
76 package const DataType.Type[][] IMPLICIT_CAST_TYPES = [
77 	[DataType.Type.Int, DataType.Type.Bool],
78 	[DataType.Type.Double],
79 	[DataType.Type.Char],
80 	[DataType.Type.Bool],
81 	[DataType.Type.Void],
82 ];
83 /// Stores numerical data types (where numbers are stored)
84 package const DataType.Type[] NUMERICAL_DATA_TYPES = [
85 	DataType.Type.Int,
86 	DataType.Type.Double,
87 ];
88 /// only these data types are currently available
89 public const DataType.Type[] AVAILABLE_DATA_TYPES = [
90 	DataType.Type.Int, DataType.Type.Double, DataType.Type.Char
91 ];
92 
93 /// Used by compiler's functions to return error
94 public struct CompileError{
95 	string msg; /// The error stored in a string
96 	uinteger lineno; /// The line number on which the error is
97 	/// constructor
98 	this(uinteger lineNumber, string errorMessage){
99 		lineno = lineNumber;
100 		msg = errorMessage;
101 	}
102 }
103 
104 /// Flags passed to bytecode generating functions
105 /// 
106 /// Not all functions look at each flag, some functions might ignore all flags
107 public enum CodeGenFlags : ubyte{
108 	None = 0, /// all flags zero
109 	PushRef = 1 << 0, /// if the code should push a reference to the needed data. By default, the actual data is pushed.
110 	PushFunctionReturn = 1 << 1, /// if the return value from FunctionCallNode should be pushed
111 }
112 
113 /// Visibility Specifiers
114 package enum Visibility{
115 	Public,
116 	Private
117 }
118 
119 /// Returns: Visibilty from a string
120 /// 
121 /// Throws: Exception if invalid input provided
122 package Visibility visibility(string s){
123 	foreach (curVisibility; EnumMembers!Visibility){
124 		if (curVisibility.to!string.lowercase == s)
125 			return curVisibility;
126 	}
127 	throw new Exception(s~" is not a visibility option");
128 }
129 
130 /// Checks if a type can be implicitly casted to another type. does not work for custom types, returns false
131 /// 
132 /// Returns: true if can cast implicitely
133 public bool canImplicitCast(DataType.Type type1, DataType.Type type2){
134 	if (type1 == DataType.Type.Custom || type2 == DataType.Type.Custom)
135 		return false;
136 	foreach(list; IMPLICIT_CAST_TYPES){
137 		if (list.hasElement(type1) && list.hasElement(type2))
138 			return true;
139 	}
140 	return false;
141 }
142 /// ditto
143 public bool canImplicitCast(DataType type1, DataType type2){
144 	if (type1.arrayDimensionCount == type1.arrayDimensionCount && type1.isRef == type2.isRef){
145 		if (type1.type == DataType.Type.Custom || type2.type == DataType.Type.Custom)
146 			return type1.typeName == type2.typeName;
147 		return canImplicitCast(type1.type, type2.type);
148 	}
149 	return false;
150 }
151 
152 /// comma separates a string.
153 /// 
154 /// Returns: the comma separated string
155 public string[] commaSeparate(char comma=',', bool excludeEmpty=false)(string str){
156 	string[] r;
157 	uinteger readFrom;
158 	for(uinteger i=0; i < str.length ; i ++){
159 		if (str[i] == comma || i+1 == str.length){
160 			if (readFrom < i)
161 				r ~= str[readFrom .. i];
162 			else static if (!excludeEmpty)
163 				r ~= "";
164 			readFrom = i+1;
165 		}
166 	}
167 	return r;
168 }
169 
170 public import qscript.qscript : Library;
171 
172 /// Function called to generate bytecode for a call to a macro function
173 public alias FunctionCallCodeGenFunc = void delegate (string name, DataType[] argTypes, NaBytecode bytecode);
174 /// Function called to generate bytecode for accessing a macro variable
175 public alias VariableCodeGenFunc = void delegate (string name, NaBytecode bytecode);
176 /// To store information about a function
177 public struct Function{
178 	/// the name of the function
179 	string name;
180 	/// the data type of the value returned by this function
181 	DataType returnType;
182 	/// stores the data type of the arguments received by this function
183 	private DataType[] _argTypes;
184 	/// the data type of the arguments received by this function
185 	@property ref DataType[] argTypes() return{
186 		return _argTypes;
187 	}
188 	/// the data type of the arguments received by this function
189 	@property ref DataType[] argTypes(DataType[] newArray) return{
190 		return _argTypes = newArray.dup;
191 	}
192 	/// constructor
193 	this (string functionName, DataType functionReturnType, DataType[] functionArgTypes){
194 		name = functionName;
195 		returnType = functionReturnType;
196 		_argTypes = functionArgTypes.dup;
197 	}
198 	/// constructor (reads from string generated by Function.toString)
199 	/// 
200 	/// Throws: Exception in case of error
201 	this (string functionString){
202 		string err = this.fromString(functionString);
203 		if (err.length)
204 			throw new Exception(err);
205 	}
206 	/// postblit
207 	this (this){
208 		this._argTypes = this._argTypes.dup;
209 	}
210 	/// Returns: a string representation of this Function
211 	string toString(){
212 		char[] r = cast(char[])"func,"~name~','~returnType.toString~',';
213 		foreach (type; _argTypes)
214 			r ~= type.toString~',';
215 		return cast(string)r;
216 	}
217 	/// Reads this Function from a string (reverse of toString)
218 	/// 
219 	/// Returns: zero length string if success, or error description in string if error
220 	string fromString(string str){
221 		if (str.length == 0)
222 			return "cannot read Function from empty string";
223 		string[] vals = commaSeparate(str);
224 		if (vals.length < 3 || vals[0] != "func")
225 			return "invalid string to read Function from";
226 		name = vals[1];
227 		try
228 			returnType = DataType(vals[2]);
229 		catch (Exception e){
230 			string r = e.msg;
231 			.destroy(e);
232 			return "error reading Function returnType: "~r;
233 		}
234 		_argTypes = [];
235 		if (vals.length > 3){
236 			_argTypes.length = vals.length - 3;
237 			foreach(i, arg; vals[3 .. $]){
238 				try
239 					_argTypes[i] = DataType(arg);
240 				catch (Exception e){
241 					string r = e.msg;
242 					.destroy(e);
243 					return "error reading Function arguments: "~r;
244 				}
245 			}
246 		}
247 		return [];
248 	}
249 }
250 /// 
251 unittest{
252 	Function func = Function("potato",DataType(DataType.Type.Void),[]);
253 	assert(Function(func.toString) == func, func.toString);
254 	func = Function("potato",DataType(DataType.Type.Int),[DataType("@potatoType[]")]);
255 	assert(Function(func.toString) == func, func.toString);
256 }
257 /// To store information about a struct
258 public struct Struct{
259 	/// the name of this struct
260 	string name;
261 	/// name of members of this struct
262 	private string[] _membersName;
263 	/// ditto
264 	@property ref string[] membersName() return{
265 		return _membersName;
266 	}
267 	/// ditto
268 	@property ref string[] membersName(string[] newVal) return{
269 		return _membersName = newVal;
270 	}
271 	/// data types of members of this struct
272 	private DataType[] _membersDataType;
273 	/// ditto
274 	@property ref DataType[] membersDataType() return{
275 		return _membersDataType;
276 	}
277 	/// ditto
278 	@property ref DataType[] membersDataType(DataType[] newVal) return{
279 		return _membersDataType = newVal;
280 	}
281 	/// postblit
282 	this (this){
283 		this._membersName = this._membersName.dup;
284 		this._membersDataType = this._membersDataType.dup;
285 	}
286 	/// constructor, reads from string (uses fromString)
287 	/// 
288 	/// Throws: Exception in case of error
289 	this(string structString){
290 		string err = fromString(structString);
291 		if (err.length)
292 			throw new Exception(err);
293 	}
294 	/// constructor
295 	this (string name, string[] members, DataType[] dTypes){
296 		this.name = name;
297 		_membersName = members.dup;
298 		_membersDataType = dTypes.dup;
299 	}
300 	/// Returns: a string representation of this Struct
301 	string toString(){
302 		char[] r = cast(char[])"struct,"~name~',';
303 		foreach (memberName; _membersName)
304 			r ~= memberName ~ ',';
305 		foreach (type; _membersDataType)
306 			r~= type.toString ~ ',';
307 		return cast(string)r;
308 	}
309 	/// Reads this Struct from a string (reverse of toString)
310 	/// 
311 	/// Returns: empty string if success, or error in string in case of error
312 	string fromString(string str){
313 		string[] vals = commaSeparate(str);
314 		if (vals.length == 0 || vals[0] != "struct" || vals.length %  2 > 0)
315 			return "invalid string to read Struct from";
316 		name = vals[1];
317 		vals = vals[2 .. $];
318 		immutable uinteger dTypeStartIndex = vals.length/2;
319 		_membersDataType.length = dTypeStartIndex;
320 		_membersName.length = _membersDataType.length;
321 		foreach (i,val; vals[0 .. dTypeStartIndex]){
322 			_membersName[i] = val;
323 			try
324 				_membersDataType[i] = DataType(vals[dTypeStartIndex+i]);
325 			catch (Exception e){
326 				string r = e.msg;
327 				.destroy(e);
328 				return "error reading Struct member data type: " ~ r;
329 			}
330 		}
331 		return [];
332 	}
333 }
334 ///
335 unittest{
336 	Struct str = Struct("potatoStruct",["a","b","c"],[DataType("int"),DataType("@double[]"),DataType("@char[]")]);
337 	assert(Struct(str.toString) == str);
338 }
339 /// To store information about a enum
340 public struct Enum{
341 	/// name of the enum
342 	string name;
343 	/// members names, index is their value
344 	private string[] _members;
345 	/// ditto
346 	@property ref string[] members() return{
347 		return _members;
348 	}
349 	/// ditto
350 	@property ref string[] members(string[] newVal) return{
351 		return _members = newVal;
352 	}
353 	/// postblit
354 	this (this){
355 		this._members = this._members.dup;
356 	}
357 	/// Constructor
358 	this (string name, string[] members){
359 		this.name = name;
360 		this._members = members.dup;
361 	}
362 	/// Constructor, reads from string (uses fromString)
363 	this (string enumString){
364 		fromString(enumString);
365 	}
366 	/// Returns: string representation of this enum
367 	string toString(){
368 		char[] r = cast(char[])"enum,"~name~',';
369 		foreach (member; _members)
370 			r ~= member ~ ',';
371 		return cast(string)r;
372 	}
373 	/// Reads this Enum from string (reverse of toString)
374 	/// 
375 	/// Returns: empty string in case of success, error in string in case of error
376 	string fromString(string str){
377 		string[] vals = commaSeparate(str);
378 		if (vals.length == 0 || vals[0] != "enum")
379 			return "invalid string to read Enum from";
380 		name = vals[1];
381 		vals = vals[2 .. $];
382 		_members = vals.dup; // ez
383 		return [];
384 	}
385 }
386 /// To store information about a global variable
387 public struct Variable{
388 	/// name of var
389 	string name;
390 	/// data type
391 	DataType type;
392 	/// constructor
393 	this(string name, DataType type){
394 		this.name = name;
395 		this.type = type;
396 	}
397 	/// Constructor, for reading from string (uses fromString)
398 	/// 
399 	/// Throws: Exception in case of error
400 	this(string varString){
401 		fromString(varString);
402 	}
403 	/// Returns: a string representation of this Variable
404 	string toString(){
405 		return "var,"~name~','~type.toString~',';
406 	}
407 	/// Reads this Variable from a string (reverse of toString)
408 	/// 
409 	/// Returns: empty string in case of success, or error in string in case of error
410 	string fromString(string str){
411 		string[] vals = commaSeparate(str);
412 		if (vals.length != 3 || vals[0] != "var")
413 			return "invalid string to read Variable from";
414 		name = vals[1];
415 		try
416 			type = DataType(vals[2]);
417 		catch (Exception e){
418 			string r = e.msg;
419 			.destroy (e);
420 			return "error reading Variable data type: " ~ r;
421 		}
422 		return [];
423 	}
424 }
425 ///
426 unittest{
427 	Variable var = Variable("i",DataType("@int[][]"));
428 	assert (Variable(var.toString) == var);
429 }
430 
431 /// used to store data types for data at compile time
432 public struct DataType{
433 	/// enum defining all data types. These are all lowercase of what they're written here
434 	public enum Type{
435 		Void, /// .
436 		Char, /// .
437 		Int, /// .
438 		Double, /// .
439 		Bool, /// .
440 		Custom, /// some other type
441 	}
442 	/// the actual data type
443 	Type type = DataType.Type.Void;
444 	/// stores if it's an array. If type is `int`, it will be 0, if `int[]` it will be 1, if `int[][]`, then 2 ...
445 	uinteger arrayDimensionCount = 0;
446 	/// stores length in case of Type.Custom
447 	uinteger customLength;
448 	/// stores if it's a reference to a type
449 	bool isRef = false;
450 	/// stores the type name in case of Type.Custom
451 	private string _name;
452 	/// Returns: this data type in human readable string.
453 	@property string name(){
454 		char[] r = cast(char[])(this.type == Type.Custom ? _name.dup : this.type.to!string.lowercase);
455 		if (this.isRef)
456 			r = '@' ~ r;
457 		uinteger i = r.length;
458 		if (this.isArray){
459 			r.length += this.arrayDimensionCount * 2;
460 			for (; i < r.length; i += 2){
461 				r[i .. i + 2] = "[]";
462 			}
463 		}
464 		return cast(string)r;
465 	}
466 	/// Just calls fromString()
467 	@property string name(string newName){
468 		this.fromString(newName);
469 		return newName;
470 	}
471 	/// Returns: only the type name, exlcuding [] or @ if present
472 	@property string typeName(){
473 		return type == Type.Custom ? _name : type.to!string;
474 	}
475 	/// Returns: true if the type is a custom one
476 	@property bool isCustom(){
477 		return type == Type.Custom;
478 	}
479 	/// returns: true if it's an array. Strings are arrays too (char[])
480 	@property bool isArray(){
481 		if (arrayDimensionCount > 0){
482 			return true;
483 		}
484 		return false;
485 	}
486 	/// Returns: true if arithmatic operators can be used on a data type
487 	@property bool isNumerical(){
488 		if (this.isArray || this.isRef)
489 			return false;
490 		return NUMERICAL_DATA_TYPES.hasElement(this.type);
491 	}
492 	/// constructor.
493 	/// 
494 	/// dataType is the type to store
495 	/// arrayDimension is the number of nested arrays
496 	/// isRef is whether the type is a reference to the actual type
497 	this (DataType.Type dataType, uinteger arrayDimension = 0, bool isReference = false){
498 		type = dataType;
499 		arrayDimensionCount = arrayDimension;
500 		isRef = isReference;
501 	}
502 	/// constructor.
503 	/// 
504 	/// dataType is the name of type to store
505 	/// arrayDimension is the number of nested arrays
506 	/// isRef is whether the type is a reference to the actual type
507 	this (string dataType, uinteger arrayDimension = 0, bool isReference = false){
508 		this.name = dataType;
509 		arrayDimensionCount = arrayDimension;
510 		isRef = isReference;
511 	}
512 	
513 	/// constructor.
514 	/// 
515 	/// `sType` is the type in string form
516 	this (string sType){
517 		fromString(sType);
518 	}
519 	/// constructor.
520 	/// 
521 	/// `data` is the data to infer type from
522 	this (Token data){
523 		fromData(data);
524 	}
525 	/// reads DataType from a string, works for base types and custom types
526 	/// 
527 	/// Throws: Exception in case of failure or bad format in string
528 	void fromString(string s){
529 		isRef = false;
530 		string sType = null;
531 		uinteger indexCount = 0;
532 		// check if it's a ref
533 		if (s.length > 0 && s[0] == '@'){
534 			isRef = true;
535 			s = s[1 .. s.length].dup;
536 		}
537 		// read the type
538 		for (uinteger i = 0; i < s.length; i ++){
539 			if (s[i] == '['){
540 				sType = s[0 .. i];
541 				break;
542 			}else if (i+1 == s.length){
543 				sType = s;
544 				break;
545 			}
546 		}
547 		// now read the index
548 		for (uinteger i = sType.length; i < s.length; i ++){
549 			if (s[i] == '['){
550 				// make sure next char is a ']'
551 				i ++;
552 				if (s[i] != ']'){
553 					throw new Exception("invalid data type format");
554 				}
555 				indexCount ++;
556 			}else{
557 				throw new Exception("invalid data type");
558 			}
559 		}
560 		bool isCustom = true;
561 		foreach (curType; EnumMembers!Type){
562 			if (curType != Type.Custom && curType.to!string.lowercase == sType){
563 				this.type = curType;
564 				isCustom = false;
565 			}
566 		}
567 		if (isCustom){
568 			this.type = Type.Custom;
569 			this._name = sType;
570 		}
571 		arrayDimensionCount = indexCount;
572 	}
573 
574 	/// identifies the data type from the actual data. Only works for base types, and with only 1 token. So arrays dont work.
575 	/// keep in mind, this won't be able to identify if the data type is a reference or not
576 	/// 
577 	/// throws Exception on failure
578 	void fromData(Token data){
579 		isRef = false;
580 		arrayDimensionCount = 0;
581 		if (data.type == Token.Type.String){
582 			arrayDimensionCount ++;
583 			type = DataType.Type.Char;
584 		}else if (data.type == Token.Type.Char){
585 			type = DataType.Type.Char;
586 		}else if (data.type == Token.Type.Integer){
587 			type = DataType.Type.Int;
588 		}else if (data.type == Token.Type.Double){
589 			type = DataType.Type.Double;
590 		}else if (data.type == Token.Type.Bool){
591 			type = DataType.Type.Bool;
592 		}else if (data.type == Token.Type.Keyword && data.token == "null"){
593 			isRef = true;
594 			type = DataType.Type.Void;
595 		}else{
596 			throw new Exception("failed to read data type");
597 		}
598 	}
599 	/// Returns: human readable string of this (calls name())
600 	string toString(){
601 		return name;
602 	}
603 }
604 /// 
605 unittest{
606 	assert(DataType("int") == DataType(DataType.Type.Int, 0));
607 	assert(DataType("char[][]") == DataType(DataType.Type.Char, 2));
608 	assert(DataType("double[][]") == DataType(DataType.Type.Double, 2));
609 	assert(DataType("void") == DataType(DataType.Type.Void, 0));
610 	// unittests for `fromData`
611 	DataType dType;
612 	dType.fromData(Token("\"bla bla\""));
613 	assert(dType == DataType("char[]"));
614 	dType.fromData(Token("20"));
615 	assert(dType == DataType("int"), dType.name);
616 	dType.fromData(Token("2.5"));
617 	assert(dType == DataType("double"));
618 	// unittests for `.name()`
619 	assert(DataType("potatoType[][]").name == "potatoType[][]");
620 	assert(DataType("double[]").name == "double[]");	
621 }
622 
623 /// all the compiler modules wrapped into a single class. This is all that should be needed to compile scripts
624 public class QSCompiler{
625 private:
626 	ASTGen _astgenerator;
627 	ASTCheck _astcheck;
628 	CodeGen _codegen;
629 
630 	CompileError[] _errors;
631 
632 	string[] _script;
633 	TokenList _tokens;
634 	ScriptNode _ast;
635 	QScriptBytecode _bytecode;
636 
637 	Library _scriptExports;
638 	Library _scriptDeclarations;
639 public:
640 	/// constructor
641 	this(Library[] libraries, NaInstruction[] instructionTable){
642 		_astgenerator = new ASTGen();
643 		_astcheck = new ASTCheck(libraries);
644 		_codegen = new CodeGen(libraries, instructionTable);
645 	}
646 	/// destructor
647 	~this(){
648 		.destroy(_astgenerator);
649 		.destroy(_astcheck);
650 		.destroy(_codegen);
651 	}
652 	/// The Library to which script's exports will be written to
653 	@property Library scriptExports(){
654 		return _scriptExports;
655 	}
656 	/// ditto
657 	@property Library scriptExports(Library newVal){
658 		return _scriptExports = newVal;
659 	}
660 	/// what errors occurred
661 	@property CompileError[] errors(){
662 		return _errors.dup;
663 	}
664 	/// clears errors
665 	void errorsClear(){
666 		_errors.length = 0;
667 	}
668 	/// get a JSON representing the generated AST
669 	/// 
670 	/// Returns: pretty printed JSON
671 	string prettyAST(){
672 		return toJSON(_ast).toPrettyString;
673 	}
674 	/// the generated bytecode. This class will *NOT* be freed by QSCompiler.
675 	/// 
676 	/// Returns: the generated bytecode as NaBytecode
677 	QScriptBytecode bytecode(){
678 		return _bytecode;
679 	}
680 	/// load a script which is to be compiled.
681 	/// 
682 	/// Each element in the array should be a line, without the newline character(s) at end
683 	void loadScript(string[] script){
684 		_script = script.dup;
685 	}
686 	/// generates tokens for a script
687 	/// 
688 	/// Returns: true if done without errors, false if there were errors
689 	bool generateTokens(){
690 		_tokens = toTokens(_script, _errors);
691 		return _errors.length == 0;
692 	}
693 	/// generates AST from tokens
694 	/// 
695 	/// Returns: true if done without errors, false if there were errors
696 	bool generateAST(){
697 		_ast = _astgenerator.generateScriptAST(_tokens);
698 		CompileError[] astErrors = _astgenerator.errors;
699 		if (astErrors.length > 0){
700 			_errors ~= astErrors;
701 			return false;
702 		}
703 		return true;
704 	}
705 	/// checks and finalises generated AST
706 	/// 
707 	/// Returns: true if done without errors, false if there were errors
708 	bool finaliseAST(){
709 		if (_scriptExports is null){
710 			_errors ~= CompileError(0, "assign a value to QSCompiler.scriptExports before calling finaliseAST");
711 			return false;
712 		}
713 		if (_scriptDeclarations !is null)
714 			.destroy(_scriptDeclarations);
715 		_scriptDeclarations = new Library("_QSCRIPT_TEMP_LIB");
716 		CompileError[] checkErrors = _astcheck.checkAST(_ast, _scriptExports, _scriptDeclarations);
717 		if (checkErrors.length){
718 			_errors ~= checkErrors;
719 			return false;
720 		}
721 		return true;
722 	}
723 	/// ditto
724 	alias checkAST = finaliseAST;
725 	/// generates bytecode from AST.
726 	/// 
727 	/// Returns: true if done without errors, false if there was some error. The error itself cannot be known if happens in CodeGen, its likely to be a bug in CodeGen.
728 	bool generateCode(){
729 		if (_scriptDeclarations is null){
730 			_errors ~= CompileError(0,"QSCompiler.finaliseAST not called before calling QSCompiler.generateCode");
731 			return false;
732 		}
733 		immutable bool r = _codegen.generateCode(_ast, _scriptDeclarations);
734 		.destroy(_scriptDeclarations);
735 		_scriptDeclarations = null;
736 		_bytecode = _codegen.bytecode;
737 		string[] resolveErrors = _bytecode.resolve;
738 		if (resolveErrors.length){
739 			foreach (err; resolveErrors)
740 				_errors ~= CompileError(0, "[bytecode.resolve]: "~err);
741 			return false;
742 		}
743 		return r;
744 	}
745 }