Added constants, these are defined with const, take their value immediately and are replaced out with their value when used. The strlen operator can be used to get a string constant's length.

Wed, 19 Dec 2012 13:44:18 +0200

author
Teemu Piippo <crimsondusk64@gmail.com>
date
Wed, 19 Dec 2012 13:44:18 +0200
changeset 68
588cc27e84bb
parent 67
0a202714eea4
child 69
29a3e669d648

Added constants, these are defined with const, take their value immediately and are replaced out with their value when used. The strlen operator can be used to get a string constant's length.

botcommands.h file | annotate | diff | comparison | revisions
commands.cxx file | annotate | diff | comparison | revisions
commands.h file | annotate | diff | comparison | revisions
common.h file | annotate | diff | comparison | revisions
databuffer.h file | annotate | diff | comparison | revisions
main.cxx file | annotate | diff | comparison | revisions
objwriter.cxx file | annotate | diff | comparison | revisions
objwriter.h file | annotate | diff | comparison | revisions
parser.cxx file | annotate | diff | comparison | revisions
scriptreader.h file | annotate | diff | comparison | revisions
--- a/botcommands.h	Wed Dec 19 04:20:02 2012 +0200
+++ b/botcommands.h	Wed Dec 19 13:44:18 2012 +0200
@@ -170,14 +170,4 @@
 
 } BOTCMD_e;
 
-//*****************************************************************************
-typedef enum
-{
-	RETURNVAL_VOID,
-	RETURNVAL_INT,
-	RETURNVAL_BOOLEAN,
-	RETURNVAL_STRING,
-
-} RETURNVAL_e;
-
 #endif	// __BOTCOMMANDS_H__
\ No newline at end of file
--- a/commands.cxx	Wed Dec 19 04:20:02 2012 +0200
+++ b/commands.cxx	Wed Dec 19 13:44:18 2012 +0200
@@ -75,7 +75,7 @@
 		
 		// Return value
 		r->MustNext ();
-		comm->returnvalue = GetCommandType (r->token);
+		comm->returnvalue = GetTypeByName (r->token);
 		if (comm->returnvalue == -1)
 			r->ParserError ("bad return value type `%s` for command %s", r->token.chars(), comm->name.chars());
 		
@@ -100,10 +100,10 @@
 			r->MustNext (":");
 			r->MustNext ();
 			
-			int type = GetCommandType (r->token);
+			type_e type = GetTypeByName (r->token);
 			if (type == -1)
 				r->ParserError ("bad argument %d type `%s`", curarg, r->token.chars());
-			if (type == RETURNVAL_VOID)
+			if (type == TYPE_VOID)
 				r->ParserError ("void is not a valid argument type!");
 			comm->argtypes[curarg] = type;
 			
@@ -122,10 +122,17 @@
 			if (curarg >= comm->numargs) {
 				r->MustNext ("=");
 				switch (type) {
-				case RETURNVAL_INT:
-				case RETURNVAL_BOOLEAN:
-					r->MustNumber(); break;
-				case RETURNVAL_STRING: r->MustString(); break;
+				case TYPE_INT:
+				case TYPE_BOOL:
+					r->MustNumber();
+					break;
+				case TYPE_STRING:
+					r->MustString();
+					break;
+				case TYPE_UNKNOWN:
+				case TYPE_FLOAT:
+				case TYPE_VOID:
+					break;
 				}
 				
 				comm->defvals[curarg] = r->token;
@@ -156,43 +163,6 @@
 }
 
 // ============================================================================
