%{ char *rcs_luastx = "$Id: lua.stx,v 3.46 1997/03/31 14:19:01 roberto Exp roberto $"; #include #include #include #include "luadebug.h" #include "luamem.h" #include "lex.h" #include "opcode.h" #include "hash.h" #include "inout.h" #include "tree.h" #include "table.h" #include "lua.h" #include "func.h" /* to avoid warnings generated by yacc */ int yyparse (void); #define malloc luaI_malloc #define realloc luaI_realloc #define free luaI_free #ifndef LISTING #define LISTING 0 #endif #ifndef CODE_BLOCK #define CODE_BLOCK 256 #endif static int maxcode; static int maxmain; static int maxcurr; static Byte *funcCode = NULL; static Byte **initcode; static Byte *basepc; static int maincode; static int pc; #define MAXVAR 32 static Long varbuffer[MAXVAR]; /* variables in an assignment list; it's long to store negative Word values */ static int nvarbuffer=0; /* number of variables at a list */ #define MAXLOCALS 32 static TaggedString *localvar[MAXLOCALS]; /* store local variable names */ static int nlocalvar=0; /* number of local variables */ #define MAXFIELDS FIELDS_PER_FLUSH*2 int lua_debug = 0; /* Internal functions */ static void yyerror (char *s) { luaI_syntaxerror(s); } static void check_space (int i) { if (pc+i>maxcurr-1) /* 1 byte free to code HALT of main code */ maxcurr = growvector(&basepc, maxcurr, Byte, codeEM, MAX_INT); } static void code_byte (Byte c) { check_space(1); basepc[pc++] = c; } static void code_word (Word n) { check_space(sizeof(Word)); memcpy(basepc+pc, &n, sizeof(Word)); pc += sizeof(Word); } static void code_float (real n) { check_space(sizeof(real)); memcpy(basepc+pc, &n, sizeof(real)); pc += sizeof(real); } static void code_code (TFunc *tf) { check_space(sizeof(TFunc *)); memcpy(basepc+pc, &tf, sizeof(TFunc *)); pc += sizeof(TFunc *); } static void code_word_at (Byte *p, int n) { Word w = n; if (w != n) yyerror("block too big"); memcpy(p, &w, sizeof(Word)); } static void flush_record (int n) { if (n == 0) return; code_byte(STOREMAP); code_byte(n); } static void flush_list (int m, int n) { if (n == 0) return; if (m == 0) code_byte(STORELIST0); else if (m < 255) { code_byte(STORELIST); code_byte(m); } else yyerror ("list constructor too long"); code_byte(n); } static void store_localvar (TaggedString *name, int n) { if (nlocalvar+n < MAXLOCALS) localvar[nlocalvar+n] = name; else yyerror ("too many local variables"); if (lua_debug) luaI_registerlocalvar(name, lua_linenumber); } static void add_localvar (TaggedString *name) { store_localvar(name, 0); nlocalvar++; } static void add_varbuffer (Long var) { if (nvarbuffer < MAXVAR) varbuffer[nvarbuffer++] = var; else yyerror ("variable buffer overflow"); } static void code_string (Word w) { code_byte(PUSHSTRING); code_word(w); } static void code_constant (TaggedString *s) { code_string(luaI_findconstant(s)); } static void code_number (float f) { Word i; if (f >= 0 && f <= (float)MAX_WORD && (float)(i=(Word)f) == f) { /* f has an (short) integer value */ if (i <= 2) code_byte(PUSH0 + i); else if (i <= 255) { code_byte(PUSHBYTE); code_byte(i); } else { code_byte(PUSHWORD); code_word(i); } } else { code_byte(PUSHFLOAT); code_float(f); } } /* ** Search a local name and if find return its index. If do not find return -1 */ static int lua_localname (TaggedString *n) { int i; for (i=nlocalvar-1; i >= 0; i--) if (n == localvar[i]) return i; /* local var */ return -1; /* global var */ } /* ** Push a variable given a number. If number is positive, push global variable ** indexed by (number -1). If negative, push local indexed by ABS(number)-1. ** Otherwise, if zero, push indexed variable (record). */ static void lua_pushvar (Long number) { if (number > 0) /* global var */ { code_byte(PUSHGLOBAL); code_word(number-1); } else if (number < 0) /* local var */ { number = (-number) - 1; if (number < 10) code_byte(PUSHLOCAL0 + number); else { code_byte(PUSHLOCAL); code_byte(number); } } else { code_byte(PUSHINDEXED); } } static void lua_codeadjust (int n) { if (n+nlocalvar == 0) code_byte(ADJUST0); else { code_byte(ADJUST); code_byte(n+nlocalvar); } } static void change2main (void) { /* (re)store main values */ pc=maincode; basepc=*initcode; maxcurr=maxmain; nlocalvar=0; } static void savemain (void) { /* save main values */ maincode=pc; *initcode=basepc; maxmain=maxcurr; } static void init_func (void) { if (funcCode == NULL) /* first function */ { funcCode = newvector(CODE_BLOCK, Byte); maxcode = CODE_BLOCK; } savemain(); /* save main values */ /* set func values */ pc=0; basepc=funcCode; maxcurr=maxcode; nlocalvar = 0; luaI_codedebugline(lua_linenumber); } static void codereturn (void) { if (nlocalvar == 0) code_byte(RETCODE0); else { code_byte(RETCODE); code_byte(nlocalvar); } } void luaI_codedebugline (int line) { static int lastline = 0; if (lua_debug && line != lastline) { code_byte(SETLINE); code_word(line); lastline = line; } } static int adjust_functioncall (Long exp, int i) { if (exp <= 0) return -exp; /* exp is -list length */ else { int temp = basepc[exp]; basepc[exp] = i; return temp+i; } } static void adjust_mult_assign (int vars, Long exps, int temps) { if (exps > 0) { /* must correct function call */ int diff = vars - basepc[exps]; if (diff >= 0) adjust_functioncall(exps, diff); else { adjust_functioncall(exps, 0); lua_codeadjust(temps); } } else if (vars != -exps) lua_codeadjust(temps); } static int close_parlist (int dots) { if (!dots) lua_codeadjust(0); else { code_byte(VARARGS); code_byte(nlocalvar); add_localvar(luaI_createfixedstring("arg")); } return lua_linenumber; } static void storesinglevar (Long v) { if (v > 0) /* global var */ { code_byte(STOREGLOBAL); code_word(v-1); } else if (v < 0) /* local var */ { int number = (-v) - 1; if (number < 10) code_byte(STORELOCAL0 + number); else { code_byte(STORELOCAL); code_byte(number); } } else code_byte(STOREINDEXED0); } static void lua_codestore (int i) { if (varbuffer[i] != 0) /* global or local var */ storesinglevar(varbuffer[i]); else /* indexed var */ { int j; int upper=0; /* number of indexed variables upper */ int param; /* number of itens until indexed expression */ for (j=i+1; j code); *initcode = newvector(CODE_BLOCK, Byte); maincode = 0; maxmain = CODE_BLOCK; change2main(); if (yyparse ()) lua_error("parse error"); savemain(); (*initcode)[maincode++] = RETCODE0; tf->size = maincode; #if LISTING { static void PrintCode (Byte *c, Byte *end); PrintCode(*initcode,*initcode+maincode); } #endif } %} %union { int vInt; float vFloat; char *pChar; Word vWord; Long vLong; TFunc *pFunc; TaggedString *pTStr; } %start chunk %token WRONGTOKEN %token NIL %token IF THEN ELSE ELSEIF WHILE DO REPEAT UNTIL END %token RETURN %token LOCAL %token FUNCTION %token DOTS %token NUMBER %token STRING %token NAME %type PrepJump %type exprlist, exprlist1 /* if > 0, points to function return counter (which has list length); if <= 0, -list lenght */ %type functioncall, expr /* if != 0, points to function return counter */ %type varlist1, funcParams, funcvalue %type fieldlist, localdeclist, decinit %type ffieldlist, ffieldlist1, semicolonpart %type lfieldlist, lfieldlist1 %type parlist, parlist1, par %type var, singlevar, funcname %type body %left AND OR %left EQ NE '>' '<' LE GE %left CONC %left '+' '-' %left '*' '/' %left UNARY NOT %right '^' %% /* beginning of rules section */ chunk : chunklist ret ; chunklist : /* empty */ | chunklist stat sc | chunklist function ; function : FUNCTION funcname body { code_byte(PUSHFUNCTION); code_code($3); storesinglevar($2); } ; funcname : var { $$ =$1; init_func(); } | varexp ':' NAME { code_constant($3); $$ = 0; /* indexed variable */ init_func(); add_localvar(luaI_createfixedstring("self")); } ; body : '(' parlist ')' block END { codereturn(); $$ = new(TFunc); luaI_initTFunc($$); $$->size = pc; $$->code = newvector(pc, Byte); $$->lineDefined = $2; memcpy($$->code, basepc, pc*sizeof(Byte)); if (lua_debug) luaI_closelocalvars($$); /* save func values */ funcCode = basepc; maxcode=maxcurr; #if LISTING PrintCode(funcCode,funcCode+pc); #endif change2main(); /* change back to main code */ } ; statlist : /* empty */ | statlist stat sc ; sc : /* empty */ | ';' ; stat : IF expr1 THEN PrepJump block PrepJump elsepart END { codeIf($4, $6); } | WHILE {$$=pc;} expr1 DO PrepJump block PrepJump END { basepc[$5] = IFFJMP; code_word_at(basepc+$5+1, pc - ($5 + sizeof(Word)+1)); basepc[$7] = UPJMP; code_word_at(basepc+$7+1, pc - ($2)); } | REPEAT {$$=pc;} block UNTIL expr1 PrepJump { basepc[$6] = IFFUPJMP; code_word_at(basepc+$6+1, pc - ($2)); } | varlist1 '=' exprlist1 { { int i; adjust_mult_assign(nvarbuffer, $3, $1 * 2 + nvarbuffer); for (i=nvarbuffer-1; i>=0; i--) lua_codestore (i); if ($1 > 1 || ($1 == 1 && varbuffer[0] != 0)) lua_codeadjust (0); } } | functioncall {;} | LOCAL localdeclist decinit { nlocalvar += $2; adjust_mult_assign($2, $3, 0); } ; elsepart : /* empty */ | ELSE block | ELSEIF expr1 THEN PrepJump block PrepJump elsepart { codeIf($4, $6); } ; block : {$$ = nlocalvar;} statlist ret { if (nlocalvar != $1) { if (lua_debug) for (; nlocalvar > $1; nlocalvar--) luaI_unregisterlocalvar(lua_linenumber); else nlocalvar = $1; lua_codeadjust (0); } } ; ret : /* empty */ | RETURN exprlist sc { adjust_functioncall($2, MULT_RET); codereturn(); } ; PrepJump : /* empty */ { $$ = pc; code_byte(0); /* open space */ code_word (0); } ; expr1 : expr { adjust_functioncall($1, 1); } ; expr : '(' expr ')' { $$ = $2; } | expr1 EQ expr1 { code_byte(EQOP); $$ = 0; } | expr1 '<' expr1 { code_byte(LTOP); $$ = 0; } | expr1 '>' expr1 { code_byte(GTOP); $$ = 0; } | expr1 NE expr1 { code_byte(EQOP); code_byte(NOTOP); $$ = 0; } | expr1 LE expr1 { code_byte(LEOP); $$ = 0; } | expr1 GE expr1 { code_byte(GEOP); $$ = 0; } | expr1 '+' expr1 { code_byte(ADDOP); $$ = 0; } | expr1 '-' expr1 { code_byte(SUBOP); $$ = 0; } | expr1 '*' expr1 { code_byte(MULTOP); $$ = 0; } | expr1 '/' expr1 { code_byte(DIVOP); $$ = 0; } | expr1 '^' expr1 { code_byte(POWOP); $$ = 0; } | expr1 CONC expr1 { code_byte(CONCOP); $$ = 0; } | '-' expr1 %prec UNARY { code_byte(MINUSOP); $$ = 0;} | table { $$ = 0; } | varexp { $$ = 0;} | NUMBER { code_number($1); $$ = 0; } | STRING { code_string($1); $$ = 0; } | NIL {code_byte(PUSHNIL); $$ = 0; } | functioncall { $$ = $1; } | NOT expr1 { code_byte(NOTOP); $$ = 0;} | expr1 AND PrepJump {code_byte(POP); } expr1 { basepc[$3] = ONFJMP; code_word_at(basepc+$3+1, pc - ($3 + sizeof(Word)+1)); $$ = 0; } | expr1 OR PrepJump {code_byte(POP); } expr1 { basepc[$3] = ONTJMP; code_word_at(basepc+$3+1, pc - ($3 + sizeof(Word)+1)); $$ = 0; } ; table : { code_byte(CREATEARRAY); $$ = pc; code_word(0); } '{' fieldlist '}' { code_word_at(basepc+$1, $3); } ; functioncall : funcvalue funcParams { code_byte(CALLFUNC); code_byte($1+$2); $$ = pc; code_byte(0); /* may be modified by other rules */ } ; funcvalue : varexp { $$ = 0; } | varexp ':' NAME { code_byte(PUSHSELF); code_word(luaI_findconstant($3)); $$ = 1; } ; funcParams : '(' exprlist ')' { $$ = adjust_functioncall($2, 1); } | table { $$ = 1; } ; exprlist : /* empty */ { $$ = 0; } | exprlist1 { $$ = $1; } ; exprlist1 : expr { if ($1 != 0) $$ = $1; else $$ = -1; } | exprlist1 ',' { $$ = adjust_functioncall($1, 1); } expr { if ($4 == 0) $$ = -($3 + 1); /* -length */ else { adjust_functioncall($4, $3); $$ = $4; } } ; parlist : /* empty */ { $$ = close_parlist(0); } | parlist1 { $$ = close_parlist($1); } ; parlist1 : par { $$ = $1; } | parlist1 ',' par { if ($1) lua_error("invalid parameter list"); $$ = $3; } ; par : NAME { add_localvar($1); $$ = 0; } | DOTS { $$ = 1; } ; fieldlist : lfieldlist { flush_list($1/FIELDS_PER_FLUSH, $1%FIELDS_PER_FLUSH); } semicolonpart { $$ = $1+$3; } | ffieldlist1 lastcomma { $$ = $1; flush_record($1%FIELDS_PER_FLUSH); } ; semicolonpart : /* empty */ { $$ = 0; } | ';' ffieldlist { $$ = $2; flush_record($2%FIELDS_PER_FLUSH); } ; lastcomma : /* empty */ | ',' ; ffieldlist : /* empty */ { $$ = 0; } | ffieldlist1 lastcomma { $$ = $1; } ; ffieldlist1 : ffield {$$=1;} | ffieldlist1 ',' ffield { $$=$1+1; if ($$%FIELDS_PER_FLUSH == 0) flush_record(FIELDS_PER_FLUSH); } ; ffield : ffieldkey '=' expr1 ; ffieldkey : '[' expr1 ']' | NAME { code_constant($1); } ; lfieldlist : /* empty */ { $$ = 0; } | lfieldlist1 lastcomma { $$ = $1; } ; lfieldlist1 : expr1 {$$=1;} | lfieldlist1 ',' expr1 { $$=$1+1; if ($$%FIELDS_PER_FLUSH == 0) flush_list($$/FIELDS_PER_FLUSH - 1, FIELDS_PER_FLUSH); } ; varlist1 : var { nvarbuffer = 0; add_varbuffer($1); $$ = ($1 == 0) ? 1 : 0; } | varlist1 ',' var { add_varbuffer($3); $$ = ($3 == 0) ? $1 + 1 : $1; } ; var : singlevar { $$ = $1; } | varexp '[' expr1 ']' { $$ = 0; /* indexed variable */ } | varexp '.' NAME { code_constant($3); $$ = 0; /* indexed variable */ } ; singlevar : NAME { int local = lua_localname($1); if (local == -1) /* global var */ $$ = luaI_findsymbol($1)+1; /* return positive value */ else $$ = -(local+1); /* return negative value */ } ; varexp : var { lua_pushvar($1); } ; localdeclist : NAME {store_localvar($1, 0); $$ = 1;} | localdeclist ',' NAME { store_localvar($3, $1); $$ = $1+1; } ; decinit : /* empty */ { $$ = 0; } | '=' exprlist1 { $$ = $2; } ; %%