%{ #include #include #include #include #include #include "robotworld.h" #define C the_str[the_ptr] #define C1 the_str[the_ptr+1] // local static char the_str[1000]; static int the_ptr; static int line; static FILE *fil; static int uniq; static std::vector variables; static std::string codestr; // total bytecode string static std::map codemap; // method to address map static bool failed; static int x_ptr; const std::string& GetCodestr() { return codestr; } std::map& GetCodemap() { return codemap; } std::vector& GetVariables() { return variables; } bool GetFailed() { return failed; } // forward declarations int yyparse(); short reg_variable(const std::string& ,int type = 0); void check_property(const std::string& , const std::string& ); void check_method(const std::string& , const std::string& ); void add_bytecode(char *, int opcode); void add_bytecode(char *, int opcode, short val); void add_bytecode(char *, int opcode, long val); void add_bytecode(char *, int opcode, const std::string& object, const std::string& property_or_method); %} %union { long l; char str[1000]; struct codestruct { char code[10000]; char bytecode[10000]; } code; } %token INT %token IDENTIFIER %token COMMENT %token Object %token EnumObjectType %token Cos Sin %token If Loop %token EQ NEQ AND OR %left ':' %left AND OR %left EQ NEQ %left '?' %left '-' '+' %left '*' '/' %left '!' %type expr %type parameters parameter %type statements statement %type if_statement assign_statement loop_statement %type object_statement %type program methods method scope %% program : methods methods : method | methods method method : IDENTIFIER scope { // if (*scope) ... add_bytecode($2.bytecode, 0x84); // ret // create codestr / codemap codemap[$1] = codestr.size() / 2; // entry point codestr += $2.bytecode; } scope : '{' '}' { *$$.code = 0; *$$.bytecode = 0; } | '{' statements '}' { strcpy($$.code, $2.code); // bytecode strcpy($$.bytecode, $2.bytecode); } statements : statement { strcpy($$.code, $1.code); // bytecode strcpy($$.bytecode, $1.bytecode); } | statements statement { strcpy($$.code, $1.code); strcat($$.code, $2.code); // bytecode strcpy($$.bytecode, $1.bytecode); strcat($$.bytecode, $2.bytecode); } statement : if_statement { strcpy($$.code, $1.code); // bytecode strcpy($$.bytecode, $1.bytecode); } | assign_statement { strcpy($$.code, $1.code); // bytecode strcpy($$.bytecode, $1.bytecode); } | loop_statement { strcpy($$.code, $1.code); // bytecode strcpy($$.bytecode, $1.bytecode); } | object_statement { strcpy($$.code, $1.code); // bytecode strcpy($$.bytecode, $1.bytecode); } | COMMENT { *$$.code = 0; *$$.bytecode = 0; } if_statement : If '(' expr ')' scope { // if (*scope) ... int x = uniq++; *$$.code = 0; strcat($$.code, " // if_statement\n"); strcat($$.code, $3.code); sprintf($$.code + strlen($$.code), " jz label_%d\n", x); strcat($$.code, $5.code); sprintf($$.code + strlen($$.code), "label_%d:\n", x); // bytecode strcpy($$.bytecode, $3.bytecode); // get expression short len = strlen($5.bytecode) / 2; // length in bytes add_bytecode($$.bytecode, 0x0a, len); // jz strcat($$.bytecode, $5.bytecode); // scope } assign_statement : IDENTIFIER '=' expr { *$$.code = 0; strcat($$.code, " // assign_statement\n"); strcat($$.code, $3.code); sprintf($$.code + strlen($$.code), " ld $%s, a\n", $1); // short var = reg_variable($1, 1); // bytecode strcpy($$.bytecode, $3.bytecode); // get expression add_bytecode($$.bytecode, 0x0d, var); // variable[] = register1 } | Object '.' IDENTIFIER '=' expr { *$$.code = 0; strcat($$.code, " // assign_statement\n"); strcat($$.code, $5.code); sprintf($$.code + strlen($$.code), " ld $%s.%s, a\n", $1, $3); // check_property($1, $3); // bytecode strcpy($$.bytecode, $5.bytecode); // get expression add_bytecode($$.bytecode, 0x0f, $1, $3); // assign object.property } loop_statement : IDENTIFIER '=' Loop Object '.' IDENTIFIER scope { short var = reg_variable($1, 2); check_property($4, $6); // bytecode *$$.bytecode = 0; add_bytecode($$.bytecode, 0x82, $4, $6); // loop init object.property strcat($$.bytecode, "0000"); // loop body length size_t x = strlen($$.bytecode); add_bytecode($$.bytecode, 0x83, var); // assign object variable: 3 bytes short len = strlen($7.bytecode) / 2 + 3; // length in bytes add_bytecode($$.bytecode, 0x0a, len); // jz end of loop: 3 bytes strcat($$.bytecode, $7.bytecode); // scope len = strlen($7.bytecode) / 2 + 6; len = -len; add_bytecode($$.bytecode, 0x81, len); // jmp loop beginning // size_t body = (strlen($$.bytecode) - x) / 2; char slask[100]; sprintf(slask, "%04x", body); memcpy($$.bytecode + x - 4, slask, 4); } | IDENTIFIER '=' Loop IDENTIFIER '.' IDENTIFIER scope { short var1 = reg_variable($1, 2); short var2 = reg_variable($4); short len = strlen($6); *$$.bytecode = 0; add_bytecode($$.bytecode, 0x11, var2); // register1 = variable[zz zz].property sprintf($$.bytecode + strlen($$.bytecode), "%02x", len); for (int i = 0; i < len; i++) { sprintf($$.bytecode + strlen($$.bytecode), "%02x", $6[i]); } strcat($$.bytecode, "0000"); // loop body length size_t x = strlen($$.bytecode); add_bytecode($$.bytecode, 0x83, var1); // assign object variable: 3 bytes len = strlen($7.bytecode) / 2 + 3; // length in bytes add_bytecode($$.bytecode, 0x0a, len); // jz end of loop: 3 bytes strcat($$.bytecode, $7.bytecode); // scope len = strlen($7.bytecode) / 2 + 6; len = -len; add_bytecode($$.bytecode, 0x81, len); // jmp loop beginning // size_t body = (strlen($$.bytecode) - x) / 2; char slask[100]; sprintf(slask, "%04x", body); memcpy($$.bytecode + x - 4, slask, 4); } object_statement : Object '.' IDENTIFIER '(' parameters ')' { *$$.code = 0; strcat($$.code, " // object_statement\n"); strcat($$.code, $5.code); sprintf($$.code + strlen($$.code), " call $%s.%s\n", $1, $3); strcat($$.code, " pop a // ? - call returns with value in a, not on stack perhaps?\n"); // check_method($1, $3); // bytecode *$$.bytecode = 0; strcpy($$.bytecode, $5.bytecode); // parameters add_bytecode($$.bytecode, 0x0e, $1, $3); // call object.method result in register1 } parameters : { *$$.code = 0; *$$.bytecode = 0; } | parameter { strcpy($$.code, $1.code); // bytecode strcpy($$.bytecode, $1.bytecode); } | parameters ', ' parameter { strcpy($$.code, $1.code); strcat($$.code, $3.code); // bytecode strcpy($$.bytecode, $1.bytecode); strcat($$.bytecode, $3.bytecode); } parameter : expr { strcpy($$.code, $1.code); strcat($$.code, " push a\n"); // bytecode strcpy($$.bytecode, $1.bytecode); add_bytecode($$.bytecode, 0x02); // push register1 } expr : INT { sprintf($$.code, " ld a, %ld\n", $1); // bytecode *$$.bytecode = 0; add_bytecode($$.bytecode, 0x03, $1); } | EnumObjectType { sprintf($$.code, " ld a, %ld\n", $1); // bytecode *$$.bytecode = 0; add_bytecode($$.bytecode, 0x03, $1); } | Object '.' IDENTIFIER { sprintf($$.code, " ld a, $%s.%s\n", $1, $3); // check_property($1, $3); // bytecode *$$.bytecode = 0; add_bytecode($$.bytecode, 0x01, $1, $3); // register1 = object.property } | IDENTIFIER { sprintf($$.code, " ld a, $%s\n", $1); // short var = reg_variable($1); // bytecode *$$.bytecode = 0; add_bytecode($$.bytecode, 0x0c, var); // register1 = variable } | IDENTIFIER '.' IDENTIFIER { sprintf($$.code, " ld a, $%s.%s\n", $1, $3); // short var = reg_variable($1); short len = strlen($3); check_property($1, $3); // bytecode *$$.bytecode = 0; add_bytecode($$.bytecode, 0x10, var); // register1 = variable[zz zz].property sprintf($$.bytecode + strlen($$.bytecode), "%02x", len); for (int i = 0; i < len; i++) { sprintf($$.bytecode + strlen($$.bytecode), "%02x", $3[i]); } } | expr '+' expr { strcpy($$.code, $3.code); strcat($$.code, " push a\n"); strcat($$.code, $1.code); strcat($$.code, " pop b\n"); strcat($$.code, " add b\n"); // bytecode strcpy($$.bytecode, $3.bytecode); add_bytecode($$.bytecode, 0x02); // push register1 strcat($$.bytecode, $1.bytecode); add_bytecode($$.bytecode, 0x24); // pop register2 add_bytecode($$.bytecode, 0x25); // add register2 } | expr '-' expr { strcpy($$.code, $3.code); strcat($$.code, " push a\n"); strcat($$.code, $1.code); strcat($$.code, " pop b\n"); strcat($$.code, " sub b\n"); // bytecode strcpy($$.bytecode, $3.bytecode); add_bytecode($$.bytecode, 0x02); // push register1 strcat($$.bytecode, $1.bytecode); add_bytecode($$.bytecode, 0x24); // pop register2 add_bytecode($$.bytecode, 0x26); // add register2 } | expr '*' expr { strcpy($$.code, $3.code); strcat($$.code, " push a\n"); strcat($$.code, $1.code); strcat($$.code, " pop b\n"); strcat($$.code, " mul b\n"); // bytecode strcpy($$.bytecode, $3.bytecode); add_bytecode($$.bytecode, 0x02); // push register1 strcat($$.bytecode, $1.bytecode); add_bytecode($$.bytecode, 0x24); // pop register2 add_bytecode($$.bytecode, 0x27); // add register2 } | expr '/' expr { strcpy($$.code, $3.code); strcat($$.code, " push a\n"); strcat($$.code, $1.code); strcat($$.code, " pop b\n"); strcat($$.code, " div b\n"); // bytecode strcpy($$.bytecode, $3.bytecode); add_bytecode($$.bytecode, 0x02); // push register1 strcat($$.bytecode, $1.bytecode); add_bytecode($$.bytecode, 0x24); // pop register2 add_bytecode($$.bytecode, 0x28); // add register2 } | expr EQ expr { strcpy($$.code, $1.code); strcat($$.code, " push a\n"); strcat($$.code, $3.code); strcat($$.code, " pop b\n"); strcat($$.code, " eq b // a=0 if a!=b, a=1 if a==b\n"); // bytecode strcpy($$.bytecode, $3.bytecode); add_bytecode($$.bytecode, 0x02); // push register1 strcat($$.bytecode, $1.bytecode); add_bytecode($$.bytecode, 0x24); // pop register2 add_bytecode($$.bytecode, 0x29); // add register2 } | expr '?' expr ':' expr { int x = uniq++; strcpy($$.code, $1.code); sprintf($$.code + strlen($$.code), " jz label_second_%d\n", x); strcat($$.code, $3.code); sprintf($$.code + strlen($$.code), " jmp label_%d\n", x); sprintf($$.code + strlen($$.code), "label_second_%d:\n", x); strcat($$.code, $5.code); sprintf($$.code + strlen($$.code), "label_%d:\n", x); // bytecode strcpy($$.bytecode, $1.bytecode); // get expression short len = strlen($3.bytecode) / 2 + 3; // first block + jmp add_bytecode($$.bytecode, 0x0a, len); // jz strcat($$.bytecode, $3.bytecode); len = strlen($5.bytecode) / 2; add_bytecode($$.bytecode, 0x81, len); // jmp strcat($$.bytecode, $5.bytecode); } | '(' expr ')' { strcpy($$.code, $2.code); // bytecode strcpy($$.bytecode, $2.bytecode); } | '!' expr { *$$.bytecode = 0; strcpy($$.bytecode, $2.bytecode); add_bytecode($$.bytecode, 0x12); // not } | expr NEQ expr { *$$.bytecode = 0; strcpy($$.bytecode, $3.bytecode); add_bytecode($$.bytecode, 0x02); // push register1 strcat($$.bytecode, $1.bytecode); add_bytecode($$.bytecode, 0x24); // pop register2 add_bytecode($$.bytecode, 0x33); // neq register2 } | expr AND expr { *$$.bytecode = 0; strcpy($$.bytecode, $3.bytecode); add_bytecode($$.bytecode, 0x02); // push register1 strcat($$.bytecode, $1.bytecode); add_bytecode($$.bytecode, 0x24); // pop register2 add_bytecode($$.bytecode, 0x34); // and register2 } | expr OR expr { *$$.bytecode = 0; strcpy($$.bytecode, $3.bytecode); add_bytecode($$.bytecode, 0x02); // push register1 strcat($$.bytecode, $1.bytecode); add_bytecode($$.bytecode, 0x24); // pop register2 add_bytecode($$.bytecode, 0x35); // or register2 } %% int yyerror(char *s) { int i; printf("%d>%s\n", line, the_str); printf("%d ", line); for (i = 1; i < x_ptr; i++) printf(" "); printf("^ yyerror '%s'\n", s); failed = true; return 0; } int yylex() { size_t x; while (C == ' ' || C == 9) { the_ptr++; } while (!C && !feof(fil)) { fgets(the_str, 1000, fil); the_str[strlen(the_str) - 1] = 0; for (size_t i = 0; i < strlen(the_str); i++) if (the_str[i] == 9) the_str[i] = ' '; the_ptr = 0; line++; while (C == ' ' || C == 9) { the_ptr++; } } if (feof(fil)) { return 0; } x_ptr = the_ptr; if (C == '=' && C1 == '=') { the_ptr += 2; return EQ; } if (C == '!' && C1 == '=') { the_ptr += 2; return NEQ; } if (C == '&' && C1 == '&') { the_ptr += 2; return AND; } if (C == '|' && C1 == '|') { the_ptr += 2; return OR; } if (C == '/' && C1 == '/') { strcpy(yylval.str, the_str + the_ptr); C = 0; return COMMENT; } if (isalpha(C)) { x = the_ptr++; while (isalnum(C) || C == '_') { the_ptr++; } std::string ord = static_cast(the_str).substr(x, the_ptr - x); strcpy(yylval.str, ord.c_str()); if (ord == "if") return If; if (ord == "radar" || ord == "turret" || ord == "gun" || ord == "robot" || ord == "shields" ) { return Object; } if (ord == "Robot") // Robot, Portal, Powerup, Base, Nothing, Item { yylval.l = MAPOBJECT_ROBOT; return EnumObjectType; } if (ord == "Portal") { yylval.l = MAPOBJECT_PORTAL; return EnumObjectType; } if (ord == "Powerup") { yylval.l = MAPOBJECT_POWERUP_UNKNOWN; return EnumObjectType; } if (ord == "Base") { yylval.l = MAPOBJECT_BASE; return EnumObjectType; } if (ord == "Item") { yylval.l = MAPOBJECT_ITEM; return EnumObjectType; } if (ord == "Nothing") { yylval.l = MAPOBJECT_NOTHING; return EnumObjectType; } if (ord == "loop") return Loop; return IDENTIFIER; } if (isdigit(C)) { bool f = false; x = the_ptr; while (isdigit(C)) // || C == '.' || C == 'e' || C == 'E') { if (!isdigit(C)) f = true; the_ptr++; } std::string ord = static_cast(the_str).substr(x, the_ptr - x); /* if (f) { yylval.d = atof(ord.c_str()); return FLOAT; } */ yylval.l = atol(ord.c_str()); return INT; } return the_str[the_ptr++]; } void parse_file(const std::string& filename) { *the_str = 0; the_ptr = 0; line = 0; uniq = 1; codestr = ""; failed = false; while (codemap.size()) { codemap.erase(codemap.begin()); } while (variables.size()) { variables.erase(variables.begin()); } if ((fil = fopen(filename.c_str(), "rt")) != NULL) { yyparse(); fclose(fil); } else { fprintf(stderr, "Couldn't open '%s'...\n", filename.c_str()); failed = true; } } short reg_variable(const std::string& name,int type) { for (std::vector::iterator it = variables.begin(); it != variables.end(); it++) { VARIABLE *p = *it; if (name == p -> name) { if (type && !p -> type) p -> type = type; if (type && p -> type && type != p -> type) fprintf(stderr,"Variable type mismatch: '%s'\n",name.c_str()); return p -> nr; } } VARIABLE *p = new VARIABLE(name, type, variables.size()); variables.push_back(p); return p -> nr; } void check_property(const std::string& object, const std::string& property) { } void check_method(const std::string& object, const std::string& method) { } void add_bytecode(char *s, int opcode) { sprintf(s + strlen(s), "%02x", opcode); } void add_bytecode(char *s, int opcode, short valx) { unsigned short val; if (valx < 0) { val = -valx; val |= 0x8000; } else { val = valx; } sprintf(s + strlen(s), "%02x%04x", opcode, val); } void add_bytecode(char *s, int opcode, long val) { sprintf(s + strlen(s), "%02x%08lx", opcode, val); } void add_bytecode(char *s, int opcode, const std::string& object, const std::string& property_or_method) { int objectcode = 0; int val = 0; { // robot, radar, turret, gun, shields if (!strcmp(object.c_str(), "robot")) { objectcode = ROBOTOBJECT_ROBOT; if (property_or_method == "ok") val = 1; if (property_or_method == "damage") val = 2; if (property_or_method == "repair_rate") val = 3; if (property_or_method == "inventory") val = 4; if (property_or_method == "power") val = 5; if (property_or_method == "charging") val = 6; } else if (!strcmp(object.c_str(), "radar")) { objectcode = ROBOTOBJECT_RADAR; if (property_or_method == "ok") val = 1; if (property_or_method == "damage") val = 2; if (property_or_method == "repair_rate") val = 3; } else if (!strcmp(object.c_str(), "turret")) { objectcode = ROBOTOBJECT_TURRET; if (property_or_method == "ok") val = 1; if (property_or_method == "damage") val = 2; if (property_or_method == "repair_rate") val = 3; } else if (!strcmp(object.c_str(), "gun")) { objectcode = ROBOTOBJECT_GUN; if (property_or_method == "ok") val = 1; if (property_or_method == "damage") val = 2; if (property_or_method == "repair_rate") val = 3; } else if (!strcmp(object.c_str(), "shields")) { objectcode = ROBOTOBJECT_SHIELDS; if (property_or_method == "ok") val = 1; if (property_or_method == "damage") val = 2; if (property_or_method == "repair_rate") val = 3; } else { printf("Unknown object: %s\n", object.c_str() ); failed = true; } } sprintf(s + strlen(s), "%02x%02x%02x", opcode, objectcode, val); }