-// Get command type by name
-int GetCommandType (str t) {
-	t = t.tolower();
-	return	(t == "int") ? TYPE_INT :
-			(t == "float") ? TYPE_FLOAT :
-			(t == "str") ? TYPE_STRING :
-			(t == "void") ? TYPE_VOID :
-			(t == "bool") ? TYPE_BOOL : -1;
-}
-
-// ============================================================================
-// Inverse operation - type name by value
-str GetTypeName (int r) {
-	switch (r) {
-	case TYPE_INT: return "int"; break;
-	case TYPE_STRING: return "str"; break;
-	case TYPE_VOID: return "void"; break;
-	case TYPE_FLOAT: return "float"; break;
-	case TYPE_BOOL: return "bool"; break;
-	}
-	
-	return "";
-}
-
-// ============================================================================
-str GetReturnTypeName (int r) {
-	switch (r) {
-	case RETURNVAL_INT: return "int"; break;
-	case RETURNVAL_STRING: return "str"; break;
-	case RETURNVAL_VOID: return "void"; break;
-	case RETURNVAL_BOOLEAN: return "bool"; break;
-	}
-	
-	return "";
-}
-
-// ============================================================================
 // Finds a command by name
 CommandDef* FindCommand (str fname) {
 	CommandDef* comm;
@@ -230,7 +200,7 @@
 		if (i >= comm->numargs) {
 			text += '=';
 			
-			bool isString = comm->argtypes[i] == RETURNVAL_STRING;
+			bool isString = comm->argtypes[i] == TYPE_STRING;
 			if (isString) text += '"';
 			
 			char defvalstring[8];
--- a/commands.h	Wed Dec 19 04:20:02 2012 +0200
+++ b/commands.h	Wed Dec 19 13:44:18 2012 +0200
@@ -44,6 +44,7 @@
 #define MAX_MAXARGS 8
 #define MAX_ARGNAMELEN 16
 
+#include "common.h"
 #include "str.h"
 #include "botcommands.h"
 
@@ -55,8 +56,8 @@
 	int number;
 	int numargs;
 	int maxargs;
-	int returnvalue;
-	int argtypes[MAX_MAXARGS];
+	type_e returnvalue;
+	type_e argtypes[MAX_MAXARGS];
 	char argnames[MAX_MAXARGS][MAX_ARGNAMELEN];
 	int defvals[MAX_MAXARGS];
 	CommandDef* next;
@@ -64,8 +65,6 @@
 
 void ReadCommands ();
 CommandDef* FindCommand (str a);
-int GetCommandType (str token);
-str GetReturnTypeName (int r);
 str GetCommandPrototype (CommandDef* comm);
 
 #ifndef __COMMANDS_CXX__
--- a/common.h	Wed Dec 19 04:20:02 2012 +0200
+++ b/common.h	Wed Dec 19 13:44:18 2012 +0200
@@ -67,6 +67,16 @@
 	MODE_ONEXIT,	// inside onexit
 };
 
+enum type_e {
+	TYPE_UNKNOWN = 0,
+	TYPE_VOID,
+	TYPE_INT,
+	TYPE_STRING,
+	TYPE_FLOAT,
+	TYPE_BOOL,
+	
+};
+
 #define CHECK_FILE(pointer,path,action) \
 	if (!pointer) { \
 		error ("couldn't open %s for %s!\n", (char*)path, action); \
@@ -89,6 +99,8 @@
 void error (const char* text, ...);
 char* ObjectFileName (str s);
 bool fexists (char* path);
+type_e GetTypeByName (str token);
+str GetTypeName (type_e type);
 
 // Make the parser's variables globally available
 extern int g_NumStates;
--- a/databuffer.h	Wed Dec 19 04:20:02 2012 +0200
+++ b/databuffer.h	Wed Dec 19 13:44:18 2012 +0200
@@ -43,6 +43,7 @@
 #include <stdio.h>
 #include <string.h>
 #include "common.h"
+#include "stringtable.h"
 
 #define MAX_MARKS 512
 
@@ -279,6 +280,22 @@
 			count += !!refs[u];
 		return count;
 	}
+	
+	// Write a float into the buffer
+	void WriteFloat (str floatstring) {
+		// TODO: Casting float to word causes the decimal to be lost.
+		// Find a way to store the number without such loss.
+		float val = atof (floatstring);
+		Write<word> (DH_PUSHNUMBER);
+		Write<word> (static_cast<word> ((val > 0) ? val : -val));
+		if (val < 0)
+			Write<word> (DH_UNARYMINUS);
+	}
+	
+	void WriteString (str string) {
+		Write<word> (DH_PUSHSTRINGINDEX);
+		Write<word> (PushToStringTable (string));
+	}
 };
 
 #endif // __DATABUFFER_H__
\ No newline at end of file
--- a/main.cxx	Wed Dec 19 04:20:02 2012 +0200
+++ b/main.cxx	Wed Dec 19 13:44:18 2012 +0200
@@ -53,28 +53,34 @@
 #include "stringtable.h"
 #include "variables.h"
 #include "array.h"
