rowo.y
%{
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include <map>
#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<VARIABLE *> variables;
static std::string codestr; // total bytecode string
static std::map<std::string, int> codemap; // method to address map
static bool failed;
static int x_ptr;
const std::string& GetCodestr() { return codestr; }
std::map<std::string, int>& GetCodemap() { return codemap; }
std::vector<VARIABLE *>& 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 <l> INT
%token <str> IDENTIFIER
%token <str> COMMENT
%token <str> Object
%token <l> EnumObjectType
%token Cos Sin
%token If Loop
%token EQ NEQ AND OR
%left ':'
%left AND OR
%left EQ NEQ
%left '?'
%left '-' '+'
%left '*' '/'
%left '!'
%type <code> expr
%type <code> parameters parameter
%type <code> statements statement
%type <code> if_statement assign_statement loop_statement
%type <code> object_statement
%type <code> 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<std::string>(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<std::string>(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<VARIABLE *>::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);
}
|