[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: lua case statement redux
- From: Norman Ramsey <nr@...>
- Date: Mon, 14 Apr 1997 19:36:10 -0400 (EDT)
Ignoring all the syntax questions, I've just extended my case-statement
patch so you can label a single arm of a case statement with multiple
expressions, again as in Modula-3. This took a bit more code; I changed
about 50 lines of code.
This patch also contains an improvement to print.c, to show targets of
jumps, and a fix for a bug in src/luac/Makefile.
Norman
I felt the need for a case statement in Lua, so I added one.
Unfortunately I had to change the virtual machine as well as the
compiler, because I couldn't figure out how to duplicate the element
on top of the stack using only the existing operators.
Still, Lua is so clean that I added or changed only 50 lines
of code. (I changed another 20 lines to improve luac -l.)
I borrowed syntax from Modula-3 for the case statement (with one
slight change); here's a rather silly example:
x = 0
while type(x) == "number" do
case x of
| 0, 1 => print(x, "x < 2")
x = x + 1
| 2 => print (x, "x = 2")
x = 2 * x
else => print(x, "x got big")
x = "foobar"
end
end
print(x)
Each arm of the case statement is tagged with a list of expressions,
and the first arm that matches any expression is executed, so the
expected output for this program is:
0
x < 2
1
x < 2
2
x = 2
4
x got big
foobar
While debugging the code, I discovered a bug in src/luac/Makefile; the
generated luac should depend on $(LIB)/liblua.a. I also improved the
code in print.c used to support luac -l; I figured that the computer
is better than I am at doing arithmetic, so jump instructions now
print as in these examples:
63 IFFJMP 24 ; target address 90
109 UPJMP 108 ; target address 4
57 JMP 49 ; target address 109
This is a patch file for lua-2.5. To build it, unpack the lua-2.5
distribution, change to the toplevel directory (called 'lua' in the
distribution), and run
patch < this_patch_file
(cd src; make parser)
make
Norman Ramsey
nr@cs.virginia.edu
*** ./src/lex.c 1997/04/14 21:59:31 1.1
--- ./src/lex.c 1997/04/14 21:59:40
***************
*** 48,53 ****
--- 48,54 ----
int token;
} reserved [] = {
{"and", AND},
+ {"case", CASE},
{"do", DO},
{"else", ELSE},
{"elseif", ELSEIF},
***************
*** 57,62 ****
--- 58,64 ----
{"local", LOCAL},
{"nil", NIL},
{"not", NOT},
+ {"of", OF},
{"or", OR},
{"repeat", REPEAT},
{"return", RETURN},
***************
*** 187,194 ****
case '=':
save_and_next();
! if (current != '=') return '=';
! else { save_and_next(); return EQ; }
case '<':
save_and_next();
--- 189,197 ----
case '=':
save_and_next();
! if (current == '>') { save_and_next(); return ARROW; }
! else if (current == '=') { save_and_next(); return EQ; }
! else return '=';
case '<':
save_and_next();
*** ./src/lua.stx 1997/04/14 21:59:33 1.1
--- ./src/lua.stx 1997/04/14 23:07:42
***************
*** 386,391 ****
--- 386,395 ----
code_word_at(basepc+thenAdd+1,elseinit-(thenAdd+sizeof(Word)+1));
}
+ static void codeIfEqJmp(Long label_address, Long target) {
+ /*assert(target >= label_address + 2);*/
+ code_word_at(basepc+label_address,target-(label_address+2));
+ }
/*
** Parse LUA code.
***************
*** 426,441 ****
%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
--- 430,447 ----
%token WRONGTOKEN
%token NIL
! %token IF THEN ELSE ELSEIF WHILE DO REPEAT UNTIL CASE OF END
%token RETURN
%token LOCAL
%token FUNCTION
%token DOTS
+ %token ARROW
%token <vFloat> NUMBER
%token <vWord> STRING
%token <pTStr> NAME
! %type <vLong> PrepJump, PrepIfEqJump
! %type <vLong> tagged_arm_and_body /* label just before ARROW */
%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
***************
*** 515,520 ****
--- 521,528 ----
stat : IF expr1 THEN PrepJump block PrepJump elsepart END
{ codeIf($4, $6); }
+ | CASE expr1 OF case_body END
+
| WHILE {$<vLong>$=pc;} expr1 DO PrepJump block PrepJump END
{
basepc[$5] = IFFJMP;
***************
*** 553,558 ****
--- 561,584 ----
{ codeIf($4, $6); }
;
+ /* keeps a copy of expr1 (from CASE expr1 of) on stack until arm is chosen */
+ /* we have to insert DUP *before* tagged_arm_and_body to avoid parser confusion */
+ case_body : '|' {code_byte(DUP);} tagged_arm_and_body
+ | ELSE ARROW {code_byte(POP);} block
+ | /* empty */ {code_byte(POP); /* should code for fallback here */ }
+ ;
+
+ tagged_arm_and_body
+ : expr1 PrepIfEqJump ',' /* if it equals tag, will jump to backpatched label */
+ {code_byte(DUP);} tagged_arm_and_body
+ {codeIfEqJmp($2,$5); $$=$5;} /* backpatch and return the label */
+ | expr1 {code_byte(EQOP);} PrepJump ARROW /* if equal, jump to block */
+ {code_byte(POP);} /* we found an arm; pop expr1 (from CASE expr1 OF) */
+ block PrepJump /* execute block, then jump to end of case stmt */
+ case_body
+ {codeIf($3,$7);$$=$3+3;} /* patch jumps; return label for other patch */
+ ;
+
block : {$<vInt>$ = nlocalvar;} statlist ret
{
if (nlocalvar != $<vInt>1)
***************
*** 583,588 ****
--- 609,620 ----
}
;
+ PrepIfEqJump : /* empty --- returns address to patch label */
+ { code_byte(EQOP); code_byte(NOTOP); code_byte(IFFJMP);
+ $$ = pc; code_word(0);
+ }
+ ;
+
expr1 : expr { adjust_functioncall($1, 1); }
;
*** ./src/luac/dump.c 1997/04/14 21:59:30 1.1
--- ./src/luac/dump.c 1997/04/14 21:59:40
***************
*** 42,47 ****
--- 42,48 ----
case PUSH0:
case PUSH1:
case PUSH2:
+ case DUP:
case PUSHLOCAL0:
case PUSHLOCAL1:
case PUSHLOCAL2:
*** ./src/luac/print.c 1997/04/14 21:59:30 1.1
--- ./src/luac/print.c 1997/04/14 22:23:19
***************
*** 32,37 ****
--- 32,38 ----
case PUSH0:
case PUSH1:
case PUSH2:
+ case DUP:
case PUSHINDEXED:
case STOREINDEXED0:
case ADJUST0:
***************
*** 102,119 ****
break;
case PUSHWORD:
case CREATEARRAY:
case ONTJMP:
case ONFJMP:
case JMP:
- case UPJMP:
case IFFJMP:
case IFFUPJMP:
- case SETLINE:
{
Word w;
p++;
get_word(w,p);
! printf("\t%d",w);
break;
}
case PUSHFLOAT:
--- 103,134 ----
break;
case PUSHWORD:
case CREATEARRAY:
+ case SETLINE:
+ {
+ Word w;
+ p++;
+ get_word(w,p);
+ printf("\t%d",w);
+ break;
+ }
case ONTJMP:
case ONFJMP:
case JMP:
case IFFJMP:
+ {
+ Word w;
+ p++;
+ get_word(w,p);
+ printf("\t%d\t\t; target address %d",w,(p-code)+w);
+ break;
+ }
+ case UPJMP:
case IFFUPJMP:
{
Word w;
p++;
get_word(w,p);
! printf("\t%d\t\t; target address %d",w,(p-code)-w);
break;
}
case PUSHFLOAT:
*** ./src/luac/print.h 1997/04/14 21:59:30 1.1
--- ./src/luac/print.h 1997/04/14 21:59:40
***************
*** 9,14 ****
--- 9,15 ----
"PUSH0",
"PUSH1",
"PUSH2",
+ "DUP",
"PUSHBYTE",
"PUSHWORD",
"PUSHFLOAT",
*** ./src/opcode.c 1997/04/14 21:59:31 1.1
--- ./src/opcode.c 1997/04/14 21:59:41
***************
*** 950,955 ****
--- 950,958 ----
incr_top;
break;
+ case DUP:
+ *top = *(top-1); incr_top; break;
+
case PUSHBYTE:
tag(top) = LUA_T_NUMBER; nvalue(top) = *pc++; incr_top; break;
*** ./src/opcode.h 1997/04/14 21:59:31 1.1
--- ./src/opcode.h 1997/04/14 21:59:41
***************
*** 23,28 ****
--- 23,29 ----
PUSH0,/* - 0.0 */
PUSH1,/* - 1.0 */
PUSH2,/* - 2.0 */
+ DUP,/* x - x x */
PUSHBYTE,/* b - (float)b */
PUSHWORD,/* w - (float)w */
PUSHFLOAT,/* f - f */
*** ./src/undump.c 1997/04/14 21:59:33 1.1
--- ./src/undump.c 1997/04/14 21:59:41
***************
*** 36,41 ****
--- 36,42 ----
case PUSH0:
case PUSH1:
case PUSH2:
+ case DUP:
case PUSHLOCAL0:
case PUSHLOCAL1:
case PUSHLOCAL2:
*** src/luac/Makefile 1997/04/14 22:34:00 1.1
--- src/luac/Makefile 1997/04/14 22:35:33
***************
*** 11,17 ****
all: $T
! $T: $(OBJS)
$(CC) -o $@ $(OBJS) -L$(LIB) -llua
clean:
--- 11,17 ----
all: $T
! $T: $(OBJS) $(LIB)/liblua.a
$(CC) -o $@ $(OBJS) -L$(LIB) -llua
clean: