// This file is a part of the Framsticks GDK. // Copyright (C) 1999-2014 Maciej Komosinski and Szymon Ulatowski. See LICENSE.txt for details. // Refer to http://www.framsticks.com/ for further information. %{ #include "framscript-defs.h" #include "common/framsg.h" #include #include #include #define YYERROR_VERBOSE #define YYPRINT(file,type,value) yyprint (file,type,value) static void yyprint (FILE *file,int type,YYSTYPE value); void handleTwoArg(YYSTYPE& result,const YYSTYPE& arg1,const YYSTYPE& arg2, int optoken,const char* opname, bool logic, bool negarg2, bool uniq); void handleCompare(YYSTYPE& result,const YYSTYPE& arg1,const YYSTYPE& arg2, int optoken,const char* opname); bool handleAssignOp(YYSTYPE& result,const YYSTYPE& var,const YYSTYPE& arg,const char* opname); bool handleAssignOp2(YYSTYPE& result,const char *var,const YYSTYPE& arg,const char* opname,int stackpos,bool push); bool variableOk(TokenValue &tok, const TokenValue& var,int &loc); bool globalOk(const TokenValue& var); void badVariable(TokenValue &tok, const TokenValue &var); bool evalVariable(TokenValue &tok, const TokenValue &var); bool doBreak(int level); bool doContinue(int level); void warnTruthValue(const TokenValue& t); void outFunName(const TokenValue& t); static const char* assign_op_names[]={"add","sub","mul","div","mod"}; %} %token_table %token CONSTANT %nonassoc ASSIGN_ADD ASSIGN_SUB ASSIGN_MUL ASSIGN_DIV ASSIGN_MOD %nonassoc PLUSPLUS MINUSMINUS %left LOGIC_AND LOGIC_OR '!' %left EQUAL NOT_EQUAL GEQUAL LEQUAL '>' '<' %left '|' '&' '^' %left '-' '+' %left '*' '/' '%' %left NEG /* negation--unary minus */ %left TYPEOF %token IDENT %token OBJNAME %token IF "if" %token ELSE "else" %token FOR "for" %token INNN "in" %token WHILE "while" %token DO "do" %token GOTO "goto" %token RETURN "return" %token BREAK "break" %token CONTINUE "continue" %token SWITCH "switch" %token CASE "case" %token DEFAULT "default" %token TYPEOF "typeof" %token ASM %token ASMLINE %token VAR "var" %token GLOBAL "global" %token FUNCTION "function" %token CALL "call" %token ASSIGN %token ASSIGN_ADD %token ASSIGN_SUB %token ASSIGN_MUL %token ASSIGN_DIV %token EQUAL %token NOT_EQUAL %token GEQUAL %token LEQUAL %token LOGIC_AND %token LOGIC_OR %token PLUSPLUS %token MINUSMINUS %token LSHIFT %token RSHIFT %% code: {$$.setInt(trstack.currentPos());} recurcode { int pos=$1.getInt(); if (pos!=trstack.currentPos()) trctx.out->printf("add %d,m0\n",pos-trstack.currentPos()); } ; recurcode: /* empty string */ | recurcode statement ; statement: ';' | VAR vardeflist ';' | GLOBAL globaldeflist ';' | IDENT ':' {trctx.out->printf(":%s\n",str($1));} | expr ';' {if (!$1.constant) { trctx.out->printf("inc m0\n"); trstack.adjust(+1); } } | functiondef | blok | if_statement | goto_statement | return_statement | for_statement | while_statement | dowhile_statement | break_statement | continue_statement | switch_statement // | error ';' | asmblock ; asmblock: ASM asmlines '}' ; asmlines: /* empty */ | ASMLINE {fputs(str($1),trctx.out);fputc('\n',trctx.out);} | asmlines ASMLINE {fputs(str($2),trctx.out);fputc('\n',trctx.out);} ; goto_statement: GOTO IDENT ';' { #ifdef FRAMSCRIPT_GOTO trctx.out->printf("jump :%s\n",str($2)); FMprintf("FramScriptCompiler","translate",FMLV_WARN,"goto is not recommended (%s line %d)",(const char*)trctx.srcname,trctx.line); #else trctx.err->printf("goto is not supported");return 1; #endif } ; return_statement: RETURN expr ';' { int offset; if (trctx.functionstackpos==999) offset=-trstack.currentPos(); else offset=trctx.functionstackpos-trstack.currentPos(); if (!offset) { if ($2.constant) trctx.out->printf("move %s,s0\nreturn\n",litstr($2)); else { trctx.out->printf("move m[m0++],s0\nreturn\n"); trstack.adjust(+1); } } else { if ($2.constant) { trctx.out->printf("add %d,m0\nmove %s,s0\nreturn\n",offset,litstr($2)); trstack.adjust(offset); } else { trctx.out->printf("move s0,s%d\nadd %d,m0\nreturn\n",offset,offset); trstack.adjust(offset); } } } | RETURN ';' { int offset; if (trctx.functionstackpos==999) offset=-trstack.currentPos(); else offset=trctx.functionstackpos-trstack.currentPos(); if (!offset) trctx.out->printf("move invalid,s0\nreturn\n"); else trctx.out->printf("add %d,m0\nmove invalid,s0\nreturn\n",offset); } ; vardeflist: vardef | vardeflist ',' vardef ; vardef: IDENT {trstack.addVariable($1.getString()); trctx.out->printf("push invalid\n");} | IDENT '=' stackexpr {trstack.adjust(1); trstack.addVariable($1.getString());} ; globaldeflist: globaldef | globaldeflist ',' globaldef ; globaldef: IDENT {trstack.globals.add($1.getString(),0); trctx.out->printf("global %s\n",str($1));} ; funparam: IDENT { trstack.addVariable($1.getString()); }; paramlist: /* empty */ {$$.setInt(0); } | funparam {$$.setInt(1); } | paramlist ',' funparam {$$.setInt($1.getInt()+1);} ; funnamelist: IDENT {outFunName($1);} | funnamelist ',' IDENT {outFunName($3);} ; functiondef: FUNCTION funnamelist { int pos=trstack.currentPos(); $$.setInt(pos); if (trctx.functionstackpos!=999) {trctx.err->printf("functions cannot be nested");return 1;} } '(' paramlist ')' { trctx.functionstackpos=trstack.currentPos(); } blok {trctx.out->printf("move invalid,s0\nreturn\n"); int pos=$3.getInt(); trstack.dropToPos(pos); trctx.functionstackpos=999; trctx.out->printf(":_skipfun_%d\n",trctx.functiontmplabel); trctx.functiontmplabel=-1; }; break_statement: BREAK ';' {if (!doBreak(1)) return 1;} | BREAK expr ';' { if (!$2.constant) {trctx.err->printf("break level must be a constant expression");return 1;} int level=$2.getInt(); if (level<1) {trctx.err->printf("break level must be a positive integer");return 1;} if (!doBreak(level)) return 1; }; continue_statement: CONTINUE ';' {if (!doContinue(1)) return 1;} | CONTINUE expr ';' { if (!$2.constant) {trctx.err->printf("continue level must be a constant expression");return 1;} int level=$2.getInt(); if (level<1) {trctx.err->printf("continue level must be a positive integer");return 1;} if (!doContinue(level)) return 1; }; while_statement: WHILE '(' { int c=trctx.labelcounter++; $$.setInt(c); $$.stack=trstack.currentPos(); trstack.loops.addLoop(c,$$.stack); trctx.out->printf(":_loop_%d\n",c);} expr ')' { int c=$3.getInt(); warnTruthValue($4); if ($4.constant) {if (!$4.getInt()) trctx.out->printf("jump :_loop_end_%d\n",c);} else { trctx.out->printf("if m[m0++],==,0,:_loop_end_%d\n",c,c); trstack.adjust(+1); } } pseudoblok_statement {trctx.out->printf("jump :_loop_%d\n:_loop_end_%d\n",$3.getInt(),$3.getInt()); trstack.adjust($3.stack-trstack.currentPos()); trstack.loops.drop(); } ; dowhile_statement: DO { int c=trctx.labelcounter++; $$.setInt(c); $$.stack=trstack.currentPos(); trstack.loops.addLoop(c,$$.stack); trctx.out->printf(":_loop_%d\n",c);} //2 pseudoblok_statement WHILE '(' expr ')' {//8 int c=$2.getInt(); warnTruthValue($6); if ($6.constant) {if ($6.getInt()) trctx.out->printf("jump :_loop_%d\n",c);} else { trctx.out->printf("if !=,m[m0++],:_loop_%d\n",c); trstack.adjust(+1); } trctx.out->printf(":_loop_end_%d\n",c); trstack.adjust($2.stack-trstack.currentPos()); trstack.loops.drop(); } ; switch_statement: SWITCH '(' {int c=trctx.labelcounter++; $1.setInt(c); trstack.loops.addLoop(c,trstack.currentPos());} stackexpr ')' {trctx.out->printf("dec m0\n");trstack.adjust(-1);} '{' inside_switch '}' {int c=$1.getInt(); trctx.out->printf("add 2,m0\n:_loop_end_%d\n",c); trstack.adjust(+2); trstack.loops.drop();} ; inside_switch: /* empty */ | case_label | inside_switch case_label ; case_label: CASE expr ':' {if (!$2.constant) {trctx.err->printf("case label must be a constant expression");return 1;} int c=trctx.labelcounter++; $1.setInt(c); trctx.out->printf("if s1,!=,%s,:_skip_%d\n",litstr($2),c); int pos=trstack.currentPos(); $$.setInt(pos); } recurcode { int pos=$4.getInt(); if (pos!=trstack.currentPos()) trctx.out->printf("add %d,m0\n",pos-trstack.currentPos()); trstack.dropToPos(pos); trctx.out->printf(":_skip_%d\n",$1.getInt()); } | DEFAULT ':' recurcode ; newvar_or_expr: VAR IDENT { $$.setInt(trstack.addVariable($2.getString())); trctx.out->printf("push invalid\n"); $$.ident=true; $$.var=true; } | VAR IDENT '=' stackexpr { //trctx.out->printf("# VAR IDENT '=' stackexpr pos=%d\n",trstack.currentPos()); trstack.adjust(+1); $$.setInt(trstack.addVariable($2.getString())); $$.ident=true; $$.var=true; } | expr_special_ident { $$=$1; } | //nic { $$.setInt(1); $$.assign=false; $$.ident=false; $$.var=false; $$.constant=true; } ; expr_or_objname: expr { $$=$1; $$.objname=false; } | OBJNAME { $$.setString($1.getString()); $$.objname=true; } ; for_statement_begin: FOR '(' { int c=trctx.labelcounter++; $$.counter=c; $$.stack=trstack.currentPos(); } newvar_or_expr { $$=$4; $$.counter=$3.counter; $$.stack=$3.stack; }; for_statement: /////////// for(in) ... //////////// for_statement_begin INNN {//3 if (!$1.ident) { trctx.err->printf("for(... in ...) requires an variable\n"); return 1; } int loc; if ($1.var) // for(var x[=expr] in $$.setInt($1.getInt()); else { // for(x in if (variableOk($$,$1,loc)) $$.setInt(loc); else if (globalOk($1)) { trctx.err->printf("global '%s' can't be iterating variable in for\n",str($1)); return 1; } else { badVariable($$,$1); return 1; } } } expr_or_objname ')' {//6 if ($4.constant) {trctx.err->printf("%s can't be iterated\n",str($4)); return 1;} if ($4.objname) trctx.out->printf("move %s.iterator,m[--m0]\n",(const char*)$4.getString()); else trctx.out->printf("move s%d,m1\nmove [m1].\"iterator\",m[--m0]\n",0); trstack.adjust(-1); // s0=iterator s1=obj (=obj.iterator) trstack.loops.addLoop($1.counter,trstack.currentPos()); trctx.out->printf(":_loop1_%d\n",$1.counter); trctx.out->printf(":_loop_%d\n",$1.counter); trctx.out->printf("move s0,m1\nmove [m1].\"next\",m2\n"); trctx.out->printf("if m2,==,0,:_loop_end_%d\n",$1.counter); trctx.out->printf("move [m1].\"value\",s%d\n",$3.getInt()-trstack.currentPos()); } pseudoblok_statement { trctx.out->printf("jump :_loop1_%d\n",$1.counter); trctx.out->printf(":_loop_end_%d\n",$1.counter); trstack.loops.drop(); if ($1.stack != trstack.currentPos()) trctx.out->printf("add %d,m0\n",$1.stack-trstack.currentPos()); trstack.adjust($1.stack-trstack.currentPos()); } | /////////// for(;;) ... //////////// for_statement_begin ';' { //3 //trctx.out->printf("# for_statement_begin pos=%d ident=%d var=%d\n",trstack.currentPos(),$1.ident,$1.var); if ((!$1.var) && ($1.ident)) { // for(x; int loc; if ((!variableOk($$,$1,loc)) || (globalOk($1))) { badVariable($$,$1); return 1; } } if (!$1.constant && !$1.ident) { trctx.out->printf("inc m0\n"); trstack.adjust(+1); } trstack.loops.addLoop($1.counter,trstack.currentPos()); trctx.out->printf(":_loop1_%d\n",$1.counter); //trctx.out->printf("# expr#2\n"); } expr_or_empty ';' { //6 int c=$1.counter; warnTruthValue($4); if ($4.constant) {if (!$4.getInt()) trctx.out->printf("jump :_loop_end_%d\n",c);} else { trctx.out->printf("if m[m0++],==,0,:_loop_end_%d\n",c,c); trstack.adjust(+1); } trctx.tmp=0; trctx.divertOut(); //trctx.out->printf("# expr#3\n"); } expr_or_empty ')' { //9 if (!$7.constant) { trctx.out->printf("inc m0\n"); trstack.adjust(+1); } trctx.restoreOut(); $$.setString(trctx.tmp); //trctx.out->printf("# pseudoblok_statement pos=%d\n",trstack.currentPos()); } pseudoblok_statement {//11 trctx.out->printf(":_loop_%d\n",$1.counter); LoopInfo* li=trstack.loops.getLoop(0); if (li->location != trstack.currentPos()) trctx.out->printf("add %d,m0\n",li->location-trstack.currentPos()); trctx.out->printf(str($9)); if (li->location != trstack.currentPos()) trctx.out->printf("sub %d,m0\n",li->location-trstack.currentPos()); trctx.out->printf("jump :_loop1_%d\n:_loop_end_%d\n",$1.counter,$1.counter); if ($1.stack != trstack.currentPos()) trctx.out->printf("add %d,m0\n",$1.stack-trstack.currentPos()); trstack.adjust($1.stack-trstack.currentPos()); trstack.loops.drop(); } ; pseudoblok_statement: {int pos=trstack.currentPos(); $$.setInt(pos); } statement { int pos=$1.getInt(); if (pos!=trstack.currentPos()) trctx.out->printf("add %d,m0\n",pos-trstack.currentPos()); trstack.dropToPos(pos); }; if_statement: if_condition pseudoblok_statement { if ($1.stack!=trstack.currentPos()) trctx.out->printf("add %d,m0\n",$1.stack-trstack.currentPos()); trstack.adjust(trstack.currentPos()-$1.stack); trctx.out->printf("jump :_if_end_%d\n:_if_else_%d\n",$1.getInt(),$1.getInt()); } ELSE {trstack.adjust($1.stack-trstack.currentPos());} pseudoblok_statement { if ($1.stack!=trstack.currentPos()) trctx.out->printf("add %d,m0\n",$1.stack-trstack.currentPos()); trstack.adjust(trstack.currentPos()-$1.stack); trctx.out->printf(":_if_end_%d\n",$1.getInt()); } | if_condition pseudoblok_statement { if ($1.stack!=trstack.currentPos()) trctx.out->printf("add %d,m0\n",$1.stack-trstack.currentPos()); trstack.dropToPos($1.stack); trctx.out->printf(":_if_else_%d\n",$1.getInt()); } ; if_condition: IF {$$.stack=trstack.currentPos();} '(' expr ')' { int c=trctx.labelcounter++; $$.setInt(c); warnTruthValue($4); if ($4.constant) { if (!$4.getInt()) trctx.out->printf("jump :_if_else_%d\n",c); } else { trctx.out->printf("if m[m0++],==,0,:_if_else_%d\n",c); trstack.adjust(+1); } $$.stack=$2.stack; }; blok: '{' { int pos=trstack.currentPos(); $$.setInt(pos); } recurcode '}' { int pos=$2.getInt(); if (pos!=trstack.currentPos()) trctx.out->printf("add %d,m0\n",pos-trstack.currentPos()); trstack.dropToPos(pos); } assign_op: ASSIGN_ADD {$$.setInt(0);} | ASSIGN_SUB {$$.setInt(1);} | ASSIGN_MUL {$$.setInt(2);} | ASSIGN_DIV {$$.setInt(3);} | ASSIGN_MOD {$$.setInt(4);} plusminus: PLUSPLUS {$$.setInt(1);} | MINUSMINUS {$$.setInt(0);} expr: expr_special_ident { //trctx.out->printf("# expr: ident=%d str=%s\n",$1.ident,(const char*)$1.getString()); if ($1.ident) { if (evalVariable($$,$1)) $$.constant=false; else return 1; } else {$$=$1; $$.ident=false;} } ; expr_or_empty: expr {$$=$1;} | //nic { $$.setInt(1); $$.assign=false; $$.constant=true; $$.ident=false; $$.ident=false; } ; expr_special_ident: CONSTANT { $$=$1; $$.constant=1; $$.ident=0; } | IDENT { $$.ident=true; $$.setString($1.getString()); } | OBJNAME ':' IDENT {$$.constant=0; $$.ident=0; trctx.out->printf("push %s:%s\n",(const char*)$1.getString(), (const char*)$3.getString()); trstack.adjust(-1); } | plusminus IDENT { $$.ident=0; int loc; if (variableOk($$,$2,loc)) { loc-=trstack.currentPos(); trctx.out->printf("%s s%d\npush s%d\n",$1.getInt()?"inc":"dec",loc,loc); trstack.adjust(-1);} else if (globalOk($2)) { trctx.out->printf("%s @%s\npush @%s\n",$1.getInt()?"inc":"dec",str($2),str($2)); trstack.adjust(-1);} else {badVariable($$,$2); return 1;} } | IDENT plusminus { $$.ident=0; int loc; if (variableOk($$,$1,loc)) {loc-=trstack.currentPos(); trctx.out->printf("push s%d\n%s s%d\n",loc,$2.getInt()?"inc":"dec",loc+1); trstack.adjust(-1);} else if (globalOk($1)) { trctx.out->printf("push @%s\n%s @%s\n",(const char*)$1.getString(), $2.getInt()?"inc":"dec",(const char*)$1.getString()); trstack.adjust(-1);} else {badVariable($$,$1); return 1;} } | IDENT assign_op expr { $$.ident=0; if (!handleAssignOp($$,$1,$3,assign_op_names[$2.getInt()])) if (globalOk($1)) {SString t="@"; t+=$1.getString(); handleAssignOp2($$,(const char*)t,$3,assign_op_names[$2.getInt()],0,1);} else { badVariable($$,$1); return 1; } } | TYPEOF expr { $$.ident=0; if ($2.constant) {$$.constant=1; $$=$2.getExtType();} else {trctx.out->printf("type s0,s0\n");} } | expr '+' expr { handleTwoArg($$,$1,$3,'+',"add",0,0,1); } | expr '-' expr { handleTwoArg($$,$1,$3,'-',"sub",0,0,0); } | expr '*' expr { handleTwoArg($$,$1,$3,'*',"mul",0,0,1); } | expr '/' expr { handleTwoArg($$,$1,$3,'/',"div",0,0,0); } | expr '&' expr { handleTwoArg($$,$1,$3,'&',"and",0,0,0); } | expr '|' expr { handleTwoArg($$,$1,$3,'|',"or",0,0,0); } | expr '%' expr { handleTwoArg($$,$1,$3,'%',"mod",0,0,0); } | expr LOGIC_AND expr { handleTwoArg($$,$1,$3,LOGIC_AND,"and",1,0,0); } | expr LOGIC_OR expr { handleTwoArg($$,$1,$3,LOGIC_OR,"or",1,0,0); } | expr LSHIFT expr { handleTwoArg($$,$1,$3,LSHIFT,"shift",0,0,0); } | expr RSHIFT expr { handleTwoArg($$,$1,$3,RSHIFT,"shift",0,1,0); } | expr EQUAL expr { handleCompare($$,$1,$3,EQUAL,"=="); } | expr NOT_EQUAL expr { handleCompare($$,$1,$3,NOT_EQUAL,"!="); } | expr GEQUAL expr { handleCompare($$,$1,$3,GEQUAL,">="); } | expr LEQUAL expr { handleCompare($$,$1,$3,LEQUAL,"<="); } | expr '>' expr { handleCompare($$,$1,$3,'>',">"); } | expr '<' expr { handleCompare($$,$1,$3,'<',"<"); } | '!' expr { $$.assign=$2.assign; $$.parens=0; $$.ident=0; if ($2.constant) {$$.constant=1; $$.setInt(!$2.getInt());} else {trctx.out->printf("setif ==,s0,s0\n");} } | '-' expr %prec NEG { $$.assign=$2.assign; $$.parens=0; $$.ident=0; if ($2.constant) { $$.constant=$2.constant; if ($2.type==TInt) $$.setInt(-$2.getInt()); else if ($2.type==TDouble) $$.setDouble(-$2.getDouble()); else $$=$2; } else { $$.constant=0; SString t="-"; t+=$2.getString(); $$.setString(t); trctx.out->printf("mul -1,s0\n"); } } | '(' expr ')' { $$ = $2; $$.assign=$2.assign?(!$2.parens):0; $$.parens=1; $$.ident=0; } | OBJNAME '.' member { $$.constant=0; $$.ident=0; SString t=$1.getString(); t+="."; t+=$3.getString(); $$.setString(t); if ($3.constant) { trctx.out->printf("push %s.%s\n",str($1),str($3)); trstack.adjust(-1); } else { trctx.out->printf("move s0,m1\nmove %s.[m1],s0\n",str($1)); } } | OBJNAME '.' member assign_op expr { $$.constant=0; $$.ident=0; SString t=$1.getString(); t+="."; t+=$3.getString(); $$.setString(t); if ($3.constant) { handleAssignOp2($$,(const char*)t,$5,assign_op_names[$4.getInt()],0,1); } else { int sp=($5.constant)?0:1; t=$1.getString();t+=".[m1]"; trctx.out->printf("move s0,m1\n",str($1)); handleAssignOp2($$,(const char*)t,$5,assign_op_names[$4.getInt()],sp,0); if (sp) {trctx.out->printf("inc m0\n"); trstack.adjust(1);} } } | plusminus OBJNAME '.' member { $$.constant=0; $$.ident=0; SString t=$2.getString(); t+="."; t+=$4.getString(); $$.setString(t); if ($4.constant) { trctx.out->printf("%s %s.%s\npush %s.%s\n",$1.getInt()?"inc":"dec", str($2),str($4),str($2),str($4)); trstack.adjust(-1); } else { trctx.out->printf("move s0,m1\n%s %s.[m1]\nmove %s.[m1],s0\n", $1.getInt()?"inc":"dec",str($2),str($2)); } } | OBJNAME '.' member plusminus { $$.constant=0; $$.ident=0; SString t=$1.getString(); t+="."; t+=$3.getString(); $$.setString(t); if ($3.constant) { trctx.out->printf("push %s.%s\n%s %s.%s\n", str($1),str($3),$4.getInt()?"inc":"dec",str($1),str($3)); trstack.adjust(-1); } else { trctx.out->printf("move s0,m1\nmove %s.[m1],s0\n%s %s.[m1]\n", str($1),$4.getInt()?"inc":"dec",str($1)); } } | OBJNAME '.' '*' { $$.constant=0; $$.ident=0; SString t=$1.getString(); t+=".*"; $$.setString(t); trctx.out->printf("push %s.*\n",str($1)); trstack.adjust(-1); } | OBJNAME '.' member '=' expr { $$=$5; $$.assign=1; $$.parens=0; $$.ident=0; if ($3.constant) { if ($$.constant) trctx.out->printf("move %s,%s.%s\n",litstr($5),str($1),str($3)); else trctx.out->printf("move s0,%s.%s\n",str($1),str($3)); } else { if ($$.constant) { trctx.out->printf("move m[m0++],m1\nmove %s,%s.[m1]\n", litstr($5),str($1)); trstack.adjust(1); } else { trctx.out->printf("move s1,m1\nmove m[m0++],s0\nmove s0,%s.[m1]\n", str($1)); trstack.adjust(1); } } } | OBJNAME '.' member '(' arguments ')' { $$.constant=0; $$.ident=0; SString t=$1.getString(); t+="."; t+=$3.getString(); $$.setString(t); int adj=0,adj2=0; if ($5.getInt()==0) {trctx.out->printf("dec m0\n");trstack.adjust(-1);adj=1;} if ($3.constant) trctx.out->printf("call %s.%s\n",str($1),str($3)); else { trctx.out->printf("move s%d,m1\ncall %s.[m1]\n",$5.getInt()+adj,str($1)); adj2=1; } adj2+=$5.getInt()-1+adj; if (adj2>0) { trctx.out->printf("add %d,m0\nxmove s%d,s0\n",adj2,-adj2); trstack.adjust(adj2); } } | CALL expr '(' arguments ')' { $$.constant=0; $$.ident=0; $$.setString($2.getString()); short adj=0; if ($4.getInt()==0) {trctx.out->printf("dec m0\n");trstack.adjust(-1);adj=1;} if ($2.constant) trctx.out->printf("call %s\n",litstr($2)); else trctx.out->printf("call s%d\n",$4.getInt()+adj); if (($4.getInt()+adj) > 0) { trctx.out->printf("add %d,m0\nxmove s%d,s0\n",$4.getInt()+adj,-($4.getInt()+adj)); trstack.adjust($4.getInt()+adj); } } | FUNCTION IDENT { $$.constant=0; $$.ident=0; SString t=":"; t+=$1.getString(); $$.setString(t); trctx.out->printf("push :%s\n",(const char*)$2.getString()); trstack.adjust(-1); } | expr '.' member { $$.constant=0; $$.ident=0; SString t=$1.getString(); t+="."; t+=$3.getString(); $$.setString(t); if ($3.constant) trctx.out->printf("move s0,m1\nmove [m1].%s,s0\n",str($3)); else // trctx.out->printf("move s1,m1\nmove m[m0++],m2\nmove [m1].[m2],s0\n"); {trctx.out->printf("move s1,m1\nmove m[m0++],m2\nmove [m1].[m2],s0\n");trstack.adjust(1);} } | plusminus expr '.' member { $$.constant=0; $$.ident=0; SString t=$2.getString(); t+="."; t+=$4.getString(); $$.setString(t); if ($4.constant) trctx.out->printf("move s0,m1\n%s [m1].%s\nmove [m1].%s,s0\n", $1.getInt()?"inc":"dec",str($4),str($4)); else // trctx.out->printf("move s1,m1\nmove m[m0++],m2\nmove [m1].[m2],s0\n"); {trctx.out->printf("move s1,m1\nmove m[m0++],m2\n%s [m1].[m2]\nmove [m1].[m2],s0\n", $1.getInt()?"inc":"dec");trstack.adjust(1);} } | expr '.' member plusminus { $$.constant=0; $$.ident=0; SString t=$1.getString(); t+="."; t+=$3.getString(); $$.setString(t); if ($3.constant) trctx.out->printf("move s0,m1\nmove [m1].%s,s0\n%s [m1].%s\n", str($3),$4.getInt()?"inc":"dec",str($3)); else // trctx.out->printf("move s1,m1\nmove m[m0++],m2\nmove [m1].[m2],s0\n"); {trctx.out->printf("move s1,m1\nmove m[m0++],m2\nmove [m1].[m2],s0\n%s [m1].[m2]\n", $4.getInt()?"inc":"dec");trstack.adjust(1);} } | expr '.' member assign_op expr { $$.constant=0; $$.ident=0; SString t=$1.getString(); t+="."; t+=$3.getString(); $$.setString(t); if ($3.constant) { int sp; if ($5.constant) {sp=0; trctx.out->printf("move s0,m1\n");} else {sp=1; trctx.out->printf("move s1,m1\n");} t="[m1]."; t+=str($3); handleAssignOp2($$,(const char*)t,$5,assign_op_names[$4.getInt()],sp,0); if (sp) {trctx.out->printf("inc m0\n");trstack.adjust(1);} } else { int sp; char *t; if ($5.constant) {sp=1; t="move s1,m1\nmove s0,m2\n";} else {sp=2; t="move s2,m1\nmove s1,m2\n";} trctx.out->printf(t); handleAssignOp2($$,"[m1].[m2]",$5,assign_op_names[$4.getInt()],sp,0); trctx.out->printf("add %d,m0\n",sp); trstack.adjust(sp); } } | expr '.' member '=' stackexpr { $$=$5; $$.assign=1; $$.parens=0; $$.ident=0; if ($3.constant) { trctx.out->printf("move s1,m1\nmove m[m0++],s0\nmove s0,[m1].%s\n",str($3)); trstack.adjust(1); } else { trctx.out->printf("move s2,m1\nmove s1,m2\nmove s0,[m1].[m2]\nadd 2,m0\nmove s-2,s0\n"); trstack.adjust(2); } } | expr '.' member '(' arguments ')' { $$.constant=0; $$.ident=0; SString t=$1.getString(); t+="."; t+=$3.getString(); $$.setString(t); int adj=0; if ($5.getInt()==0) {trctx.out->printf("dec m0\n");trstack.adjust(-1);adj=1;} if ($3.constant) { trctx.out->printf("move s%d,m1\ncall [m1].%s\n",$5.getInt()+adj,str($3)); adj+=1; } else { trctx.out->printf("move s%d,m2\nmove s%d,m1\ncall [m2].[m1]\n", $5.getInt()+adj+1,$5.getInt()+adj); adj+=2; } if (($5.getInt()+adj) > 1) { trctx.out->printf("add %d,m0\nxmove s%d,s0\n",$5.getInt()-1+adj,-($5.getInt()-1+adj)); trstack.adjust($5.getInt()-1+adj); } } | expr '[' expr ']' '=' expr // shortcut: expr.set(expr,expr) { $$=$6; $$.assign=1; $$.parens=0; $$.ident=0; if ($3.constant) { if ($6.constant) {trctx.out->printf("move s0,m1\ncall [m1].\"set\",%s,%s\ninc m0\n",litstr($3),litstr($6));$$=$6;trstack.adjust(+1);} else {trctx.out->printf("move s1,m1\npush s0\nmove %s,s1\ncall [m1].\"set\"\nadd 2,m0\nmove s-2,s0\n",litstr($3));trstack.adjust(+1);} } else { if ($6.constant) {trctx.out->printf("move s1,m1\npush %s\ncall [m1].\"set\"\nadd 3,m0\n",litstr($6)); trstack.adjust(+2);} else {trctx.out->printf("move s2,m1\ncall [m1].\"set\"\nadd 2,m0\nmove s-2,s0\n"); trstack.adjust(+2);} } } | expr '[' expr ']' /* shortcut: expr.get(expr) */ { $$.constant=0; $$.ident=0; SString t=$1.getString(); t+=".get"; $$.setString(t); if ($3.constant) { trctx.out->printf("move s0,m1\ncall [m1].\"get\",%s\n",litstr($3)); } else { trctx.out->printf("move s1,m1\ncall [m1].\"get\"\ninc m0\nmove s-1,s0\n"); trstack.adjust(+1); } } | IDENT '=' expr { $$=$3; $$.assign=1; $$.ident=0; int loc=trstack.getVariableLocation($1.getString()); if (loc!=TranslatorStack::NOTFOUND) { if ($3.constant) trctx.out->printf("move %s,s%d\n",litstr($3),loc-trstack.currentPos()); else trctx.out->printf("move s0,s%d\n",loc-trstack.currentPos()); } else if (globalOk($1)) { $$=$3; $$.ident=0; $$.assign=1; if ($3.constant) trctx.out->printf("move %s,@%s\n",litstr($3),str($1)); else trctx.out->printf("move s0,@%s\n",str($1));} else {trctx.err->printf("undefined variable: %s\n",str($1)); return 1;} } | OBJNAME '[' expr ']' /* shortcut: OBJNAME.get(expr) */ { $$.constant=0; $$.ident=0; SString t=$1.getString(); t+=".get"; $$.setString(t); if ($3.constant) { trctx.out->printf("dec m0\ncall %s.get,%s\n",str($1),litstr($3)); trstack.adjust(-1); } else { trctx.out->printf("call %s.get\n",str($1)); } } | IDENT '(' arguments ')' { $$.constant=0; $$.ident=0; $$.setString("function call"); if ($3.getInt()==0) {trctx.out->printf("dec m0\n");trstack.adjust(-1);} trctx.out->printf("call :%s\n",str($1)); if ($3.getInt()>1) { trctx.out->printf("add %d,m0\nxmove s%d,s0\n",$3.getInt()-1,-($3.getInt()-1)); trstack.adjust($3.getInt()-1); } } | '[' {$$.ident=0; trctx.out->printf("add -2,m0\ncall Vector.new\nmove s0,s1\n");trstack.adjust(-2);} // s1=vector, s0=nieuzywane ale zarezerwowane zeby nie przesuwac stosu przy kazdym elemencie (trafia tu wartosc zwracana przez add/set) v_elements ']' {$$.constant=0; trctx.out->printf("inc m0\n");trstack.adjust(1);} | '{' {$$.ident=0; trctx.out->printf("add -2,m0\ncall Dictionary.new\nmove s0,s1\n");trstack.adjust(-2);} // s1=dict, s0=nieuzywane ale zarezerwowane zeby nie przesuwac stosu przy kazdym elemencie (trafia tu wartosc zwracana przez add/set) d_elements '}' {$$.constant=0; trctx.out->printf("inc m0\n"); trstack.adjust(1);} // | '&' stackexpr {trctx.out->printf("call Ref.new\n");} | '&' IDENT { $$.ident=0; int loc=trstack.getVariableLocation($2.getString()); if (loc!=TranslatorStack::NOTFOUND) { trctx.out->printf("push &%d\ncall Ref.newS\n",loc-trstack.currentPos());trstack.adjust(-1); } else if (globalOk($2)) { trctx.out->printf("gpush &@%s\ncall Ref.newO\ninc m0\nmove s-1,s0\n",str($2)); trstack.adjust(-1); } else {trctx.err->printf("undefined variable: %s\n",str($1)); return 1;} } | '&' OBJNAME '.' member { $$.ident=0; if ($4.constant) { trctx.out->printf("dec m0\ncall Ref.newO,%s.*,%s:%s\n",str($2),str($2),str($4)); trstack.adjust(-1); } else { trctx.out->printf("call Ref.newO,%s.*,s0\n",str($2)); } } | '&' '(' stackexpr ')' '.' member { $$.ident=0; if ($6.constant) { trctx.out->printf("call Ref.newO,s0,%s\n",litstr($6)); } else { trctx.out->printf("call Ref.newO,s1,s0\ninc m0\nmove s-1,s0\n"); trstack.adjust(1); } } | '(' stackexpr ',' stackexpr ',' stackexpr ')' { $$.ident=0; trctx.out->printf("call XYZ.new\nadd 2,m0\nmove s-2,s0\n");trstack.adjust(2);} ; v_elements: /* empty */ | v_element | v_elements ',' v_element ; d_elements: /* empty */ | d_element | d_elements ',' d_element ; v_element: expr { if ($1.constant) trctx.out->printf("move s1,m1\ncall [m1].Vector:add,%s\n",litstr($1)); else {trctx.out->printf("move s2,m1\ncall [m1].Vector:add\ninc m0\n");trstack.adjust(1);} } ; d_element: expr ':' expr { if ($1.constant) { if ($3.constant) trctx.out->printf("move s1,m1\ncall [m1].Dictionary:set,%s,%s\n",litstr($1),litstr($3)); else {trctx.out->printf("move s2,m1\nmove %s,s1\ncall [m1].Dictionary:set\ninc m0\n",litstr($1));trstack.adjust(1);} } else { if ($3.constant) {trctx.out->printf("move s2,m1\nmove s0,s1\nmove %s,s0\ncall [m1].Dictionary:set\ninc m0\n",litstr($3));trstack.adjust(1);} else {trctx.out->printf("move s3,m1\ncall [m1].Dictionary:set\nadd 2,m0\n");trstack.adjust(2);} } } ; member: IDENT { $$=$1; $$.constant=1;} | OBJNAME ':' IDENT { SString t=$1.getString();t+=":";t+=$3.getString(); $$.setString(t);$$.constant=1;} | '[' stackexpr ']' { SString t="["; t+=$2.getString(); t+="]"; $$.setString(t); $$.constant=0;} stackexpr: expr {if ($1.constant) {trctx.out->printf("push %s\n",litstr($1)); trstack.adjust(-1); $$.constant=0;} } arguments: /* empty */ { $$.setInt(0); } | stackexpr { $$.setInt(1); } | arguments ',' stackexpr {$$.setInt($1.getInt()+1);} ; %% SString makeLitString(const ExtValue& val) { if (val.type!=TString) return val.getString(); SString s=val.getString(); int len=s.len(); SString ret((len*11)/10+10); ret+='\"'; const char*t=(const char*)s; while(len>0) { switch(*t) { case '\n': ret+="\\n"; break; case '\r': ret+="\\r"; break; case '\t': ret+="\\t"; break; default: ret+=*t; } t++; len--; } ret+='\"'; return ret; } static void yyprint (FILE *file,int type,YYSTYPE value) { fprintf(file,"(%s)%s",str(value),value.constant?"c":""); } int yyerror (const char *s) /* Called by yyparse on error */ { trctx.err->printf ("%s\n",s); return 0; // w przykladach do bisona tez nie dali returna... } void handleTwoArg(YYSTYPE& result,const YYSTYPE& arg1,const YYSTYPE& arg2, int optoken,const char* opname,bool logic,bool negarg2,bool uniq) { result.ident=false; if (arg1.constant && arg2.constant) { result=arg1; switch(optoken) { case '+': result+=arg2; break; case '-': result-=arg2; break; case '*': result*=arg2; break; case '/': result/=arg2; break; case '%': result%=arg2; break; case '&': result.setInt(arg1.getInt() & arg2.getInt()); break; case '|': result.setInt(arg1.getInt() | arg2.getInt()); break; case LSHIFT: result.setInt(arg1.getInt() << arg2.getInt()); break; case RSHIFT: result.setInt(arg1.getInt() >> arg2.getInt()); break; case LOGIC_AND: result.setInt(arg1.getInt() && arg2.getInt()); break; case LOGIC_OR: result.setInt(arg1.getInt() || arg2.getInt()); break; } } else { //TODO: prawie kazde uzycie uniq jest niepotrzebne bo typem rzadko bedzie vector, ale w wiekszosci miejsc okreslenie typu wartosci // byloby bardzo trudne lub niemozliwe. mozna byloby natomiast zapamietywac przy parsowaniu czy dana wartosc na stosie jest // skopiowana ze zmiennej/pola czy jest wynikiem wczesniejszej operacji co pozwoliloby na likwidacje bardzo wielu uniq result.constant=0; result.assign=arg1.assign || arg2.assign; result.parens=0; result.setString(opname); if (arg1.constant) trctx.out->printf("move %s,m1\n%s%s%s s0,m1\nmove m1,s0\n",litstr(arg1), (logic?"setif !=,m1,m1\nsetif !=,s0,s0\n":""), negarg2?"neg s0\n":"", opname); else if (arg2.constant) { if (logic) trctx.out->printf("move %s,m1\nsetif !=,m1,m1\nsetif !=,s0,s0\n%s m1,s0\n", litstr(arg2),opname); else { if (negarg2) trctx.out->printf("%s %d,s0\n",opname,-arg2.getInt()); else trctx.out->printf("%s%s %s,s0\n",(uniq?"uniq s0\n":""),opname,litstr(arg2)); } } else { trctx.out->printf("%s%s%s s0,s1\ninc m0\n", (logic?"setif !=,s0,s0\nsetif !=,s1,s1\n":(uniq?"uniq s1\n":"")), negarg2?"neg s0\n":"", opname); trstack.adjust(+1); } } } bool handleAssignOp(YYSTYPE& result,const YYSTYPE& var,const YYSTYPE& arg,const char* opname) { int loc; if (variableOk(result,var,loc)) { loc-=trstack.currentPos(); if (arg.constant) { trctx.out->printf("%s %s,s%d\npush s%d\n",opname,litstr(arg),loc,loc); trstack.adjust(-1); } else trctx.out->printf("%s s0,s%d\nmove s%d,s0\n",opname,loc,loc); return 1; } return 0; } bool handleAssignOp2(YYSTYPE& result,const char *var,const YYSTYPE& arg,const char* opname,int stackpos,bool push) { if (arg.constant) { trctx.out->printf("%s %s,%s\n",opname,litstr(arg),var); if (!push) trctx.out->printf("move %s,s%d\n",var,stackpos); else { trctx.out->printf("push %s\n",var); trstack.adjust(-1); } } else trctx.out->printf("%s s0,%s\nmove %s,s%d\n",opname,var,var,stackpos); return 1; } void handleCompare(YYSTYPE& result,const YYSTYPE& arg1,const YYSTYPE& arg2,int optoken,const char* opname) { result.ident=0; if (arg1.constant && arg2.constant) { result.constant=1; switch(optoken) { case '>': result.setInt(arg1.compare(arg2) > 0); break; case '<': result.setInt(arg1.compare(arg2) < 0); break; case EQUAL: result.setInt(arg1.compare(arg2) == 0); break; case NOT_EQUAL: result.setInt(arg1.compare(arg2) != 0); break; case GEQUAL: result.setInt(arg1.compare(arg2) >= 0); break; case LEQUAL: result.setInt(arg1.compare(arg2) <= 0); break; } } else { result.constant=0; result.assign=arg1.assign || arg2.assign; result.parens=0; result.setString(opname); if (arg1.constant) trctx.out->printf("setif %s,%s,s0,s0\n",litstr(arg1),opname); else if (arg2.constant) trctx.out->printf("setif s0,%s,%s,s0\n",opname,litstr(arg2)); else { trctx.out->printf("setif s1,%s,s0,s1\ninc m0\n",opname); trstack.adjust(+1); } } } bool variableOk(TokenValue &tok, const TokenValue& var,int &loc) { loc=trstack.getVariableLocation(var.getString()); if (loc != TranslatorStack::NOTFOUND) {tok.setInt(loc); tok.constant=0; return 1;} return 0; } bool globalOk(const TokenValue& var) { SymTabEntry* found=trstack.globals.find(var.getString()); if (found) return true; return framscriptIsGlobalName(var.getString()); } void badVariable(TokenValue &tok, const TokenValue& var) { tok=var; tok.constant=1; trctx.err->printf("undefined variable '%s'\n",str(var)); } bool doBreak(int level) { if (trstack.loops.size()printf("invalid 'break'"); return 0;} LoopInfo* li=trstack.loops.getLoop(level-1); if (li->location != trstack.currentPos()) trctx.out->printf("add %d,m0\n",li->location-trstack.currentPos()); trctx.out->printf("jump :_loop_end_%d\n",li->id); return 1; } bool doContinue(int level) { if (trstack.loops.size()printf("invalid 'continue'"); return 0;} LoopInfo* li=trstack.loops.getLoop(level-1); if (li->location != trstack.currentPos()) trctx.out->printf("add %d,m0\n",li->location-trstack.currentPos()); trctx.out->printf("jump :_loop_%d\n",li->id); return 1; } int lookupToken(char *s) { int len=strlen(s); int i; const char *t; for (i = 0; i < YYNTOKENS; i++) { t=yytname[i]; if (t && (t[0]=='"') && (!strncmp(t+1,s,len)) && (t[len+1]=='"') && (t[len+2] == 0)) return yytoknum[i]; } return -1; } void warnTruthValue(const TokenValue& t) { if (t.assign && (!t.parens)) FMprintf("FramScriptCompiler","translate",FMLV_WARN,"Assignment used as truth value, use double parens if you really mean it (%s line %d)",(const char*)trctx.srcname,trctx.line); } void outFunName(const TokenValue& t) { if (trctx.functiontmplabel<0) { trctx.functiontmplabel=trctx.labelcounter++; trctx.out->printf("jump :_skipfun_%d\n",trctx.functiontmplabel); } trctx.out->printf(":%s\n",str(t)); } bool evalVariable(TokenValue &tok,const TokenValue &var) { int loc; if (variableOk(tok,var,loc)) { trctx.out->printf("push s%d\n",loc-trstack.currentPos()); trstack.adjust(-1); return true; } else if (globalOk(var)) { trctx.out->printf("push @%s\n",(const char*)var.getString()); trstack.adjust(-1); return true; } else { badVariable(tok,var); return false; } }