| %{ |
| |
| char *rcs_luastx = "$Id: lua.stx,v 3.46 1997/03/31 14:19:01 roberto Exp roberto $"; |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #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 <nvarbuffer; j++) |
| if (varbuffer[j] == 0) upper++; |
| param = upper*2 + i; |
| if (param == 0) |
| code_byte(STOREINDEXED0); |
| else |
| { |
| code_byte(STOREINDEXED); |
| code_byte(param); |
| } |
| } |
| } |
| |
| static void codeIf (Long thenAdd, Long elseAdd) |
| { |
| Long elseinit = elseAdd+sizeof(Word)+1; |
| if (pc == elseinit) /* no else */ |
| { |
| pc -= sizeof(Word)+1; |
| elseinit = pc; |
| } |
| else |
| { |
| basepc[elseAdd] = JMP; |
| code_word_at(basepc+elseAdd+1, pc-elseinit); |
| } |
| basepc[thenAdd] = IFFJMP; |
| code_word_at(basepc+thenAdd+1,elseinit-(thenAdd+sizeof(Word)+1)); |
| } |
| |
| |
| /* |
| ** Parse LUA code. |
| */ |
| void lua_parse (TFunc *tf) |
| { |
| initcode = &(tf->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 <vFloat> NUMBER |
| %token <vWord> STRING |
| %token <pTStr> NAME |
| |
| %type <vLong> PrepJump |
| %type <vLong> exprlist, exprlist1 /* if > 0, points to function return |
| counter (which has list length); if <= 0, -list lenght */ |
| %type <vLong> functioncall, expr /* if != 0, points to function return |
| counter */ |
| %type <vInt> varlist1, funcParams, funcvalue |
| %type <vInt> fieldlist, localdeclist, decinit |
| %type <vInt> ffieldlist, ffieldlist1, semicolonpart |
| %type <vInt> lfieldlist, lfieldlist1 |
| %type <vInt> parlist, parlist1, par |
| %type <vLong> var, singlevar, funcname |
| %type <pFunc> 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 {$<vLong>$=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 - ($<vLong>2)); |
| } |
| |
| | REPEAT {$<vLong>$=pc;} block UNTIL expr1 PrepJump |
| { |
| basepc[$6] = IFFUPJMP; |
| code_word_at(basepc+$6+1, pc - ($<vLong>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 : {$<vInt>$ = nlocalvar;} statlist ret |
| { |
| if (nlocalvar != $<vInt>1) |
| { |
| if (lua_debug) |
| for (; nlocalvar > $<vInt>1; nlocalvar--) |
| luaI_unregisterlocalvar(lua_linenumber); |
| else |
| nlocalvar = $<vInt>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); |
| $<vLong>$ = pc; code_word(0); |
| } |
| '{' fieldlist '}' |
| { |
| code_word_at(basepc+$<vLong>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 ',' { $<vLong>$ = adjust_functioncall($1, 1); } expr |
| { |
| if ($4 == 0) $$ = -($<vLong>3 + 1); /* -length */ |
| else |
| { |
| adjust_functioncall($4, $<vLong>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; } |
| ; |
| |
| %% |