Implemented DOLIST/DOTIMES as macros, without magic in the interpreter.

This commit is contained in:
jjgarcia 2005-10-24 08:33:32 +00:00
parent 5ea5102b91
commit 5643e40059
8 changed files with 86 additions and 315 deletions

View file

@ -71,8 +71,6 @@ static int c_case(cl_object args, int flags);
static int c_catch(cl_object args, int flags);
static int c_compiler_let(cl_object args, int flags);
static int c_cond(cl_object args, int flags);
static int c_dolist(cl_object args, int flags);
static int c_dotimes(cl_object args, int flags);
static int c_eval_when(cl_object args, int flags);
static int c_flet(cl_object args, int flags);
static int c_funcall(cl_object args, int flags);
@ -102,6 +100,7 @@ static int c_tagbody(cl_object args, int flags);
static int c_throw(cl_object args, int flags);
static int c_unwind_protect(cl_object args, int flags);
static int c_while(cl_object args, int flags);
static int c_until(cl_object args, int flags);
static int compile_body(cl_object args, int flags);
static int compile_form(cl_object args, int push);
@ -232,8 +231,6 @@ static compiler_record database[] = {
{@'catch', c_catch, 1},
{@'ext::compiler-let', c_compiler_let, 0},
{@'cond', c_cond, 1},
{@'dolist', c_dolist, 1},
{@'dotimes', c_dotimes, 1},
{@'eval-when', c_eval_when, 0},
{@'flet', c_flet, 1},
{@'function', c_function, 1},
@ -265,6 +262,7 @@ static compiler_record database[] = {
{@'unwind-protect', c_unwind_protect, 1},
{@'values', c_values, 1},
{@'si::while', c_while, 0},
{@'si::until', c_until, 0},
{NULL, NULL, 1}
};
@ -922,7 +920,7 @@ c_cond(cl_object args, int flags) {
*/
static int
c_while(cl_object body, int flags) {
c_while_until(cl_object body, int flags, bool is_while) {
cl_object test = pop(&body);
cl_index labelt, labelb;
@ -938,99 +936,20 @@ c_while(cl_object body, int flags) {
/* Compile test */
asm_complete(OP_JMP, labelt);
compile_form(test, FLAG_VALUES);
asm_op(OP_JNIL);
asm_op(is_while? OP_JT : OP_JNIL);
asm_arg(labelb - current_pc());
return flags;
}
/*
The OP_DOLIST & OP_DOTIMES operators save the lexical
environment and establishes a NIL block to execute the
enclosed forms, which iterate over the elements in a list or
over a range of integer numbers. At the exit of the block,
either by means of a OP_RETFROM jump or because of normal
termination, the lexical environment is restored, and all
bindings undone.
[OP_DOTIMES/OP_DOLIST + labelz + labelo]
... ; bindings
OP_EXIT
... ; body
... ; stepping forms
OP_EXIT
labelo: ... ; output form
OP_EXIT
labelz:
*/
static int
c_dolist_dotimes(int op, cl_object args, int flags) {
cl_object head = pop(&args);
cl_object var = pop(&head);
cl_object list = pop(&head);
cl_object specials, body;
cl_index labelz, labelo;
cl_object old_variables = ENV->variables;
body = c_process_declarations(args);
specials = VALUES(3);
if (!SYMBOLP(var))
FEillegal_variable_name(var);
/* Compute list and enter loop */
compile_form(list, FLAG_VALUES);
labelz = asm_jmp(op);
labelo = current_pc(); asm_arg(0);
/* Bind block */
c_register_block(Cnil);
/* Initialize the variable */
compile_form((op == OP_DOLIST)? Cnil : MAKE_FIXNUM(0), FLAG_REG0);
c_bind(var, specials);
asm_op(OP_EXIT);
/* From here on, declarations apply */
c_declare_specials(specials);
/* Variable assignment and iterated body */
compile_setq(OP_SETQ, var);
c_tagbody(body, 0);
asm_op(OP_EXIT);
/* Output */
asm_complete(0, labelo);
if (head != Cnil && CDR(head) != Cnil)
FEprogram_error("DOLIST: Too many output forms.", 0);
flags = maybe_values(flags);
if (Null(head)) {
compile_body(Cnil, flags);
} else {
compile_setq(OP_SETQ, var);
compile_form(pop(&head), flags);
}
asm_op(OP_EXIT);
/* Exit point for block */
asm_complete(op, labelz);
ENV->variables = old_variables;
return flags;
}
static int
c_dolist(cl_object args, int flags) {
return c_dolist_dotimes(OP_DOLIST, args, flags);
c_while(cl_object body, int flags) {
return c_while_until(body, flags, 1);
}
static int
c_dotimes(cl_object args, int flags) {
return c_dolist_dotimes(OP_DOTIMES, args, flags);
c_until(cl_object body, int flags) {
return c_while_until(body, flags, 0);
}
static int
@ -1053,7 +972,7 @@ c_eval_when(cl_object args, int flags) {
...
fun2
...
OP_EXIT
OP_UNBIND n
labelz:
*/
static cl_index

View file

@ -110,70 +110,6 @@ NO_ARGS:
/* -------------------- DISASSEMBLER CORE -------------------- */
/* OP_DOLIST labelz, labelo
... ; code to bind the local variable
OP_EXIT
... ; code executed on each iteration
OP_EXIT
labelo:
... ; code executed at the end
OP_EXIT
labelz:
High level construct for the DOLIST iterator. The list over which
we iterate is stored in VALUES(0).
*/
static cl_opcode *
disassemble_dolist(cl_object bytecodes, cl_opcode *vector) {
cl_opcode *exit, *output;
cl_object lex_old = cl_env.lex_env;
GET_LABEL(exit, vector);
GET_LABEL(output, vector);
print_oparg("DOLIST\t", exit-base);
vector = disassemble(bytecodes, vector);
print_noarg("\t\t; dolist binding");
vector = disassemble(bytecodes, vector);
print_noarg("\t\t; dolist body");
vector = disassemble(bytecodes, vector);
print_noarg("\t\t; dolist");
cl_env.lex_env = lex_old;
return vector;
}
/* OP_TIMES labelz, labelo
... ; code to bind the local variable
OP_EXIT
... ; code executed on each iteration
OP_EXIT
labelo:
... ; code executed at the end
OP_EXIT
labelz:
High level construct for the DOTIMES iterator. The number of times
we iterate is stored in VALUES(0).
*/
static cl_opcode *
disassemble_dotimes(cl_object bytecodes, cl_opcode *vector) {
cl_opcode *exit, *output;
cl_object lex_old = cl_env.lex_env;
GET_LABEL(exit, vector);
GET_LABEL(output, vector);
print_oparg("DOTIMES\t", exit-base);
vector = disassemble(bytecodes, vector);
print_noarg("\t\t; dotimes times");
vector = disassemble(bytecodes, vector);
print_noarg("\t\t; dotimes body");
vector = disassemble(bytecodes, vector);
print_noarg("\t\t; dotimes");
cl_env.lex_env = lex_old;
return vector;
}
/* OP_FLET nfun{arg}
fun1{object}
...
@ -474,7 +410,7 @@ disassemble(cl_object bytecodes, cl_opcode *vector) {
case OP_CATCH: string = "CATCH\t";
goto JMP;
/* OP_EXIT
Marks the end of a high level construct (DOLIST, DOTIMES...)
Marks the end of a high level construct
*/
case OP_EXIT: print_noarg("EXIT");
return vector;
@ -655,10 +591,6 @@ disassemble(cl_object bytecodes, cl_opcode *vector) {
*/
case OP_NTHVAL: string = "NTHVAL\t";
goto NOARG;
case OP_DOLIST: vector = disassemble_dolist(bytecodes, vector);
break;
case OP_DOTIMES: vector = disassemble_dotimes(bytecodes, vector);
break;
/* OP_DO label
... ; code executed within a NIL block
OP_EXIT_FRAME

View file

@ -446,116 +446,6 @@ interpret_funcall(cl_narg narg, cl_object fun) {
/* -------------------- THE INTERPRETER -------------------- */
/* OP_DOLIST labelz, labelo
... ; code to bind the local variable
OP_EXIT
... ; code executed on each iteration
OP_EXIT
labelo:
... ; code executed at the end
OP_EXIT
labelz:
High level construct for the DOLIST iterator. The list over which
we iterate is stored in VALUES(0).
*/
static cl_opcode *
interpret_dolist(cl_object bytecodes, cl_opcode *vector) {
cl_opcode *volatile exit;
cl_opcode *output;
GET_LABEL(exit, vector);
GET_LABEL(output, vector);
/* 1) Set NIL block */
CL_BLOCK_BEGIN(id) {
cl_object list = VALUES(0);
bind_block(Cnil, id);
/* 2) Build list & bind variable*/
vector = interpret(bytecodes, vector);
/* 3) Repeat until list is exahusted */
while (!endp(list)) {
NVALUES = 1;
VALUES(0) = CAR(list);
interpret(bytecodes, vector);
list = CDR(list);
}
VALUES(0) = Cnil;
NVALUES = 1;
interpret(bytecodes, output);
/* 4) Restore environment */
cl_env.lex_env = cl_env.frs_top->frs_lex;
bds_unwind(cl_env.frs_top->frs_bds_top);
} CL_BLOCK_END;
return exit;
}
/* OP_TIMES labelz, labelo
... ; code to bind the local variable
OP_EXIT
... ; code executed on each iteration
OP_EXIT
labelo:
... ; code executed at the end
OP_EXIT
labelz:
High level construct for the DOTIMES iterator. The number of times
we iterate is stored in VALUES(0).
*/
static cl_opcode *
interpret_dotimes(cl_object bytecodes, cl_opcode *vector) {
cl_opcode *volatile exit;
cl_opcode *output;
GET_LABEL(exit, vector);
GET_LABEL(output, vector);
CL_BLOCK_BEGIN(id) {
cl_object length = VALUES(0);
/* 1) Set up a nil block */
bind_block(Cnil, id);
/* 2) Retrieve number and bind variables */
vector = interpret(bytecodes, vector);
if (FIXNUMP(length)) {
cl_fixnum i, l = fix(length);
/* 3) Loop while needed */
for (i = 0; i < l; i++) {
NVALUES = 1;
VALUES(0) = MAKE_FIXNUM(i);
interpret(bytecodes, vector);
}
length = MAKE_FIXNUM(i);
} else {
cl_object i;
for (i = MAKE_FIXNUM(0);
number_compare(i, length) < 0;
i = one_plus(i))
{
NVALUES = 1;
VALUES(0) = i;
interpret(bytecodes, vector);
}
length = i;
}
NVALUES = 1;
VALUES(0) = length;
interpret(bytecodes, output);
/* 4) Restore environment */
cl_env.lex_env = cl_env.frs_top->frs_lex;
bds_unwind(cl_env.frs_top->frs_bds_top);
} CL_BLOCK_END;
return exit;
}
static cl_object
close_around(cl_object fun, cl_object lex) {
cl_object v = cl_alloc_object(t_bytecodes);
@ -569,7 +459,7 @@ close_around(cl_object fun, cl_object lex) {
...
funn{object}
...
OP_EXIT
OP_UNBIND n
Executes the enclosed code in a lexical enviroment extended with
the functions "fun1" ... "funn".
@ -591,12 +481,12 @@ interpret_flet(cl_object bytecodes, cl_opcode *vector) {
return vector;
}
/* OP_FLET nfun{arg}
/* OP_LABELS nfun{arg}
fun1{object}
...
funn{object}
...
OP_EXIT
OP_UNBIND n
Executes the enclosed code in a lexical enviroment extended with
the functions "fun1" ... "funn".
@ -1055,7 +945,7 @@ interpret(cl_object bytecodes, void *pc) {
/* OP_BLOCK label{arg}
...
OP_EXIT
OP_EXIT_FRAME
label:
Executes the enclosed code in a named block.
@ -1114,7 +1004,7 @@ interpret(cl_object bytecodes, void *pc) {
...
labeln:
...
OP_EXIT
OP_EXIT_TAGBODY
High level construct for the TAGBODY form.
*/
@ -1159,14 +1049,6 @@ interpret(cl_object bytecodes, void *pc) {
VALUES(0) = reg0 = Cnil;
NVALUES = 0;
break;
case OP_DOLIST:
vector = interpret_dolist(bytecodes, vector);
reg0 = VALUES(0);
break;
case OP_DOTIMES:
vector = interpret_dotimes(bytecodes, vector);
reg0 = VALUES(0);
break;
case OP_MSETQ:
vector = interpret_msetq(bytecodes, vector);
reg0 = VALUES(0);
@ -1246,10 +1128,10 @@ interpret(cl_object bytecodes, void *pc) {
}
/* OP_PROTECT label
... ; code to be protected and whose value is output
OP_EXIT
OP_PROTECT_NORMAL
label:
... ; code executed at exit
OP_EXIT
OP_PROTECT_EXIT
High level construct for UNWIND-PROTECT. The first piece of code is
executed and its output value is saved. Then the second piece of code

View file

@ -1524,6 +1524,7 @@ cl_symbols[] = {
#endif
{SYS_ "WHILE", SI_ORDINARY, NULL, -1, OBJNULL},
{SYS_ "UNTIL", SI_ORDINARY, NULL, -1, OBJNULL},
{SYS_ "QUASIQUOTE", SI_ORDINARY, NULL, -1, OBJNULL},
{SYS_ "*EXIT-HOOKS*", SI_SPECIAL, NULL, -1, Cnil},

View file

@ -1524,6 +1524,7 @@ cl_symbols[] = {
#endif
{SYS_ "WHILE",NULL},
{SYS_ "UNTIL",NULL},
{SYS_ "QUASIQUOTE",NULL},
{SYS_ "*EXIT-HOOKS*",NULL},

View file

@ -163,8 +163,6 @@ enum {
OP_TAGBODY,
OP_EXIT_TAGBODY,
OP_EXIT_FRAME,
OP_DOLIST,
OP_DOTIMES,
OP_PROTECT,
OP_PROTECT_NORMAL,
OP_PROTECT_EXIT,

View file

@ -247,7 +247,8 @@ SECOND-FORM."
(defmacro multiple-value-bind (vars form &rest body)
`(multiple-value-call #'(lambda (&optional ,@(mapcar #'list vars)) ,@body) ,form))
(defmacro sys::while (test &body body)
(defun while-until (test body jmp-op)
(declare (si::c-local))
(let ((label (gensym))
(exit (gensym)))
`(TAGBODY
@ -255,7 +256,13 @@ SECOND-FORM."
,label
,@body
,exit
(UNLESS ,test (GO ,label)))))
(,jmp-op ,test (GO ,label)))))
(defmacro sys::while (test &body body)
(while-until test body 'when))
(defmacro sys::until (test &body body)
(while-until test body 'unless))
(defmacro case (keyform &rest clauses &aux (form nil) (key (gensym)))
(dolist (clause (reverse clauses)
@ -276,25 +283,6 @@ SECOND-FORM."
(defmacro return (&optional (val nil)) `(RETURN-FROM NIL ,val))
(defmacro dolist ((var form &optional (val nil)) &rest body
&aux (temp (gensym)) decl)
(multiple-value-setq (decl body)
(find-declarations body))
;; Since ENDP did not complain, this is definitely a (CDR ,temp) is safe
`(DO* ((,temp ,form (CDR (THE CONS ,temp))) (,var))
((ENDP ,temp) ,val)
,@decl
(SETQ ,var (CAR ,temp))
,@body
))
(defmacro dotimes ((var form &optional (val nil)) &rest body
&aux (temp (gensym)))
`(DO* ((,temp ,form) (,var 0 (1+ ,var)))
((>= ,var ,temp) ,val)
(DECLARE (FIXNUM ,var)) ; Beppe (:READ-ONLY ,temp)
,@body))
;; Declarations
(defmacro declaim (&rest decl-specs)
(if (cdr decl-specs)

View file

@ -41,6 +41,56 @@
;; This is also needed for booting ECL. In particular it is required in
;; defmacro.lsp.
;;
(let ((f #'(ext::lambda-block dolist (whole env)
(let (body pop finished control var expr exit)
(setq body (rest whole))
(when (endp body)
(simple-program-error "Syntax error in ~A:~%~A" 'DOLIST whole))
(setq control (first body) body (rest body))
(when (endp control)
(simple-program-error "Syntax error in ~A:~%~A" 'DOLIST whole))
(setq var (first control) control (rest control))
(if (<= 1 (length control) 2)
(setq expr (first control) exit (rest control))
(simple-program-error "Syntax error in ~A:~%~A" 'DOLIST whole))
(multiple-value-bind (declarations body)
(process-declarations body nil)
`(block nil
(let* ((%dolist-var ,expr)
,var)
(declare ,@declarations)
(si::while %dolist-var
(setq ,var (first %dolist-var))
,@body
(setq %dolist-var (rest %dolist-var)))
,(when exit `(setq ,var nil))
,@exit)))))))
(si::fset 'dolist f t))
(let ((f #'(ext::lambda-block dotimes (whole env)
(let (body pop finished control var expr exit)
(setq body (rest whole))
(when (endp body)
(simple-program-error "Syntax error in ~A:~%~A" 'DOTIMES whole))
(setq control (first body) body (rest body))
(when (endp control)
(simple-program-error "Syntax error in ~A:~%~A" 'DOTIMES whole))
(setq var (first control) control (rest control))
(if (<= 1 (length control) 2)
(setq expr (first control) exit (rest control))
(simple-program-error "Syntax error in ~A:~%~A" 'DOTIMES whole))
(multiple-value-bind (declarations body)
(process-declarations body nil)
`(block nil
(let* ((%dotimes-var ,expr)
(,var 0))
(declare ,@declarations)
(si::while (< ,var %dotimes-var)
,@body
(setq ,var (1+ ,var)))
,@exit)))))))
(si::fset 'dotimes f t))
(let ((f #'(ext::lambda-block do/do*-expand (whole env)
(let (do/do* control test result vl step let psetq body)
(setq do/do* (first whole) body (rest whole))
@ -48,13 +98,13 @@
(setq let 'LET psetq 'PSETQ)
(setq let 'LET* psetq 'SETQ))
(when (endp body)
(simple-program-error "Syntax error in DO/DO* body:~%~A" whole))
(simple-program-error "Syntax error in ~A:~%~A" do/do* whole))
(setq control (first body) body (rest body))
(when (endp body)
(simple-program-error "Syntax error in DO/DO* body:~%~A" whole))
(simple-program-error "Syntax error in ~A:~%~A" do/do* whole))
(setq test (first body) body (rest body))
(when (endp test)
(simple-program-error "Syntax error in DO/DO* body:~%~A" whole))
(simple-program-error "Syntax error in ~A:~%~A" do/do* whole))
(setq result (rest test) test (first test))
(dolist (c control)
(when (symbolp c) (setq c (list c)))
@ -65,15 +115,15 @@
(setq vl (cons (butlast c) vl)
step (list* (third c) (first c) step)))
(t
(simple-program-error "Syntax error in DO/DO* body:~%~A" whole))))
(multiple-value-bind (declarations real-body doc)
(simple-program-error "Syntax error in ~A:~%~A" do/do* whole))))
(multiple-value-bind (declarations real-body)
(process-declarations body nil)
`(BLOCK NIL
(,let ,(nreverse vl)
(declare ,@declarations)
(sys::while ,test
,@real-body
,@(when step (list (cons psetq (nreverse step)))))
(sys::until ,test
,@real-body
,@(when step (list (cons psetq (nreverse step)))))
,@(or result '(nil)))))))))
(si::fset 'do f t)
(si::fset 'do* f t))