compile.c revision f14f4646
1/*
2 * Copyright (c) 2002 by The XFree86 Project, Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17 * THE XFREE86 PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
18 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
19 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 * SOFTWARE.
21 *
22 * Except as contained in this notice, the name of the XFree86 Project shall
23 * not be used in advertising or otherwise to promote the sale, use or other
24 * dealings in this Software without prior written authorization from the
25 * XFree86 Project.
26 *
27 * Author: Paulo César Pereira de Andrade
28 */
29
30/* $XFree86: xc/programs/xedit/lisp/compile.c,v 1.15tsi Exp $ */
31
32#define VARIABLE_USED		0x0001
33#define VARIABLE_ARGUMENT	0x0002
34
35/*
36 * Prototypes
37 */
38static void ComPredicate(LispCom*, LispBuiltin*, LispBytePredicate);
39static void ComReturnFrom(LispCom*, LispBuiltin*, int);
40
41static int ComConstantp(LispCom*, LispObj*);
42static void ComAddVariable(LispCom*, LispObj*, LispObj*);
43static int ComGetVariable(LispCom*, LispObj*);
44static void ComVariableSetFlag(LispCom*, LispAtom*, int);
45#define COM_VARIABLE_USED(atom)				\
46    ComVariableSetFlag(com, atom, VARIABLE_USED)
47#define COM_VARIABLE_ARGUMENT(atom)			\
48	ComVariableSetFlag(com, atom, VARIABLE_ARGUMENT)
49
50static int FindIndex(void*, void**, int);
51static int compare(const void*, const void*);
52static int BuildTablePointer(void*, void***, int*);
53
54static void ComLabel(LispCom*, LispObj*);
55static void ComPush(LispCom*, LispObj*, LispObj*, int, int, int);
56static int ComCall(LispCom*, LispArgList*, LispObj*, LispObj*, int, int, int);
57static void ComFuncall(LispCom*, LispObj*, LispObj*, int);
58static void ComProgn(LispCom*, LispObj*);
59static void ComEval(LispCom*, LispObj*);
60
61static void ComRecursiveCall(LispCom*, LispArgList*, LispObj*, LispObj*);
62static void ComInlineCall(LispCom*, LispArgList*, LispObj*, LispObj*, LispObj*);
63
64static void ComMacroBackquote(LispCom*, LispObj*);
65static void ComMacroCall(LispCom*, LispArgList*, LispObj*, LispObj*, LispObj*);
66static LispObj *ComMacroExpandBackquote(LispCom*, LispObj*);
67static LispObj *ComMacroExpand(LispCom*, LispObj*);
68static LispObj *ComMacroExpandFuncall(LispCom*, LispObj*, LispObj*);
69static LispObj *ComMacroExpandEval(LispCom*, LispObj*);
70
71/*
72 * Implementation
73 */
74void
75Com_And(LispCom *com, LispBuiltin *builtin)
76/*
77 and &rest args
78 */
79{
80    LispObj *args;
81
82    args = ARGUMENT(0);
83
84    if (CONSP(args)) {
85	/* Evaluate first argument */
86	ComEval(com, CAR(args));
87	args = CDR(args);
88
89	/* If more than one argument, create jump list */
90	if (CONSP(args)) {
91	    CodeTree *tree = NULL, *group;
92
93	    group = NEW_TREE(CodeTreeJumpIf);
94	    group->code = XBC_JUMPNIL;
95
96	    for (; CONSP(args); args = CDR(args)) {
97		ComEval(com, CAR(args));
98		tree = NEW_TREE(CodeTreeJumpIf);
99		tree->code = XBC_JUMPNIL;
100		group->group = tree;
101		group = tree;
102	    }
103	    /*  Finish form the last CodeTree code is changed to sign the
104	     * end of the AND list */
105	    group->code = XBC_NOOP;
106	    if (group)
107		group->group = tree;
108	}
109    }
110    else
111	/* Identity of AND is T */
112	com_Bytecode(com, XBC_T);
113}
114
115void
116Com_Block(LispCom *com, LispBuiltin *builtin)
117/*
118 block name &rest body
119 */
120{
121
122    LispObj *name, *body;
123
124    body = ARGUMENT(1);
125    name = ARGUMENT(0);
126
127    if (name != NIL && name != T && !SYMBOLP(name))
128	LispDestroy("%s: %s cannot name a block",
129		    STRFUN(builtin), STROBJ(name));
130    if (CONSP(body)) {
131	CompileIniBlock(com, LispBlockTag, name);
132	ComProgn(com, body);
133	CompileFiniBlock(com);
134    }
135    else
136	/* Just load NIL without starting an empty block */
137	com_Bytecode(com, XBC_NIL);
138}
139
140void
141Com_C_r(LispCom *com, LispBuiltin *builtin)
142/*
143 c[ad]{1,4}r list
144 */
145{
146    LispObj *list;
147    char *desc;
148
149    list = ARGUMENT(0);
150
151    desc = STRFUN(builtin);
152    if (*desc == 'F')		/* FIRST */
153	desc = "CAR";
154    else if (*desc == 'R')	/* REST */
155	desc = "CDR";
156
157    /* Check if it is a list of constants */
158    while (desc[1] != 'R')
159	desc++;
160    ComEval(com, list);
161    while (*desc != 'C') {
162	com_Bytecode(com, *desc == 'A' ? XBC_CAR : XBC_CDR);
163	--desc;
164    }
165}
166
167void
168Com_Cond(LispCom *com, LispBuiltin *builtin)
169/*
170 cond &rest body
171 */
172{
173    int count;
174    LispObj *code, *body;
175    CodeTree *group, *tree;
176
177    body = ARGUMENT(0);
178
179    count = 0;
180    group = NULL;
181    if (CONSP(body)) {
182	for (; CONSP(body); body = CDR(body)) {
183	    code = CAR(body);
184	    CHECK_CONS(code);
185	    ++count;
186	    ComEval(com, CAR(code));
187	    tree = NEW_TREE(CodeTreeCond);
188	    if (group)
189		group->group = tree;
190	    tree->code = XBC_JUMPNIL;
191	    group = tree;
192	    /* The code to execute if the test is true */
193	    ComProgn(com, CDR(code));
194	    /* Add a node signaling the end of the PROGN code */
195	    tree = NEW_TREE(CodeTreeCond);
196	    tree->code = XBC_JUMPT;
197	    if (group)
198		group->group = tree;
199	    group = tree;
200	}
201    }
202    if (!count)
203	com_Bytecode(com, XBC_NIL);
204    else
205	/* Where to jump after T progn */
206	group->code = XBC_NOOP;
207}
208
209void
210Com_Cons(LispCom *com, LispBuiltin *builtin)
211/*
212 cons car cdr
213 */
214{
215    LispObj *car, *cdr;
216
217    cdr = ARGUMENT(1);
218    car = ARGUMENT(0);
219
220    if (ComConstantp(com, car) && ComConstantp(com, cdr))
221	com_BytecodeCons(com, XBC_CCONS, car, cdr);
222    else {
223	++com->stack.cpstack;
224	if (com->stack.pstack < com->stack.cpstack)
225	    com->stack.pstack = com->stack.cpstack;
226	ComEval(com, car);
227	com_Bytecode(com, XBC_CSTAR);
228	ComEval(com, cdr);
229	com_Bytecode(com, XBC_CFINI);
230	--com->stack.cpstack;
231    }
232}
233
234void
235Com_Consp(LispCom *com, LispBuiltin *builtin)
236/*
237 consp object
238 */
239{
240    ComPredicate(com, builtin, XBP_CONSP);
241}
242
243void
244Com_Dolist(LispCom *com, LispBuiltin *builtin)
245/*
246 dolist init &rest body
247 */
248{
249    int unbound, item;
250    LispObj *symbol, *list, *result;
251    LispObj *init, *body;
252    CodeTree *group, *tree;
253
254    body = ARGUMENT(1);
255    init = ARGUMENT(0);
256
257    CHECK_CONS(init);
258    symbol = CAR(init);
259    CHECK_SYMBOL(symbol);
260    CHECK_CONSTANT(symbol);
261    init = CDR(init);
262    if (CONSP(init)) {
263	list = CAR(init);
264	init = CDR(init);
265    }
266    else
267	list = NIL;
268    if (CONSP(init)) {
269	result = CAR(init);
270	if (CONSP(CDR(init)))
271	    LispDestroy("%s: too many arguments %s",
272			STRFUN(builtin), STROBJ(CDR(init)));
273    }
274    else
275	result = NIL;
276
277    /*	Generate code for the body of the form.
278     *	The generated code uses two objects unavailable to user code,
279     * in the format:
280     *	(block NIL
281     *	    (let ((? list) (item NIL))
282     *		(tagbody
283     *		    .			    ; the DOT object as a label
284     *		    (when (consp list)
285     *			(setq item (car ?))
286     *			@body		    ; code to be executed
287     *			(setq ? (cdr ?))
288     *			(go .)
289     *		    )
290     *		)
291     *		(setq item nil)
292     *		result
293     *	    )
294     *	)
295     */
296
297    /* XXX All of the logic below should be simplified at some time
298     * by adding more opcodes for compound operations ... */
299
300    /* Relative offsets the locally added variables will have at run time */
301    unbound = lisp__data.env.length - lisp__data.env.lex;
302    item = unbound + 1;
303
304    /* Start BLOCK NIL */
305    FORM_ENTER();
306    CompileIniBlock(com, LispBlockTag, NIL);
307
308    /* Add the <?> variable */
309    ComPush(com, UNBOUND, list, 1, 0, 0);
310    /* Add the <item> variable */
311    ComPush(com, symbol, NIL, 0, 0, 0);
312    /* Stack length is increased */
313    CompileStackEnter(com, 2, 0);
314    /* Bind variables */
315    com_Bind(com, 2);
316    com->block->bind += 2;
317    lisp__data.env.head += 2;
318
319    /* Remember that iteration variable is used even if it not referenced */
320    COM_VARIABLE_USED(symbol->data.atom);
321
322    /* Initialize the TAGBODY */
323    FORM_ENTER();
324    CompileIniBlock(com, LispBlockBody, NIL);
325
326    /* Create the <.> label */
327    ComLabel(com, DOT);
328
329    /* Load <?> variable */
330    com_BytecodeShort(com, XBC_LOAD, unbound);
331    /* Check if <?> is a list */
332    com_BytecodeChar(com, XBC_PRED, XBP_CONSP);
333
334    /* Start WHEN block */
335    group = NEW_TREE(CodeTreeJumpIf);
336    group->code = XBC_JUMPNIL;
337    /* Load <?> again */
338    com_BytecodeShort(com, XBC_LOAD, unbound);
339    /* Get CAR of <?> */
340    com_Bytecode(com, XBC_CAR);
341    /* Store it in <item> */
342    com_BytecodeShort(com, XBC_SET, item);
343    /* Execute @BODY */
344    ComProgn(com, body);
345
346    /* Load <?> again */
347    com_BytecodeShort(com, XBC_LOAD, unbound);
348    /* Get CDR of <?> */
349    com_Bytecode(com, XBC_CDR);
350    /* Change value of <?> */
351    com_BytecodeShort(com, XBC_SET, unbound);
352
353    /* GO back to <.> */
354    tree = NEW_TREE(CodeTreeGo);
355    tree->data.object = DOT;
356
357    /* Finish WHEN block */
358    tree = NEW_TREE(CodeTreeJumpIf);
359    tree->code = XBC_NOOP;
360    group->group = tree;
361
362    /* Finish the TAGBODY */
363    CompileFiniBlock(com);
364    FORM_LEAVE();
365
366    /* Set <item> to NIL, in case result references it...
367     * Loaded value is NIL as the CONSP predicate */
368    com_BytecodeShort(com, XBC_SET, item);
369
370    /* Evaluate <result> */
371    ComEval(com, result);
372
373    /* Unbind variables */
374    lisp__data.env.head -= 2;
375    lisp__data.env.length -= 2;
376    com->block->bind -= 2;
377    com_Unbind(com, 2);
378    /* Stack length is reduced. */
379    CompileStackLeave(com, 2, 0);
380
381    /* Finish BLOCK NIL */
382    CompileFiniBlock(com);
383    FORM_LEAVE();
384}
385
386void
387Com_Eq(LispCom *com, LispBuiltin *builtin)
388/*
389 eq left right
390 eql left right
391 equal left right
392 equalp left right
393 */
394{
395    LispObj *left, *right;
396    LispByteOpcode code;
397    char *name;
398
399    right = ARGUMENT(1);
400    left = ARGUMENT(0);
401
402    CompileStackEnter(com, 1, 1);
403    /* Just like preparing to call a builtin function */
404    ComEval(com, left);
405    com_Bytecode(com, XBC_PUSH);
406    /* The second argument is now loaded */
407    ComEval(com, right);
408
409    /* Compare arguments and restore builtin stack */
410    name = STRFUN(builtin);
411    switch (name[3]) {
412	case 'L':
413	    code = XBC_EQL;
414	    break;
415	case 'U':
416	    code = name[5] == 'P' ? XBC_EQUALP : XBC_EQUAL;
417	    break;
418	default:
419	    code = XBC_EQ;
420	    break;
421    }
422    com_Bytecode(com, code);
423
424    CompileStackLeave(com, 1, 1);
425}
426
427void
428Com_Go(LispCom *com, LispBuiltin *builtin)
429/*
430 go tag
431 */
432{
433    int bind;
434    LispObj *tag;
435    CodeTree *tree;
436    CodeBlock *block;
437
438    tag = ARGUMENT(0);
439
440    block = com->block;
441    bind = block->bind;
442
443    while (block) {
444	if (block->type == LispBlockClosure || block->type == LispBlockBody)
445	    break;
446	block = block->prev;
447	if (block)
448	    bind += block->bind;
449    }
450
451    if (!block || block->type != LispBlockBody)
452	LispDestroy("%s called not within a block", STRFUN(builtin));
453
454    /* Unbind any local variables */
455    com_Unbind(com, bind);
456    tree = NEW_TREE(CodeTreeGo);
457    tree->data.object = tag;
458}
459
460void
461Com_If(LispCom *com, LispBuiltin *builtin)
462/*
463 if test then &optional else
464 */
465{
466    CodeTree *group, *tree;
467    LispObj *test, *then, *oelse;
468
469    oelse = ARGUMENT(2);
470    then = ARGUMENT(1);
471    test = ARGUMENT(0);
472
473    /* Build code to execute test */
474    ComEval(com, test);
475
476    /* Add jump node to use if test is NIL */
477    group = NEW_TREE(CodeTreeJumpIf);
478    group->code = XBC_JUMPNIL;
479
480    /* Build T code */
481    ComEval(com, then);
482
483    if (oelse != UNSPEC) {
484	/* Remember start of NIL code */
485	tree = NEW_TREE(CodeTreeJump);
486	tree->code = XBC_JUMP;
487	group->group = tree;
488	group = tree;
489	/* Build NIL code */
490	ComEval(com, oelse);
491    }
492
493    /* Remember jump of T code */
494    tree = NEW_TREE(CodeTreeJumpIf);
495    tree->code = XBC_NOOP;
496    group->group = tree;
497}
498
499void
500Com_Last(LispCom *com, LispBuiltin *builtin)
501/*
502 last list &optional count
503 */
504{
505    LispObj *list, *count;
506
507    count = ARGUMENT(1);
508    list = ARGUMENT(0);
509
510    ComEval(com, list);
511    CompileStackEnter(com, 1, 1);
512    com_Bytecode(com, XBC_PUSH);
513    if (count == UNSPEC)
514	count = FIXNUM(1);
515    ComEval(com, count);
516    CompileStackLeave(com, 1, 1);
517    com_Bytecode(com, XBC_LAST);
518}
519
520void
521Com_Length(LispCom *com, LispBuiltin *builtin)
522/*
523 length sequence
524 */
525{
526    LispObj *sequence;
527
528    sequence = ARGUMENT(0);
529
530    ComEval(com, sequence);
531    com_Bytecode(com, XBC_LENGTH);
532}
533
534void
535Com_Let(LispCom *com, LispBuiltin *builtin)
536/*
537 let init &rest body
538 */
539{
540    int count;
541    LispObj *symbol, *value, *pair;
542
543    LispObj *init, *body;
544
545    body = ARGUMENT(1);
546    init = ARGUMENT(0);
547
548    if (init == NIL) {
549	/* If no local variables */
550	ComProgn(com, body);
551	return;
552    }
553    CHECK_CONS(init);
554
555    /* Could optimize if the body is empty and the
556     * init form is known to have no side effects */
557
558    for (count = 0; CONSP(init); init = CDR(init), count++) {
559	pair = CAR(init);
560	if (CONSP(pair)) {
561	    symbol = CAR(pair);
562	    pair = CDR(pair);
563	    if (CONSP(pair)) {
564		value = CAR(pair);
565		if (CDR(pair) != NIL)
566		    LispDestroy("%s: too much arguments to initialize %s",
567				STRFUN(builtin), STROBJ(symbol));
568	    }
569	    else
570		value = NIL;
571	}
572	else {
573	    symbol = pair;
574	    value = NIL;
575	}
576	CHECK_SYMBOL(symbol);
577	CHECK_CONSTANT(symbol);
578
579	/* Add the variable */
580	ComPush(com, symbol, value, 1, 0, 0);
581    }
582
583    /* Stack length is increased */
584    CompileStackEnter(com, count, 0);
585    /* Bind the added variables */
586    com_Bind(com, count);
587    com->block->bind += count;
588    lisp__data.env.head += count;
589    /* Generate code for the body of the form */
590    ComProgn(com, body);
591    /* Unbind the added variables */
592    lisp__data.env.head -= count;
593    lisp__data.env.length -= count;
594    com->block->bind -= count;
595    com_Unbind(com, count);
596    /* Stack length is reduced. */
597    CompileStackLeave(com, count, 0);
598}
599
600void
601Com_Letx(LispCom *com, LispBuiltin *builtin)
602/*
603 let* init &rest body
604 */
605{
606    int count;
607    LispObj *symbol, *value, *pair;
608
609    LispObj *init, *body;
610
611    body = ARGUMENT(1);
612    init = ARGUMENT(0);
613
614    if (init == NIL) {
615	/* If no local variables */
616	ComProgn(com, body);
617	return;
618    }
619    CHECK_CONS(body);
620
621    /* Could optimize if the body is empty and the
622     * init form is known to have no side effects */
623
624    for (count = 0; CONSP(init); init = CDR(init), count++) {
625	pair = CAR(init);
626	if (CONSP(pair)) {
627	    symbol = CAR(pair);
628	    pair = CDR(pair);
629	    if (CONSP(pair)) {
630		value = CAR(pair);
631		if (CDR(pair) != NIL)
632		    LispDestroy("%s: too much arguments to initialize %s",
633				STRFUN(builtin), STROBJ(symbol));
634	    }
635	    else
636		value = NIL;
637	}
638	else {
639	    symbol = pair;
640	    value = NIL;
641	}
642	CHECK_SYMBOL(symbol);
643	CHECK_CONSTANT(symbol);
644
645	/* LET* is identical to &AUX arguments, just bind the symbol */
646	ComPush(com, symbol, value, 1, 0, 0);
647	/* Every added variable is binded */
648	com_Bind(com, 1);
649	/* Must be binded at compile time also */
650	++lisp__data.env.head;
651	++com->block->bind;
652    }
653
654    /* Generate code for the body of the form */
655    CompileStackEnter(com, count, 0);
656    ComProgn(com, body);
657    com_Unbind(com, count);
658    com->block->bind -= count;
659    lisp__data.env.head -= count;
660    lisp__data.env.length -= count;
661    CompileStackLeave(com, count, 0);
662}
663
664void
665Com_Listp(LispCom *com, LispBuiltin *builtin)
666/*
667 listp object
668 */
669{
670    ComPredicate(com, builtin, XBP_LISTP);
671}
672
673void
674Com_Loop(LispCom *com, LispBuiltin *builtin)
675/*
676 loop &rest body
677 */
678{
679    CodeTree *tree, *group;
680    LispObj *body;
681
682    body = ARGUMENT(0);
683
684    /* Start NIL block */
685    CompileIniBlock(com, LispBlockTag, NIL);
686
687    /* Insert node to mark LOOP start */
688    tree = NEW_TREE(CodeTreeJump);
689    tree->code = XBC_NOOP;
690
691    /* Execute @BODY */
692    if (CONSP(body))
693	ComProgn(com, body);
694    else
695	/* XXX bytecode.c code require that blocks have at least one opcode */
696	com_Bytecode(com, XBC_NIL);
697
698    /* Insert node to jump of start of LOOP */
699    group = NEW_TREE(CodeTreeJump);
700    group->code = XBC_JUMP;
701    group->group = tree;
702
703    /* Finish NIL block */
704    CompileFiniBlock(com);
705}
706
707void
708Com_Nthcdr(LispCom *com, LispBuiltin *builtin)
709/*
710 nthcdr index list
711 */
712{
713    LispObj *oindex, *list;
714
715    list = ARGUMENT(1);
716    oindex = ARGUMENT(0);
717
718    ComEval(com, oindex);
719    CompileStackEnter(com, 1, 1);
720    com_Bytecode(com, XBC_PUSH);
721    ComEval(com, list);
722    CompileStackLeave(com, 1, 1);
723    com_Bytecode(com, XBC_NTHCDR);
724}
725
726void
727Com_Null(LispCom *com, LispBuiltin *builtin)
728/*
729 null list
730 */
731{
732    LispObj *list;
733
734    list = ARGUMENT(0);
735
736    if (list == NIL)
737	com_Bytecode(com, XBC_T);
738    else if (ComConstantp(com, list))
739	com_Bytecode(com, XBC_NIL);
740    else {
741	ComEval(com, list);
742	com_Bytecode(com, XBC_INV);
743    }
744}
745
746void
747Com_Numberp(LispCom *com, LispBuiltin *builtin)
748/*
749 numberp object
750 */
751{
752    ComPredicate(com, builtin, XBP_NUMBERP);
753}
754
755void
756Com_Or(LispCom *com, LispBuiltin *builtin)
757/*
758 or &rest args
759 */
760{
761    LispObj *args;
762
763    args = ARGUMENT(0);
764
765    if (CONSP(args)) {
766	/* Evaluate first argument */
767	ComEval(com, CAR(args));
768	args = CDR(args);
769
770	/* If more than one argument, create jump list */
771	if (CONSP(args)) {
772	    CodeTree *tree = NULL, *group;
773
774	    group = NEW_TREE(CodeTreeJumpIf);
775	    group->code = XBC_JUMPT;
776
777	    for (; CONSP(args); args = CDR(args)) {
778		ComEval(com, CAR(args));
779		tree = NEW_TREE(CodeTreeJumpIf);
780		tree->code = XBC_JUMPT;
781		group->group = tree;
782		group = tree;
783	    }
784	    /*  Finish form the last CodeTree code is changed to sign the
785	     * end of the AND list */
786	    group->code = XBC_NOOP;
787	    group->group = tree;
788	}
789    }
790    else
791	/* Identity of OR is NIL */
792	com_Bytecode(com, XBC_NIL);
793}
794
795void
796Com_Progn(LispCom *com, LispBuiltin *builtin)
797/*
798 progn &rest body
799 */
800{
801    LispObj *body;
802
803    body = ARGUMENT(0);
804
805    ComProgn(com, body);
806}
807
808void
809Com_Return(LispCom *com, LispBuiltin *builtin)
810/*
811 return &optional result
812 */
813{
814    ComReturnFrom(com, builtin, 0);
815}
816
817void
818Com_ReturnFrom(LispCom *com, LispBuiltin *builtin)
819/*
820 return-from name &optional result
821 */
822{
823    ComReturnFrom(com, builtin, 1);
824}
825
826void
827Com_Rplac_(LispCom *com, LispBuiltin *builtin)
828/*
829 rplac[ad] place value
830 */
831{
832    LispObj *place, *value;
833
834    value = ARGUMENT(1);
835    place = ARGUMENT(0);
836
837    CompileStackEnter(com, 1, 1);
838    ComEval(com, place);
839    com_Bytecode(com, XBC_PUSH);
840    ComEval(com, value);
841    com_Bytecode(com, STRFUN(builtin)[5] == 'A' ? XBC_RPLACA : XBC_RPLACD);
842    CompileStackLeave(com, 1, 1);
843}
844
845void
846Com_Setq(LispCom *com, LispBuiltin *builtin)
847/*
848 setq &rest form
849 */
850{
851    int offset;
852    LispObj *form, *symbol, *value;
853
854    form = ARGUMENT(0);
855
856    for (; CONSP(form); form = CDR(form)) {
857	symbol = CAR(form);
858	CHECK_SYMBOL(symbol);
859	CHECK_CONSTANT(symbol);
860	form = CDR(form);
861	if (!CONSP(form))
862	    LispDestroy("%s: odd number of arguments", STRFUN(builtin));
863	value = CAR(form);
864	/* Generate code to load value */
865	ComEval(com, value);
866	offset = ComGetVariable(com, symbol);
867	if (offset >= 0)
868	    com_Set(com, offset);
869	else
870	    com_SetSym(com, symbol->data.atom);
871    }
872}
873
874void
875Com_Tagbody(LispCom *com, LispBuiltin *builtin)
876/*
877 tagbody &rest body
878 */
879{
880    LispObj *body;
881
882    body = ARGUMENT(0);
883
884    if (CONSP(body)) {
885	CompileIniBlock(com, LispBlockBody, NIL);
886	ComProgn(com, body);
887	/* Tagbody returns NIL */
888	com_Bytecode(com, XBC_NIL);
889	CompileFiniBlock(com);
890    }
891    else
892	/* Tagbody always returns NIL */
893	com_Bytecode(com, XBC_NIL);
894}
895
896void
897Com_Unless(LispCom *com, LispBuiltin *builtin)
898/*
899 unless test &rest body
900 */
901{
902    CodeTree *group, *tree;
903    LispObj *test, *body;
904
905    body = ARGUMENT(1);
906    test = ARGUMENT(0);
907
908    /* Generate code to evaluate test */
909    ComEval(com, test);
910    /* Add node after test */
911    group = NEW_TREE(CodeTreeJumpIf);
912    group->code = XBC_JUMPT;
913    /* Generate NIL code */
914    ComProgn(com, body);
915    /* Insert node to know where to jump if test is T */
916    tree = NEW_TREE(CodeTreeJumpIf);
917    tree->code = XBC_NOOP;
918    group->group = tree;
919}
920
921void
922Com_Until(LispCom *com, LispBuiltin *builtin)
923/*
924 until test &rest body
925 */
926{
927    CodeTree *tree, *group, *ltree, *lgroup;
928    LispObj *test, *body;
929
930    body = ARGUMENT(1);
931    test = ARGUMENT(0);
932
933    /* Insert node to mark LOOP start */
934    ltree = NEW_TREE(CodeTreeJump);
935    ltree->code = XBC_NOOP;
936
937    /* Build code for test */
938    ComEval(com, test);
939    group = NEW_TREE(CodeTreeJumpIf);
940    group->code = XBC_JUMPT;
941
942    /* Execute @BODY */
943    ComProgn(com, body);
944
945    /* Insert node to jump to test again */
946    lgroup = NEW_TREE(CodeTreeJump);
947    lgroup->code = XBC_JUMP;
948    lgroup->group = ltree;
949
950    /* Insert node to know where to jump if test is T */
951    tree = NEW_TREE(CodeTreeJumpIf);
952    tree->code = XBC_NOOP;
953    group->group = tree;
954}
955
956void
957Com_When(LispCom *com, LispBuiltin *builtin)
958/*
959 when test &rest body
960 */
961{
962    CodeTree *group, *tree;
963    LispObj *test, *body;
964
965    body = ARGUMENT(1);
966    test = ARGUMENT(0);
967
968    /* Generate code to evaluate test */
969    ComEval(com, test);
970    /* Add node after test */
971    group = NEW_TREE(CodeTreeJumpIf);
972    group->code = XBC_JUMPNIL;
973    /* Generate T code */
974    ComProgn(com, body);
975    /* Insert node to know where to jump if test is NIL */
976    tree = NEW_TREE(CodeTreeJumpIf);
977    tree->code = XBC_NOOP;
978    group->group = tree;
979}
980
981void
982Com_While(LispCom *com, LispBuiltin *builtin)
983/*
984 while test &rest body
985 */
986{
987    CodeTree *tree, *group, *ltree, *lgroup;
988    LispObj *test, *body;
989
990    body = ARGUMENT(1);
991    test = ARGUMENT(0);
992
993    /* Insert node to mark LOOP start */
994    ltree = NEW_TREE(CodeTreeJump);
995    ltree->code = XBC_NOOP;
996
997    /* Build code for test */
998    ComEval(com, test);
999    group = NEW_TREE(CodeTreeJumpIf);
1000    group->code = XBC_JUMPNIL;
1001
1002    /* Execute @BODY */
1003    ComProgn(com, body);
1004
1005    /* Insert node to jump to test again */
1006    lgroup = NEW_TREE(CodeTreeJump);
1007    lgroup->code = XBC_JUMP;
1008    lgroup->group = ltree;
1009
1010    /* Insert node to know where to jump if test is NIL */
1011    tree = NEW_TREE(CodeTreeJumpIf);
1012    tree->code = XBC_NOOP;
1013    group->group = tree;
1014}
1015
1016
1017/***********************************************************************
1018 * Com_XXX helper functions
1019 ***********************************************************************/
1020static void
1021ComPredicate(LispCom *com, LispBuiltin *builtin, LispBytePredicate predicate)
1022{
1023    LispObj *object;
1024
1025    object = ARGUMENT(0);
1026
1027    if (ComConstantp(com, object)) {
1028	switch (predicate) {
1029	    case XBP_CONSP:
1030		com_Bytecode(com, CONSP(object) ? XBC_T : XBC_NIL);
1031		break;
1032	    case XBP_LISTP:
1033		com_Bytecode(com, CONSP(object) || object == NIL ?
1034			     XBC_T : XBC_NIL);
1035		break;
1036	    case XBP_NUMBERP:
1037		com_Bytecode(com, NUMBERP(object) ? XBC_T : XBC_NIL);
1038		break;
1039	}
1040    }
1041    else {
1042	ComEval(com, object);
1043	com_BytecodeChar(com, XBC_PRED, predicate);
1044    }
1045}
1046
1047/* XXX Could receive an argument telling if is the last statement in the
1048 * block(s), i.e. if a jump opcode should be generated or just the
1049 * evaluation of the returned value. Probably this is better done in
1050 * an optimization step. */
1051static void
1052ComReturnFrom(LispCom *com, LispBuiltin *builtin, int from)
1053{
1054    int bind;
1055    CodeTree *tree;
1056    LispObj *name, *result;
1057    CodeBlock *block = com->block;
1058
1059    if (from) {
1060	result = ARGUMENT(1);
1061	name = ARGUMENT(0);
1062    }
1063    else {
1064	result = ARGUMENT(0);
1065	name = NIL;
1066    }
1067    if (result == UNSPEC)
1068	result = NIL;
1069
1070    bind = block->bind;
1071    while (block) {
1072	if (block->type == LispBlockClosure)
1073	    /* A function call */
1074	    break;
1075	else if (block->type == LispBlockTag && block->tag == name)
1076	    break;
1077	block = block->prev;
1078	if (block)
1079	    bind += block->bind;
1080    }
1081
1082    if (!block || block->tag != name)
1083	LispDestroy("%s: no visible %s block", STRFUN(builtin), STROBJ(name));
1084
1085    /* Generate code to load result */
1086    ComEval(com, result);
1087
1088    /* Check for added variables that the jump is skiping the unbind opcode */
1089    com_Unbind(com, bind);
1090
1091    tree = NEW_TREE(CodeTreeReturn);
1092    tree->data.block = block;
1093}
1094
1095/***********************************************************************
1096 * Helper functions
1097 ***********************************************************************/
1098static int
1099ComConstantp(LispCom *com, LispObj *object)
1100{
1101    switch (OBJECT_TYPE(object)) {
1102	case LispAtom_t:
1103	    /* Keywords are guaranteed to evaluate to itself */
1104	    if (object->data.atom->package == lisp__data.keyword)
1105		break;
1106	    return (0);
1107
1108	    /* Function call */
1109	case LispCons_t:
1110
1111	    /* Need macro expansion, these are special abstract objects */
1112	case LispQuote_t:
1113	case LispBackquote_t:
1114	case LispComma_t:
1115	case LispFunctionQuote_t:
1116	    return (0);
1117
1118	    /* Anything else is a literal constant */
1119	default:
1120	    break;
1121    }
1122
1123    return (1);
1124}
1125
1126static int
1127FindIndex(void *item, void **table, int length)
1128{
1129    long cmp;
1130    int left, right, i;
1131
1132    left = 0;
1133    right = length - 1;
1134    while (left <= right) {
1135	i = (left + right) >> 1;
1136	cmp = (char*)item - (char*)table[i];
1137	if (cmp == 0)
1138	    return (i);
1139	else if (cmp < 0)
1140	    right = i - 1;
1141	else
1142	    left = i + 1;
1143    }
1144
1145    return (-1);
1146}
1147
1148static int
1149compare(const void *left, const void *right)
1150{
1151    long cmp = *(char**)left - *(char**)right;
1152
1153    return (cmp < 0 ? -1 : 1);
1154}
1155
1156static int
1157BuildTablePointer(void *pointer, void ***pointers, int *num_pointers)
1158{
1159    int i;
1160
1161    if ((i = FindIndex(pointer, *pointers, *num_pointers)) < 0) {
1162	*pointers = LispRealloc(*pointers,
1163				sizeof(void*) * (*num_pointers + 1));
1164	(*pointers)[*num_pointers] = pointer;
1165	if (++*num_pointers > 1)
1166	    qsort(*pointers, *num_pointers, sizeof(void*), compare);
1167	i = FindIndex(pointer, *pointers, *num_pointers);
1168    }
1169
1170    return (i);
1171}
1172
1173static void
1174ComAddVariable(LispCom *com, LispObj *symbol, LispObj *value)
1175{
1176    LispAtom *atom = symbol->data.atom;
1177
1178    if (atom && atom->key && !com->macro) {
1179	int i, length = com->block->variables.length;
1180
1181	i = BuildTablePointer(atom, (void***)&com->block->variables.symbols,
1182			      &com->block->variables.length);
1183
1184	if (com->block->variables.length != length) {
1185	    com->block->variables.flags =
1186		LispRealloc(com->block->variables.flags,
1187			    com->block->variables.length * sizeof(int));
1188
1189	    /* Variable was inserted in the middle of the list */
1190	    if (i < length)
1191		memmove(com->block->variables.flags + i + 1,
1192			com->block->variables.flags + i,
1193			(length - i) * sizeof(int));
1194
1195	    com->block->variables.flags[i] = 0;
1196	}
1197    }
1198
1199    LispAddVar(symbol, value);
1200}
1201
1202static int
1203ComGetVariable(LispCom *com, LispObj *symbol)
1204{
1205    LispAtom *name;
1206    int i, base, offset;
1207    Atom_id id;
1208
1209    name = symbol->data.atom;
1210    if (name->constant) {
1211	if (name->package == lisp__data.keyword)
1212	    /*	Just load <symbol> from the byte stream, keywords are
1213	     * guaranteed to evaluate to itself. */
1214	    return (SYMBOL_KEYWORD);
1215	return (SYMBOL_CONSTANT);
1216    }
1217
1218    offset = name->offset;
1219    id = name->key;
1220    base = lisp__data.env.lex;
1221    i = lisp__data.env.head - 1;
1222
1223    /* If variable is local */
1224    if (offset <= i && offset >= com->lex && lisp__data.env.names[offset] == id) {
1225	COM_VARIABLE_USED(name);
1226	/* Relative offset */
1227	return (offset - base);
1228    }
1229
1230    /* name->offset may have been changed in a macro expansion */
1231    for (; i >= com->lex; i--)
1232	if (lisp__data.env.names[i] == id) {
1233	    name->offset = i;
1234	    COM_VARIABLE_USED(name);
1235	    return (i - base);
1236	}
1237
1238    if (!name->a_object) {
1239	++com->warnings;
1240	LispWarning("variable %s is neither declared nor bound",
1241		    name->key->value);
1242    }
1243
1244    /* Not found, resolve <symbol> at run time */
1245    return (SYMBOL_UNBOUND);
1246}
1247
1248static void
1249ComVariableSetFlag(LispCom *com, LispAtom *atom, int flag)
1250{
1251    int i;
1252    CodeBlock *block = com->block;
1253
1254    while (block) {
1255	i = FindIndex(atom, (void**)block->variables.symbols,
1256		      block->variables.length);
1257	if (i >= 0) {
1258	    block->variables.flags[i] |= flag;
1259	    /*  Descend block list if an argument to function being called
1260	     * has the same name as a bound variable in the current function.
1261	     */
1262	    if ((flag & VARIABLE_ARGUMENT) ||
1263		!(block->variables.flags[i] & VARIABLE_ARGUMENT))
1264		break;
1265	}
1266	block = block->prev;
1267    }
1268}
1269
1270/***********************************************************************
1271 * Bytecode compiler functions
1272 ***********************************************************************/
1273static void
1274ComLabel(LispCom *com, LispObj *label)
1275{
1276    int i;
1277    CodeTree *tree;
1278
1279    for (i = 0; i < com->block->tagbody.length; i++)
1280	if (label == com->block->tagbody.labels[i])
1281	    LispDestroy("TAGBODY: tag %s specified more than once",
1282			STROBJ(label));
1283
1284    if (com->block->tagbody.length >= com->block->tagbody.space) {
1285	com->block->tagbody.labels =
1286	    LispRealloc(com->block->tagbody.labels,
1287			sizeof(LispObj*) * (com->block->tagbody.space + 8));
1288	/*  Reserve space, will be used at link time when
1289	 * resolving GO jumps. */
1290	com->block->tagbody.codes =
1291	    LispRealloc(com->block->tagbody.codes,
1292			sizeof(CodeTree*) * (com->block->tagbody.space + 8));
1293	com->block->tagbody.space += 8;
1294    }
1295
1296    com->block->tagbody.labels[com->block->tagbody.length++] = label;
1297    tree = NEW_TREE(CodeTreeLabel);
1298    tree->data.object = label;
1299}
1300
1301static void
1302ComPush(LispCom *com, LispObj *symbol, LispObj *value,
1303	int eval, int builtin, int compile)
1304{
1305    /*  If <compile> is set, it is pushing an argument to one of
1306     * Com_XXX functions. */
1307    if (compile) {
1308	if (builtin)
1309	    lisp__data.stack.values[lisp__data.stack.length++] = value;
1310	else
1311	    ComAddVariable(com, symbol, value);
1312	return;
1313    }
1314
1315    /*  If <com->macro> is set, it is expanding a macro, just add the local
1316     * variable <symbol> bounded to <value>, so that it will be available
1317     * when calling the interpreter to expand the macro. */
1318    else if (com->macro) {
1319	ComAddVariable(com, symbol, value);
1320	return;
1321    }
1322
1323    /*  If <eval> is set, it must generate the opcodes to evaluate <value>.
1324     * If <value> is a constant, just generate the opcodes to load it. */
1325    else if (eval && !ComConstantp(com, value)) {
1326	switch (OBJECT_TYPE(value)) {
1327	    case LispAtom_t: {
1328		int offset = ComGetVariable(com, value);
1329
1330		if (offset >= 0) {
1331		    /* Load <value> from user stack at the relative offset */
1332		    if (builtin)
1333			com_LoadPush(com, offset);
1334		    else
1335			com_LoadLet(com, offset, symbol->data.atom);
1336		}
1337		/* ComConstantp() does not return true for this, as the
1338		 * current value must be computed. */
1339		else if (offset == SYMBOL_CONSTANT) {
1340		    value = value->data.atom->property->value;
1341		    if (builtin)
1342			com_LoadConPush(com, value);
1343		    else
1344			com_LoadConLet(com, value, symbol->data.atom);
1345		}
1346		else {
1347		    /* Load value bound to <value> at run time */
1348		    if (builtin)
1349			com_LoadSymPush(com, value->data.atom);
1350		    else
1351			com_LoadSymLet(com, value->data.atom,
1352				       symbol->data.atom);
1353		}
1354	    }	break;
1355
1356	    default:
1357		/* Generate code to evaluate <value> */
1358		ComEval(com, value);
1359		if (builtin)
1360		    com_Bytecode(com, XBC_PUSH);
1361		else
1362		    com_Let(com, symbol->data.atom);
1363		break;
1364	}
1365
1366	/*  Remember <symbol> will be bound, <value> only matters for
1367	 * the Com_XXX  functions */
1368	if (builtin)
1369	    lisp__data.stack.values[lisp__data.stack.length++] = value;
1370	else
1371	    ComAddVariable(com, symbol, value);
1372	return;
1373    }
1374
1375    if (builtin) {
1376	/* Load <value> as a constant in builtin stack */
1377	com_LoadConPush(com, value);
1378	lisp__data.stack.values[lisp__data.stack.length++] = value;
1379    }
1380    else {
1381	/* Load <value> as a constant in stack */
1382	com_LoadConLet(com, value, symbol->data.atom);
1383	/* Remember <symbol> will be bound */
1384	ComAddVariable(com, symbol, value);
1385    }
1386}
1387
1388/*  This function does almost the same job as LispMakeEnvironment, but
1389 * it is not optimized for speed, as it is not building argument lists
1390 * to user code, but to Com_XXX functions, or helping in generating the
1391 * opcodes to load arguments at bytecode run time. */
1392static int
1393ComCall(LispCom *com, LispArgList *alist,
1394	LispObj *name, LispObj *values,
1395	int eval, int builtin, int compile)
1396{
1397    char *desc;
1398    int i, count, base;
1399    LispObj **symbols, **defaults, **sforms;
1400
1401    if (builtin) {
1402	base = lisp__data.stack.length;
1403	/* This should never be executed, but make the check for safety */
1404	if (base + alist->num_arguments > lisp__data.stack.space) {
1405	    do
1406		LispMoreStack();
1407	    while (base + alist->num_arguments > lisp__data.stack.space);
1408	}
1409    }
1410    else
1411	base = lisp__data.env.length;
1412
1413    desc = alist->description;
1414    switch (*desc++) {
1415	case '.':
1416	    goto normal_label;
1417	case 'o':
1418	    goto optional_label;
1419	case 'k':
1420	    goto key_label;
1421	case 'r':
1422	    goto rest_label;
1423	case 'a':
1424	    goto aux_label;
1425	default:
1426	    goto done_label;
1427    }
1428
1429
1430    /* Normal arguments */
1431normal_label:
1432    i = 0;
1433    symbols = alist->normals.symbols;
1434    count = alist->normals.num_symbols;
1435    for (; i < count && CONSP(values); i++, values = CDR(values)) {
1436	ComPush(com, symbols[i], CAR(values), eval, builtin, compile);
1437	if (!builtin && !com->macro)
1438	    COM_VARIABLE_ARGUMENT(symbols[i]->data.atom);
1439    }
1440    if (i < count)
1441	LispDestroy("%s: too few arguments", STROBJ(name));
1442
1443    switch (*desc++) {
1444	case 'o':
1445	    goto optional_label;
1446	case 'k':
1447	    goto key_label;
1448	case 'r':
1449	    goto rest_label;
1450	case 'a':
1451	    goto aux_label;
1452	default:
1453	    goto done_label;
1454    }
1455
1456
1457    /* &OPTIONAL */
1458optional_label:
1459    i = 0;
1460    count = alist->optionals.num_symbols;
1461    symbols = alist->optionals.symbols;
1462    defaults = alist->optionals.defaults;
1463    sforms = alist->optionals.sforms;
1464    for (; i < count && CONSP(values); i++, values = CDR(values)) {
1465	ComPush(com, symbols[i], CAR(values), eval, builtin, compile);
1466	if (!builtin && !com->macro)
1467	    COM_VARIABLE_ARGUMENT(symbols[i]->data.atom);
1468	if (sforms[i]) {
1469	    ComPush(com, sforms[i], T, 0, builtin, compile);
1470	    if (!builtin && !com->macro)
1471		COM_VARIABLE_ARGUMENT(sforms[i]->data.atom);
1472	}
1473    }
1474    for (; i < count; i++) {
1475	if (!builtin) {
1476	    int lex = com->lex;
1477	    int head = lisp__data.env.head;
1478
1479	    com->lex = base;
1480	    lisp__data.env.head = lisp__data.env.length;
1481	    /* default arguments are evaluated for macros */
1482	    ComPush(com, symbols[i], defaults[i], 1, 0, compile);
1483	    if (!com->macro)
1484		COM_VARIABLE_ARGUMENT(symbols[i]->data.atom);
1485	    lisp__data.env.head = head;
1486	    com->lex = lex;
1487	}
1488	else
1489	    ComPush(com, symbols[i], defaults[i], eval, 1, compile);
1490	if (sforms[i]) {
1491	    ComPush(com, sforms[i], NIL, 0, builtin, compile);
1492	    if (!builtin && !com->macro)
1493		COM_VARIABLE_ARGUMENT(sforms[i]->data.atom);
1494	}
1495    }
1496
1497    switch (*desc++) {
1498	case 'k':
1499	    goto key_label;
1500	case 'r':
1501	    goto rest_label;
1502	case 'a':
1503	    goto aux_label;
1504	default:
1505	    goto done_label;
1506    }
1507
1508
1509    /* &KEY */
1510key_label:
1511    {
1512	int varset;
1513	LispObj *val, *karg, **keys;
1514
1515	count = alist->keys.num_symbols;
1516	symbols = alist->keys.symbols;
1517	defaults = alist->keys.defaults;
1518	sforms = alist->keys.sforms;
1519	keys = alist->keys.keys;
1520
1521	/* Check if arguments are correctly specified */
1522	for (karg = values; CONSP(karg); karg = CDR(karg)) {
1523	    val = CAR(karg);
1524	    if (KEYWORDP(val)) {
1525		for (i = 0; i < alist->keys.num_symbols; i++)
1526		    if (!keys[i] && symbols[i] == val)
1527			break;
1528	    }
1529
1530	    else if (!builtin &&
1531		     QUOTEP(val) && SYMBOLP(val->data.quote)) {
1532		for (i = 0; i < alist->keys.num_symbols; i++)
1533		    if (keys[i] && ATOMID(keys[i]) == ATOMID(val->data.quote))
1534			break;
1535	    }
1536
1537	    else
1538		/* Just make the error test true */
1539		i = alist->keys.num_symbols;
1540
1541	    if (i == alist->keys.num_symbols) {
1542		/* If not in argument specification list... */
1543		char function_name[36];
1544
1545		strcpy(function_name, STROBJ(name));
1546		LispDestroy("%s: invalid keyword %s",
1547			    function_name, STROBJ(val));
1548	    }
1549
1550	    karg = CDR(karg);
1551	    if (!CONSP(karg))
1552		LispDestroy("%s: &KEY needs arguments as pairs",
1553			    STROBJ(name));
1554	}
1555
1556	/* Add variables */
1557	for (i = 0; i < alist->keys.num_symbols; i++) {
1558	    val = defaults[i];
1559	    varset = 0;
1560	    if (!builtin && keys[i]) {
1561		Atom_id atom = ATOMID(keys[i]);
1562
1563		/* Special keyword specification, need to compare ATOMID
1564		 * and keyword specification must be a quoted object */
1565		for (karg = values; CONSP(karg); karg = CDR(karg)) {
1566		    val = CAR(karg);
1567		    if (QUOTEP(val) && atom == ATOMID(val->data.quote)) {
1568			val = CADR(karg);
1569			varset = 1;
1570			break;
1571		    }
1572		    karg = CDR(karg);
1573		}
1574	    }
1575
1576	    else {
1577		/* Normal keyword specification, can compare object pointers,
1578		 * as they point to the same object in the keyword package */
1579		for (karg = values; CONSP(karg); karg = CDR(karg)) {
1580		    /* Don't check if argument is a valid keyword or
1581		     * special quoted keyword */
1582		    if (symbols[i] == CAR(karg)) {
1583			val = CADR(karg);
1584			varset = 1;
1585			break;
1586		    }
1587		    karg = CDR(karg);
1588		}
1589	    }
1590
1591	    /* Add the variable to environment */
1592	    if (varset) {
1593		ComPush(com, symbols[i], val, eval, builtin, compile);
1594		if (sforms[i])
1595		    ComPush(com, sforms[i], T, 0, builtin, compile);
1596	    }
1597	    else {
1598		/* default arguments are evaluated for macros */
1599		if (!builtin) {
1600		    int lex = com->lex;
1601		    int head = lisp__data.env.head;
1602
1603		    com->lex = base;
1604		    lisp__data.env.head = lisp__data.env.length;
1605		    ComPush(com, symbols[i], val, eval, 0, compile);
1606		    lisp__data.env.head = head;
1607		    com->lex = lex;
1608		}
1609		else
1610		    ComPush(com, symbols[i], val, eval, builtin, compile);
1611		if (sforms[i])
1612		    ComPush(com, sforms[i], NIL, 0, builtin, compile);
1613	    }
1614	    if (!builtin && !com->macro) {
1615		COM_VARIABLE_ARGUMENT(symbols[i]->data.atom);
1616		if (sforms[i])
1617		    COM_VARIABLE_ARGUMENT(sforms[i]->data.atom);
1618	    }
1619	}
1620    }
1621
1622    if (*desc == 'a') {
1623	/* &KEY uses all remaining arguments */
1624	values = NIL;
1625	goto aux_label;
1626    }
1627    goto finished_label;
1628
1629
1630    /* &REST */
1631rest_label:
1632    if (!eval || !CONSP(values) || (compile && !builtin))
1633	ComPush(com, alist->rest, values, eval, builtin, compile);
1634    else {
1635	char *string;
1636	LispObj *list, *car = NIL;
1637	int count, constantp;
1638
1639	/* Count number of arguments and check if it is a list of constants */
1640	for (count = 0, constantp = 1, list = values;
1641	     CONSP(list);
1642	     list = CDR(list), count++) {
1643	    car = CAR(list);
1644	    if (!ComConstantp(com, car))
1645		constantp = 0;
1646	}
1647
1648	string = builtin ? ATOMID(name)->value : NULL;
1649	/* XXX FIXME should have a flag indicating if function call
1650	 * change the &REST arguments even if it is a constant list
1651	 * (or if the returned value may be changed). */
1652	if (string && (count < MAX_BCONS || constantp) &&
1653	    strcmp(string, "LIST") &&
1654	    strcmp(string, "APPLY") &&	/* XXX depends on function argument */
1655	    strcmp(string, "VECTOR") &&
1656	    /* Append does not copy the last/single list */
1657	    (strcmp(string, "APPEND") || !CONSP(car))) {
1658	    if (constantp) {
1659		/* If the builtin function changes the &REST parameters, must
1660		 * define a Com_XXX function for it. */
1661		ComPush(com, alist->rest, values, 0, builtin, compile);
1662	    }
1663	    else {
1664		CompileStackEnter(com, count - 1, 1);
1665		for (; CONSP(CDR(values)); values = CDR(values)) {
1666		    /* Evaluate this argument */
1667		    ComEval(com, CAR(values));
1668		    /* Save result in builtin stack */
1669		    com_Bytecode(com, XBC_PUSH);
1670		}
1671		CompileStackLeave(com, count - 1, 1);
1672		/* The last argument is not saved in the stack */
1673		ComEval(com, CAR(values));
1674		values = NIL;
1675		com_Bytecode(com, (LispByteOpcode)(XBC_BCONS + (count - 1)));
1676	    }
1677	}
1678	else {
1679	    /* Allocate a fresh list of cons */
1680
1681	    /* Generate code to load object */
1682	    ComEval(com, CAR(values));
1683
1684	    com->stack.cpstack += 2;
1685	    if (com->stack.pstack < com->stack.cpstack)
1686		com->stack.pstack = com->stack.cpstack;
1687	    /* Start building a gc protected list, with the loaded value */
1688	    com_Bytecode(com, XBC_LSTAR);
1689
1690	    for (values = CDR(values); CONSP(values); values = CDR(values)) {
1691		/* Generate code to load object */
1692		ComEval(com, CAR(values));
1693
1694		/* Add loaded value to gc protected list */
1695		com_Bytecode(com, XBC_LCONS);
1696	    }
1697
1698	    /* Finish gc protected list */
1699	    com_Bytecode(com, XBC_LFINI);
1700
1701	    /* Push loaded value */
1702	    if (builtin)
1703		com_Bytecode(com, XBC_PUSH);
1704	    else {
1705		com_Let(com, alist->rest->data.atom);
1706
1707		/* Remember this symbol will be bound */
1708		ComAddVariable(com, alist->rest, values);
1709	    }
1710	    com->stack.cpstack -= 2;
1711	}
1712    }
1713    if (!builtin && !com->macro)
1714	COM_VARIABLE_ARGUMENT(alist->rest->data.atom);
1715    if (*desc != 'a')
1716	goto finished_label;
1717
1718
1719    /* &AUX */
1720aux_label:
1721    i = 0;
1722    count = alist->auxs.num_symbols;
1723    symbols = alist->auxs.symbols;
1724    defaults = alist->auxs.initials;
1725    if (!builtin && !compile) {
1726	int lex = com->lex;
1727
1728	com->lex = base;
1729	lisp__data.env.head = lisp__data.env.length;
1730	for (; i < count; i++) {
1731	    ComPush(com, symbols[i], defaults[i], 1, 0, 0);
1732	    if (!com->macro)
1733		COM_VARIABLE_ARGUMENT(symbols[i]->data.atom);
1734	    ++lisp__data.env.head;
1735	}
1736	com->lex = lex;
1737    }
1738    else {
1739	for (; i < count; i++) {
1740	    ComPush(com, symbols[i], defaults[i], eval, builtin, compile);
1741	    if (!builtin && !com->macro)
1742		COM_VARIABLE_ARGUMENT(symbols[i]->data.atom);
1743	}
1744    }
1745
1746done_label:
1747    if (CONSP(values))
1748	LispDestroy("%s: too many arguments", STROBJ(name));
1749
1750finished_label:
1751    if (builtin)
1752	lisp__data.stack.base = base;
1753    else
1754	lisp__data.env.head = lisp__data.env.length;
1755
1756    return (base);
1757}
1758
1759static void
1760ComFuncall(LispCom *com, LispObj *function, LispObj *arguments, int eval)
1761{
1762    int base, compile;
1763    LispAtom *atom;
1764    LispArgList *alist;
1765    LispBuiltin *builtin;
1766    LispObj *lambda;
1767
1768    switch (OBJECT_TYPE(function)) {
1769	case LispFunction_t:
1770	    function = function->data.atom->object;
1771	case LispAtom_t:
1772	    atom = function->data.atom;
1773	    alist = atom->property->alist;
1774
1775	    if (atom->a_builtin) {
1776		builtin = atom->property->fun.builtin;
1777		compile = builtin->compile != NULL;
1778
1779		/*  If one of:
1780		 * 	o expanding a macro
1781		 *	o calling a builtin special form
1782		 *	o builtin function is a macro
1783		 * don't evaluate arguments. */
1784		if (com->macro || compile || builtin->type == LispMacro)
1785		    eval = 0;
1786
1787		if (!com->macro && builtin->type == LispMacro) {
1788		    /* Set flag of variable used, in case variable is only
1789		     * used as a builtin macro argument. */
1790		    LispObj *obj;
1791
1792		    for (obj = arguments; CONSP(obj); obj = CDR(obj)) {
1793			if (SYMBOLP(CAR(obj)))
1794			    COM_VARIABLE_USED(CAR(obj)->data.atom);
1795		    }
1796		}
1797
1798		FORM_ENTER();
1799		if (!compile && !com->macro)
1800		    CompileStackEnter(com, alist->num_arguments, 1);
1801
1802		/* Build argument list in the interpreter stacks */
1803		base = ComCall(com, alist, function, arguments,
1804			       eval, 1, compile);
1805
1806		/* If <compile> is set, it is a special form */
1807		if (compile)
1808		    builtin->compile(com, builtin);
1809
1810		/* Else, generate opcodes to call builtin function */
1811		else {
1812		    com_Call(com, alist->num_arguments, builtin);
1813		    CompileStackLeave(com, alist->num_arguments, 1);
1814		}
1815		lisp__data.stack.base = lisp__data.stack.length = base;
1816		FORM_LEAVE();
1817	    }
1818	    else if (atom->a_function) {
1819		int macro;
1820
1821		lambda = atom->property->fun.function;
1822		macro = lambda->funtype == LispMacro;
1823
1824		/* If <macro> is set, expand macro */
1825		if (macro)
1826		    ComMacroCall(com, alist, function, lambda, arguments);
1827
1828		else {
1829		    if (com->toplevel->type == LispBlockClosure &&
1830			com->toplevel->tag == function)
1831			ComRecursiveCall(com, alist, function, arguments);
1832		    else {
1833#if 0
1834			ComInlineCall(com, alist, function, arguments,
1835				      lambda->data.lambda.code);
1836#else
1837			com_Funcall(com, function, arguments);
1838#endif
1839		    }
1840		}
1841	    }
1842	    else if (atom->a_defstruct &&
1843		     atom->property->structure.function != STRUCT_NAME &&
1844		     atom->property->structure.function != STRUCT_CONSTRUCTOR) {
1845		LispObj *definition = atom->property->structure.definition;
1846
1847		if (!CONSP(arguments) || CONSP(CDR(arguments)))
1848		    LispDestroy("%s: too %s arguments", atom->key->value,
1849				CONSP(arguments) ? "many" : "few");
1850
1851		ComEval(com, CAR(arguments));
1852		if (atom->property->structure.function == STRUCT_CHECK)
1853		    com_Structp(com, definition);
1854		else
1855		    com_Struct(com,
1856			       atom->property->structure.function, definition);
1857	    }
1858	    else if (atom->a_compiled) {
1859		FORM_ENTER();
1860		CompileStackEnter(com, alist->num_arguments, 0);
1861
1862		/* Build argument list in the interpreter stacks */
1863		base = ComCall(com, alist, function, arguments, 1, 0, 0);
1864		com_Bytecall(com, alist->num_arguments,
1865			     atom->property->fun.function);
1866		CompileStackLeave(com, alist->num_arguments, 0);
1867		lisp__data.env.head = lisp__data.env.length = base;
1868		FORM_LEAVE();
1869	    }
1870	    else {
1871		/* Not yet defined function/macro. */
1872		++com->warnings;
1873		LispWarning("call to undefined function %s", atom->key->value);
1874		com_Funcall(com, function, arguments);
1875	    }
1876	    break;
1877
1878	case LispLambda_t:
1879	    lambda = function->data.lambda.code;
1880	    alist = (LispArgList*)function->data.lambda.name->data.opaque.data;
1881	    ComInlineCall(com, alist, NIL, arguments, lambda->data.lambda.code);
1882	    break;
1883
1884	case LispCons_t:
1885	    if (CAR(function) == Olambda) {
1886		function = EVAL(function);
1887		if (LAMBDAP(function)) {
1888		    GC_ENTER();
1889
1890		    GC_PROTECT(function);
1891		    lambda = function->data.lambda.code;
1892		    alist = (LispArgList*)function->data.lambda.name->data.opaque.data;
1893		    ComInlineCall(com, alist, NIL, arguments, lambda->data.lambda.code);
1894		    GC_LEAVE();
1895		    break;
1896		}
1897	    }
1898
1899	default:
1900	    /*  XXX If bytecode objects are made available, should
1901	     * handle it here. */
1902	    LispDestroy("EVAL: %s is invalid as a function",
1903			STROBJ(function));
1904	    /*NOTREACHED*/
1905	    break;
1906    }
1907}
1908
1909/* Generate opcodes for an implicit PROGN */
1910static void
1911ComProgn(LispCom *com, LispObj *code)
1912{
1913    if (CONSP(code)) {
1914	for (; CONSP(code); code = CDR(code))
1915	    ComEval(com, CAR(code));
1916    }
1917    else
1918	/* If no code to execute, empty PROGN returns NIL */
1919	com_Bytecode(com, XBC_NIL);
1920}
1921
1922/* Generate opcodes to evaluate <object>. */
1923static void
1924ComEval(LispCom *com, LispObj *object)
1925{
1926    int offset;
1927    LispObj *form;
1928
1929    switch (OBJECT_TYPE(object)) {
1930	case LispAtom_t:
1931	    if (IN_TAGBODY())
1932		ComLabel(com, object);
1933	    else {
1934		offset = ComGetVariable(com, object);
1935		if (offset >= 0)
1936		    /* Load from user stack at relative offset */
1937		    com_Load(com, offset);
1938		else if (offset == SYMBOL_KEYWORD)
1939		    com_LoadCon(com, object);
1940		else if (offset == SYMBOL_CONSTANT)
1941		    /* Symbol defined as constant, just load it's value */
1942		    com_LoadCon(com, LispGetVar(object));
1943		else
1944		    /* Load value bound to symbol at run time */
1945		    com_LoadSym(com, object->data.atom);
1946	    }
1947	    break;
1948
1949	case LispCons_t: {
1950	    /* Macro expansion may be done in the object form */
1951	    form = com->form;
1952	    com->form = object;
1953	    ComFuncall(com, CAR(object), CDR(object), 1);
1954	    com->form = form;
1955	}   break;
1956
1957	case LispQuote_t:
1958	    com_LoadCon(com, object->data.quote);
1959	    break;
1960
1961	case LispBackquote_t:
1962	    /* Macro expansion is stored in the current value of com->form */
1963	    ComMacroBackquote(com, object);
1964	    break;
1965
1966	case LispComma_t:
1967	    LispDestroy("EVAL: comma outside of backquote");
1968	    break;
1969
1970	case LispFunctionQuote_t:
1971	    object = object->data.quote;
1972	    if (SYMBOLP(object))
1973		object = LispSymbolFunction(object);
1974	    else if (CONSP(object) && CAR(object) == Olambda) {
1975		/* object will only be associated with bytecode later,
1976		 * so, make sure it is protected until compilation finishes */
1977		object = EVAL(object);
1978		RPLACD(com->plist, CONS(CAR(com->plist), CDR(com->plist)));
1979		RPLACA(com->plist, object);
1980	    }
1981	    else
1982		LispDestroy("FUNCTION: %s is not a function", STROBJ(object));
1983	    com_LoadCon(com, object);
1984	    break;
1985
1986	case LispFixnum_t:
1987	    if (IN_TAGBODY()) {
1988		ComLabel(com, object);
1989		break;
1990	    }
1991	    /*FALLTROUGH*/
1992
1993	default:
1994	    /* Constant object */
1995	    com_LoadCon(com, object);
1996	    break;
1997    }
1998}
1999
2000/***********************************************************************
2001 * Lambda expansion helper functions
2002 ***********************************************************************/
2003static void
2004ComRecursiveCall(LispCom *com, LispArgList *alist,
2005		 LispObj *name, LispObj *arguments)
2006{
2007    int base, lex;
2008
2009    /* Save state */
2010    lex = lisp__data.env.lex;
2011
2012    FORM_ENTER();
2013
2014    /* Generate code to push function arguments in the stack */
2015    base = ComCall(com, alist, name, arguments, 1, 0, 0);
2016
2017    /* Stack will grow this amount */
2018    CompileStackEnter(com, alist->num_arguments, 0);
2019
2020#if 0
2021    /* Make the variables available at run time */
2022    com_Bind(com, alist->num_arguments);
2023    com->block->bind += alist->num_arguments;
2024#endif
2025
2026    com_BytecodeChar(com, XBC_LETREC, alist->num_arguments);
2027
2028#if 0
2029    /* The variables are now unbound */
2030    com_Unbind(com, alist->num_arguments);
2031    com->block->bind -= alist->num_arguments;
2032#endif
2033
2034    /* Stack length is reduced */
2035    CompileStackLeave(com, alist->num_arguments, 0);
2036    FORM_LEAVE();
2037
2038    /* Restore state */
2039    lisp__data.env.lex = lex;
2040    lisp__data.env.head = lisp__data.env.length = base;
2041}
2042
2043static void
2044ComInlineCall(LispCom *com, LispArgList *alist,
2045	      LispObj *name, LispObj *arguments, LispObj *lambda)
2046{
2047    int base, lex;
2048
2049    /* Save state */
2050    lex = lisp__data.env.lex;
2051
2052    FORM_ENTER();
2053    /* Start the inline function block */
2054    CompileIniBlock(com, LispBlockClosure, name);
2055
2056    /* Generate code to push function arguments in the stack */
2057    base = ComCall(com, alist, name, arguments, 1, 0, 0);
2058
2059    /* Stack will grow this amount */
2060    CompileStackEnter(com, alist->num_arguments, 0);
2061
2062    /* Make the variables available at run time */
2063    com_Bind(com, alist->num_arguments);
2064    com->block->bind += alist->num_arguments;
2065
2066    /* Expand the lambda list */
2067    ComProgn(com, lambda);
2068
2069    /* The variables are now unbound */
2070    com_Unbind(com, alist->num_arguments);
2071    com->block->bind -= alist->num_arguments;
2072
2073    /* Stack length is reduced */
2074    CompileStackLeave(com, alist->num_arguments, 0);
2075
2076    /* Finish the inline function block */
2077    CompileFiniBlock(com);
2078    FORM_LEAVE();
2079
2080    /* Restore state */
2081    lisp__data.env.lex = lex;
2082    lisp__data.env.head = lisp__data.env.length = base;
2083}
2084
2085/***********************************************************************
2086 * Macro expansion helper functions.
2087 ***********************************************************************/
2088static LispObj *
2089ComMacroExpandBackquote(LispCom *com, LispObj *object)
2090{
2091    return (LispEvalBackquote(object->data.quote, 1));
2092}
2093
2094static LispObj *
2095ComMacroExpandFuncall(LispCom *com, LispObj *function, LispObj *arguments)
2096{
2097    return (LispFuncall(function, arguments, 1));
2098}
2099
2100static LispObj *
2101ComMacroExpandEval(LispCom *com, LispObj *object)
2102{
2103    LispObj *result;
2104
2105    switch (OBJECT_TYPE(object)) {
2106	case LispAtom_t:
2107	    result = LispGetVar(object);
2108
2109	    /* Macro expansion requires bounded symbols */
2110	    if (result == NULL)
2111		LispDestroy("EVAL: the variable %s is unbound",
2112			    STROBJ(object));
2113	    break;
2114
2115	case LispCons_t:
2116	    result = ComMacroExpandFuncall(com, CAR(object), CDR(object));
2117	    break;
2118
2119	case LispQuote_t:
2120	    result = object->data.quote;
2121	    break;
2122
2123	case LispBackquote_t:
2124	    result = ComMacroExpandBackquote(com, object);
2125	    break;
2126
2127	case LispComma_t:
2128	    LispDestroy("EVAL: comma outside of backquote");
2129
2130	case LispFunctionQuote_t:
2131	    result = EVAL(object);
2132	    break;
2133
2134	default:
2135	    result = object;
2136	    break;
2137    }
2138
2139    return (result);
2140}
2141
2142static LispObj *
2143ComMacroExpand(LispCom *com, LispObj *lambda)
2144{
2145    LispObj *result, **presult = &result;
2146    int jumped, *pjumped = &jumped, backquote, *pbackquote = &backquote;
2147    LispBlock *block;
2148
2149    int interpreter_lex, interpreter_head, interpreter_base;
2150
2151    /* Save interpreter state */
2152    interpreter_base = lisp__data.stack.length;
2153    interpreter_head = lisp__data.env.length;
2154    interpreter_lex = lisp__data.env.lex;
2155
2156    /* Use the variables */
2157    *presult = NIL;
2158    *pjumped = 1;
2159    *pbackquote = !CONSP(lambda);
2160
2161    block = LispBeginBlock(NIL, LispBlockProtect);
2162    if (setjmp(block->jmp) == 0) {
2163	if (!backquote) {
2164	    for (; CONSP(lambda); lambda = CDR(lambda))
2165		result = ComMacroExpandEval(com, CAR(lambda));
2166	}
2167	else
2168	    result = ComMacroExpandBackquote(com, lambda);
2169
2170	*pjumped = 0;
2171    }
2172    LispEndBlock(block);
2173
2174    /* If tried to jump out of the macro expansion block */
2175    if (!lisp__data.destroyed && jumped)
2176	LispDestroy("*** EVAL: bad jump in macro expansion");
2177
2178    /* Macro expansion did something wrong */
2179    if (lisp__data.destroyed) {
2180	LispMessage("*** EVAL: aborting macro expansion");
2181	LispDestroy(".");
2182    }
2183
2184    /* Restore interpreter state */
2185    lisp__data.env.lex = interpreter_lex;
2186    lisp__data.stack.length = interpreter_base;
2187    lisp__data.env.head = lisp__data.env.length = interpreter_head;
2188
2189    return (result);
2190}
2191
2192static void
2193ComMacroCall(LispCom *com, LispArgList *alist,
2194	     LispObj *name, LispObj *lambda, LispObj *arguments)
2195{
2196    int base;
2197    LispObj *body;
2198
2199    ++com->macro;
2200    base = ComCall(com, alist, name, arguments, 0, 0, 0);
2201    body = lambda->data.lambda.code;
2202    body = ComMacroExpand(com, body);
2203    --com->macro;
2204    lisp__data.env.head = lisp__data.env.length = base;
2205
2206    /* Macro is expanded, store the result */
2207    CAR(com->form) = body;
2208    ComEval(com, body);
2209}
2210
2211static void
2212ComMacroBackquote(LispCom *com, LispObj *lambda)
2213{
2214    LispObj *body;
2215
2216    ++com->macro;
2217    body = ComMacroExpand(com, lambda);
2218    --com->macro;
2219
2220    /* Macro is expanded, store the result */
2221    CAR(com->form) = body;
2222
2223    com_LoadCon(com, body);
2224}
2225