compile.c revision 5dfecf96
15dfecf96Smrg/*
25dfecf96Smrg * Copyright (c) 2002 by The XFree86 Project, Inc.
35dfecf96Smrg *
45dfecf96Smrg * Permission is hereby granted, free of charge, to any person obtaining a
55dfecf96Smrg * copy of this software and associated documentation files (the "Software"),
65dfecf96Smrg * to deal in the Software without restriction, including without limitation
75dfecf96Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
85dfecf96Smrg * and/or sell copies of the Software, and to permit persons to whom the
95dfecf96Smrg * Software is furnished to do so, subject to the following conditions:
105dfecf96Smrg *
115dfecf96Smrg * The above copyright notice and this permission notice shall be included in
125dfecf96Smrg * all copies or substantial portions of the Software.
135dfecf96Smrg *
145dfecf96Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
155dfecf96Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
165dfecf96Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
175dfecf96Smrg * THE XFREE86 PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
185dfecf96Smrg * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
195dfecf96Smrg * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
205dfecf96Smrg * SOFTWARE.
215dfecf96Smrg *
225dfecf96Smrg * Except as contained in this notice, the name of the XFree86 Project shall
235dfecf96Smrg * not be used in advertising or otherwise to promote the sale, use or other
245dfecf96Smrg * dealings in this Software without prior written authorization from the
255dfecf96Smrg * XFree86 Project.
265dfecf96Smrg *
275dfecf96Smrg * Author: Paulo César Pereira de Andrade
285dfecf96Smrg */
295dfecf96Smrg
305dfecf96Smrg/* $XFree86: xc/programs/xedit/lisp/compile.c,v 1.15tsi Exp $ */
315dfecf96Smrg
325dfecf96Smrg#define VARIABLE_USED		0x0001
335dfecf96Smrg#define VARIABLE_ARGUMENT	0x0002
345dfecf96Smrg
355dfecf96Smrg/*
365dfecf96Smrg * Prototypes
375dfecf96Smrg */
385dfecf96Smrgstatic void ComPredicate(LispCom*, LispBuiltin*, LispBytePredicate);
395dfecf96Smrgstatic void ComReturnFrom(LispCom*, LispBuiltin*, int);
405dfecf96Smrg
415dfecf96Smrgstatic int ComConstantp(LispCom*, LispObj*);
425dfecf96Smrgstatic void ComAddVariable(LispCom*, LispObj*, LispObj*);
435dfecf96Smrgstatic int ComGetVariable(LispCom*, LispObj*);
445dfecf96Smrgstatic void ComVariableSetFlag(LispCom*, LispAtom*, int);
455dfecf96Smrg#define COM_VARIABLE_USED(atom)				\
465dfecf96Smrg    ComVariableSetFlag(com, atom, VARIABLE_USED)
475dfecf96Smrg#define COM_VARIABLE_ARGUMENT(atom)			\
485dfecf96Smrg	ComVariableSetFlag(com, atom, VARIABLE_ARGUMENT)
495dfecf96Smrg
505dfecf96Smrgstatic int FindIndex(void*, void**, int);
515dfecf96Smrgstatic int compare(const void*, const void*);
525dfecf96Smrgstatic int BuildTablePointer(void*, void***, int*);
535dfecf96Smrg
545dfecf96Smrgstatic void ComLabel(LispCom*, LispObj*);
555dfecf96Smrgstatic void ComPush(LispCom*, LispObj*, LispObj*, int, int, int);
565dfecf96Smrgstatic int ComCall(LispCom*, LispArgList*, LispObj*, LispObj*, int, int, int);
575dfecf96Smrgstatic void ComFuncall(LispCom*, LispObj*, LispObj*, int);
585dfecf96Smrgstatic void ComProgn(LispCom*, LispObj*);
595dfecf96Smrgstatic void ComEval(LispCom*, LispObj*);
605dfecf96Smrg
615dfecf96Smrgstatic void ComRecursiveCall(LispCom*, LispArgList*, LispObj*, LispObj*);
625dfecf96Smrgstatic void ComInlineCall(LispCom*, LispArgList*, LispObj*, LispObj*, LispObj*);
635dfecf96Smrg
645dfecf96Smrgstatic void ComMacroBackquote(LispCom*, LispObj*);
655dfecf96Smrgstatic void ComMacroCall(LispCom*, LispArgList*, LispObj*, LispObj*, LispObj*);
665dfecf96Smrgstatic LispObj *ComMacroExpandBackquote(LispCom*, LispObj*);
675dfecf96Smrgstatic LispObj *ComMacroExpand(LispCom*, LispObj*);
685dfecf96Smrgstatic LispObj *ComMacroExpandFuncall(LispCom*, LispObj*, LispObj*);
695dfecf96Smrgstatic LispObj *ComMacroExpandEval(LispCom*, LispObj*);
705dfecf96Smrg
715dfecf96Smrg/*
725dfecf96Smrg * Implementation
735dfecf96Smrg */
745dfecf96Smrgvoid
755dfecf96SmrgCom_And(LispCom *com, LispBuiltin *builtin)
765dfecf96Smrg/*
775dfecf96Smrg and &rest args
785dfecf96Smrg */
795dfecf96Smrg{
805dfecf96Smrg    LispObj *args;
815dfecf96Smrg
825dfecf96Smrg    args = ARGUMENT(0);
835dfecf96Smrg
845dfecf96Smrg    if (CONSP(args)) {
855dfecf96Smrg	/* Evaluate first argument */
865dfecf96Smrg	ComEval(com, CAR(args));
875dfecf96Smrg	args = CDR(args);
885dfecf96Smrg
895dfecf96Smrg	/* If more than one argument, create jump list */
905dfecf96Smrg	if (CONSP(args)) {
915dfecf96Smrg	    CodeTree *tree = NULL, *group;
925dfecf96Smrg
935dfecf96Smrg	    group = NEW_TREE(CodeTreeJumpIf);
945dfecf96Smrg	    group->code = XBC_JUMPNIL;
955dfecf96Smrg
965dfecf96Smrg	    for (; CONSP(args); args = CDR(args)) {
975dfecf96Smrg		ComEval(com, CAR(args));
985dfecf96Smrg		tree = NEW_TREE(CodeTreeJumpIf);
995dfecf96Smrg		tree->code = XBC_JUMPNIL;
1005dfecf96Smrg		group->group = tree;
1015dfecf96Smrg		group = tree;
1025dfecf96Smrg	    }
1035dfecf96Smrg	    /*  Finish form the last CodeTree code is changed to sign the
1045dfecf96Smrg	     * end of the AND list */
1055dfecf96Smrg	    group->code = XBC_NOOP;
1065dfecf96Smrg	    if (group)
1075dfecf96Smrg		group->group = tree;
1085dfecf96Smrg	}
1095dfecf96Smrg    }
1105dfecf96Smrg    else
1115dfecf96Smrg	/* Identity of AND is T */
1125dfecf96Smrg	com_Bytecode(com, XBC_T);
1135dfecf96Smrg}
1145dfecf96Smrg
1155dfecf96Smrgvoid
1165dfecf96SmrgCom_Block(LispCom *com, LispBuiltin *builtin)
1175dfecf96Smrg/*
1185dfecf96Smrg block name &rest body
1195dfecf96Smrg */
1205dfecf96Smrg{
1215dfecf96Smrg
1225dfecf96Smrg    LispObj *name, *body;
1235dfecf96Smrg
1245dfecf96Smrg    body = ARGUMENT(1);
1255dfecf96Smrg    name = ARGUMENT(0);
1265dfecf96Smrg
1275dfecf96Smrg    if (name != NIL && name != T && !SYMBOLP(name))
1285dfecf96Smrg	LispDestroy("%s: %s cannot name a block",
1295dfecf96Smrg		    STRFUN(builtin), STROBJ(name));
1305dfecf96Smrg    if (CONSP(body)) {
1315dfecf96Smrg	CompileIniBlock(com, LispBlockTag, name);
1325dfecf96Smrg	ComProgn(com, body);
1335dfecf96Smrg	CompileFiniBlock(com);
1345dfecf96Smrg    }
1355dfecf96Smrg    else
1365dfecf96Smrg	/* Just load NIL without starting an empty block */
1375dfecf96Smrg	com_Bytecode(com, XBC_NIL);
1385dfecf96Smrg}
1395dfecf96Smrg
1405dfecf96Smrgvoid
1415dfecf96SmrgCom_C_r(LispCom *com, LispBuiltin *builtin)
1425dfecf96Smrg/*
1435dfecf96Smrg c[ad]{1,4}r list
1445dfecf96Smrg */
1455dfecf96Smrg{
1465dfecf96Smrg    LispObj *list;
1475dfecf96Smrg    char *desc;
1485dfecf96Smrg
1495dfecf96Smrg    list = ARGUMENT(0);
1505dfecf96Smrg
1515dfecf96Smrg    desc = STRFUN(builtin);
1525dfecf96Smrg    if (*desc == 'F')		/* FIRST */
1535dfecf96Smrg	desc = "CAR";
1545dfecf96Smrg    else if (*desc == 'R')	/* REST */
1555dfecf96Smrg	desc = "CDR";
1565dfecf96Smrg
1575dfecf96Smrg    /* Check if it is a list of constants */
1585dfecf96Smrg    while (desc[1] != 'R')
1595dfecf96Smrg	desc++;
1605dfecf96Smrg    ComEval(com, list);
1615dfecf96Smrg    while (*desc != 'C') {
1625dfecf96Smrg	com_Bytecode(com, *desc == 'A' ? XBC_CAR : XBC_CDR);
1635dfecf96Smrg	--desc;
1645dfecf96Smrg    }
1655dfecf96Smrg}
1665dfecf96Smrg
1675dfecf96Smrgvoid
1685dfecf96SmrgCom_Cond(LispCom *com, LispBuiltin *builtin)
1695dfecf96Smrg/*
1705dfecf96Smrg cond &rest body
1715dfecf96Smrg */
1725dfecf96Smrg{
1735dfecf96Smrg    int count;
1745dfecf96Smrg    LispObj *code, *body;
1755dfecf96Smrg    CodeTree *group, *tree;
1765dfecf96Smrg
1775dfecf96Smrg    body = ARGUMENT(0);
1785dfecf96Smrg
1795dfecf96Smrg    count = 0;
1805dfecf96Smrg    group = NULL;
1815dfecf96Smrg    if (CONSP(body)) {
1825dfecf96Smrg	for (; CONSP(body); body = CDR(body)) {
1835dfecf96Smrg	    code = CAR(body);
1845dfecf96Smrg	    CHECK_CONS(code);
1855dfecf96Smrg	    ++count;
1865dfecf96Smrg	    ComEval(com, CAR(code));
1875dfecf96Smrg	    tree = NEW_TREE(CodeTreeCond);
1885dfecf96Smrg	    if (group)
1895dfecf96Smrg		group->group = tree;
1905dfecf96Smrg	    tree->code = XBC_JUMPNIL;
1915dfecf96Smrg	    group = tree;
1925dfecf96Smrg	    /* The code to execute if the test is true */
1935dfecf96Smrg	    ComProgn(com, CDR(code));
1945dfecf96Smrg	    /* Add a node signaling the end of the PROGN code */
1955dfecf96Smrg	    tree = NEW_TREE(CodeTreeCond);
1965dfecf96Smrg	    tree->code = XBC_JUMPT;
1975dfecf96Smrg	    if (group)
1985dfecf96Smrg		group->group = tree;
1995dfecf96Smrg	    group = tree;
2005dfecf96Smrg	}
2015dfecf96Smrg    }
2025dfecf96Smrg    if (!count)
2035dfecf96Smrg	com_Bytecode(com, XBC_NIL);
2045dfecf96Smrg    else
2055dfecf96Smrg	/* Where to jump after T progn */
2065dfecf96Smrg	group->code = XBC_NOOP;
2075dfecf96Smrg}
2085dfecf96Smrg
2095dfecf96Smrgvoid
2105dfecf96SmrgCom_Cons(LispCom *com, LispBuiltin *builtin)
2115dfecf96Smrg/*
2125dfecf96Smrg cons car cdr
2135dfecf96Smrg */
2145dfecf96Smrg{
2155dfecf96Smrg    LispObj *car, *cdr;
2165dfecf96Smrg
2175dfecf96Smrg    cdr = ARGUMENT(1);
2185dfecf96Smrg    car = ARGUMENT(0);
2195dfecf96Smrg
2205dfecf96Smrg    if (ComConstantp(com, car) && ComConstantp(com, cdr))
2215dfecf96Smrg	com_BytecodeCons(com, XBC_CCONS, car, cdr);
2225dfecf96Smrg    else {
2235dfecf96Smrg	++com->stack.cpstack;
2245dfecf96Smrg	if (com->stack.pstack < com->stack.cpstack)
2255dfecf96Smrg	    com->stack.pstack = com->stack.cpstack;
2265dfecf96Smrg	ComEval(com, car);
2275dfecf96Smrg	com_Bytecode(com, XBC_CSTAR);
2285dfecf96Smrg	ComEval(com, cdr);
2295dfecf96Smrg	com_Bytecode(com, XBC_CFINI);
2305dfecf96Smrg	--com->stack.cpstack;
2315dfecf96Smrg    }
2325dfecf96Smrg}
2335dfecf96Smrg
2345dfecf96Smrgvoid
2355dfecf96SmrgCom_Consp(LispCom *com, LispBuiltin *builtin)
2365dfecf96Smrg/*
2375dfecf96Smrg consp object
2385dfecf96Smrg */
2395dfecf96Smrg{
2405dfecf96Smrg    ComPredicate(com, builtin, XBP_CONSP);
2415dfecf96Smrg}
2425dfecf96Smrg
2435dfecf96Smrgvoid
2445dfecf96SmrgCom_Dolist(LispCom *com, LispBuiltin *builtin)
2455dfecf96Smrg/*
2465dfecf96Smrg dolist init &rest body
2475dfecf96Smrg */
2485dfecf96Smrg{
2495dfecf96Smrg    int unbound, item;
2505dfecf96Smrg    LispObj *symbol, *list, *result;
2515dfecf96Smrg    LispObj *init, *body;
2525dfecf96Smrg    CodeTree *group, *tree;
2535dfecf96Smrg
2545dfecf96Smrg    body = ARGUMENT(1);
2555dfecf96Smrg    init = ARGUMENT(0);
2565dfecf96Smrg
2575dfecf96Smrg    CHECK_CONS(init);
2585dfecf96Smrg    symbol = CAR(init);
2595dfecf96Smrg    CHECK_SYMBOL(symbol);
2605dfecf96Smrg    CHECK_CONSTANT(symbol);
2615dfecf96Smrg    init = CDR(init);
2625dfecf96Smrg    if (CONSP(init)) {
2635dfecf96Smrg	list = CAR(init);
2645dfecf96Smrg	init = CDR(init);
2655dfecf96Smrg    }
2665dfecf96Smrg    else
2675dfecf96Smrg	list = NIL;
2685dfecf96Smrg    if (CONSP(init)) {
2695dfecf96Smrg	result = CAR(init);
2705dfecf96Smrg	if (CONSP(CDR(init)))
2715dfecf96Smrg	    LispDestroy("%s: too many arguments %s",
2725dfecf96Smrg			STRFUN(builtin), STROBJ(CDR(init)));
2735dfecf96Smrg    }
2745dfecf96Smrg    else
2755dfecf96Smrg	result = NIL;
2765dfecf96Smrg
2775dfecf96Smrg    /*	Generate code for the body of the form.
2785dfecf96Smrg     *	The generated code uses two objects unavailable to user code,
2795dfecf96Smrg     * in the format:
2805dfecf96Smrg     *	(block NIL
2815dfecf96Smrg     *	    (let ((? list) (item NIL))
2825dfecf96Smrg     *		(tagbody
2835dfecf96Smrg     *		    .			    ; the DOT object as a label
2845dfecf96Smrg     *		    (when (consp list)
2855dfecf96Smrg     *			(setq item (car ?))
2865dfecf96Smrg     *			@body		    ; code to be executed
2875dfecf96Smrg     *			(setq ? (cdr ?))
2885dfecf96Smrg     *			(go .)
2895dfecf96Smrg     *		    )
2905dfecf96Smrg     *		)
2915dfecf96Smrg     *		(setq item nil)
2925dfecf96Smrg     *		result
2935dfecf96Smrg     *	    )
2945dfecf96Smrg     *	)
2955dfecf96Smrg     */
2965dfecf96Smrg
2975dfecf96Smrg    /* XXX All of the logic below should be simplified at some time
2985dfecf96Smrg     * by adding more opcodes for compound operations ... */
2995dfecf96Smrg
3005dfecf96Smrg    /* Relative offsets the locally added variables will have at run time */
3015dfecf96Smrg    unbound = lisp__data.env.length - lisp__data.env.lex;
3025dfecf96Smrg    item = unbound + 1;
3035dfecf96Smrg
3045dfecf96Smrg    /* Start BLOCK NIL */
3055dfecf96Smrg    FORM_ENTER();
3065dfecf96Smrg    CompileIniBlock(com, LispBlockTag, NIL);
3075dfecf96Smrg
3085dfecf96Smrg    /* Add the <?> variable */
3095dfecf96Smrg    ComPush(com, UNBOUND, list, 1, 0, 0);
3105dfecf96Smrg    /* Add the <item> variable */
3115dfecf96Smrg    ComPush(com, symbol, NIL, 0, 0, 0);
3125dfecf96Smrg    /* Stack length is increased */
3135dfecf96Smrg    CompileStackEnter(com, 2, 0);
3145dfecf96Smrg    /* Bind variables */
3155dfecf96Smrg    com_Bind(com, 2);
3165dfecf96Smrg    com->block->bind += 2;
3175dfecf96Smrg    lisp__data.env.head += 2;
3185dfecf96Smrg
3195dfecf96Smrg    /* Remember that iteration variable is used even if it not referenced */
3205dfecf96Smrg    COM_VARIABLE_USED(symbol->data.atom);
3215dfecf96Smrg
3225dfecf96Smrg    /* Initialize the TAGBODY */
3235dfecf96Smrg    FORM_ENTER();
3245dfecf96Smrg    CompileIniBlock(com, LispBlockBody, NIL);
3255dfecf96Smrg
3265dfecf96Smrg    /* Create the <.> label */
3275dfecf96Smrg    ComLabel(com, DOT);
3285dfecf96Smrg
3295dfecf96Smrg    /* Load <?> variable */
3305dfecf96Smrg    com_BytecodeShort(com, XBC_LOAD, unbound);
3315dfecf96Smrg    /* Check if <?> is a list */
3325dfecf96Smrg    com_BytecodeChar(com, XBC_PRED, XBP_CONSP);
3335dfecf96Smrg
3345dfecf96Smrg    /* Start WHEN block */
3355dfecf96Smrg    group = NEW_TREE(CodeTreeJumpIf);
3365dfecf96Smrg    group->code = XBC_JUMPNIL;
3375dfecf96Smrg    /* Load <?> again */
3385dfecf96Smrg    com_BytecodeShort(com, XBC_LOAD, unbound);
3395dfecf96Smrg    /* Get CAR of <?> */
3405dfecf96Smrg    com_Bytecode(com, XBC_CAR);
3415dfecf96Smrg    /* Store it in <item> */
3425dfecf96Smrg    com_BytecodeShort(com, XBC_SET, item);
3435dfecf96Smrg    /* Execute @BODY */
3445dfecf96Smrg    ComProgn(com, body);
3455dfecf96Smrg
3465dfecf96Smrg    /* Load <?> again */
3475dfecf96Smrg    com_BytecodeShort(com, XBC_LOAD, unbound);
3485dfecf96Smrg    /* Get CDR of <?> */
3495dfecf96Smrg    com_Bytecode(com, XBC_CDR);
3505dfecf96Smrg    /* Change value of <?> */
3515dfecf96Smrg    com_BytecodeShort(com, XBC_SET, unbound);
3525dfecf96Smrg
3535dfecf96Smrg    /* GO back to <.> */
3545dfecf96Smrg    tree = NEW_TREE(CodeTreeGo);
3555dfecf96Smrg    tree->data.object = DOT;
3565dfecf96Smrg
3575dfecf96Smrg    /* Finish WHEN block */
3585dfecf96Smrg    tree = NEW_TREE(CodeTreeJumpIf);
3595dfecf96Smrg    tree->code = XBC_NOOP;
3605dfecf96Smrg    group->group = tree;
3615dfecf96Smrg
3625dfecf96Smrg    /* Finish the TAGBODY */
3635dfecf96Smrg    CompileFiniBlock(com);
3645dfecf96Smrg    FORM_LEAVE();
3655dfecf96Smrg
3665dfecf96Smrg    /* Set <item> to NIL, in case result references it...
3675dfecf96Smrg     * Loaded value is NIL as the CONSP predicate */
3685dfecf96Smrg    com_BytecodeShort(com, XBC_SET, item);
3695dfecf96Smrg
3705dfecf96Smrg    /* Evaluate <result> */
3715dfecf96Smrg    ComEval(com, result);
3725dfecf96Smrg
3735dfecf96Smrg    /* Unbind variables */
3745dfecf96Smrg    lisp__data.env.head -= 2;
3755dfecf96Smrg    lisp__data.env.length -= 2;
3765dfecf96Smrg    com->block->bind -= 2;
3775dfecf96Smrg    com_Unbind(com, 2);
3785dfecf96Smrg    /* Stack length is reduced. */
3795dfecf96Smrg    CompileStackLeave(com, 2, 0);
3805dfecf96Smrg
3815dfecf96Smrg    /* Finish BLOCK NIL */
3825dfecf96Smrg    CompileFiniBlock(com);
3835dfecf96Smrg    FORM_LEAVE();
3845dfecf96Smrg}
3855dfecf96Smrg
3865dfecf96Smrgvoid
3875dfecf96SmrgCom_Eq(LispCom *com, LispBuiltin *builtin)
3885dfecf96Smrg/*
3895dfecf96Smrg eq left right
3905dfecf96Smrg eql left right
3915dfecf96Smrg equal left right
3925dfecf96Smrg equalp left right
3935dfecf96Smrg */
3945dfecf96Smrg{
3955dfecf96Smrg    LispObj *left, *right;
3965dfecf96Smrg    LispByteOpcode code;
3975dfecf96Smrg    char *name;
3985dfecf96Smrg
3995dfecf96Smrg    right = ARGUMENT(1);
4005dfecf96Smrg    left = ARGUMENT(0);
4015dfecf96Smrg
4025dfecf96Smrg    CompileStackEnter(com, 1, 1);
4035dfecf96Smrg    /* Just like preparing to call a builtin function */
4045dfecf96Smrg    ComEval(com, left);
4055dfecf96Smrg    com_Bytecode(com, XBC_PUSH);
4065dfecf96Smrg    /* The second argument is now loaded */
4075dfecf96Smrg    ComEval(com, right);
4085dfecf96Smrg
4095dfecf96Smrg    /* Compare arguments and restore builtin stack */
4105dfecf96Smrg    name = STRFUN(builtin);
4115dfecf96Smrg    switch (name[3]) {
4125dfecf96Smrg	case 'L':
4135dfecf96Smrg	    code = XBC_EQL;
4145dfecf96Smrg	    break;
4155dfecf96Smrg	case 'U':
4165dfecf96Smrg	    code = name[5] == 'P' ? XBC_EQUALP : XBC_EQUAL;
4175dfecf96Smrg	    break;
4185dfecf96Smrg	default:
4195dfecf96Smrg	    code = XBC_EQ;
4205dfecf96Smrg	    break;
4215dfecf96Smrg    }
4225dfecf96Smrg    com_Bytecode(com, code);
4235dfecf96Smrg
4245dfecf96Smrg    CompileStackLeave(com, 1, 1);
4255dfecf96Smrg}
4265dfecf96Smrg
4275dfecf96Smrgvoid
4285dfecf96SmrgCom_Go(LispCom *com, LispBuiltin *builtin)
4295dfecf96Smrg/*
4305dfecf96Smrg go tag
4315dfecf96Smrg */
4325dfecf96Smrg{
4335dfecf96Smrg    int bind;
4345dfecf96Smrg    LispObj *tag;
4355dfecf96Smrg    CodeTree *tree;
4365dfecf96Smrg    CodeBlock *block;
4375dfecf96Smrg
4385dfecf96Smrg    tag = ARGUMENT(0);
4395dfecf96Smrg
4405dfecf96Smrg    block = com->block;
4415dfecf96Smrg    bind = block->bind;
4425dfecf96Smrg
4435dfecf96Smrg    while (block) {
4445dfecf96Smrg	if (block->type == LispBlockClosure || block->type == LispBlockBody)
4455dfecf96Smrg	    break;
4465dfecf96Smrg	block = block->prev;
4475dfecf96Smrg	if (block)
4485dfecf96Smrg	    bind += block->bind;
4495dfecf96Smrg    }
4505dfecf96Smrg
4515dfecf96Smrg    if (!block || block->type != LispBlockBody)
4525dfecf96Smrg	LispDestroy("%s called not within a block", STRFUN(builtin));
4535dfecf96Smrg
4545dfecf96Smrg    /* Unbind any local variables */
4555dfecf96Smrg    com_Unbind(com, bind);
4565dfecf96Smrg    tree = NEW_TREE(CodeTreeGo);
4575dfecf96Smrg    tree->data.object = tag;
4585dfecf96Smrg}
4595dfecf96Smrg
4605dfecf96Smrgvoid
4615dfecf96SmrgCom_If(LispCom *com, LispBuiltin *builtin)
4625dfecf96Smrg/*
4635dfecf96Smrg if test then &optional else
4645dfecf96Smrg */
4655dfecf96Smrg{
4665dfecf96Smrg    CodeTree *group, *tree;
4675dfecf96Smrg    LispObj *test, *then, *oelse;
4685dfecf96Smrg
4695dfecf96Smrg    oelse = ARGUMENT(2);
4705dfecf96Smrg    then = ARGUMENT(1);
4715dfecf96Smrg    test = ARGUMENT(0);
4725dfecf96Smrg
4735dfecf96Smrg    /* Build code to execute test */
4745dfecf96Smrg    ComEval(com, test);
4755dfecf96Smrg
4765dfecf96Smrg    /* Add jump node to use if test is NIL */
4775dfecf96Smrg    group = NEW_TREE(CodeTreeJumpIf);
4785dfecf96Smrg    group->code = XBC_JUMPNIL;
4795dfecf96Smrg
4805dfecf96Smrg    /* Build T code */
4815dfecf96Smrg    ComEval(com, then);
4825dfecf96Smrg
4835dfecf96Smrg    if (oelse != UNSPEC) {
4845dfecf96Smrg	/* Remember start of NIL code */
4855dfecf96Smrg	tree = NEW_TREE(CodeTreeJump);
4865dfecf96Smrg	tree->code = XBC_JUMP;
4875dfecf96Smrg	group->group = tree;
4885dfecf96Smrg	group = tree;
4895dfecf96Smrg	/* Build NIL code */
4905dfecf96Smrg	ComEval(com, oelse);
4915dfecf96Smrg    }
4925dfecf96Smrg
4935dfecf96Smrg    /* Remember jump of T code */
4945dfecf96Smrg    tree = NEW_TREE(CodeTreeJumpIf);
4955dfecf96Smrg    tree->code = XBC_NOOP;
4965dfecf96Smrg    group->group = tree;
4975dfecf96Smrg}
4985dfecf96Smrg
4995dfecf96Smrgvoid
5005dfecf96SmrgCom_Last(LispCom *com, LispBuiltin *builtin)
5015dfecf96Smrg/*
5025dfecf96Smrg last list &optional count
5035dfecf96Smrg */
5045dfecf96Smrg{
5055dfecf96Smrg    LispObj *list, *count;
5065dfecf96Smrg
5075dfecf96Smrg    count = ARGUMENT(1);
5085dfecf96Smrg    list = ARGUMENT(0);
5095dfecf96Smrg
5105dfecf96Smrg    ComEval(com, list);
5115dfecf96Smrg    CompileStackEnter(com, 1, 1);
5125dfecf96Smrg    com_Bytecode(com, XBC_PUSH);
5135dfecf96Smrg    if (count == UNSPEC)
5145dfecf96Smrg	count = FIXNUM(1);
5155dfecf96Smrg    ComEval(com, count);
5165dfecf96Smrg    CompileStackLeave(com, 1, 1);
5175dfecf96Smrg    com_Bytecode(com, XBC_LAST);
5185dfecf96Smrg}
5195dfecf96Smrg
5205dfecf96Smrgvoid
5215dfecf96SmrgCom_Length(LispCom *com, LispBuiltin *builtin)
5225dfecf96Smrg/*
5235dfecf96Smrg length sequence
5245dfecf96Smrg */
5255dfecf96Smrg{
5265dfecf96Smrg    LispObj *sequence;
5275dfecf96Smrg
5285dfecf96Smrg    sequence = ARGUMENT(0);
5295dfecf96Smrg
5305dfecf96Smrg    ComEval(com, sequence);
5315dfecf96Smrg    com_Bytecode(com, XBC_LENGTH);
5325dfecf96Smrg}
5335dfecf96Smrg
5345dfecf96Smrgvoid
5355dfecf96SmrgCom_Let(LispCom *com, LispBuiltin *builtin)
5365dfecf96Smrg/*
5375dfecf96Smrg let init &rest body
5385dfecf96Smrg */
5395dfecf96Smrg{
5405dfecf96Smrg    int count;
5415dfecf96Smrg    LispObj *symbol, *value, *pair;
5425dfecf96Smrg
5435dfecf96Smrg    LispObj *init, *body;
5445dfecf96Smrg
5455dfecf96Smrg    body = ARGUMENT(1);
5465dfecf96Smrg    init = ARGUMENT(0);
5475dfecf96Smrg
5485dfecf96Smrg    if (init == NIL) {
5495dfecf96Smrg	/* If no local variables */
5505dfecf96Smrg	ComProgn(com, body);
5515dfecf96Smrg	return;
5525dfecf96Smrg    }
5535dfecf96Smrg    CHECK_CONS(init);
5545dfecf96Smrg
5555dfecf96Smrg    /* Could optimize if the body is empty and the
5565dfecf96Smrg     * init form is known to have no side effects */
5575dfecf96Smrg
5585dfecf96Smrg    for (count = 0; CONSP(init); init = CDR(init), count++) {
5595dfecf96Smrg	pair = CAR(init);
5605dfecf96Smrg	if (CONSP(pair)) {
5615dfecf96Smrg	    symbol = CAR(pair);
5625dfecf96Smrg	    pair = CDR(pair);
5635dfecf96Smrg	    if (CONSP(pair)) {
5645dfecf96Smrg		value = CAR(pair);
5655dfecf96Smrg		if (CDR(pair) != NIL)
5665dfecf96Smrg		    LispDestroy("%s: too much arguments to initialize %s",
5675dfecf96Smrg				STRFUN(builtin), STROBJ(symbol));
5685dfecf96Smrg	    }
5695dfecf96Smrg	    else
5705dfecf96Smrg		value = NIL;
5715dfecf96Smrg	}
5725dfecf96Smrg	else {
5735dfecf96Smrg	    symbol = pair;
5745dfecf96Smrg	    value = NIL;
5755dfecf96Smrg	}
5765dfecf96Smrg	CHECK_SYMBOL(symbol);
5775dfecf96Smrg	CHECK_CONSTANT(symbol);
5785dfecf96Smrg
5795dfecf96Smrg	/* Add the variable */
5805dfecf96Smrg	ComPush(com, symbol, value, 1, 0, 0);
5815dfecf96Smrg    }
5825dfecf96Smrg
5835dfecf96Smrg    /* Stack length is increased */
5845dfecf96Smrg    CompileStackEnter(com, count, 0);
5855dfecf96Smrg    /* Bind the added variables */
5865dfecf96Smrg    com_Bind(com, count);
5875dfecf96Smrg    com->block->bind += count;
5885dfecf96Smrg    lisp__data.env.head += count;
5895dfecf96Smrg    /* Generate code for the body of the form */
5905dfecf96Smrg    ComProgn(com, body);
5915dfecf96Smrg    /* Unbind the added variables */
5925dfecf96Smrg    lisp__data.env.head -= count;
5935dfecf96Smrg    lisp__data.env.length -= count;
5945dfecf96Smrg    com->block->bind -= count;
5955dfecf96Smrg    com_Unbind(com, count);
5965dfecf96Smrg    /* Stack length is reduced. */
5975dfecf96Smrg    CompileStackLeave(com, count, 0);
5985dfecf96Smrg}
5995dfecf96Smrg
6005dfecf96Smrgvoid
6015dfecf96SmrgCom_Letx(LispCom *com, LispBuiltin *builtin)
6025dfecf96Smrg/*
6035dfecf96Smrg let* init &rest body
6045dfecf96Smrg */
6055dfecf96Smrg{
6065dfecf96Smrg    int count;
6075dfecf96Smrg    LispObj *symbol, *value, *pair;
6085dfecf96Smrg
6095dfecf96Smrg    LispObj *init, *body;
6105dfecf96Smrg
6115dfecf96Smrg    body = ARGUMENT(1);
6125dfecf96Smrg    init = ARGUMENT(0);
6135dfecf96Smrg
6145dfecf96Smrg    if (init == NIL) {
6155dfecf96Smrg	/* If no local variables */
6165dfecf96Smrg	ComProgn(com, body);
6175dfecf96Smrg	return;
6185dfecf96Smrg    }
6195dfecf96Smrg    CHECK_CONS(body);
6205dfecf96Smrg
6215dfecf96Smrg    /* Could optimize if the body is empty and the
6225dfecf96Smrg     * init form is known to have no side effects */
6235dfecf96Smrg
6245dfecf96Smrg    for (count = 0; CONSP(init); init = CDR(init), count++) {
6255dfecf96Smrg	pair = CAR(init);
6265dfecf96Smrg	if (CONSP(pair)) {
6275dfecf96Smrg	    symbol = CAR(pair);
6285dfecf96Smrg	    pair = CDR(pair);
6295dfecf96Smrg	    if (CONSP(pair)) {
6305dfecf96Smrg		value = CAR(pair);
6315dfecf96Smrg		if (CDR(pair) != NIL)
6325dfecf96Smrg		    LispDestroy("%s: too much arguments to initialize %s",
6335dfecf96Smrg				STRFUN(builtin), STROBJ(symbol));
6345dfecf96Smrg	    }
6355dfecf96Smrg	    else
6365dfecf96Smrg		value = NIL;
6375dfecf96Smrg	}
6385dfecf96Smrg	else {
6395dfecf96Smrg	    symbol = pair;
6405dfecf96Smrg	    value = NIL;
6415dfecf96Smrg	}
6425dfecf96Smrg	CHECK_SYMBOL(symbol);
6435dfecf96Smrg	CHECK_CONSTANT(symbol);
6445dfecf96Smrg
6455dfecf96Smrg	/* LET* is identical to &AUX arguments, just bind the symbol */
6465dfecf96Smrg	ComPush(com, symbol, value, 1, 0, 0);
6475dfecf96Smrg	/* Every added variable is binded */
6485dfecf96Smrg	com_Bind(com, 1);
6495dfecf96Smrg	/* Must be binded at compile time also */
6505dfecf96Smrg	++lisp__data.env.head;
6515dfecf96Smrg	++com->block->bind;
6525dfecf96Smrg    }
6535dfecf96Smrg
6545dfecf96Smrg    /* Generate code for the body of the form */
6555dfecf96Smrg    CompileStackEnter(com, count, 0);
6565dfecf96Smrg    ComProgn(com, body);
6575dfecf96Smrg    com_Unbind(com, count);
6585dfecf96Smrg    com->block->bind -= count;
6595dfecf96Smrg    lisp__data.env.head -= count;
6605dfecf96Smrg    lisp__data.env.length -= count;
6615dfecf96Smrg    CompileStackLeave(com, count, 0);
6625dfecf96Smrg}
6635dfecf96Smrg
6645dfecf96Smrgvoid
6655dfecf96SmrgCom_Listp(LispCom *com, LispBuiltin *builtin)
6665dfecf96Smrg/*
6675dfecf96Smrg listp object
6685dfecf96Smrg */
6695dfecf96Smrg{
6705dfecf96Smrg    ComPredicate(com, builtin, XBP_LISTP);
6715dfecf96Smrg}
6725dfecf96Smrg
6735dfecf96Smrgvoid
6745dfecf96SmrgCom_Loop(LispCom *com, LispBuiltin *builtin)
6755dfecf96Smrg/*
6765dfecf96Smrg loop &rest body
6775dfecf96Smrg */
6785dfecf96Smrg{
6795dfecf96Smrg    CodeTree *tree, *group;
6805dfecf96Smrg    LispObj *body;
6815dfecf96Smrg
6825dfecf96Smrg    body = ARGUMENT(0);
6835dfecf96Smrg
6845dfecf96Smrg    /* Start NIL block */
6855dfecf96Smrg    CompileIniBlock(com, LispBlockTag, NIL);
6865dfecf96Smrg
6875dfecf96Smrg    /* Insert node to mark LOOP start */
6885dfecf96Smrg    tree = NEW_TREE(CodeTreeJump);
6895dfecf96Smrg    tree->code = XBC_NOOP;
6905dfecf96Smrg
6915dfecf96Smrg    /* Execute @BODY */
6925dfecf96Smrg    if (CONSP(body))
6935dfecf96Smrg	ComProgn(com, body);
6945dfecf96Smrg    else
6955dfecf96Smrg	/* XXX bytecode.c code require that blocks have at least one opcode */
6965dfecf96Smrg	com_Bytecode(com, XBC_NIL);
6975dfecf96Smrg
6985dfecf96Smrg    /* Insert node to jump of start of LOOP */
6995dfecf96Smrg    group = NEW_TREE(CodeTreeJump);
7005dfecf96Smrg    group->code = XBC_JUMP;
7015dfecf96Smrg    group->group = tree;
7025dfecf96Smrg
7035dfecf96Smrg    /* Finish NIL block */
7045dfecf96Smrg    CompileFiniBlock(com);
7055dfecf96Smrg}
7065dfecf96Smrg
7075dfecf96Smrgvoid
7085dfecf96SmrgCom_Nthcdr(LispCom *com, LispBuiltin *builtin)
7095dfecf96Smrg/*
7105dfecf96Smrg nthcdr index list
7115dfecf96Smrg */
7125dfecf96Smrg{
7135dfecf96Smrg    LispObj *oindex, *list;
7145dfecf96Smrg
7155dfecf96Smrg    list = ARGUMENT(1);
7165dfecf96Smrg    oindex = ARGUMENT(0);
7175dfecf96Smrg
7185dfecf96Smrg    ComEval(com, oindex);
7195dfecf96Smrg    CompileStackEnter(com, 1, 1);
7205dfecf96Smrg    com_Bytecode(com, XBC_PUSH);
7215dfecf96Smrg    ComEval(com, list);
7225dfecf96Smrg    CompileStackLeave(com, 1, 1);
7235dfecf96Smrg    com_Bytecode(com, XBC_NTHCDR);
7245dfecf96Smrg}
7255dfecf96Smrg
7265dfecf96Smrgvoid
7275dfecf96SmrgCom_Null(LispCom *com, LispBuiltin *builtin)
7285dfecf96Smrg/*
7295dfecf96Smrg null list
7305dfecf96Smrg */
7315dfecf96Smrg{
7325dfecf96Smrg    LispObj *list;
7335dfecf96Smrg
7345dfecf96Smrg    list = ARGUMENT(0);
7355dfecf96Smrg
7365dfecf96Smrg    if (list == NIL)
7375dfecf96Smrg	com_Bytecode(com, XBC_T);
7385dfecf96Smrg    else if (ComConstantp(com, list))
7395dfecf96Smrg	com_Bytecode(com, XBC_NIL);
7405dfecf96Smrg    else {
7415dfecf96Smrg	ComEval(com, list);
7425dfecf96Smrg	com_Bytecode(com, XBC_INV);
7435dfecf96Smrg    }
7445dfecf96Smrg}
7455dfecf96Smrg
7465dfecf96Smrgvoid
7475dfecf96SmrgCom_Numberp(LispCom *com, LispBuiltin *builtin)
7485dfecf96Smrg/*
7495dfecf96Smrg numberp object
7505dfecf96Smrg */
7515dfecf96Smrg{
7525dfecf96Smrg    ComPredicate(com, builtin, XBP_NUMBERP);
7535dfecf96Smrg}
7545dfecf96Smrg
7555dfecf96Smrgvoid
7565dfecf96SmrgCom_Or(LispCom *com, LispBuiltin *builtin)
7575dfecf96Smrg/*
7585dfecf96Smrg or &rest args
7595dfecf96Smrg */
7605dfecf96Smrg{
7615dfecf96Smrg    LispObj *args;
7625dfecf96Smrg
7635dfecf96Smrg    args = ARGUMENT(0);
7645dfecf96Smrg
7655dfecf96Smrg    if (CONSP(args)) {
7665dfecf96Smrg	/* Evaluate first argument */
7675dfecf96Smrg	ComEval(com, CAR(args));
7685dfecf96Smrg	args = CDR(args);
7695dfecf96Smrg
7705dfecf96Smrg	/* If more than one argument, create jump list */
7715dfecf96Smrg	if (CONSP(args)) {
7725dfecf96Smrg	    CodeTree *tree = NULL, *group;
7735dfecf96Smrg
7745dfecf96Smrg	    group = NEW_TREE(CodeTreeJumpIf);
7755dfecf96Smrg	    group->code = XBC_JUMPT;
7765dfecf96Smrg
7775dfecf96Smrg	    for (; CONSP(args); args = CDR(args)) {
7785dfecf96Smrg		ComEval(com, CAR(args));
7795dfecf96Smrg		tree = NEW_TREE(CodeTreeJumpIf);
7805dfecf96Smrg		tree->code = XBC_JUMPT;
7815dfecf96Smrg		group->group = tree;
7825dfecf96Smrg		group = tree;
7835dfecf96Smrg	    }
7845dfecf96Smrg	    /*  Finish form the last CodeTree code is changed to sign the
7855dfecf96Smrg	     * end of the AND list */
7865dfecf96Smrg	    group->code = XBC_NOOP;
7875dfecf96Smrg	    group->group = tree;
7885dfecf96Smrg	}
7895dfecf96Smrg    }
7905dfecf96Smrg    else
7915dfecf96Smrg	/* Identity of OR is NIL */
7925dfecf96Smrg	com_Bytecode(com, XBC_NIL);
7935dfecf96Smrg}
7945dfecf96Smrg
7955dfecf96Smrgvoid
7965dfecf96SmrgCom_Progn(LispCom *com, LispBuiltin *builtin)
7975dfecf96Smrg/*
7985dfecf96Smrg progn &rest body
7995dfecf96Smrg */
8005dfecf96Smrg{
8015dfecf96Smrg    LispObj *body;
8025dfecf96Smrg
8035dfecf96Smrg    body = ARGUMENT(0);
8045dfecf96Smrg
8055dfecf96Smrg    ComProgn(com, body);
8065dfecf96Smrg}
8075dfecf96Smrg
8085dfecf96Smrgvoid
8095dfecf96SmrgCom_Return(LispCom *com, LispBuiltin *builtin)
8105dfecf96Smrg/*
8115dfecf96Smrg return &optional result
8125dfecf96Smrg */
8135dfecf96Smrg{
8145dfecf96Smrg    ComReturnFrom(com, builtin, 0);
8155dfecf96Smrg}
8165dfecf96Smrg
8175dfecf96Smrgvoid
8185dfecf96SmrgCom_ReturnFrom(LispCom *com, LispBuiltin *builtin)
8195dfecf96Smrg/*
8205dfecf96Smrg return-from name &optional result
8215dfecf96Smrg */
8225dfecf96Smrg{
8235dfecf96Smrg    ComReturnFrom(com, builtin, 1);
8245dfecf96Smrg}
8255dfecf96Smrg
8265dfecf96Smrgvoid
8275dfecf96SmrgCom_Rplac_(LispCom *com, LispBuiltin *builtin)
8285dfecf96Smrg/*
8295dfecf96Smrg rplac[ad] place value
8305dfecf96Smrg */
8315dfecf96Smrg{
8325dfecf96Smrg    LispObj *place, *value;
8335dfecf96Smrg
8345dfecf96Smrg    value = ARGUMENT(1);
8355dfecf96Smrg    place = ARGUMENT(0);
8365dfecf96Smrg
8375dfecf96Smrg    CompileStackEnter(com, 1, 1);
8385dfecf96Smrg    ComEval(com, place);
8395dfecf96Smrg    com_Bytecode(com, XBC_PUSH);
8405dfecf96Smrg    ComEval(com, value);
8415dfecf96Smrg    com_Bytecode(com, STRFUN(builtin)[5] == 'A' ? XBC_RPLACA : XBC_RPLACD);
8425dfecf96Smrg    CompileStackLeave(com, 1, 1);
8435dfecf96Smrg}
8445dfecf96Smrg
8455dfecf96Smrgvoid
8465dfecf96SmrgCom_Setq(LispCom *com, LispBuiltin *builtin)
8475dfecf96Smrg/*
8485dfecf96Smrg setq &rest form
8495dfecf96Smrg */
8505dfecf96Smrg{
8515dfecf96Smrg    int offset;
8525dfecf96Smrg    LispObj *form, *symbol, *value;
8535dfecf96Smrg
8545dfecf96Smrg    form = ARGUMENT(0);
8555dfecf96Smrg
8565dfecf96Smrg    for (; CONSP(form); form = CDR(form)) {
8575dfecf96Smrg	symbol = CAR(form);
8585dfecf96Smrg	CHECK_SYMBOL(symbol);
8595dfecf96Smrg	CHECK_CONSTANT(symbol);
8605dfecf96Smrg	form = CDR(form);
8615dfecf96Smrg	if (!CONSP(form))
8625dfecf96Smrg	    LispDestroy("%s: odd number of arguments", STRFUN(builtin));
8635dfecf96Smrg	value = CAR(form);
8645dfecf96Smrg	/* Generate code to load value */
8655dfecf96Smrg	ComEval(com, value);
8665dfecf96Smrg	offset = ComGetVariable(com, symbol);
8675dfecf96Smrg	if (offset >= 0)
8685dfecf96Smrg	    com_Set(com, offset);
8695dfecf96Smrg	else
8705dfecf96Smrg	    com_SetSym(com, symbol->data.atom);
8715dfecf96Smrg    }
8725dfecf96Smrg}
8735dfecf96Smrg
8745dfecf96Smrgvoid
8755dfecf96SmrgCom_Tagbody(LispCom *com, LispBuiltin *builtin)
8765dfecf96Smrg/*
8775dfecf96Smrg tagbody &rest body
8785dfecf96Smrg */
8795dfecf96Smrg{
8805dfecf96Smrg    LispObj *body;
8815dfecf96Smrg
8825dfecf96Smrg    body = ARGUMENT(0);
8835dfecf96Smrg
8845dfecf96Smrg    if (CONSP(body)) {
8855dfecf96Smrg	CompileIniBlock(com, LispBlockBody, NIL);
8865dfecf96Smrg	ComProgn(com, body);
8875dfecf96Smrg	/* Tagbody returns NIL */
8885dfecf96Smrg	com_Bytecode(com, XBC_NIL);
8895dfecf96Smrg	CompileFiniBlock(com);
8905dfecf96Smrg    }
8915dfecf96Smrg    else
8925dfecf96Smrg	/* Tagbody always returns NIL */
8935dfecf96Smrg	com_Bytecode(com, XBC_NIL);
8945dfecf96Smrg}
8955dfecf96Smrg
8965dfecf96Smrgvoid
8975dfecf96SmrgCom_Unless(LispCom *com, LispBuiltin *builtin)
8985dfecf96Smrg/*
8995dfecf96Smrg unless test &rest body
9005dfecf96Smrg */
9015dfecf96Smrg{
9025dfecf96Smrg    CodeTree *group, *tree;
9035dfecf96Smrg    LispObj *test, *body;
9045dfecf96Smrg
9055dfecf96Smrg    body = ARGUMENT(1);
9065dfecf96Smrg    test = ARGUMENT(0);
9075dfecf96Smrg
9085dfecf96Smrg    /* Generate code to evaluate test */
9095dfecf96Smrg    ComEval(com, test);
9105dfecf96Smrg    /* Add node after test */
9115dfecf96Smrg    group = NEW_TREE(CodeTreeJumpIf);
9125dfecf96Smrg    group->code = XBC_JUMPT;
9135dfecf96Smrg    /* Generate NIL code */
9145dfecf96Smrg    ComProgn(com, body);
9155dfecf96Smrg    /* Insert node to know where to jump if test is T */
9165dfecf96Smrg    tree = NEW_TREE(CodeTreeJumpIf);
9175dfecf96Smrg    tree->code = XBC_NOOP;
9185dfecf96Smrg    group->group = tree;
9195dfecf96Smrg}
9205dfecf96Smrg
9215dfecf96Smrgvoid
9225dfecf96SmrgCom_Until(LispCom *com, LispBuiltin *builtin)
9235dfecf96Smrg/*
9245dfecf96Smrg until test &rest body
9255dfecf96Smrg */
9265dfecf96Smrg{
9275dfecf96Smrg    CodeTree *tree, *group, *ltree, *lgroup;
9285dfecf96Smrg    LispObj *test, *body;
9295dfecf96Smrg
9305dfecf96Smrg    body = ARGUMENT(1);
9315dfecf96Smrg    test = ARGUMENT(0);
9325dfecf96Smrg
9335dfecf96Smrg    /* Insert node to mark LOOP start */
9345dfecf96Smrg    ltree = NEW_TREE(CodeTreeJump);
9355dfecf96Smrg    ltree->code = XBC_NOOP;
9365dfecf96Smrg
9375dfecf96Smrg    /* Build code for test */
9385dfecf96Smrg    ComEval(com, test);
9395dfecf96Smrg    group = NEW_TREE(CodeTreeJumpIf);
9405dfecf96Smrg    group->code = XBC_JUMPT;
9415dfecf96Smrg
9425dfecf96Smrg    /* Execute @BODY */
9435dfecf96Smrg    ComProgn(com, body);
9445dfecf96Smrg
9455dfecf96Smrg    /* Insert node to jump to test again */
9465dfecf96Smrg    lgroup = NEW_TREE(CodeTreeJump);
9475dfecf96Smrg    lgroup->code = XBC_JUMP;
9485dfecf96Smrg    lgroup->group = ltree;
9495dfecf96Smrg
9505dfecf96Smrg    /* Insert node to know where to jump if test is T */
9515dfecf96Smrg    tree = NEW_TREE(CodeTreeJumpIf);
9525dfecf96Smrg    tree->code = XBC_NOOP;
9535dfecf96Smrg    group->group = tree;
9545dfecf96Smrg}
9555dfecf96Smrg
9565dfecf96Smrgvoid
9575dfecf96SmrgCom_When(LispCom *com, LispBuiltin *builtin)
9585dfecf96Smrg/*
9595dfecf96Smrg when test &rest body
9605dfecf96Smrg */
9615dfecf96Smrg{
9625dfecf96Smrg    CodeTree *group, *tree;
9635dfecf96Smrg    LispObj *test, *body;
9645dfecf96Smrg
9655dfecf96Smrg    body = ARGUMENT(1);
9665dfecf96Smrg    test = ARGUMENT(0);
9675dfecf96Smrg
9685dfecf96Smrg    /* Generate code to evaluate test */
9695dfecf96Smrg    ComEval(com, test);
9705dfecf96Smrg    /* Add node after test */
9715dfecf96Smrg    group = NEW_TREE(CodeTreeJumpIf);
9725dfecf96Smrg    group->code = XBC_JUMPNIL;
9735dfecf96Smrg    /* Generate T code */
9745dfecf96Smrg    ComProgn(com, body);
9755dfecf96Smrg    /* Insert node to know where to jump if test is NIL */
9765dfecf96Smrg    tree = NEW_TREE(CodeTreeJumpIf);
9775dfecf96Smrg    tree->code = XBC_NOOP;
9785dfecf96Smrg    group->group = tree;
9795dfecf96Smrg}
9805dfecf96Smrg
9815dfecf96Smrgvoid
9825dfecf96SmrgCom_While(LispCom *com, LispBuiltin *builtin)
9835dfecf96Smrg/*
9845dfecf96Smrg while test &rest body
9855dfecf96Smrg */
9865dfecf96Smrg{
9875dfecf96Smrg    CodeTree *tree, *group, *ltree, *lgroup;
9885dfecf96Smrg    LispObj *test, *body;
9895dfecf96Smrg
9905dfecf96Smrg    body = ARGUMENT(1);
9915dfecf96Smrg    test = ARGUMENT(0);
9925dfecf96Smrg
9935dfecf96Smrg    /* Insert node to mark LOOP start */
9945dfecf96Smrg    ltree = NEW_TREE(CodeTreeJump);
9955dfecf96Smrg    ltree->code = XBC_NOOP;
9965dfecf96Smrg
9975dfecf96Smrg    /* Build code for test */
9985dfecf96Smrg    ComEval(com, test);
9995dfecf96Smrg    group = NEW_TREE(CodeTreeJumpIf);
10005dfecf96Smrg    group->code = XBC_JUMPNIL;
10015dfecf96Smrg
10025dfecf96Smrg    /* Execute @BODY */
10035dfecf96Smrg    ComProgn(com, body);
10045dfecf96Smrg
10055dfecf96Smrg    /* Insert node to jump to test again */
10065dfecf96Smrg    lgroup = NEW_TREE(CodeTreeJump);
10075dfecf96Smrg    lgroup->code = XBC_JUMP;
10085dfecf96Smrg    lgroup->group = ltree;
10095dfecf96Smrg
10105dfecf96Smrg    /* Insert node to know where to jump if test is NIL */
10115dfecf96Smrg    tree = NEW_TREE(CodeTreeJumpIf);
10125dfecf96Smrg    tree->code = XBC_NOOP;
10135dfecf96Smrg    group->group = tree;
10145dfecf96Smrg}
10155dfecf96Smrg
10165dfecf96Smrg
10175dfecf96Smrg/***********************************************************************
10185dfecf96Smrg * Com_XXX helper functions
10195dfecf96Smrg ***********************************************************************/
10205dfecf96Smrgstatic void
10215dfecf96SmrgComPredicate(LispCom *com, LispBuiltin *builtin, LispBytePredicate predicate)
10225dfecf96Smrg{
10235dfecf96Smrg    LispObj *object;
10245dfecf96Smrg
10255dfecf96Smrg    object = ARGUMENT(0);
10265dfecf96Smrg
10275dfecf96Smrg    if (ComConstantp(com, object)) {
10285dfecf96Smrg	switch (predicate) {
10295dfecf96Smrg	    case XBP_CONSP:
10305dfecf96Smrg		com_Bytecode(com, CONSP(object) ? XBC_T : XBC_NIL);
10315dfecf96Smrg		break;
10325dfecf96Smrg	    case XBP_LISTP:
10335dfecf96Smrg		com_Bytecode(com, CONSP(object) || object == NIL ?
10345dfecf96Smrg			     XBC_T : XBC_NIL);
10355dfecf96Smrg		break;
10365dfecf96Smrg	    case XBP_NUMBERP:
10375dfecf96Smrg		com_Bytecode(com, NUMBERP(object) ? XBC_T : XBC_NIL);
10385dfecf96Smrg		break;
10395dfecf96Smrg	}
10405dfecf96Smrg    }
10415dfecf96Smrg    else {
10425dfecf96Smrg	ComEval(com, object);
10435dfecf96Smrg	com_BytecodeChar(com, XBC_PRED, predicate);
10445dfecf96Smrg    }
10455dfecf96Smrg}
10465dfecf96Smrg
10475dfecf96Smrg/* XXX Could receive an argument telling if is the last statement in the
10485dfecf96Smrg * block(s), i.e. if a jump opcode should be generated or just the
10495dfecf96Smrg * evaluation of the returned value. Probably this is better done in
10505dfecf96Smrg * an optimization step. */
10515dfecf96Smrgstatic void
10525dfecf96SmrgComReturnFrom(LispCom *com, LispBuiltin *builtin, int from)
10535dfecf96Smrg{
10545dfecf96Smrg    int bind;
10555dfecf96Smrg    CodeTree *tree;
10565dfecf96Smrg    LispObj *name, *result;
10575dfecf96Smrg    CodeBlock *block = com->block;
10585dfecf96Smrg
10595dfecf96Smrg    if (from) {
10605dfecf96Smrg	result = ARGUMENT(1);
10615dfecf96Smrg	name = ARGUMENT(0);
10625dfecf96Smrg    }
10635dfecf96Smrg    else {
10645dfecf96Smrg	result = ARGUMENT(0);
10655dfecf96Smrg	name = NIL;
10665dfecf96Smrg    }
10675dfecf96Smrg    if (result == UNSPEC)
10685dfecf96Smrg	result = NIL;
10695dfecf96Smrg
10705dfecf96Smrg    bind = block->bind;
10715dfecf96Smrg    while (block) {
10725dfecf96Smrg	if (block->type == LispBlockClosure)
10735dfecf96Smrg	    /* A function call */
10745dfecf96Smrg	    break;
10755dfecf96Smrg	else if (block->type == LispBlockTag && block->tag == name)
10765dfecf96Smrg	    break;
10775dfecf96Smrg	block = block->prev;
10785dfecf96Smrg	if (block)
10795dfecf96Smrg	    bind += block->bind;
10805dfecf96Smrg    }
10815dfecf96Smrg
10825dfecf96Smrg    if (!block || block->tag != name)
10835dfecf96Smrg	LispDestroy("%s: no visible %s block", STRFUN(builtin), STROBJ(name));
10845dfecf96Smrg
10855dfecf96Smrg    /* Generate code to load result */
10865dfecf96Smrg    ComEval(com, result);
10875dfecf96Smrg
10885dfecf96Smrg    /* Check for added variables that the jump is skiping the unbind opcode */
10895dfecf96Smrg    com_Unbind(com, bind);
10905dfecf96Smrg
10915dfecf96Smrg    tree = NEW_TREE(CodeTreeReturn);
10925dfecf96Smrg    tree->data.block = block;
10935dfecf96Smrg}
10945dfecf96Smrg
10955dfecf96Smrg/***********************************************************************
10965dfecf96Smrg * Helper functions
10975dfecf96Smrg ***********************************************************************/
10985dfecf96Smrgstatic int
10995dfecf96SmrgComConstantp(LispCom *com, LispObj *object)
11005dfecf96Smrg{
11015dfecf96Smrg    switch (OBJECT_TYPE(object)) {
11025dfecf96Smrg	case LispAtom_t:
11035dfecf96Smrg	    /* Keywords are guaranteed to evaluate to itself */
11045dfecf96Smrg	    if (object->data.atom->package == lisp__data.keyword)
11055dfecf96Smrg		break;
11065dfecf96Smrg	    return (0);
11075dfecf96Smrg
11085dfecf96Smrg	    /* Function call */
11095dfecf96Smrg	case LispCons_t:
11105dfecf96Smrg
11115dfecf96Smrg	    /* Need macro expansion, these are special abstract objects */
11125dfecf96Smrg	case LispQuote_t:
11135dfecf96Smrg	case LispBackquote_t:
11145dfecf96Smrg	case LispComma_t:
11155dfecf96Smrg	case LispFunctionQuote_t:
11165dfecf96Smrg	    return (0);
11175dfecf96Smrg
11185dfecf96Smrg	    /* Anything else is a literal constant */
11195dfecf96Smrg	default:
11205dfecf96Smrg	    break;
11215dfecf96Smrg    }
11225dfecf96Smrg
11235dfecf96Smrg    return (1);
11245dfecf96Smrg}
11255dfecf96Smrg
11265dfecf96Smrgstatic int
11275dfecf96SmrgFindIndex(void *item, void **table, int length)
11285dfecf96Smrg{
11295dfecf96Smrg    long cmp;
11305dfecf96Smrg    int left, right, i;
11315dfecf96Smrg
11325dfecf96Smrg    left = 0;
11335dfecf96Smrg    right = length - 1;
11345dfecf96Smrg    while (left <= right) {
11355dfecf96Smrg	i = (left + right) >> 1;
11365dfecf96Smrg	cmp = (char*)item - (char*)table[i];
11375dfecf96Smrg	if (cmp == 0)
11385dfecf96Smrg	    return (i);
11395dfecf96Smrg	else if (cmp < 0)
11405dfecf96Smrg	    right = i - 1;
11415dfecf96Smrg	else
11425dfecf96Smrg	    left = i + 1;
11435dfecf96Smrg    }
11445dfecf96Smrg
11455dfecf96Smrg    return (-1);
11465dfecf96Smrg}
11475dfecf96Smrg
11485dfecf96Smrgstatic int
11495dfecf96Smrgcompare(const void *left, const void *right)
11505dfecf96Smrg{
11515dfecf96Smrg    long cmp = *(char**)left - *(char**)right;
11525dfecf96Smrg
11535dfecf96Smrg    return (cmp < 0 ? -1 : 1);
11545dfecf96Smrg}
11555dfecf96Smrg
11565dfecf96Smrgstatic int
11575dfecf96SmrgBuildTablePointer(void *pointer, void ***pointers, int *num_pointers)
11585dfecf96Smrg{
11595dfecf96Smrg    int i;
11605dfecf96Smrg
11615dfecf96Smrg    if ((i = FindIndex(pointer, *pointers, *num_pointers)) < 0) {
11625dfecf96Smrg	*pointers = LispRealloc(*pointers,
11635dfecf96Smrg				sizeof(void*) * (*num_pointers + 1));
11645dfecf96Smrg	(*pointers)[*num_pointers] = pointer;
11655dfecf96Smrg	if (++*num_pointers > 1)
11665dfecf96Smrg	    qsort(*pointers, *num_pointers, sizeof(void*), compare);
11675dfecf96Smrg	i = FindIndex(pointer, *pointers, *num_pointers);
11685dfecf96Smrg    }
11695dfecf96Smrg
11705dfecf96Smrg    return (i);
11715dfecf96Smrg}
11725dfecf96Smrg
11735dfecf96Smrgstatic void
11745dfecf96SmrgComAddVariable(LispCom *com, LispObj *symbol, LispObj *value)
11755dfecf96Smrg{
11765dfecf96Smrg    LispAtom *atom = symbol->data.atom;
11775dfecf96Smrg
11785dfecf96Smrg    if (atom && atom->string && !com->macro) {
11795dfecf96Smrg	int i, length = com->block->variables.length;
11805dfecf96Smrg
11815dfecf96Smrg	i = BuildTablePointer(atom, (void***)&com->block->variables.symbols,
11825dfecf96Smrg			      &com->block->variables.length);
11835dfecf96Smrg
11845dfecf96Smrg	if (com->block->variables.length != length) {
11855dfecf96Smrg	    com->block->variables.flags =
11865dfecf96Smrg		LispRealloc(com->block->variables.flags,
11875dfecf96Smrg			    com->block->variables.length * sizeof(int));
11885dfecf96Smrg
11895dfecf96Smrg	    /* Variable was inserted in the middle of the list */
11905dfecf96Smrg	    if (i < length)
11915dfecf96Smrg		memmove(com->block->variables.flags + i + 1,
11925dfecf96Smrg			com->block->variables.flags + i,
11935dfecf96Smrg			(length - i) * sizeof(int));
11945dfecf96Smrg
11955dfecf96Smrg	    com->block->variables.flags[i] = 0;
11965dfecf96Smrg	}
11975dfecf96Smrg    }
11985dfecf96Smrg
11995dfecf96Smrg    LispAddVar(symbol, value);
12005dfecf96Smrg}
12015dfecf96Smrg
12025dfecf96Smrgstatic int
12035dfecf96SmrgComGetVariable(LispCom *com, LispObj *symbol)
12045dfecf96Smrg{
12055dfecf96Smrg    LispAtom *name;
12065dfecf96Smrg    int i, base, offset;
12075dfecf96Smrg    Atom_id id;
12085dfecf96Smrg
12095dfecf96Smrg    name = symbol->data.atom;
12105dfecf96Smrg    if (name->constant) {
12115dfecf96Smrg	if (name->package == lisp__data.keyword)
12125dfecf96Smrg	    /*	Just load <symbol> from the byte stream, keywords are
12135dfecf96Smrg	     * guaranteed to evaluate to itself. */
12145dfecf96Smrg	    return (SYMBOL_KEYWORD);
12155dfecf96Smrg	return (SYMBOL_CONSTANT);
12165dfecf96Smrg    }
12175dfecf96Smrg
12185dfecf96Smrg    offset = name->offset;
12195dfecf96Smrg    id = name->string;
12205dfecf96Smrg    base = lisp__data.env.lex;
12215dfecf96Smrg    i = lisp__data.env.head - 1;
12225dfecf96Smrg
12235dfecf96Smrg    /* If variable is local */
12245dfecf96Smrg    if (offset <= i && offset >= com->lex && lisp__data.env.names[offset] == id) {
12255dfecf96Smrg	COM_VARIABLE_USED(name);
12265dfecf96Smrg	/* Relative offset */
12275dfecf96Smrg	return (offset - base);
12285dfecf96Smrg    }
12295dfecf96Smrg
12305dfecf96Smrg    /* name->offset may have been changed in a macro expansion */
12315dfecf96Smrg    for (; i >= com->lex; i--)
12325dfecf96Smrg	if (lisp__data.env.names[i] == id) {
12335dfecf96Smrg	    name->offset = i;
12345dfecf96Smrg	    COM_VARIABLE_USED(name);
12355dfecf96Smrg	    return (i - base);
12365dfecf96Smrg	}
12375dfecf96Smrg
12385dfecf96Smrg    if (!name->a_object) {
12395dfecf96Smrg	++com->warnings;
12405dfecf96Smrg	LispWarning("variable %s is neither declared nor bound",
12415dfecf96Smrg		    name->string);
12425dfecf96Smrg    }
12435dfecf96Smrg
12445dfecf96Smrg    /* Not found, resolve <symbol> at run time */
12455dfecf96Smrg    return (SYMBOL_UNBOUND);
12465dfecf96Smrg}
12475dfecf96Smrg
12485dfecf96Smrgstatic void
12495dfecf96SmrgComVariableSetFlag(LispCom *com, LispAtom *atom, int flag)
12505dfecf96Smrg{
12515dfecf96Smrg    int i;
12525dfecf96Smrg    CodeBlock *block = com->block;
12535dfecf96Smrg
12545dfecf96Smrg    while (block) {
12555dfecf96Smrg	i = FindIndex(atom, (void**)block->variables.symbols,
12565dfecf96Smrg		      block->variables.length);
12575dfecf96Smrg	if (i >= 0) {
12585dfecf96Smrg	    block->variables.flags[i] |= flag;
12595dfecf96Smrg	    /*  Descend block list if an argument to function being called
12605dfecf96Smrg	     * has the same name as a bound variable in the current function.
12615dfecf96Smrg	     */
12625dfecf96Smrg	    if ((flag & VARIABLE_ARGUMENT) ||
12635dfecf96Smrg		!(block->variables.flags[i] & VARIABLE_ARGUMENT))
12645dfecf96Smrg		break;
12655dfecf96Smrg	}
12665dfecf96Smrg	block = block->prev;
12675dfecf96Smrg    }
12685dfecf96Smrg}
12695dfecf96Smrg
12705dfecf96Smrg/***********************************************************************
12715dfecf96Smrg * Bytecode compiler functions
12725dfecf96Smrg ***********************************************************************/
12735dfecf96Smrgstatic void
12745dfecf96SmrgComLabel(LispCom *com, LispObj *label)
12755dfecf96Smrg{
12765dfecf96Smrg    int i;
12775dfecf96Smrg    CodeTree *tree;
12785dfecf96Smrg
12795dfecf96Smrg    for (i = 0; i < com->block->tagbody.length; i++)
12805dfecf96Smrg	if (label == com->block->tagbody.labels[i])
12815dfecf96Smrg	    LispDestroy("TAGBODY: tag %s specified more than once",
12825dfecf96Smrg			STROBJ(label));
12835dfecf96Smrg
12845dfecf96Smrg    if (com->block->tagbody.length >= com->block->tagbody.space) {
12855dfecf96Smrg	com->block->tagbody.labels =
12865dfecf96Smrg	    LispRealloc(com->block->tagbody.labels,
12875dfecf96Smrg			sizeof(LispObj*) * (com->block->tagbody.space + 8));
12885dfecf96Smrg	/*  Reserve space, will be used at link time when
12895dfecf96Smrg	 * resolving GO jumps. */
12905dfecf96Smrg	com->block->tagbody.codes =
12915dfecf96Smrg	    LispRealloc(com->block->tagbody.codes,
12925dfecf96Smrg			sizeof(CodeTree*) * (com->block->tagbody.space + 8));
12935dfecf96Smrg	com->block->tagbody.space += 8;
12945dfecf96Smrg    }
12955dfecf96Smrg
12965dfecf96Smrg    com->block->tagbody.labels[com->block->tagbody.length++] = label;
12975dfecf96Smrg    tree = NEW_TREE(CodeTreeLabel);
12985dfecf96Smrg    tree->data.object = label;
12995dfecf96Smrg}
13005dfecf96Smrg
13015dfecf96Smrgstatic void
13025dfecf96SmrgComPush(LispCom *com, LispObj *symbol, LispObj *value,
13035dfecf96Smrg	int eval, int builtin, int compile)
13045dfecf96Smrg{
13055dfecf96Smrg    /*  If <compile> is set, it is pushing an argument to one of
13065dfecf96Smrg     * Com_XXX functions. */
13075dfecf96Smrg    if (compile) {
13085dfecf96Smrg	if (builtin)
13095dfecf96Smrg	    lisp__data.stack.values[lisp__data.stack.length++] = value;
13105dfecf96Smrg	else
13115dfecf96Smrg	    ComAddVariable(com, symbol, value);
13125dfecf96Smrg	return;
13135dfecf96Smrg    }
13145dfecf96Smrg
13155dfecf96Smrg    /*  If <com->macro> is set, it is expanding a macro, just add the local
13165dfecf96Smrg     * variable <symbol> bounded to <value>, so that it will be available
13175dfecf96Smrg     * when calling the interpreter to expand the macro. */
13185dfecf96Smrg    else if (com->macro) {
13195dfecf96Smrg	ComAddVariable(com, symbol, value);
13205dfecf96Smrg	return;
13215dfecf96Smrg    }
13225dfecf96Smrg
13235dfecf96Smrg    /*  If <eval> is set, it must generate the opcodes to evaluate <value>.
13245dfecf96Smrg     * If <value> is a constant, just generate the opcodes to load it. */
13255dfecf96Smrg    else if (eval && !ComConstantp(com, value)) {
13265dfecf96Smrg	switch (OBJECT_TYPE(value)) {
13275dfecf96Smrg	    case LispAtom_t: {
13285dfecf96Smrg		int offset = ComGetVariable(com, value);
13295dfecf96Smrg
13305dfecf96Smrg		if (offset >= 0) {
13315dfecf96Smrg		    /* Load <value> from user stack at the relative offset */
13325dfecf96Smrg		    if (builtin)
13335dfecf96Smrg			com_LoadPush(com, offset);
13345dfecf96Smrg		    else
13355dfecf96Smrg			com_LoadLet(com, offset, symbol->data.atom);
13365dfecf96Smrg		}
13375dfecf96Smrg		/* ComConstantp() does not return true for this, as the
13385dfecf96Smrg		 * current value must be computed. */
13395dfecf96Smrg		else if (offset == SYMBOL_CONSTANT) {
13405dfecf96Smrg		    value = value->data.atom->property->value;
13415dfecf96Smrg		    if (builtin)
13425dfecf96Smrg			com_LoadConPush(com, value);
13435dfecf96Smrg		    else
13445dfecf96Smrg			com_LoadConLet(com, value, symbol->data.atom);
13455dfecf96Smrg		}
13465dfecf96Smrg		else {
13475dfecf96Smrg		    /* Load value bound to <value> at run time */
13485dfecf96Smrg		    if (builtin)
13495dfecf96Smrg			com_LoadSymPush(com, value->data.atom);
13505dfecf96Smrg		    else
13515dfecf96Smrg			com_LoadSymLet(com, value->data.atom,
13525dfecf96Smrg				       symbol->data.atom);
13535dfecf96Smrg		}
13545dfecf96Smrg	    }	break;
13555dfecf96Smrg
13565dfecf96Smrg	    default:
13575dfecf96Smrg		/* Generate code to evaluate <value> */
13585dfecf96Smrg		ComEval(com, value);
13595dfecf96Smrg		if (builtin)
13605dfecf96Smrg		    com_Bytecode(com, XBC_PUSH);
13615dfecf96Smrg		else
13625dfecf96Smrg		    com_Let(com, symbol->data.atom);
13635dfecf96Smrg		break;
13645dfecf96Smrg	}
13655dfecf96Smrg
13665dfecf96Smrg	/*  Remember <symbol> will be bound, <value> only matters for
13675dfecf96Smrg	 * the Com_XXX  functions */
13685dfecf96Smrg	if (builtin)
13695dfecf96Smrg	    lisp__data.stack.values[lisp__data.stack.length++] = value;
13705dfecf96Smrg	else
13715dfecf96Smrg	    ComAddVariable(com, symbol, value);
13725dfecf96Smrg	return;
13735dfecf96Smrg    }
13745dfecf96Smrg
13755dfecf96Smrg    if (builtin) {
13765dfecf96Smrg	/* Load <value> as a constant in builtin stack */
13775dfecf96Smrg	com_LoadConPush(com, value);
13785dfecf96Smrg	lisp__data.stack.values[lisp__data.stack.length++] = value;
13795dfecf96Smrg    }
13805dfecf96Smrg    else {
13815dfecf96Smrg	/* Load <value> as a constant in stack */
13825dfecf96Smrg	com_LoadConLet(com, value, symbol->data.atom);
13835dfecf96Smrg	/* Remember <symbol> will be bound */
13845dfecf96Smrg	ComAddVariable(com, symbol, value);
13855dfecf96Smrg    }
13865dfecf96Smrg}
13875dfecf96Smrg
13885dfecf96Smrg/*  This function does almost the same job as LispMakeEnvironment, but
13895dfecf96Smrg * it is not optimized for speed, as it is not building argument lists
13905dfecf96Smrg * to user code, but to Com_XXX functions, or helping in generating the
13915dfecf96Smrg * opcodes to load arguments at bytecode run time. */
13925dfecf96Smrgstatic int
13935dfecf96SmrgComCall(LispCom *com, LispArgList *alist,
13945dfecf96Smrg	LispObj *name, LispObj *values,
13955dfecf96Smrg	int eval, int builtin, int compile)
13965dfecf96Smrg{
13975dfecf96Smrg    char *desc;
13985dfecf96Smrg    int i, count, base;
13995dfecf96Smrg    LispObj **symbols, **defaults, **sforms;
14005dfecf96Smrg
14015dfecf96Smrg    if (builtin) {
14025dfecf96Smrg	base = lisp__data.stack.length;
14035dfecf96Smrg	/* This should never be executed, but make the check for safety */
14045dfecf96Smrg	if (base + alist->num_arguments > lisp__data.stack.space) {
14055dfecf96Smrg	    do
14065dfecf96Smrg		LispMoreStack();
14075dfecf96Smrg	    while (base + alist->num_arguments > lisp__data.stack.space);
14085dfecf96Smrg	}
14095dfecf96Smrg    }
14105dfecf96Smrg    else
14115dfecf96Smrg	base = lisp__data.env.length;
14125dfecf96Smrg
14135dfecf96Smrg    desc = alist->description;
14145dfecf96Smrg    switch (*desc++) {
14155dfecf96Smrg	case '.':
14165dfecf96Smrg	    goto normal_label;
14175dfecf96Smrg	case 'o':
14185dfecf96Smrg	    goto optional_label;
14195dfecf96Smrg	case 'k':
14205dfecf96Smrg	    goto key_label;
14215dfecf96Smrg	case 'r':
14225dfecf96Smrg	    goto rest_label;
14235dfecf96Smrg	case 'a':
14245dfecf96Smrg	    goto aux_label;
14255dfecf96Smrg	default:
14265dfecf96Smrg	    goto done_label;
14275dfecf96Smrg    }
14285dfecf96Smrg
14295dfecf96Smrg
14305dfecf96Smrg    /* Normal arguments */
14315dfecf96Smrgnormal_label:
14325dfecf96Smrg    i = 0;
14335dfecf96Smrg    symbols = alist->normals.symbols;
14345dfecf96Smrg    count = alist->normals.num_symbols;
14355dfecf96Smrg    for (; i < count && CONSP(values); i++, values = CDR(values)) {
14365dfecf96Smrg	ComPush(com, symbols[i], CAR(values), eval, builtin, compile);
14375dfecf96Smrg	if (!builtin && !com->macro)
14385dfecf96Smrg	    COM_VARIABLE_ARGUMENT(symbols[i]->data.atom);
14395dfecf96Smrg    }
14405dfecf96Smrg    if (i < count)
14415dfecf96Smrg	LispDestroy("%s: too few arguments", STROBJ(name));
14425dfecf96Smrg
14435dfecf96Smrg    switch (*desc++) {
14445dfecf96Smrg	case 'o':
14455dfecf96Smrg	    goto optional_label;
14465dfecf96Smrg	case 'k':
14475dfecf96Smrg	    goto key_label;
14485dfecf96Smrg	case 'r':
14495dfecf96Smrg	    goto rest_label;
14505dfecf96Smrg	case 'a':
14515dfecf96Smrg	    goto aux_label;
14525dfecf96Smrg	default:
14535dfecf96Smrg	    goto done_label;
14545dfecf96Smrg    }
14555dfecf96Smrg
14565dfecf96Smrg
14575dfecf96Smrg    /* &OPTIONAL */
14585dfecf96Smrgoptional_label:
14595dfecf96Smrg    i = 0;
14605dfecf96Smrg    count = alist->optionals.num_symbols;
14615dfecf96Smrg    symbols = alist->optionals.symbols;
14625dfecf96Smrg    defaults = alist->optionals.defaults;
14635dfecf96Smrg    sforms = alist->optionals.sforms;
14645dfecf96Smrg    for (; i < count && CONSP(values); i++, values = CDR(values)) {
14655dfecf96Smrg	ComPush(com, symbols[i], CAR(values), eval, builtin, compile);
14665dfecf96Smrg	if (!builtin && !com->macro)
14675dfecf96Smrg	    COM_VARIABLE_ARGUMENT(symbols[i]->data.atom);
14685dfecf96Smrg	if (sforms[i]) {
14695dfecf96Smrg	    ComPush(com, sforms[i], T, 0, builtin, compile);
14705dfecf96Smrg	    if (!builtin && !com->macro)
14715dfecf96Smrg		COM_VARIABLE_ARGUMENT(sforms[i]->data.atom);
14725dfecf96Smrg	}
14735dfecf96Smrg    }
14745dfecf96Smrg    for (; i < count; i++) {
14755dfecf96Smrg	if (!builtin) {
14765dfecf96Smrg	    int lex = com->lex;
14775dfecf96Smrg	    int head = lisp__data.env.head;
14785dfecf96Smrg
14795dfecf96Smrg	    com->lex = base;
14805dfecf96Smrg	    lisp__data.env.head = lisp__data.env.length;
14815dfecf96Smrg	    /* default arguments are evaluated for macros */
14825dfecf96Smrg	    ComPush(com, symbols[i], defaults[i], 1, 0, compile);
14835dfecf96Smrg	    if (!com->macro)
14845dfecf96Smrg		COM_VARIABLE_ARGUMENT(symbols[i]->data.atom);
14855dfecf96Smrg	    lisp__data.env.head = head;
14865dfecf96Smrg	    com->lex = lex;
14875dfecf96Smrg	}
14885dfecf96Smrg	else
14895dfecf96Smrg	    ComPush(com, symbols[i], defaults[i], eval, 1, compile);
14905dfecf96Smrg	if (sforms[i]) {
14915dfecf96Smrg	    ComPush(com, sforms[i], NIL, 0, builtin, compile);
14925dfecf96Smrg	    if (!builtin && !com->macro)
14935dfecf96Smrg		COM_VARIABLE_ARGUMENT(sforms[i]->data.atom);
14945dfecf96Smrg	}
14955dfecf96Smrg    }
14965dfecf96Smrg
14975dfecf96Smrg    switch (*desc++) {
14985dfecf96Smrg	case 'k':
14995dfecf96Smrg	    goto key_label;
15005dfecf96Smrg	case 'r':
15015dfecf96Smrg	    goto rest_label;
15025dfecf96Smrg	case 'a':
15035dfecf96Smrg	    goto aux_label;
15045dfecf96Smrg	default:
15055dfecf96Smrg	    goto done_label;
15065dfecf96Smrg    }
15075dfecf96Smrg
15085dfecf96Smrg
15095dfecf96Smrg    /* &KEY */
15105dfecf96Smrgkey_label:
15115dfecf96Smrg    {
15125dfecf96Smrg	int varset;
15135dfecf96Smrg	LispObj *val, *karg, **keys;
15145dfecf96Smrg
15155dfecf96Smrg	count = alist->keys.num_symbols;
15165dfecf96Smrg	symbols = alist->keys.symbols;
15175dfecf96Smrg	defaults = alist->keys.defaults;
15185dfecf96Smrg	sforms = alist->keys.sforms;
15195dfecf96Smrg	keys = alist->keys.keys;
15205dfecf96Smrg
15215dfecf96Smrg	/* Check if arguments are correctly specified */
15225dfecf96Smrg	for (karg = values; CONSP(karg); karg = CDR(karg)) {
15235dfecf96Smrg	    val = CAR(karg);
15245dfecf96Smrg	    if (KEYWORDP(val)) {
15255dfecf96Smrg		for (i = 0; i < alist->keys.num_symbols; i++)
15265dfecf96Smrg		    if (!keys[i] && symbols[i] == val)
15275dfecf96Smrg			break;
15285dfecf96Smrg	    }
15295dfecf96Smrg
15305dfecf96Smrg	    else if (!builtin &&
15315dfecf96Smrg		     QUOTEP(val) && SYMBOLP(val->data.quote)) {
15325dfecf96Smrg		for (i = 0; i < alist->keys.num_symbols; i++)
15335dfecf96Smrg		    if (keys[i] && ATOMID(keys[i]) == ATOMID(val->data.quote))
15345dfecf96Smrg			break;
15355dfecf96Smrg	    }
15365dfecf96Smrg
15375dfecf96Smrg	    else
15385dfecf96Smrg		/* Just make the error test true */
15395dfecf96Smrg		i = alist->keys.num_symbols;
15405dfecf96Smrg
15415dfecf96Smrg	    if (i == alist->keys.num_symbols) {
15425dfecf96Smrg		/* If not in argument specification list... */
15435dfecf96Smrg		char function_name[36];
15445dfecf96Smrg
15455dfecf96Smrg		strcpy(function_name, STROBJ(name));
15465dfecf96Smrg		LispDestroy("%s: invalid keyword %s",
15475dfecf96Smrg			    function_name, STROBJ(val));
15485dfecf96Smrg	    }
15495dfecf96Smrg
15505dfecf96Smrg	    karg = CDR(karg);
15515dfecf96Smrg	    if (!CONSP(karg))
15525dfecf96Smrg		LispDestroy("%s: &KEY needs arguments as pairs",
15535dfecf96Smrg			    STROBJ(name));
15545dfecf96Smrg	}
15555dfecf96Smrg
15565dfecf96Smrg	/* Add variables */
15575dfecf96Smrg	for (i = 0; i < alist->keys.num_symbols; i++) {
15585dfecf96Smrg	    val = defaults[i];
15595dfecf96Smrg	    varset = 0;
15605dfecf96Smrg	    if (!builtin && keys[i]) {
15615dfecf96Smrg		Atom_id atom = ATOMID(keys[i]);
15625dfecf96Smrg
15635dfecf96Smrg		/* Special keyword specification, need to compare ATOMID
15645dfecf96Smrg		 * and keyword specification must be a quoted object */
15655dfecf96Smrg		for (karg = values; CONSP(karg); karg = CDR(karg)) {
15665dfecf96Smrg		    val = CAR(karg);
15675dfecf96Smrg		    if (QUOTEP(val) && atom == ATOMID(val->data.quote)) {
15685dfecf96Smrg			val = CADR(karg);
15695dfecf96Smrg			varset = 1;
15705dfecf96Smrg			break;
15715dfecf96Smrg		    }
15725dfecf96Smrg		    karg = CDR(karg);
15735dfecf96Smrg		}
15745dfecf96Smrg	    }
15755dfecf96Smrg
15765dfecf96Smrg	    else {
15775dfecf96Smrg		/* Normal keyword specification, can compare object pointers,
15785dfecf96Smrg		 * as they point to the same object in the keyword package */
15795dfecf96Smrg		for (karg = values; CONSP(karg); karg = CDR(karg)) {
15805dfecf96Smrg		    /* Don't check if argument is a valid keyword or
15815dfecf96Smrg		     * special quoted keyword */
15825dfecf96Smrg		    if (symbols[i] == CAR(karg)) {
15835dfecf96Smrg			val = CADR(karg);
15845dfecf96Smrg			varset = 1;
15855dfecf96Smrg			break;
15865dfecf96Smrg		    }
15875dfecf96Smrg		    karg = CDR(karg);
15885dfecf96Smrg		}
15895dfecf96Smrg	    }
15905dfecf96Smrg
15915dfecf96Smrg	    /* Add the variable to environment */
15925dfecf96Smrg	    if (varset) {
15935dfecf96Smrg		ComPush(com, symbols[i], val, eval, builtin, compile);
15945dfecf96Smrg		if (sforms[i])
15955dfecf96Smrg		    ComPush(com, sforms[i], T, 0, builtin, compile);
15965dfecf96Smrg	    }
15975dfecf96Smrg	    else {
15985dfecf96Smrg		/* default arguments are evaluated for macros */
15995dfecf96Smrg		if (!builtin) {
16005dfecf96Smrg		    int lex = com->lex;
16015dfecf96Smrg		    int head = lisp__data.env.head;
16025dfecf96Smrg
16035dfecf96Smrg		    com->lex = base;
16045dfecf96Smrg		    lisp__data.env.head = lisp__data.env.length;
16055dfecf96Smrg		    ComPush(com, symbols[i], val, eval, 0, compile);
16065dfecf96Smrg		    lisp__data.env.head = head;
16075dfecf96Smrg		    com->lex = lex;
16085dfecf96Smrg		}
16095dfecf96Smrg		else
16105dfecf96Smrg		    ComPush(com, symbols[i], val, eval, builtin, compile);
16115dfecf96Smrg		if (sforms[i])
16125dfecf96Smrg		    ComPush(com, sforms[i], NIL, 0, builtin, compile);
16135dfecf96Smrg	    }
16145dfecf96Smrg	    if (!builtin && !com->macro) {
16155dfecf96Smrg		COM_VARIABLE_ARGUMENT(symbols[i]->data.atom);
16165dfecf96Smrg		if (sforms[i])
16175dfecf96Smrg		    COM_VARIABLE_ARGUMENT(sforms[i]->data.atom);
16185dfecf96Smrg	    }
16195dfecf96Smrg	}
16205dfecf96Smrg    }
16215dfecf96Smrg
16225dfecf96Smrg    if (*desc == 'a') {
16235dfecf96Smrg	/* &KEY uses all remaining arguments */
16245dfecf96Smrg	values = NIL;
16255dfecf96Smrg	goto aux_label;
16265dfecf96Smrg    }
16275dfecf96Smrg    goto finished_label;
16285dfecf96Smrg
16295dfecf96Smrg
16305dfecf96Smrg    /* &REST */
16315dfecf96Smrgrest_label:
16325dfecf96Smrg    if (!eval || !CONSP(values) || (compile && !builtin))
16335dfecf96Smrg	ComPush(com, alist->rest, values, eval, builtin, compile);
16345dfecf96Smrg    else {
16355dfecf96Smrg	char *string;
16365dfecf96Smrg	LispObj *list, *car = NIL;
16375dfecf96Smrg	int count, constantp;
16385dfecf96Smrg
16395dfecf96Smrg	/* Count number of arguments and check if it is a list of constants */
16405dfecf96Smrg	for (count = 0, constantp = 1, list = values;
16415dfecf96Smrg	     CONSP(list);
16425dfecf96Smrg	     list = CDR(list), count++) {
16435dfecf96Smrg	    car = CAR(list);
16445dfecf96Smrg	    if (!ComConstantp(com, car))
16455dfecf96Smrg		constantp = 0;
16465dfecf96Smrg	}
16475dfecf96Smrg
16485dfecf96Smrg	string = builtin ? ATOMID(name) : NULL;
16495dfecf96Smrg	/* XXX FIXME should have a flag indicating if function call
16505dfecf96Smrg	 * change the &REST arguments even if it is a constant list
16515dfecf96Smrg	 * (or if the returned value may be changed). */
16525dfecf96Smrg	if (string && (count < MAX_BCONS || constantp) &&
16535dfecf96Smrg	    strcmp(string, "LIST") &&
16545dfecf96Smrg	    strcmp(string, "APPLY") &&	/* XXX depends on function argument */
16555dfecf96Smrg	    strcmp(string, "VECTOR") &&
16565dfecf96Smrg	    /* Append does not copy the last/single list */
16575dfecf96Smrg	    (strcmp(string, "APPEND") || !CONSP(car))) {
16585dfecf96Smrg	    if (constantp) {
16595dfecf96Smrg		/* If the builtin function changes the &REST parameters, must
16605dfecf96Smrg		 * define a Com_XXX function for it. */
16615dfecf96Smrg		ComPush(com, alist->rest, values, 0, builtin, compile);
16625dfecf96Smrg	    }
16635dfecf96Smrg	    else {
16645dfecf96Smrg		CompileStackEnter(com, count - 1, 1);
16655dfecf96Smrg		for (; CONSP(CDR(values)); values = CDR(values)) {
16665dfecf96Smrg		    /* Evaluate this argument */
16675dfecf96Smrg		    ComEval(com, CAR(values));
16685dfecf96Smrg		    /* Save result in builtin stack */
16695dfecf96Smrg		    com_Bytecode(com, XBC_PUSH);
16705dfecf96Smrg		}
16715dfecf96Smrg		CompileStackLeave(com, count - 1, 1);
16725dfecf96Smrg		/* The last argument is not saved in the stack */
16735dfecf96Smrg		ComEval(com, CAR(values));
16745dfecf96Smrg		values = NIL;
16755dfecf96Smrg		com_Bytecode(com, (LispByteOpcode)(XBC_BCONS + (count - 1)));
16765dfecf96Smrg	    }
16775dfecf96Smrg	}
16785dfecf96Smrg	else {
16795dfecf96Smrg	    /* Allocate a fresh list of cons */
16805dfecf96Smrg
16815dfecf96Smrg	    /* Generate code to load object */
16825dfecf96Smrg	    ComEval(com, CAR(values));
16835dfecf96Smrg
16845dfecf96Smrg	    com->stack.cpstack += 2;
16855dfecf96Smrg	    if (com->stack.pstack < com->stack.cpstack)
16865dfecf96Smrg		com->stack.pstack = com->stack.cpstack;
16875dfecf96Smrg	    /* Start building a gc protected list, with the loaded value */
16885dfecf96Smrg	    com_Bytecode(com, XBC_LSTAR);
16895dfecf96Smrg
16905dfecf96Smrg	    for (values = CDR(values); CONSP(values); values = CDR(values)) {
16915dfecf96Smrg		/* Generate code to load object */
16925dfecf96Smrg		ComEval(com, CAR(values));
16935dfecf96Smrg
16945dfecf96Smrg		/* Add loaded value to gc protected list */
16955dfecf96Smrg		com_Bytecode(com, XBC_LCONS);
16965dfecf96Smrg	    }
16975dfecf96Smrg
16985dfecf96Smrg	    /* Finish gc protected list */
16995dfecf96Smrg	    com_Bytecode(com, XBC_LFINI);
17005dfecf96Smrg
17015dfecf96Smrg	    /* Push loaded value */
17025dfecf96Smrg	    if (builtin)
17035dfecf96Smrg		com_Bytecode(com, XBC_PUSH);
17045dfecf96Smrg	    else {
17055dfecf96Smrg		com_Let(com, alist->rest->data.atom);
17065dfecf96Smrg
17075dfecf96Smrg		/* Remember this symbol will be bound */
17085dfecf96Smrg		ComAddVariable(com, alist->rest, values);
17095dfecf96Smrg	    }
17105dfecf96Smrg	    com->stack.cpstack -= 2;
17115dfecf96Smrg	}
17125dfecf96Smrg    }
17135dfecf96Smrg    if (!builtin && !com->macro)
17145dfecf96Smrg	COM_VARIABLE_ARGUMENT(alist->rest->data.atom);
17155dfecf96Smrg    if (*desc != 'a')
17165dfecf96Smrg	goto finished_label;
17175dfecf96Smrg
17185dfecf96Smrg
17195dfecf96Smrg    /* &AUX */
17205dfecf96Smrgaux_label:
17215dfecf96Smrg    i = 0;
17225dfecf96Smrg    count = alist->auxs.num_symbols;
17235dfecf96Smrg    symbols = alist->auxs.symbols;
17245dfecf96Smrg    defaults = alist->auxs.initials;
17255dfecf96Smrg    if (!builtin && !compile) {
17265dfecf96Smrg	int lex = com->lex;
17275dfecf96Smrg
17285dfecf96Smrg	com->lex = base;
17295dfecf96Smrg	lisp__data.env.head = lisp__data.env.length;
17305dfecf96Smrg	for (; i < count; i++) {
17315dfecf96Smrg	    ComPush(com, symbols[i], defaults[i], 1, 0, 0);
17325dfecf96Smrg	    if (!com->macro)
17335dfecf96Smrg		COM_VARIABLE_ARGUMENT(symbols[i]->data.atom);
17345dfecf96Smrg	    ++lisp__data.env.head;
17355dfecf96Smrg	}
17365dfecf96Smrg	com->lex = lex;
17375dfecf96Smrg    }
17385dfecf96Smrg    else {
17395dfecf96Smrg	for (; i < count; i++) {
17405dfecf96Smrg	    ComPush(com, symbols[i], defaults[i], eval, builtin, compile);
17415dfecf96Smrg	    if (!builtin && !com->macro)
17425dfecf96Smrg		COM_VARIABLE_ARGUMENT(symbols[i]->data.atom);
17435dfecf96Smrg	}
17445dfecf96Smrg    }
17455dfecf96Smrg
17465dfecf96Smrgdone_label:
17475dfecf96Smrg    if (CONSP(values))
17485dfecf96Smrg	LispDestroy("%s: too many arguments", STROBJ(name));
17495dfecf96Smrg
17505dfecf96Smrgfinished_label:
17515dfecf96Smrg    if (builtin)
17525dfecf96Smrg	lisp__data.stack.base = base;
17535dfecf96Smrg    else
17545dfecf96Smrg	lisp__data.env.head = lisp__data.env.length;
17555dfecf96Smrg
17565dfecf96Smrg    return (base);
17575dfecf96Smrg}
17585dfecf96Smrg
17595dfecf96Smrgstatic void
17605dfecf96SmrgComFuncall(LispCom *com, LispObj *function, LispObj *arguments, int eval)
17615dfecf96Smrg{
17625dfecf96Smrg    int base, compile;
17635dfecf96Smrg    LispAtom *atom;
17645dfecf96Smrg    LispArgList *alist;
17655dfecf96Smrg    LispBuiltin *builtin;
17665dfecf96Smrg    LispObj *lambda;
17675dfecf96Smrg
17685dfecf96Smrg    switch (OBJECT_TYPE(function)) {
17695dfecf96Smrg	case LispFunction_t:
17705dfecf96Smrg	    function = function->data.atom->object;
17715dfecf96Smrg	case LispAtom_t:
17725dfecf96Smrg	    atom = function->data.atom;
17735dfecf96Smrg	    alist = atom->property->alist;
17745dfecf96Smrg
17755dfecf96Smrg	    if (atom->a_builtin) {
17765dfecf96Smrg		builtin = atom->property->fun.builtin;
17775dfecf96Smrg		compile = builtin->compile != NULL;
17785dfecf96Smrg
17795dfecf96Smrg		/*  If one of:
17805dfecf96Smrg		 * 	o expanding a macro
17815dfecf96Smrg		 *	o calling a builtin special form
17825dfecf96Smrg		 *	o builtin function is a macro
17835dfecf96Smrg		 * don't evaluate arguments. */
17845dfecf96Smrg		if (com->macro || compile || builtin->type == LispMacro)
17855dfecf96Smrg		    eval = 0;
17865dfecf96Smrg
17875dfecf96Smrg		if (!com->macro && builtin->type == LispMacro) {
17885dfecf96Smrg		    /* Set flag of variable used, in case variable is only
17895dfecf96Smrg		     * used as a builtin macro argument. */
17905dfecf96Smrg		    LispObj *obj;
17915dfecf96Smrg
17925dfecf96Smrg		    for (obj = arguments; CONSP(obj); obj = CDR(obj)) {
17935dfecf96Smrg			if (SYMBOLP(CAR(obj)))
17945dfecf96Smrg			    COM_VARIABLE_USED(CAR(obj)->data.atom);
17955dfecf96Smrg		    }
17965dfecf96Smrg		}
17975dfecf96Smrg
17985dfecf96Smrg		FORM_ENTER();
17995dfecf96Smrg		if (!compile && !com->macro)
18005dfecf96Smrg		    CompileStackEnter(com, alist->num_arguments, 1);
18015dfecf96Smrg
18025dfecf96Smrg		/* Build argument list in the interpreter stacks */
18035dfecf96Smrg		base = ComCall(com, alist, function, arguments,
18045dfecf96Smrg			       eval, 1, compile);
18055dfecf96Smrg
18065dfecf96Smrg		/* If <compile> is set, it is a special form */
18075dfecf96Smrg		if (compile)
18085dfecf96Smrg		    builtin->compile(com, builtin);
18095dfecf96Smrg
18105dfecf96Smrg		/* Else, generate opcodes to call builtin function */
18115dfecf96Smrg		else {
18125dfecf96Smrg		    com_Call(com, alist->num_arguments, builtin);
18135dfecf96Smrg		    CompileStackLeave(com, alist->num_arguments, 1);
18145dfecf96Smrg		}
18155dfecf96Smrg		lisp__data.stack.base = lisp__data.stack.length = base;
18165dfecf96Smrg		FORM_LEAVE();
18175dfecf96Smrg	    }
18185dfecf96Smrg	    else if (atom->a_function) {
18195dfecf96Smrg		int macro;
18205dfecf96Smrg
18215dfecf96Smrg		lambda = atom->property->fun.function;
18225dfecf96Smrg		macro = lambda->funtype == LispMacro;
18235dfecf96Smrg
18245dfecf96Smrg		/* If <macro> is set, expand macro */
18255dfecf96Smrg		if (macro)
18265dfecf96Smrg		    ComMacroCall(com, alist, function, lambda, arguments);
18275dfecf96Smrg
18285dfecf96Smrg		else {
18295dfecf96Smrg		    if (com->toplevel->type == LispBlockClosure &&
18305dfecf96Smrg			com->toplevel->tag == function)
18315dfecf96Smrg			ComRecursiveCall(com, alist, function, arguments);
18325dfecf96Smrg		    else {
18335dfecf96Smrg#if 0
18345dfecf96Smrg			ComInlineCall(com, alist, function, arguments,
18355dfecf96Smrg				      lambda->data.lambda.code);
18365dfecf96Smrg#else
18375dfecf96Smrg			com_Funcall(com, function, arguments);
18385dfecf96Smrg#endif
18395dfecf96Smrg		    }
18405dfecf96Smrg		}
18415dfecf96Smrg	    }
18425dfecf96Smrg	    else if (atom->a_defstruct &&
18435dfecf96Smrg		     atom->property->structure.function != STRUCT_NAME &&
18445dfecf96Smrg		     atom->property->structure.function != STRUCT_CONSTRUCTOR) {
18455dfecf96Smrg		LispObj *definition = atom->property->structure.definition;
18465dfecf96Smrg
18475dfecf96Smrg		if (!CONSP(arguments) || CONSP(CDR(arguments)))
18485dfecf96Smrg		    LispDestroy("%s: too %s arguments", atom->string,
18495dfecf96Smrg				CONSP(arguments) ? "many" : "few");
18505dfecf96Smrg
18515dfecf96Smrg		ComEval(com, CAR(arguments));
18525dfecf96Smrg		if (atom->property->structure.function == STRUCT_CHECK)
18535dfecf96Smrg		    com_Structp(com, definition);
18545dfecf96Smrg		else
18555dfecf96Smrg		    com_Struct(com,
18565dfecf96Smrg			       atom->property->structure.function, definition);
18575dfecf96Smrg	    }
18585dfecf96Smrg	    else if (atom->a_compiled) {
18595dfecf96Smrg		FORM_ENTER();
18605dfecf96Smrg		CompileStackEnter(com, alist->num_arguments, 0);
18615dfecf96Smrg
18625dfecf96Smrg		/* Build argument list in the interpreter stacks */
18635dfecf96Smrg		base = ComCall(com, alist, function, arguments, 1, 0, 0);
18645dfecf96Smrg		com_Bytecall(com, alist->num_arguments,
18655dfecf96Smrg			     atom->property->fun.function);
18665dfecf96Smrg		CompileStackLeave(com, alist->num_arguments, 0);
18675dfecf96Smrg		lisp__data.env.head = lisp__data.env.length = base;
18685dfecf96Smrg		FORM_LEAVE();
18695dfecf96Smrg	    }
18705dfecf96Smrg	    else {
18715dfecf96Smrg		/* Not yet defined function/macro. */
18725dfecf96Smrg		++com->warnings;
18735dfecf96Smrg		LispWarning("call to undefined function %s", atom->string);
18745dfecf96Smrg		com_Funcall(com, function, arguments);
18755dfecf96Smrg	    }
18765dfecf96Smrg	    break;
18775dfecf96Smrg
18785dfecf96Smrg	case LispLambda_t:
18795dfecf96Smrg	    lambda = function->data.lambda.code;
18805dfecf96Smrg	    alist = (LispArgList*)function->data.lambda.name->data.opaque.data;
18815dfecf96Smrg	    ComInlineCall(com, alist, NIL, arguments, lambda->data.lambda.code);
18825dfecf96Smrg	    break;
18835dfecf96Smrg
18845dfecf96Smrg	case LispCons_t:
18855dfecf96Smrg	    if (CAR(function) == Olambda) {
18865dfecf96Smrg		function = EVAL(function);
18875dfecf96Smrg		if (LAMBDAP(function)) {
18885dfecf96Smrg		    GC_ENTER();
18895dfecf96Smrg
18905dfecf96Smrg		    GC_PROTECT(function);
18915dfecf96Smrg		    lambda = function->data.lambda.code;
18925dfecf96Smrg		    alist = (LispArgList*)function->data.lambda.name->data.opaque.data;
18935dfecf96Smrg		    ComInlineCall(com, alist, NIL, arguments, lambda->data.lambda.code);
18945dfecf96Smrg		    GC_LEAVE();
18955dfecf96Smrg		    break;
18965dfecf96Smrg		}
18975dfecf96Smrg	    }
18985dfecf96Smrg
18995dfecf96Smrg	default:
19005dfecf96Smrg	    /*  XXX If bytecode objects are made available, should
19015dfecf96Smrg	     * handle it here. */
19025dfecf96Smrg	    LispDestroy("EVAL: %s is invalid as a function",
19035dfecf96Smrg			STROBJ(function));
19045dfecf96Smrg	    /*NOTREACHED*/
19055dfecf96Smrg	    break;
19065dfecf96Smrg    }
19075dfecf96Smrg}
19085dfecf96Smrg
19095dfecf96Smrg/* Generate opcodes for an implicit PROGN */
19105dfecf96Smrgstatic void
19115dfecf96SmrgComProgn(LispCom *com, LispObj *code)
19125dfecf96Smrg{
19135dfecf96Smrg    if (CONSP(code)) {
19145dfecf96Smrg	for (; CONSP(code); code = CDR(code))
19155dfecf96Smrg	    ComEval(com, CAR(code));
19165dfecf96Smrg    }
19175dfecf96Smrg    else
19185dfecf96Smrg	/* If no code to execute, empty PROGN returns NIL */
19195dfecf96Smrg	com_Bytecode(com, XBC_NIL);
19205dfecf96Smrg}
19215dfecf96Smrg
19225dfecf96Smrg/* Generate opcodes to evaluate <object>. */
19235dfecf96Smrgstatic void
19245dfecf96SmrgComEval(LispCom *com, LispObj *object)
19255dfecf96Smrg{
19265dfecf96Smrg    int offset;
19275dfecf96Smrg    LispObj *form;
19285dfecf96Smrg
19295dfecf96Smrg    switch (OBJECT_TYPE(object)) {
19305dfecf96Smrg	case LispAtom_t:
19315dfecf96Smrg	    if (IN_TAGBODY())
19325dfecf96Smrg		ComLabel(com, object);
19335dfecf96Smrg	    else {
19345dfecf96Smrg		offset = ComGetVariable(com, object);
19355dfecf96Smrg		if (offset >= 0)
19365dfecf96Smrg		    /* Load from user stack at relative offset */
19375dfecf96Smrg		    com_Load(com, offset);
19385dfecf96Smrg		else if (offset == SYMBOL_KEYWORD)
19395dfecf96Smrg		    com_LoadCon(com, object);
19405dfecf96Smrg		else if (offset == SYMBOL_CONSTANT)
19415dfecf96Smrg		    /* Symbol defined as constant, just load it's value */
19425dfecf96Smrg		    com_LoadCon(com, LispGetVar(object));
19435dfecf96Smrg		else
19445dfecf96Smrg		    /* Load value bound to symbol at run time */
19455dfecf96Smrg		    com_LoadSym(com, object->data.atom);
19465dfecf96Smrg	    }
19475dfecf96Smrg	    break;
19485dfecf96Smrg
19495dfecf96Smrg	case LispCons_t: {
19505dfecf96Smrg	    /* Macro expansion may be done in the object form */
19515dfecf96Smrg	    form = com->form;
19525dfecf96Smrg	    com->form = object;
19535dfecf96Smrg	    ComFuncall(com, CAR(object), CDR(object), 1);
19545dfecf96Smrg	    com->form = form;
19555dfecf96Smrg	}   break;
19565dfecf96Smrg
19575dfecf96Smrg	case LispQuote_t:
19585dfecf96Smrg	    com_LoadCon(com, object->data.quote);
19595dfecf96Smrg	    break;
19605dfecf96Smrg
19615dfecf96Smrg	case LispBackquote_t:
19625dfecf96Smrg	    /* Macro expansion is stored in the current value of com->form */
19635dfecf96Smrg	    ComMacroBackquote(com, object);
19645dfecf96Smrg	    break;
19655dfecf96Smrg
19665dfecf96Smrg	case LispComma_t:
19675dfecf96Smrg	    LispDestroy("EVAL: comma outside of backquote");
19685dfecf96Smrg	    break;
19695dfecf96Smrg
19705dfecf96Smrg	case LispFunctionQuote_t:
19715dfecf96Smrg	    object = object->data.quote;
19725dfecf96Smrg	    if (SYMBOLP(object))
19735dfecf96Smrg		object = LispSymbolFunction(object);
19745dfecf96Smrg	    else if (CONSP(object) && CAR(object) == Olambda) {
19755dfecf96Smrg		/* object will only be associated with bytecode later,
19765dfecf96Smrg		 * so, make sure it is protected until compilation finishes */
19775dfecf96Smrg		object = EVAL(object);
19785dfecf96Smrg		RPLACD(com->plist, CONS(CAR(com->plist), CDR(com->plist)));
19795dfecf96Smrg		RPLACA(com->plist, object);
19805dfecf96Smrg	    }
19815dfecf96Smrg	    else
19825dfecf96Smrg		LispDestroy("FUNCTION: %s is not a function", STROBJ(object));
19835dfecf96Smrg	    com_LoadCon(com, object);
19845dfecf96Smrg	    break;
19855dfecf96Smrg
19865dfecf96Smrg	case LispFixnum_t:
19875dfecf96Smrg	    if (IN_TAGBODY()) {
19885dfecf96Smrg		ComLabel(com, object);
19895dfecf96Smrg		break;
19905dfecf96Smrg	    }
19915dfecf96Smrg	    /*FALLTROUGH*/
19925dfecf96Smrg
19935dfecf96Smrg	default:
19945dfecf96Smrg	    /* Constant object */
19955dfecf96Smrg	    com_LoadCon(com, object);
19965dfecf96Smrg	    break;
19975dfecf96Smrg    }
19985dfecf96Smrg}
19995dfecf96Smrg
20005dfecf96Smrg/***********************************************************************
20015dfecf96Smrg * Lambda expansion helper functions
20025dfecf96Smrg ***********************************************************************/
20035dfecf96Smrgstatic void
20045dfecf96SmrgComRecursiveCall(LispCom *com, LispArgList *alist,
20055dfecf96Smrg		 LispObj *name, LispObj *arguments)
20065dfecf96Smrg{
20075dfecf96Smrg    int base, lex;
20085dfecf96Smrg
20095dfecf96Smrg    /* Save state */
20105dfecf96Smrg    lex = lisp__data.env.lex;
20115dfecf96Smrg
20125dfecf96Smrg    FORM_ENTER();
20135dfecf96Smrg
20145dfecf96Smrg    /* Generate code to push function arguments in the stack */
20155dfecf96Smrg    base = ComCall(com, alist, name, arguments, 1, 0, 0);
20165dfecf96Smrg
20175dfecf96Smrg    /* Stack will grow this amount */
20185dfecf96Smrg    CompileStackEnter(com, alist->num_arguments, 0);
20195dfecf96Smrg
20205dfecf96Smrg#if 0
20215dfecf96Smrg    /* Make the variables available at run time */
20225dfecf96Smrg    com_Bind(com, alist->num_arguments);
20235dfecf96Smrg    com->block->bind += alist->num_arguments;
20245dfecf96Smrg#endif
20255dfecf96Smrg
20265dfecf96Smrg    com_BytecodeChar(com, XBC_LETREC, alist->num_arguments);
20275dfecf96Smrg
20285dfecf96Smrg#if 0
20295dfecf96Smrg    /* The variables are now unbound */
20305dfecf96Smrg    com_Unbind(com, alist->num_arguments);
20315dfecf96Smrg    com->block->bind -= alist->num_arguments;
20325dfecf96Smrg#endif
20335dfecf96Smrg
20345dfecf96Smrg    /* Stack length is reduced */
20355dfecf96Smrg    CompileStackLeave(com, alist->num_arguments, 0);
20365dfecf96Smrg    FORM_LEAVE();
20375dfecf96Smrg
20385dfecf96Smrg    /* Restore state */
20395dfecf96Smrg    lisp__data.env.lex = lex;
20405dfecf96Smrg    lisp__data.env.head = lisp__data.env.length = base;
20415dfecf96Smrg}
20425dfecf96Smrg
20435dfecf96Smrgstatic void
20445dfecf96SmrgComInlineCall(LispCom *com, LispArgList *alist,
20455dfecf96Smrg	      LispObj *name, LispObj *arguments, LispObj *lambda)
20465dfecf96Smrg{
20475dfecf96Smrg    int base, lex;
20485dfecf96Smrg
20495dfecf96Smrg    /* Save state */
20505dfecf96Smrg    lex = lisp__data.env.lex;
20515dfecf96Smrg
20525dfecf96Smrg    FORM_ENTER();
20535dfecf96Smrg    /* Start the inline function block */
20545dfecf96Smrg    CompileIniBlock(com, LispBlockClosure, name);
20555dfecf96Smrg
20565dfecf96Smrg    /* Generate code to push function arguments in the stack */
20575dfecf96Smrg    base = ComCall(com, alist, name, arguments, 1, 0, 0);
20585dfecf96Smrg
20595dfecf96Smrg    /* Stack will grow this amount */
20605dfecf96Smrg    CompileStackEnter(com, alist->num_arguments, 0);
20615dfecf96Smrg
20625dfecf96Smrg    /* Make the variables available at run time */
20635dfecf96Smrg    com_Bind(com, alist->num_arguments);
20645dfecf96Smrg    com->block->bind += alist->num_arguments;
20655dfecf96Smrg
20665dfecf96Smrg    /* Expand the lambda list */
20675dfecf96Smrg    ComProgn(com, lambda);
20685dfecf96Smrg
20695dfecf96Smrg    /* The variables are now unbound */
20705dfecf96Smrg    com_Unbind(com, alist->num_arguments);
20715dfecf96Smrg    com->block->bind -= alist->num_arguments;
20725dfecf96Smrg
20735dfecf96Smrg    /* Stack length is reduced */
20745dfecf96Smrg    CompileStackLeave(com, alist->num_arguments, 0);
20755dfecf96Smrg
20765dfecf96Smrg    /* Finish the inline function block */
20775dfecf96Smrg    CompileFiniBlock(com);
20785dfecf96Smrg    FORM_LEAVE();
20795dfecf96Smrg
20805dfecf96Smrg    /* Restore state */
20815dfecf96Smrg    lisp__data.env.lex = lex;
20825dfecf96Smrg    lisp__data.env.head = lisp__data.env.length = base;
20835dfecf96Smrg}
20845dfecf96Smrg
20855dfecf96Smrg/***********************************************************************
20865dfecf96Smrg * Macro expansion helper functions.
20875dfecf96Smrg ***********************************************************************/
20885dfecf96Smrgstatic LispObj *
20895dfecf96SmrgComMacroExpandBackquote(LispCom *com, LispObj *object)
20905dfecf96Smrg{
20915dfecf96Smrg    return (LispEvalBackquote(object->data.quote, 1));
20925dfecf96Smrg}
20935dfecf96Smrg
20945dfecf96Smrgstatic LispObj *
20955dfecf96SmrgComMacroExpandFuncall(LispCom *com, LispObj *function, LispObj *arguments)
20965dfecf96Smrg{
20975dfecf96Smrg    return (LispFuncall(function, arguments, 1));
20985dfecf96Smrg}
20995dfecf96Smrg
21005dfecf96Smrgstatic LispObj *
21015dfecf96SmrgComMacroExpandEval(LispCom *com, LispObj *object)
21025dfecf96Smrg{
21035dfecf96Smrg    LispObj *result;
21045dfecf96Smrg
21055dfecf96Smrg    switch (OBJECT_TYPE(object)) {
21065dfecf96Smrg	case LispAtom_t:
21075dfecf96Smrg	    result = LispGetVar(object);
21085dfecf96Smrg
21095dfecf96Smrg	    /* Macro expansion requires bounded symbols */
21105dfecf96Smrg	    if (result == NULL)
21115dfecf96Smrg		LispDestroy("EVAL: the variable %s is unbound",
21125dfecf96Smrg			    STROBJ(object));
21135dfecf96Smrg	    break;
21145dfecf96Smrg
21155dfecf96Smrg	case LispCons_t:
21165dfecf96Smrg	    result = ComMacroExpandFuncall(com, CAR(object), CDR(object));
21175dfecf96Smrg	    break;
21185dfecf96Smrg
21195dfecf96Smrg	case LispQuote_t:
21205dfecf96Smrg	    result = object->data.quote;
21215dfecf96Smrg	    break;
21225dfecf96Smrg
21235dfecf96Smrg	case LispBackquote_t:
21245dfecf96Smrg	    result = ComMacroExpandBackquote(com, object);
21255dfecf96Smrg	    break;
21265dfecf96Smrg
21275dfecf96Smrg	case LispComma_t:
21285dfecf96Smrg	    LispDestroy("EVAL: comma outside of backquote");
21295dfecf96Smrg
21305dfecf96Smrg	case LispFunctionQuote_t:
21315dfecf96Smrg	    result = EVAL(object);
21325dfecf96Smrg	    break;
21335dfecf96Smrg
21345dfecf96Smrg	default:
21355dfecf96Smrg	    result = object;
21365dfecf96Smrg	    break;
21375dfecf96Smrg    }
21385dfecf96Smrg
21395dfecf96Smrg    return (result);
21405dfecf96Smrg}
21415dfecf96Smrg
21425dfecf96Smrgstatic LispObj *
21435dfecf96SmrgComMacroExpand(LispCom *com, LispObj *lambda)
21445dfecf96Smrg{
21455dfecf96Smrg    LispObj *result, **presult = &result;
21465dfecf96Smrg    int jumped, *pjumped = &jumped, backquote, *pbackquote = &backquote;
21475dfecf96Smrg    LispBlock *block;
21485dfecf96Smrg
21495dfecf96Smrg    int interpreter_lex, interpreter_head, interpreter_base;
21505dfecf96Smrg
21515dfecf96Smrg    /* Save interpreter state */
21525dfecf96Smrg    interpreter_base = lisp__data.stack.length;
21535dfecf96Smrg    interpreter_head = lisp__data.env.length;
21545dfecf96Smrg    interpreter_lex = lisp__data.env.lex;
21555dfecf96Smrg
21565dfecf96Smrg    /* Use the variables */
21575dfecf96Smrg    *presult = NIL;
21585dfecf96Smrg    *pjumped = 1;
21595dfecf96Smrg    *pbackquote = !CONSP(lambda);
21605dfecf96Smrg
21615dfecf96Smrg    block = LispBeginBlock(NIL, LispBlockProtect);
21625dfecf96Smrg    if (setjmp(block->jmp) == 0) {
21635dfecf96Smrg	if (!backquote) {
21645dfecf96Smrg	    for (; CONSP(lambda); lambda = CDR(lambda))
21655dfecf96Smrg		result = ComMacroExpandEval(com, CAR(lambda));
21665dfecf96Smrg	}
21675dfecf96Smrg	else
21685dfecf96Smrg	    result = ComMacroExpandBackquote(com, lambda);
21695dfecf96Smrg
21705dfecf96Smrg	*pjumped = 0;
21715dfecf96Smrg    }
21725dfecf96Smrg    LispEndBlock(block);
21735dfecf96Smrg
21745dfecf96Smrg    /* If tried to jump out of the macro expansion block */
21755dfecf96Smrg    if (!lisp__data.destroyed && jumped)
21765dfecf96Smrg	LispDestroy("*** EVAL: bad jump in macro expansion");
21775dfecf96Smrg
21785dfecf96Smrg    /* Macro expansion did something wrong */
21795dfecf96Smrg    if (lisp__data.destroyed) {
21805dfecf96Smrg	LispMessage("*** EVAL: aborting macro expansion");
21815dfecf96Smrg	LispDestroy(".");
21825dfecf96Smrg    }
21835dfecf96Smrg
21845dfecf96Smrg    /* Restore interpreter state */
21855dfecf96Smrg    lisp__data.env.lex = interpreter_lex;
21865dfecf96Smrg    lisp__data.stack.length = interpreter_base;
21875dfecf96Smrg    lisp__data.env.head = lisp__data.env.length = interpreter_head;
21885dfecf96Smrg
21895dfecf96Smrg    return (result);
21905dfecf96Smrg}
21915dfecf96Smrg
21925dfecf96Smrgstatic void
21935dfecf96SmrgComMacroCall(LispCom *com, LispArgList *alist,
21945dfecf96Smrg	     LispObj *name, LispObj *lambda, LispObj *arguments)
21955dfecf96Smrg{
21965dfecf96Smrg    int base;
21975dfecf96Smrg    LispObj *body;
21985dfecf96Smrg
21995dfecf96Smrg    ++com->macro;
22005dfecf96Smrg    base = ComCall(com, alist, name, arguments, 0, 0, 0);
22015dfecf96Smrg    body = lambda->data.lambda.code;
22025dfecf96Smrg    body = ComMacroExpand(com, body);
22035dfecf96Smrg    --com->macro;
22045dfecf96Smrg    lisp__data.env.head = lisp__data.env.length = base;
22055dfecf96Smrg
22065dfecf96Smrg    /* Macro is expanded, store the result */
22075dfecf96Smrg    CAR(com->form) = body;
22085dfecf96Smrg    ComEval(com, body);
22095dfecf96Smrg}
22105dfecf96Smrg
22115dfecf96Smrgstatic void
22125dfecf96SmrgComMacroBackquote(LispCom *com, LispObj *lambda)
22135dfecf96Smrg{
22145dfecf96Smrg    LispObj *body;
22155dfecf96Smrg
22165dfecf96Smrg    ++com->macro;
22175dfecf96Smrg    body = ComMacroExpand(com, lambda);
22185dfecf96Smrg    --com->macro;
22195dfecf96Smrg
22205dfecf96Smrg    /* Macro is expanded, store the result */
22215dfecf96Smrg    CAR(com->form) = body;
22225dfecf96Smrg
22235dfecf96Smrg    com_LoadCon(com, body);
22245dfecf96Smrg}
2225