+#include "databuffer.h"
 
 #include "bots.h"
 #include "botcommands.h"
 
 // List of keywords
 const char* g_Keywords[] = {
+	"bool",
 	"break",
 	"case",
 	"continue",
+	"const",
 	"default",
 	"do",
 	"else",
 	"event",
+	"float",
 	"for",
 	"goto",
 	"if",
+	"int",
 	"mainloop",
 	"onenter",
 	"onexit",
 	"state",
 	"switch",
-	"var"
+	"str"
+	"void",
 	"while",
 	
 	// These ones aren't implemented yet but I plan to do so, thus they are
@@ -244,4 +250,32 @@
 
 unsigned int NumKeywords () {
 	return sizeof (g_Keywords) / sizeof (const char*);
+}
+
+
+// ============================================================================
+type_e GetTypeByName (str t) {
+	t = t.tolower();
+	return	(t == "int") ? TYPE_INT :
+			(t == "float") ? TYPE_FLOAT :
+			(t == "str") ? TYPE_STRING :
+			(t == "void") ? TYPE_VOID :
+			(t == "bool") ? TYPE_BOOL :
+			TYPE_UNKNOWN;
+}
+
+
+// ============================================================================
+// Inverse operation - type name by value
+str GetTypeName (type_e type) {
+	switch (type) {
+	case TYPE_INT: return "int"; break;
+	case TYPE_STRING: return "str"; break;
+	case TYPE_VOID: return "void"; break;
+	case TYPE_FLOAT: return "float"; break;
+	case TYPE_BOOL: return "bool"; break;
+	case TYPE_UNKNOWN: return "???"; break;
+	}
+	
+	return "";
 }
\ No newline at end of file
--- a/objwriter.cxx	Wed Dec 19 04:20:02 2012 +0200
+++ b/objwriter.cxx	Wed Dec 19 13:44:18 2012 +0200
@@ -45,6 +45,7 @@
 #include "common.h"
 #include "str.h"
 #include "objwriter.h"
+#include "databuffer.h"
 #include "stringtable.h"
 
 #include "bots.h"
--- a/objwriter.h	Wed Dec 19 04:20:02 2012 +0200
+++ b/objwriter.h	Wed Dec 19 13:44:18 2012 +0200
@@ -45,8 +45,8 @@
 #include <typeinfo>
 #include <string.h>
 #include "common.h"
+#include "str.h"
 #include "databuffer.h"
-#include "str.h"
 
 class ObjWriter {
 public:
@@ -99,7 +99,6 @@
 	void MoveMark (unsigned int mark);
 	void OffsetMark (unsigned int mark, int offset);
 	void DeleteMark (unsigned int mark);
-	
 	template <class T> void Write (T stuff) {
 		GetCurrentBuffer ()->Write<T> (stuff);
 	}
--- a/parser.cxx	Wed Dec 19 04:20:02 2012 +0200
+++ b/parser.cxx	Wed Dec 19 13:44:18 2012 +0200
@@ -50,6 +50,7 @@
 #include "commands.h"
 #include "stringtable.h"
 #include "variables.h"
+#include "array.h"
 
 #define MUST_TOPLEVEL if (g_CurMode != MODE_TOPLEVEL) \
 	ParserError ("%s-statements may only be defined at top level!", token.chars());
@@ -70,6 +71,7 @@
 bool g_CanElse = false;
 str* g_UndefinedLabels[MAX_MARKS];
 bool g_Neurosphere = false; // neurosphere-compat
+array<constinfo_t> g_ConstInfo;
 
 // ============================================================================
 // Main parser code. Begins read of the script file, checks the syntax of it
@@ -258,7 +260,7 @@
 			MUST_NOT_TOPLEVEL
 			MustNext ("{");
 			
-			// Don't use PushScope that will reset the scope.
+			// Don't use PushScope as it resets the scope
 			g_ScopeCursor++;
 			if (g_ScopeCursor >= MAX_SCOPE)
 				ParserError ("too deep scope");
@@ -554,6 +556,47 @@
 		}
 		
 		// ============================================================
+		if (token == "const") {
+			constinfo_t info;
+			
+			// Get the type
+			MustNext ();
+			info.type = GetTypeByName (token);
+			
+			if (info.type == TYPE_UNKNOWN || info.type == TYPE_VOID)
+				ParserError ("unknown type `%s` for constant", (char*)token);
+			
+			MustNext ();
+			info.name = token;
+			
+			MustNext ("=");
+			
+			switch (info.type) {
+			case TYPE_BOOL:
+			case TYPE_INT:
+				MustNumber (false);
+				info.val = token;
+				break;
+			case TYPE_STRING:
+				MustString ();
+				info.val = token;
+				break;
+			case TYPE_FLOAT:
+				MustNext ();
+				info.val = ParseFloat ();
+				break;
+			case TYPE_UNKNOWN:
+			case TYPE_VOID:
+				break;
+			}
+			
+			g_ConstInfo << info;
+			
+			MustNext (";");
+			continue;
+		}
+		
+		// ============================================================
 		if (token == "}") {
 			// Closing brace
 			
@@ -821,7 +864,7 @@
 
 // ============================================================================
 // Parses an expression, potentially recursively
-DataBuffer* ScriptReader::ParseExpression (int reqtype) {
+DataBuffer* ScriptReader::ParseExpression (type_e reqtype) {
 	DataBuffer* retbuf = new DataBuffer (64);
 	
 	// Parse first operand
@@ -880,6 +923,9 @@
 	else
 		oper += token;
 	
+	if (-oper == "strlen")
+		return OPER_STRLEN;
+	
 	// Check one-char operators
 	bool equalsnext = ISNEXT ("=");
 	
@@ -940,10 +986,26 @@
 }
 
 // ============================================================================
+str ScriptReader::ParseFloat () {
+	MustNumber (true);
+	str floatstring = token;
+	
+	// Go after the decimal point
+	if (PeekNext () == ".") {
+		Next (".");
+		MustNumber (false);
+		floatstring += ".";
+		floatstring += token;
+	}
+	
+	return floatstring;
+}
+
+// ============================================================================
 // Parses a value in the expression and returns the data needed to push
 // it, contained in a data buffer. A value can be either a variable, a command,
 // a literal or an expression.
-DataBuffer* ScriptReader::ParseExprValue (int reqtype) {
+DataBuffer* ScriptReader::ParseExprValue (type_e reqtype) {
 	DataBuffer* b = new DataBuffer(16);
 	
 	ScriptVar* g;
@@ -953,7 +1015,24 @@
 	if (negate) // Jump past the "!"
 		Next ();
 	
-	if (token == "(") {
+	// Handle strlen
+	if (token == "strlen") {
+		MustNext ("(");
+		MustNext ();
+		
+		// By this token we should get a string constant.
+		constinfo_t* constant = FindConstant (token);
+		if (!constant || constant->type != TYPE_STRING)
+			ParserError ("strlen only works with const str");
+		
+		if (reqtype != TYPE_INT)
+			ParserError ("strlen returns int but %s is expected\n", (char*)GetTypeName (reqtype));
+		
+		b->Write<word> (DH_PUSHNUMBER);
+		b->Write<word> (constant->val.len ());
+		
+		MustNext (")");
+	} else if (token == "(") {
 		// Expression
 		MustNext ();
 		DataBuffer* c = ParseExpression (reqtype);
@@ -966,15 +1045,38 @@
 		if (reqtype && comm->returnvalue != reqtype)
 			ParserError ("%s returns an incompatible data type", comm->name.chars());
 		b = ParseCommand (comm);
+	} else if (constinfo_t* constant = FindConstant (token)) {
+		// Type check
+		if (reqtype != constant->type)
+			ParserError ("constant `%s` is %s, expression requires %s\n",
+				(char*)constant->name, (char*)GetTypeName (constant->type),
+				(char*)GetTypeName (reqtype));
+		
+		switch (constant->type) {
+		case TYPE_BOOL:
+		case TYPE_INT:
+			b->Write<word> (DH_PUSHNUMBER);
+			b->Write<word> (atoi (constant->val));
+			break;
+		case TYPE_FLOAT:
+			b->WriteFloat (constant->val);
+			break;
+		case TYPE_STRING:
+			b->WriteString (constant->val);
+			break;
+		case TYPE_VOID:
+		case TYPE_UNKNOWN:
+			break;
+		}
 	} else if ((g = FindGlobalVariable (token))) {
 		// Global variable
 		b->Write<word> (DH_PUSHGLOBALVAR);
 		b->Write<word> (g->index);
 	} else {
 		// If nothing else, check for literal
-		printf ("reqtype: %d\n", reqtype);
 		switch (reqtype) {
 		case TYPE_VOID:
+		case TYPE_UNKNOWN:
 			ParserError ("unknown identifier `%s` (expected keyword, function or variable)", token.chars());
 			break;
 		case TYPE_BOOL:
@@ -996,37 +1098,21 @@
 			// string if it finds it in the table, or writes it to the
 			// table and returns it index if it doesn't find it there.
 			MustString (true);
-			b->Write<word> (DH_PUSHSTRINGINDEX);
-			b->Write<word> (PushToStringTable (token.chars()));
+			b->WriteString (token);
 			break;
 		case TYPE_FLOAT: {
-			str floatstring;
-			
-			MustNumber (true);
-			floatstring += token;
-			
-			// Go after the decimal point
-			if (PeekNext () == ".") {
-				MustNext (".");
-				MustNumber (false);
-				floatstring += ".";
-				floatstring += token;
-			}
-			
-			// TODO: Casting float to word causes the decimal to be lost.
-			// Find a way to store the number without such loss.
-			float val = atof (floatstring);
-			b->Write<word> (DH_PUSHNUMBER);
-			b->Write<word> (static_cast<word> ((val > 0) ? val : -val));
-			if (val < 0)
-				b->Write<word> (DH_UNARYMINUS);
+			str floatstring = ParseFloat ();
 			
 			// TODO: Keep this check after decimal loss is fixed, but make
 			// it a real precision loss check. 55.5123 -> 55.512299, this
 			// should probably be warned of.
-			float check = static_cast<float> (static_cast<word> (val));
-			if (val != check)
-				ParserWarning ("floating point number %f loses precision (-> %f)", val, check);
+			float check = static_cast<float> (static_cast<word> (atof (floatstring)));
+			if (atof (floatstring) != check)
+				ParserWarning ("floating point number %f loses precision (-> %f)",
+					atof (floatstring), check);
+			
+			b->WriteFloat (floatstring);
+			break;
 		}
 		}
 	}
@@ -1048,7 +1134,6 @@
 	// Get an operator
 	MustNext ();
 	int oper = ParseOperator ();
-	printf ("got operator %d\n", oper);
 	if (!IsAssignmentOperator (oper))
 		ParserError ("expected assignment operator");
 	
@@ -1099,11 +1184,12 @@
 }
 
 DataBuffer* ScriptReader::ParseStatement (ObjWriter* w) {
+	if (FindConstant (token)) // There should not be constants here.
+		ParserError ("invalid use for constant\n");
+	
 	// If it's a variable, expect assignment.
-	if (ScriptVar* var = FindGlobalVariable (token)) {
-		DataBuffer* b = ParseAssignment (var);
-		return b;
-	}
+	if (ScriptVar* var = FindGlobalVariable (token))
+		return ParseAssignment (var);
 	
 	return NULL;
 }
@@ -1129,4 +1215,11 @@
 	// 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;
+}
+
+constinfo_t* FindConstant (str token) {
+	for (uint i = 0; i < g_ConstInfo.size(); i++)
+		if (g_ConstInfo[i].name == token)
+			return &g_ConstInfo[i];
+	return NULL;
 }
\ No newline at end of file
--- a/scriptreader.h	Wed Dec 19 04:20:02 2012 +0200
+++ b/scriptreader.h	Wed Dec 19 13:44:18 2012 +0200
@@ -43,8 +43,8 @@
 
 #include <stdio.h>
 #include "str.h"
+#include "commands.h"
 #include "objwriter.h"
-#include "commands.h"
 
 #define MAX_FILESTACK 8
 #define MAX_SCOPE 32
@@ -52,14 +52,6 @@
 
 class ScriptVar;
 
-enum type_e {
-	TYPE_VOID = 0,
-	TYPE_INT,
-	TYPE_STRING,
-	TYPE_FLOAT,
-	TYPE_BOOL
-};
-
 // Operators
 enum operator_e {
 	OPER_ADD,
@@ -89,6 +81,7 @@
 	OPER_BITWISEAND,
 	OPER_BITWISEEOR,
 	OPER_TERNARY,
+	OPER_STRLEN,
 };
 
 // Mark types
@@ -135,6 +128,13 @@
 };
 
 // ============================================================================
+typedef struct {
+	str name;
+	type_e type;
+	str val;
+} constinfo_t;
+
+// ============================================================================
 // The script reader reads the script, parses it and tells the object writer
 // the bytecode it needs to write to file.
 class ScriptReader {
@@ -181,10 +181,11 @@
 	// parser.cxx:
 	void ParseBotScript (ObjWriter* w);
 	DataBuffer* ParseCommand (CommandDef* comm);
-	DataBuffer* ParseExpression (int reqtype);
+	DataBuffer* ParseExpression (type_e reqtype);
 	DataBuffer* ParseAssignment (ScriptVar* var);
 	int ParseOperator (bool peek = false);
-	DataBuffer* ParseExprValue (int reqtype);
+	DataBuffer* ParseExprValue (type_e reqtype);
+	str ParseFloat ();
 	void PushScope ();
 	
 	// preprocessor.cxx:
@@ -204,6 +205,7 @@
 	str PPReadWord (char &term);
 };
 
+constinfo_t* FindConstant (str token);
 extern bool g_Neurosphere;
 
 #endif // __SCRIPTREADER_H__
\ No newline at end of file

mercurial