* if/elseif/else now compiles into VM code, probably speeding execution by gscript

Mon, 16 Feb 2015 12:58:31 +0200

author
cemkalyoncu
date
Mon, 16 Feb 2015 12:58:31 +0200
branch
gscript
changeset 627
baaf20034d1b
parent 626
5a1c61c1bd0d
child 628
4775ada881e0

* if/elseif/else now compiles into VM code, probably speeding execution by
2 orders of magnitute

Source/Scripting/Compilers.h file | annotate | diff | comparison | revisions
Source/Scripting/Compilers/AST.cpp file | annotate | diff | comparison | revisions
Source/Scripting/Compilers/AST.h file | annotate | diff | comparison | revisions
Source/Scripting/Compilers/PD.cpp file | annotate | diff | comparison | revisions
Source/Scripting/Scripting.cpp file | annotate | diff | comparison | revisions
--- a/Source/Scripting/Compilers.h	Fri Feb 13 11:02:15 2015 +0200
+++ b/Source/Scripting/Compilers.h	Mon Feb 16 12:58:31 2015 +0200
@@ -6,6 +6,7 @@
 
 #include "Data.h"
 #include "Instruction.h"
+#include "Compilers/AST.h"
 #include "../Types.h"
 
 namespace Gorgon {	namespace Scripting { 
@@ -60,14 +61,17 @@
 	/// Programming dialect compiler
 	class Programming : public Base {
 	public:
-
+		Programming() : compiler(List) { }
+		
 		virtual unsigned Compile(const std::string &input) override;
 		
 		virtual void Finalize() override;
 
 	private:
+		ASTCompiler compiler;
 		std::string left;
 		std::vector<unsigned> linestarts;
+		int waiting=0;
 	};
 	
 	/// Disassembles the given instruction
--- a/Source/Scripting/Compilers/AST.cpp	Fri Feb 13 11:02:15 2015 +0200
+++ b/Source/Scripting/Compilers/AST.cpp	Mon Feb 16 12:58:31 2015 +0200
@@ -361,12 +361,13 @@
 		Utils::ASSERT_FALSE("Unknown AST Node");
 	}
 	
-	//This function compiles a given AST tree
-	unsigned CompileAST(ASTNode *tree, std::vector<Instruction> &list) {
+	unsigned ASTCompiler::Compile(ASTNode *tree) {
 		auto sz=list.size();
 		
 		//temporaries start from 1
 		Byte tempind=1;
+
+		std::vector<Instruction> writebacks;
 		
 		//Assignment operation
 		if(tree->Type==ASTNode::Assignment) {
@@ -374,7 +375,6 @@
 			inst.Type=InstructionType::Assignment;
 			
 			inst.RHS=compilevalue(tree->Leaves[1], list, tempind);
-			std::vector<Instruction> writebacks;
 			
 			if(tree->Leaves[0].Type==ASTNode::Identifier || tree->Leaves[0].Type==ASTNode::Variable) {
 				inst.Name.Type=ValueType::Variable;
@@ -420,10 +420,92 @@
 		
 		//Keyword call
 		else if(tree->Type==ASTNode::Keyword) {
-// 			if(tree->Text=="if") {
-// 				
-// 			}
-// 			else {
+			//fully compile if keyword
+			if(String::ToLower(tree->Text)=="if") {
+				if(tree->Leaves.GetCount()==0) {
+					throw MissingParameterException("condition", 0, "Bool", "Condition for If keyword is not specified");
+				}
+				else if(tree->Leaves.GetCount()>1) {
+					throw TooManyParametersException(tree->Leaves.GetCount(), 1, "If keyword requires only a single condition");
+				}
+				
+				Instruction jf;
+				jf.Type=InstructionType::JumpFalse;
+				jf.RHS=compilevalue(tree->Leaves[0], list, tempind);
+				
+				scopes.push_back(scope{scope::ifkeyword, (int)list.size()});
+				
+				list.push_back(jf);
+				waitingcount++;
+			}
+			else if(String::ToLower(tree->Text)=="else") {
+				if(scopes.size()==0 || scopes.back().type!=scope::ifkeyword) {
+					throw FlowException("Else without If");
+				}
+				if(scopes.back().elsepassed) {
+					throw FlowException("Multiple Else for a single If");
+				}
+				
+				Instruction ja;
+				ja.Type=InstructionType::Jump;
+				list.push_back(ja);
+				
+				list[scopes.back().indices.back()].JumpOffset=list.size()-scopes.back().indices.back();
+				scopes.back().indices.back()=list.size()-1;
+				scopes.back().elsepassed=true;
+			}
+			else if(String::ToLower(tree->Text)=="elseif") {
+				if(scopes.size()==0 || scopes.back().type!=scope::ifkeyword) {
+					throw FlowException("ElseIf without If");
+				}
+				if(scopes.back().elsepassed) {
+					throw FlowException("ElseIf statements must be before Else statement");
+				}
+				if(tree->Leaves.GetCount()==0) {
+					throw MissingParameterException("condition", 0, "Bool", "Condition for ElseIf keyword is not specified");
+				}
+				else if(tree->Leaves.GetCount()>1) {
+					throw TooManyParametersException(tree->Leaves.GetCount(), 1, "ElseIf keyword requires only a single condition");
+				}
+				
+				Instruction ja;
+				ja.Type=InstructionType::Jump;
+				list.push_back(ja);
+				
+				list[scopes.back().indices.back()].JumpOffset=list.size()-scopes.back().indices.back();
+				scopes.back().indices.back()=list.size()-1;
+				
+				Instruction jf;
+				jf.Type=InstructionType::JumpFalse;
+				jf.RHS=compilevalue(tree->Leaves[0], list, tempind);
+				
+				scopes.back().indices.push_back(list.size());
+				list.push_back(jf);				
+			}
+			else if(String::ToLower(tree->Text)=="end" && (scopes.size()==0 || scopes.back().type!=scope::unknown) ) {
+				if(scopes.size()==0) {
+					throw FlowException("end without keyword");
+				}
+				
+				switch(scopes.back().type) {
+				case scope::ifkeyword:
+					for(auto &index : scopes.back().indices) {
+						list[index].JumpOffset=list.size()-index;
+					}
+					break;
+				}
+				
+				scopes.pop_back();
+				waitingcount--;
+			}
+			else {
+				if(String::ToLower(tree->Text)=="end") {
+					scopes.pop_back();
+				}
+				else if(Keywords.Functions[tree->Text].IsScoped()) {
+					scopes.push_back(scope{scope::unknown, (int)list.size()});
+				}
+				
 				Instruction instmark, inst;
 				
 				//Mark the start of the keyword
@@ -443,12 +525,21 @@
 					inst.Parameters.push_back(compilevalue(p, list, tempind));
 				}
 				list.push_back(inst);
-// 			}
+			}
 		}
 		else {
 			ASSERT(false, "Unknown top level AST Node");
 		}
 		
+		//release used temporaries
+		for(int i=1;i<tempind;i++) {
+			Instruction inst;
+			inst.Type=InstructionType::RemoveTemp;
+			inst.Store=i;
+			
+			list.push_back(inst);
+		}
+		
 		return list.size()-sz;
 	}
 	
--- a/Source/Scripting/Compilers/AST.h	Fri Feb 13 11:02:15 2015 +0200
+++ b/Source/Scripting/Compilers/AST.h	Mon Feb 16 12:58:31 2015 +0200
@@ -100,11 +100,47 @@
 
 	};
 	
-	/// This function compiles given abstract syntax tree, returns the number of instructions generated.
-	///@param tree is the AST to be compiled
-	///@param list is the list where the instructions will be placed. Instructions will be added
-	/// at the end of the list
-	unsigned CompileAST(ASTNode *tree, std::vector<Instruction> &list);
+	/**
+	 * ASTCompiler stores states for AST compiler. This class requires the list of instructions to be saved.
+	 * 
+	 */
+	class ASTCompiler {
+	public:
+		
+		/// AST compiler requires a vector of instructions. The compiler appends elements to the end of the
+		/// list. However, it is important not to modify the list while IsReady function returns false.
+		ASTCompiler(std::vector<Instruction> &list) : list(list) { }
+		
+		/// This function compiles given abstract syntax tree, returns the number of instructions generated.
+		/// You should check IsReady function before using instructions.
+		///@param tree is the AST to be compiled
+		unsigned Compile(ASTNode *tree);
+		
+		/// If this function returns true, it is ok to use instructions from the list. A return value of 
+		/// false means not all instructions are fully completed. In these cases, more ASTs should be
+		/// supplied to the compiler
+		bool IsReady() const { return waitingcount==0; }
+		
+	private:
+		
+		struct scope {
+			enum scopetype {
+				unknown,
+				ifkeyword,
+			} type;
+			
+			scope(scopetype type, int index) : type(type) { indices.push_back(index); }
+			
+			bool elsepassed=0;
+			std::vector<int> indices;
+		};
+		
+		std::vector<scope> scopes;
+		
+		int waitingcount = 0;
+		std::vector<Instruction> &list;
+		
+	};
 
 	/// Converts given AST to an SVG file. This function requires GraphViz dot to be in path. The SVG will be saved
 	/// as temp.dot.svg in the current directory.
--- a/Source/Scripting/Compilers/PD.cpp	Fri Feb 13 11:02:15 2015 +0200
+++ b/Source/Scripting/Compilers/PD.cpp	Mon Feb 16 12:58:31 2015 +0200
@@ -945,7 +945,8 @@
 
 		left.append(input);
 		
-		int elements=0;
+		int elements=waiting;
+		waiting=0;
 		
  		while(left.length()) { //parse until we run out of data
 			std::string process;
@@ -956,7 +957,7 @@
 			
 			if(ret) {
 				try {
-					elements+=CompileAST(ret, List);
+					elements+=compiler.Compile(ret);
 				}
 				catch(...) {
 					delete ret;
@@ -965,15 +966,21 @@
 			else {
 				break;
 			}
-
-			std::vector<std::string> strlines;
-			for(auto it=List.end()-elements;it!=List.end();++it) {
-				strlines.push_back(Disassemble(&(*it)));
+			
+			if(showsvg__) {
+				std::vector<std::string> strlines;
+				for(auto it=List.end()-elements;it!=List.end();++it) {
+					strlines.push_back(Disassemble(&(*it)));
+				}
+				
+				ASTToSVG(input, *ret, strlines, true);
 			}
 			
-			if(showsvg__)
-				ASTToSVG(input, *ret, strlines, true);
-			
+		}
+		
+		if(!compiler.IsReady()) {
+			waiting=elements;
+			elements=0;
 		}
 		
 		return elements;
--- a/Source/Scripting/Scripting.cpp	Fri Feb 13 11:02:15 2015 +0200
+++ b/Source/Scripting/Scripting.cpp	Mon Feb 16 12:58:31 2015 +0200
@@ -22,8 +22,9 @@
 				}
 				pline++;
 
+				int compiled=0;
 				try {
-					parser->Compile(str);
+					compiled=parser->Compile(str);
 				}
 				catch(ParseError &err) {
 					if(err.GetLine()<0)
@@ -32,10 +33,11 @@
 					throw err;
 				}
 
-				for(auto &inst : parser->List)
-					lines.push_back({inst, pline});
+				for(int i=parser->List.size()-compiled;i<parser->List.size();i++) {
+					lines.push_back({parser->List[i], pline});
+				}
 
-				parser->List.clear();
+				parser->List.erase(parser->List.end()-compiled, parser->List.end());
 			}
 			
 			return &lines[line].instruction;

mercurial