parser.cxx

changeset 50
2e333a3ca49a
parent 49
8e2f7a031410
child 51
2cfa6edbf928
--- a/parser.cxx	Mon Aug 13 19:12:21 2012 +0300
+++ b/parser.cxx	Mon Aug 13 23:10:39 2012 +0300
@@ -70,10 +70,10 @@
 // Main parser code. Begins read of the script file, checks the syntax of it
 // and writes the data to the object file via ObjWriter - which also takes care
 // of necessary buffering so stuff is written in the correct order.
-void ScriptReader::BeginParse (ObjWriter* w) {
+void ScriptReader::ParseBotScript (ObjWriter* w) {
 	while (Next()) {
 		// ============================================================
-		if (!token.icompare ("state")) {
+		if (!token.compare ("state")) {
 			MUST_TOPLEVEL
 			
 			MustString ();
@@ -108,7 +108,7 @@
 		}
 		
 		// ============================================================
-		if (!token.icompare ("event")) {
+		if (!token.compare ("event")) {
 			MUST_TOPLEVEL
 			
 			// Event definition
@@ -129,7 +129,7 @@
 		}
 		
 		// ============================================================
-		if (!token.icompare ("mainloop")) {
+		if (!token.compare ("mainloop")) {
 			MUST_TOPLEVEL
 			MustNext ("{");
 			
@@ -140,7 +140,7 @@
 		}
 		
 		// ============================================================
-		if (!token.icompare ("onenter") || !token.icompare ("onexit")) {
+		if (!token.compare ("onenter") || !token.compare ("onexit")) {
 			MUST_TOPLEVEL
 			bool onenter = !token.compare ("onenter");
 			MustNext ("{");
@@ -176,25 +176,8 @@
 		}
 		
 		// ============================================================
-		// Label
-		if (!PeekNext().compare (":")) {
-			MUST_NOT_TOPLEVEL
-			
-			if (IsKeyword (token))
-				ParserError ("label name `%s` conflicts with keyword\n", token.chars());
-			if (FindCommand (token))
-				ParserError ("label name `%s` conflicts with command name\n", token.chars());
-			if (FindGlobalVariable (token))
-				ParserError ("label name `%s` conflicts with variable\n", token.chars());
-			
-			w->AddMark (token);
-			MustNext (":");
-			continue;
-		}
-		
-		// ============================================================
 		// Goto
-		if (!token.icompare ("goto")) {
+		if (!token.compare ("goto")) {
 			MUST_NOT_TOPLEVEL
 			
 			// Get the name of the label
@@ -216,7 +199,7 @@
 		
 		// ============================================================
 		// If
-		if (!token.icompare ("if")) {
+		if (!token.compare ("if")) {
 			MUST_NOT_TOPLEVEL
 			PushBlockStack ();
 			
@@ -266,7 +249,6 @@
 			MustNext ();
 			DataBuffer* expr = ParseExpression (TYPE_INT);
 			MustNext (")");
-			
 			MustNext ("{");
 			
 			// Write condition
@@ -285,7 +267,7 @@
 		
 		// ============================================================
 		// For loop
-		if (!token.icompare ("for")) {
+		if (!token.compare ("for")) {
 			MUST_NOT_TOPLEVEL
 			PushBlockStack ();
 			
@@ -328,7 +310,7 @@
 		
 		// ============================================================
 		// Do/while loop
-		if (!token.icompare ("do")) {
+		if (!token.compare ("do")) {
 			MUST_NOT_TOPLEVEL
 			PushBlockStack ();
 			MustNext ("{");
@@ -339,7 +321,7 @@
 		
 		// ============================================================
 		// Switch
-		if (!token.icompare ("switch")) {
+		if (!token.compare ("switch")) {
 			/* This goes a bit tricky. switch is structured in the
 			 * bytecode followingly:
 			 * (expression)
@@ -362,50 +344,70 @@
 			MustNext ("{");
 			blockstack[g_BlockStackCursor].type = BLOCKTYPE_SWITCH;
 			blockstack[g_BlockStackCursor].mark1 = w->AddMark (""); // end mark
+			blockstack[g_BlockStackCursor].buffer1 = NULL; // default header
 			continue;
 		}
 		
 		// ============================================================
-		if (!token.icompare ("case")) {
+		if (!token.compare ("case")) {
 			// case is only allowed inside switch
-			if (g_BlockStackCursor <= 0 || blockstack[g_BlockStackCursor].type != BLOCKTYPE_SWITCH)
-				ParserError ("`case` outside switch");
-			
 			BlockInformation* info = &blockstack[g_BlockStackCursor];
-			info->casecursor++;
-			if (info->casecursor >= MAX_CASE)
-				ParserError ("too many `case` statements in one switch");
+			if (info->type != BLOCKTYPE_SWITCH)
+				ParserError ("case label outside switch");
 			
 			// Get the literal (Zandronum does not support expressions here)
 			MustNumber ();
 			int num = atoi (token.chars ());
-			
 			MustNext (":");
 			
-			// Init a mark for the case buffer
-			int m = w->AddMark ("");
-			info->casemarks[info->casecursor] = m;
+			for (int i = 0; i < MAX_CASE; i++)
+				if (info->casenumbers[i] == num)
+					ParserError ("multiple case %d labels in one switch", num);
 			
 			// Write down the expression and case-go-to. This builds
 			// the case tree. The closing event will write the actual
 			// blocks and move the marks appropriately.
-			//
-			// NULL the switch buffer for the case-go-to statement,
+			//	AddSwitchCase will add the reference to the mark
+			// for the case block that this heralds, and takes care
+			// of buffering setup and stuff like that.
+			//	NULL the switch buffer for the case-go-to statement,
 			// we want it all under the switch, not into the case-buffers.
 			w->SwitchBuffer = NULL;
 			w->Write<word> (DH_CASEGOTO);
 			w->Write<word> (num);
-			w->AddReference (m);
+			AddSwitchCase (w, NULL);
+			info->casenumbers[info->casecursor] = num;
+			continue;
+		}
+		
+		if (!token.compare ("default")) {
+			BlockInformation* info = &blockstack[g_BlockStackCursor];
+			if (info->type != BLOCKTYPE_SWITCH)
+				ParserError ("default label outside switch");
+			
+			if (info->buffer1)
+				ParserError ("multiple default labels in one switch");
 			
-			// Init a buffer for the case block, tell the object
-			// writer to record all written data to it.
-			info->casebuffers[info->casecursor] = w->SwitchBuffer = new DataBuffer;
+			MustNext (":");
+			
+			// The default header is buffered into buffer1, since
+			// it has to be the last of the case headers
+			//
+			// Since the expression is pushed into the switch
+			// and is only popped when case succeeds, we have
+			// to pop it with DH_DROP manually if we end up in
+			// a default.
+			DataBuffer* b = new DataBuffer;
+			info->buffer1 = b;
+			b->Write<word> (DH_DROP);
+			b->Write<word> (DH_GOTO);
+			AddSwitchCase (w, b);
 			continue;
 		}
 		
 		// ============================================================
 		// Break statement.
-		if (!token.icompare ("break")) {
+		if (!token.compare ("break")) {
 			if (!g_BlockStackCursor)
 				ParserError ("unexpected `break`");
 			
@@ -434,6 +436,23 @@
 		}
 		
 		// ============================================================
+		// Label
+		if (!PeekNext().compare (":")) {
+			MUST_NOT_TOPLEVEL
+			
+			if (IsKeyword (token))
+				ParserError ("label name `%s` conflicts with keyword\n", token.chars());
+			if (FindCommand (token))
+				ParserError ("label name `%s` conflicts with command name\n", token.chars());
+			if (FindGlobalVariable (token))
+				ParserError ("label name `%s` conflicts with variable\n", token.chars());
+			
+			w->AddMark (token);
+			MustNext (":");
+			continue;
+		}
+		
+		// ============================================================
 		if (!token.compare ("}")) {
 			// Closing brace
 			
@@ -471,7 +490,6 @@
 					w->AddReference (info->mark1);
 					break;
 				}
-				
 				case BLOCKTYPE_SWITCH: {
 					// Switch closes. Move down to the record buffer of
 					// the lower block.
@@ -481,6 +499,17 @@
 					else
 						w->SwitchBuffer = NULL;
 					
+					// If there was a default in the switch, write its header down now.
+					// If not, write instruction to jump to the end of switch after
+					// the headers (thus won't fall-through if no case matched)
+					if (info->buffer1)
+						w->WriteBuffer (info->buffer1);
+					else {
+						w->Write<word> (DH_DROP);
+						w->Write<word> (DH_GOTO);
+						w->AddReference (info->mark1);
+					}
+					
 					// Go through all of the buffers we
 					// recorded down and write them.
 					for (unsigned int u = 0; u < MAX_CASE; u++) {
@@ -821,6 +850,7 @@
 	for (int i = 0; i < MAX_CASE; i++) {
 		info->casemarks[i] = MAX_MARKS;
 		info->casebuffers[i] = NULL;
+		info->casenumbers[i] = -1;
 	}
 }
 
@@ -834,4 +864,27 @@
 	// If it's not a keyword, parse it as an expression.
 	DataBuffer* b = ParseExpression (TYPE_VOID);
 	return b;
+}
+
+void ScriptReader::AddSwitchCase (ObjWriter* w, DataBuffer* b) {
+	BlockInformation* info = &blockstack[g_BlockStackCursor];
+	
+	info->casecursor++;
+	if (info->casecursor >= MAX_CASE)
+		ParserError ("too many cases in one switch");
+	
+	// Init a mark for the case buffer
+	int m = w->AddMark ("");
+	info->casemarks[info->casecursor] = m;
+	
+	// Add a reference to the mark. "case" and "default" both
+	// add the necessary bytecode before the reference.
+	if (b)
+		b->AddMarkReference (m);
+	else
+		w->AddReference (m);
+	
+	// Init a buffer for the case block and tell the object
+	// writer to record all written data to it.
+	info->casebuffers[info->casecursor] = w->SwitchBuffer = new DataBuffer;
 }
\ No newline at end of file

mercurial