home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga MA Magazine 1998 #6
/
amigamamagazinepolishissue1998.iso
/
coders
/
jËzyki_programowania
/
clisp
/
src
/
archive
/
clisp.src.lha
/
src
/
control.d
< prev
next >
Wrap
Text File
|
1996-04-15
|
100KB
|
2,321 lines
# Special-Forms, Kontrollstrukturen, Evaluator-Nahes für CLISP
# Bruno Haible 22.4.1995
#include "lispbibl.c"
LISPFUN(exit,0,1,norest,nokey,0,NIL)
# (SYSTEM::%EXIT [errorp]) verläßt das System
{ var reg1 object errorp = STACK_0;
final_exitcode = ((eq(errorp,unbound) || nullp(errorp)) ? 0 : 1);
quit();
}
LISPSPECFORM(eval_when, 1,0,body)
# (EVAL-WHEN ({situation}) {form}), CLTL S. 69
{ var reg1 object situations = STACK_1; # Liste der Situationen
# Symbol EVAL oder Liste (NOT COMPILE) darin suchen:
while (consp(situations))
{ var reg2 object situation = Car(situations);
if (eq(situation,S(eval))) # Symbol EVAL gefunden?
goto found;
if (consp(situation) && eq(Car(situation),S(not)))
{ situation = Cdr(situation);
if (consp(situation) && nullp(Cdr(situation))
&& eq(Car(situation),S(compile)) # Liste (NOT COMPILE) gefunden?
)
goto found;
}
situations = Cdr(situations);
}
# Symbol EVAL nicht gefunden
value1 = NIL; mv_count=1; # Wert NIL
skipSTACK(2);
return;
found: # Symbol EVAL gefunden
{var reg2 object body = popSTACK();
skipSTACK(1);
implicit_progn(body,NIL); # body auswerten
return;
}}
LISPSPECFORM(quote, 1,0,nobody)
# (QUOTE object) == 'object, CLTL S. 86
{ value1 = popSTACK(); mv_count=1; } # Argument als Wert
# Fehlermeldung bei FUNCTION/FLET/LABELS, wenn kein Funktionssymbol vorliegt.
# > caller: Aufrufer, ein Symbol
# > obj: fehlerhaftes Funktionssymbol
nonreturning_function(local, fehler_funsymbol, (object caller, object obj));
local void fehler_funsymbol(caller,obj)
var reg2 object caller;
var reg1 object obj;
{ pushSTACK(obj);
pushSTACK(caller);
//: DEUTSCH "~: Funktionsname ~ ist kein Symbol."
//: ENGLISH "~: function name ~ should be a symbol"
//: FRANCAIS "~: Le nom de fonction ~ n'est pas un symbôle."
fehler(program_error,GETTEXT("~: function name ~ should be a symbol"));
}
LISPSPECFORM(function, 1,1,nobody)
# (FUNCTION funname), CLTL. S. 87
# entweder (FUNCTION symbol)
# oder (FUNCTION (LAMBDA . lambdabody))
# oder (FUNCTION name (LAMBDA . lambdabody))
{ var reg1 object funname; # Funktionsname (Symbol oder Lambdabody)
var reg2 object name; # Name (Symbol)
if (eq(STACK_0,unbound))
# 1 Argument
{ funname = STACK_1;
if (funnamep(funname))
# (FUNCTION symbol) - Syntax
{ # Symbol im aktuellen Funktions-Environment suchen:
var reg3 object fun = sym_function(funname,aktenv.fun_env);
# SUBR oder Closure oder Foreign-Function zurückgeben, sonst Fehler:
if (!(subrp(fun) || closurep(fun) || ffunctionp(fun)))
{ pushSTACK(funname); # Wert für Slot NAME von CELL-ERROR
pushSTACK(funname);
pushSTACK(S(function));
//: DEUTSCH "~: Die Funktion ~ ist nicht definiert."
//: ENGLISH "~: undefined function ~"
//: FRANCAIS "~: La fonction ~ n'est pas définie."
fehler(undefined_function,GETTEXT("~: undefined function ~"));
}
value1 = fun; mv_count=1; skipSTACK(2); return;
}
name = S(Klambda); # :LAMBDA als Default-Name
}
else
# 2 Argumente
{ name = STACK_1; # 1. Argument
if (!funnamep(name)) { fehler_funsymbol(S(function),name); }
funname = STACK_0; # 2. Argument, hoffentlich Lambdaausdruck
}
if (!(consp(funname) && eq(Car(funname),S(lambda)))) # Cons (LAMBDA . ...) ?
{ pushSTACK(funname);
pushSTACK(S(function));
//: DEUTSCH "~: ~ ist keine Funktionsbezeichnung."
//: ENGLISH "~: ~ is not a function name"
//: FRANCAIS "~: ~ n'est pas un nom de fonction."
fehler(program_error,GETTEXT("~: ~ is not a function name"));
}
# Lambdaausdruck
# im aktuellen Environment in eine Closure umwandeln:
value1 = get_closure(Cdr(funname),name,&aktenv); mv_count=1;
skipSTACK(2); return;
}
# Fehler, wenn ein Symbol keinen Wert hat.
# > symbol: Symbol
# > subr_self: Aufrufer (ein SUBR)
nonreturning_function(local, fehler_no_value, (object symbol));
local void fehler_no_value(symbol)
var reg1 object symbol;
{ pushSTACK(symbol); # Wert für Slot NAME von CELL-ERROR
pushSTACK(symbol);
pushSTACK(TheSubr(subr_self)->name);
//: DEUTSCH "~: ~ hat keinen dynamischen Wert."
//: ENGLISH "~: ~ has no dynamic value"
//: FRANCAIS "~: ~ n'a pas de valeur dynamique."
fehler(unbound_variable,GETTEXT("~: ~ has no dynamic value"));
}
LISPFUNN(psymbol_value,1)
# (SYS::%SYMBOL-VALUE symbol), CLTL S. 90
{ var reg1 object symbol = popSTACK();
if (!symbolp(symbol)) { fehler_symbol(symbol); }
{var reg2 object val = Symbol_value(symbol);
if (eq(val,unbound)) { fehler_no_value(symbol); }
value1 = val; mv_count=1;
}}
LISPFUNN(symbol_value,1)
# (SYMBOL-VALUE symbol), CLTL S. 90
{ var reg1 object symbol = popSTACK();
if (!symbolp(symbol)) { fehler_symbol(symbol); }
{var reg2 object val = Symbol_value(symbol);
if (eq(val,unbound)) { fehler_no_value(symbol); }
if (symbolmacrop(val)) # Symbol-Macro?
# ja -> expandieren und evaluieren:
{ eval_noenv(TheSymbolmacro(val)->symbolmacro_expansion); mv_count=1; }
else
{ value1 = val; mv_count=1; }
}}
# Fehlermeldung wegen undefinierter Funktion.
# fehler_undef_function(caller,symbol);
# > caller: Aufrufer (ein Symbol)
# > symbol: Symbol oder (SETF symbol)
nonreturning_function(global, fehler_undef_function, (object caller, object symbol));
global void fehler_undef_function(caller,symbol)
var reg2 object caller;
var reg1 object symbol;
{ pushSTACK(symbol); # Wert für Slot NAME von CELL-ERROR
pushSTACK(symbol);
pushSTACK(caller);
//: DEUTSCH "~: ~ hat keine globale Funktionsdefinition."
//: ENGLISH "~: ~ has no global function definition"
//: FRANCAIS "~: ~ n'as pas de définition de fonction globale."
fehler(undefined_function,GETTEXT("~: ~ has no global function definition"));
}
LISPFUNN(symbol_function,1)
# (SYMBOL-FUNCTION symbol), CLTL S. 90
{ var reg1 object symbol = popSTACK();
if (!symbolp(symbol)) { fehler_symbol(symbol); }
{var reg2 object val = Symbol_function(symbol);
if (eq(val,unbound)) { fehler_undef_function(S(symbol_value),symbol); }
value1 = val; mv_count=1;
}}
LISPFUNN(fdefinition,1)
# (FDEFINITION funname), CLTL2 S. 120
{ var reg3 object funname = popSTACK();
var reg1 object symbol = funname;
if (!funnamep(symbol)) { fehler_symbol(symbol); }
if (!symbolp(symbol))
{ symbol = get(Car(Cdr(symbol)),S(setf_function)); # (get ... 'SYS::SETF-FUNCTION)
if (!symbolp(symbol)) # sollte (uninterniertes) Symbol sein
{ fehler_undef_function(S(fdefinition),funname); } # sonst undefiniert
}
{var reg2 object val = Symbol_function(symbol);
if (eq(val,unbound))
{ fehler_undef_function(S(fdefinition),funname); }
value1 = val; mv_count=1;
}}
LISPFUNN(boundp,1)
# (BOUNDP symbol), CLTL S. 90
{ var reg1 object symbol = popSTACK();
if (!symbolp(symbol)) { fehler_symbol(symbol); }
value1 = (eq(Symbol_value(symbol),unbound) ? NIL : T); mv_count=1;
}
LISPFUNN(fboundp,1)
# (FBOUNDP symbol), CLTL S. 90, CLTL2 S. 120
{ var reg1 object symbol = popSTACK();
if (!funnamep(symbol)) { fehler_symbol(symbol); }
if (!symbolp(symbol))
{ symbol = get(Car(Cdr(symbol)),S(setf_function)); # (get ... 'SYS::SETF-FUNCTION)
if (!symbolp(symbol)) # sollte (uninterniertes) Symbol sein
goto undef; # sonst undefiniert
}
if (eq(Symbol_function(symbol),unbound))
{ undef: value1 = NIL; }
else
{ value1 = T; }
mv_count=1;
}
LISPFUNN(special_form_p,1)
# (SPECIAL-FORM-P symbol), CLTL S. 91
{ var reg1 object symbol = popSTACK();
if (!symbolp(symbol)) { fehler_symbol(symbol); }
{var reg2 object obj = Symbol_function(symbol);
value1 = (fsubrp(obj) ? T : NIL); mv_count=1;
}}
# Fehlermeldung bei Zuweisung, wenn ein Symbol eine Konstante ist.
# (Einer Konstante kann nicht zugewiesen werden.)
# fehler_symbol_constant(caller,symbol);
# > caller: Aufrufer (ein Symbol)
# > symbol: konstantes Symbol
nonreturning_function(local, fehler_symbol_constant, (object caller, object symbol));
local void fehler_symbol_constant(caller,symbol)
var reg2 object caller;
var reg1 object symbol;
{ pushSTACK(symbol);
pushSTACK(caller);
//: DEUTSCH "~: Der Konstanten ~ kann kein Wert zugewiesen werden."
//: ENGLISH "~: the value of the constant ~ may not be altered"
//: FRANCAIS "~: Aucune valeur ne peut être assignée à la constante ~."
fehler(error,GETTEXT("~: the value of the constant ~ may not be altered"));
}
# UP: überprüft den Body einer SETQ- oder PSETQ-Form.
# > caller: Aufrufer (ein Symbol)
# > STACK_0: Body
# < ergebnis: TRUE falls Symbol-Macros zu expandieren sind.
local boolean check_setq_body (object caller);
local boolean check_setq_body(caller)
var reg3 object caller;
{ var reg1 object body = STACK_0;
while (consp(body))
{ var reg2 object symbol = Car(body); # Variable
if (!symbolp(symbol)) { fehler_kein_symbol(caller,symbol); }
if (constantp(TheSymbol(symbol)))
{ fehler_symbol_constant(caller,symbol); }
if (sym_macrop(symbol))
{ return TRUE; }
body = Cdr(body);
if (atomp(body))
{ if (!nullp(body)) goto fehler_dotted;
# Der ganze Body noch in STACK_0.
pushSTACK(caller);
//: DEUTSCH "~ mit ungerader Anzahl von Argumenten: ~"
//: ENGLISH "~ called with odd number of arguments: ~"
//: FRANCAIS "~ appelé avec un nombre impair d'arguments : ~"
fehler(program_error,GETTEXT("~ called with odd number of arguments: ~"));
}
body = Cdr(body);
}
# body ist zu Ende.
if (!nullp(body))
{ fehler_dotted: # Der ganze Body noch in STACK_0.
pushSTACK(caller);
//: DEUTSCH "Dotted List als Argumentliste an ~ : ~"
//: ENGLISH "dotted list given to ~ : ~"
//: FRANCAIS "Liste pointée d'arguments fournie à ~ : ~"
fehler(program_error,GETTEXT("dotted list given to ~ : ~"));
}
return FALSE;
}
LISPSPECFORM(setq, 0,0,body)
# (SETQ {var form}), CLTL S. 91
{ if (check_setq_body(S(setq)))
{ var reg1 object form = allocate_cons();
Car(form) = S(setf); Cdr(form) = popSTACK(); # aus SETQ mache SETF
eval(form);
}
else
{ var reg1 object body = popSTACK();
if (consp(body))
{ do { var reg2 object symbol = Car(body); # Variable
body = Cdr(body);
pushSTACK(Cdr(body)); # Restliste retten
pushSTACK(symbol); # Symbol retten
eval(Car(body)); # nächste Form auswerten
symbol = popSTACK();
setq(symbol,value1); # Zuweisung durchführen
body = popSTACK();
}
while (consp(body));
# value1 ist noch das letzte Auswertungs-Ergebnis.
}
else
{ value1 = NIL; } # Defaultwert bei (SETQ)
mv_count=1;
} }
LISPSPECFORM(psetq, 0,0,body)
# (PSETQ {var form}), CLTL S. 92
{ if (check_setq_body(S(psetq)))
{ var reg1 object form = allocate_cons();
Car(form) = S(psetf); Cdr(form) = popSTACK(); # aus PSETQ mache PSETF
eval(form);
}
else
{ var reg1 object body = popSTACK();
var reg4 uintL body_length = llength(body)/2; # Anzahl der Paare (var form)
get_space_on_STACK(body_length*2*sizeof(object)); # Platz im STACK belegen
{ var reg2 uintL count;
dotimesL(count,body_length,
{ pushSTACK(Car(body)); # Variable auf den Stack
body = Cdr(body);
pushSTACK(Cdr(body)); # Restliche Liste auf den Stack
eval(Car(body)); # nächste Form auswerten
body = STACK_0;
STACK_0 = value1; # ihr Ergebnis in den Stack
});
}
{ var reg3 uintL count;
dotimesL(count,body_length,
{ var reg2 object val = popSTACK(); # Wert
var reg1 object sym = popSTACK(); # Symbol
setq(sym,val); # Zuweisung durchführen
});
}
value1 = NIL; mv_count=1; # Wert NIL
} }
LISPFUNN(set,2)
# (SET symbol value), CLTL S. 92
{ var reg1 object symbol = STACK_1;
if (!symbolp(symbol)) { fehler_symbol(symbol); }
if (constantp(TheSymbol(symbol))) # Konstante?
{ fehler_symbol_constant(S(set),symbol); }
if (sym_symbolmacrop(symbol)) # Symbol-Macro?
# Evaluiere `(SETF ,expansion (QUOTE ,value))
{ pushSTACK(S(setf));
pushSTACK(TheSymbolmacro(Symbol_value(symbol))->symbolmacro_expansion);
pushSTACK(S(quote)); pushSTACK(STACK_(0+3)); pushSTACK(listof(2));
eval_noenv(listof(3)); mv_count=1;
}
else
{ set_Symbol_value(symbol,STACK_0); value1 = STACK_0; mv_count=1; }
skipSTACK(2);
}
LISPFUNN(makunbound,1)
# (MAKUNBOUND symbol), CLTL S. 92
{ var reg1 object symbol = popSTACK();
if (!symbolp(symbol)) { fehler_symbol(symbol); }
if (constantp(TheSymbol(symbol)))
{ pushSTACK(symbol);
pushSTACK(S(makunbound));
//: DEUTSCH "~: Der Wert der Konstanten ~ muß erhalten bleiben."
//: ENGLISH "~: the value of the constant ~ must not be removed"
//: FRANCAIS "~: La valeur de la constante ~ doit être conservée."
fehler(error,GETTEXT("~: the value of the constant ~ must not be removed"));
}
set_Symbol_value(symbol,unbound);
value1 = symbol; mv_count=1;
}
LISPFUNN(fmakunbound,1)
# (FMAKUNBOUND symbol), CLTL S. 92, CLTL2 S. 123
{ var reg3 object funname = popSTACK();
var reg1 object symbol = funname;
if (!funnamep(symbol)) { fehler_symbol(symbol); }
if (!symbolp(symbol))
{ symbol = get(Car(Cdr(symbol)),S(setf_function)); # (get ... 'SYS::SETF-FUNCTION)
if (!symbolp(symbol)) # sollte (uninterniertes) Symbol sein
goto undef; # sonst undefiniert
}
{ var reg2 object obj = Symbol_function(symbol);
if (fsubrp(obj))
{ pushSTACK(symbol);
pushSTACK(S(fmakunbound));
//: DEUTSCH "~: Definition der Spezialform ~ darf nicht gelöscht werden."
//: ENGLISH "~: the special form definition of ~ must not be removed"
//: FRANCAIS "~: La définition de la forme spéciale ~ doit être conservée."
fehler(error,GETTEXT("~: the special form definition of ~ must not be removed"));
} }
Symbol_function(symbol) = unbound;
undef: value1 = funname; mv_count=1;
}
LISPFUN(apply,2,0,rest,nokey,0,NIL)
# (APPLY function {arg} arglist), CLTL S. 107
{ BEFORE(rest_args_pointer);
apply(Before(rest_args_pointer), # function
argcount, # Anzahl der {arg} auf dem Stack
popSTACK() # arglist
);
skipSTACK(1); # function aus dem Stack entfernen
}
LISPFUN(pfuncall,1,0,rest,nokey,0,NIL)
# (SYS::%FUNCALL function {arg})
{ funcall(Before(rest_args_pointer),argcount); skipSTACK(1); }
LISPFUN(funcall,1,0,rest,nokey,0,NIL)
# (FUNCALL function {arg}), CLTL S. 108
{ funcall(Before(rest_args_pointer),argcount); skipSTACK(1); }
LISPSPECFORM(progn, 0,0,body)
# (PROGN {form}), CLTL S. 109
{ implicit_progn(popSTACK(),NIL); }
# Macro: Wertet die Formen einer Formenliste aus.
# implicit_prog();
# > -(STACK): Formenliste
# erhöht STACK um 1
# kann GC auslösen
#define implicit_prog() \
{ while (mconsp(STACK_0)) \
{ var reg1 object forms = STACK_0; \
STACK_0 = Cdr(forms); \
eval(Car(forms)); # nächste Form evaluieren \
} \
skipSTACK(1); \
}
LISPSPECFORM(prog1, 1,0,body)
# (PROG1 form1 {form}), CLTL S. 109
{ STACK_1 = (eval(STACK_1),value1); # form1 evaluieren, Wert retten
implicit_prog();
value1 = popSTACK(); mv_count=1; # geretteten Wert zurückgeben
}
LISPSPECFORM(prog2, 2,0,body)
# (PROG2 form1 form2 {form}), CLTL S. 109
{ eval(STACK_2); # form1 evaluieren
eval(STACK_1); STACK_2 = value1; # form2 evaluieren, Wert retten
STACK_1 = STACK_0; skipSTACK(1);
implicit_prog();
value1 = popSTACK(); mv_count=1; # geretteten Wert zurückgeben
}
# Fehlermeldung wegen nicht erlaubter Docstrings
# fehler_docstring(caller,body);
# > caller: Aufrufer, ein Symbol
# > body: gesamter Body
nonreturning_function(local, fehler_docstring, (object caller, object body));
local void fehler_docstring(caller,body)
var reg1 object caller;
var reg2 object body;
{ pushSTACK(body);
pushSTACK(caller);
//: DEUTSCH "~: Doc-Strings sind nicht hier erlaubt: ~"
//: ENGLISH "~: doc-strings are not allowed here: ~"
//: FRANCAIS "~: Une chaîne de documentation n'est pas permise ici : ~"
fehler(program_error,GETTEXT("~: doc-strings are not allowed here: ~"));
}
# UP für LET, LET*, LOCALLY, MULTIPLE-VALUE-BIND, SYMBOL-MACROLET:
# Kompiliert die aktuelle Form und führt sie in kompiliertem Zustand aus.
# compile_form()
# > im STACK: EVAL-Frame mit der Form
# < mv_count/mv_space: Werte
# kann GC auslösen
local Values compile_eval_form (void);
local Values compile_eval_form()
{ # (SYS::COMPILE-FORM form venv fenv benv genv denv) ausführen:
# Die ganze Form aus dem EVAL-Frame im Stack holen:
pushSTACK(STACK_(frame_form)); # als 1. Argument
{var reg1 environment* stack_env = nest_aktenv(); # aktuelles Environment nesten, auf den STACK legen
#if !defined(STACK_UP)
var environment my_env;
my_env = *stack_env; # und hierher übertragen
skipSTACK(5); # und wieder vom STACK nehmen
pushSTACK(my_env.var_env); # 2. Argument
pushSTACK(my_env.fun_env); # 3. Argument
pushSTACK(my_env.block_env); # 4. Argument
pushSTACK(my_env.go_env); # 5. Argument
pushSTACK(my_env.decl_env); # 6. Argument
#endif
funcall(S(compile_form),6);
}# Die sich ergebende compilierte Closure mit 0 Argumenten aufrufen:
funcall(value1,0);
}
# UP für LET, LET*, LOCALLY, MULTIPLE-VALUE-BIND, SYMBOL-MACROLET:
# Analysiert die Variablen und Deklarationen, baut einen Variablenbindungs-
# Frame auf und erweitert VENV und evtl. auch DENV durch einen Frame.
# make_variable_frame(caller,varspecs,&bind_ptr,&bind_count)
# > object caller: Aufrufer, ein Symbol
# > object varspecs: Liste von Variablen-Specifiern
# > object value2: Liste von Declaration-Specifiern
# > object value1: Liste ({form}) von Formen
# < Stackaufbau: Variablenbindungsframe, Env-Bindungs-Frame, ({form}).
# < object* bind_ptr: Pointer über die erste "richtige" Bindung.
# < uintC bind_count: Anzahl der "richtigen" Bindungen.
# verändert STACK
# kann GC auslösen
local void make_variable_frame (object caller, object varspecs, object** bind_ptr_, uintC* bind_count_);
local void make_variable_frame(caller,varspecs,bind_ptr_,bind_count_)
var reg10 object caller;
var reg10 object varspecs;
var reg10 object** bind_ptr_;
var reg10 uintC* bind_count_;
{ var reg10 object declarations = value2;
# Variablenbindungs-Frame aufbauen:
{ var reg9 object* top_of_frame = STACK; # Pointer übern Frame
# zuerst die Special-deklarierten Variablen aus declarations
# im Stack ablegen:
var reg9 object* spec_pointer = args_end_pointer;
var reg8 uintL spec_anz = 0; # Anzahl der SPECIAL-Referenzen
{ var reg3 object declspecs = declarations;
while (consp(declspecs))
{ var reg1 object declspec = Car(declspecs); # nächste Deklaration
if (consp(declspec) && eq(Car(declspec),S(special))) # (SPECIAL ...) ?
{ while (consp( declspec = Cdr(declspec) ))
{ var reg2 object declsym = Car(declspec); # nächstes Special-deklariertes Item
if (!symbolp(declsym)) # sollte ein Symbol sein
{ pushSTACK(declsym);
pushSTACK(caller);
//: DEUTSCH "~: ~ ist kein Symbol, wurde aber als SPECIAL deklariert."
//: ENGLISH "~: ~ is not a symbol, but was declared SPECIAL"
//: FRANCAIS "~: ~ n'est pas un symbôle mais fut déclaré SPECIAL."
fehler(program_error,GETTEXT("~: ~ is not a symbol, but was declared SPECIAL"));
}
# Special-deklariertes Symbol im Stack ablegen:
pushSTACK(specdecl); # SPECDECL als "Wert"
pushSTACK_symbolwithflags(declsym,wbit(active_bit_o)); # Symbol aktiv
check_STACK();
spec_anz++;
} }
declspecs = Cdr(declspecs);
} }
*bind_ptr_ = args_end_pointer; # Pointer über erste "richtige" Bindung
# Dann die "richtigen" Variablenbindungen (jeweils die Variable
# und ihren unausgewerteten Init) im Stack ablegen:
{var reg7 uintL var_anz = 0; # Anzahl der Variablenbindungen
{ while (consp(varspecs))
{ var reg4 object varspec = Car(varspecs); # nächstes varspec
# in Symbol und Init aufspalten:
var reg5 object symbol;
var reg6 object init;
if (symbolp(varspec) && !eq(caller,S(symbol_macrolet))) # Symbol ?
{ symbol = varspec; init = unbound; }
elif # zweielementige Liste, mit Symbol als CAR ?
(consp(varspec)
&& (symbol = Car(varspec), varspec = Cdr(varspec),
symbolp(symbol) && consp(varspec) && nullp(Cdr(varspec))
) )
{ init = Car(varspec); }
else
{ pushSTACK(Car(varspecs));
pushSTACK(caller);
//: DEUTSCH "~: ~ ist keine korrekte Variablenspezifikation."
//: ENGLISH "~: illegal variable specification ~"
//: FRANCAIS "~: ~ n'est pas une spécification de variable licite."
fehler(program_error,GETTEXT("~: illegal variable specification ~"));
}
pushSTACK(init); # Init und
pushSTACK_symbolwithflags(symbol,0); # Variable ablegen
check_STACK();
# feststellen, ob statische oder dynamische Bindung:
if (!special_var_p(TheSymbol(symbol)) || eq(caller,S(symbol_macrolet)))
{ # Variable unter den Special-deklarierten?
#ifdef NO_symbolflags
var reg1 object* ptr = spec_pointer;
var reg2 uintL count;
dotimesL(count,spec_anz,
{ NEXT(ptr);
if (eq(NEXT(ptr),symbol))
{ if (eq(NEXT(ptr),fixnum(bit(active_bit)))) goto dynamic; }
else
{ NEXT(ptr); }
});
#else
var reg3 object to_compare = as_object(as_oint(symbol) | wbit(active_bit_o));
var reg1 object* ptr = spec_pointer;
var reg2 uintL count;
dotimesL(count,spec_anz,
{ NEXT(ptr);
if (eq(NEXT(ptr),to_compare))
goto dynamic;
});
#endif
# Nein -> statische Bindung
}
else
{ # dynamisch binden
if (FALSE)
{ dynamic:
if (eq(caller,S(symbol_macrolet)))
{ pushSTACK(symbol);
pushSTACK(caller);
//: DEUTSCH "~: Symbol ~ darf nicht gleichzeitig SPECIAL und Makro deklariert werden."
//: ENGLISH "~: symbol ~ must not be declared SPECIAL and a macro at the same time"
//: FRANCAIS "~ : Le symbole ~ ne peut être déclaré SPECIAL et macro en même temps."
fehler(program_error,GETTEXT("~: symbol ~ must not be declared SPECIAL and a macro at the same time"));
} }
*(oint*)(&STACK_0) |= wbit(dynam_bit_o);
}
varspecs = Cdr(varspecs);
var_anz++;
} }
*bind_count_ = var_anz;
var_anz += spec_anz; # Gesamtzahl Symbol/Wert-Paare
#ifndef UNIX_DEC_ULTRIX_GCCBUG
if (var_anz > (uintC)(~(uintC)0)) # paßt es in ein uintC ?
{ pushSTACK(caller);
//: DEUTSCH "~: Zuviele Variablen und/oder Deklarationen."
//: ENGLISH "~: too many variables and/or declarations"
//: FRANCAIS "~: Trop de déclarations et/ou de variables."
fehler(program_error,GETTEXT("~: too many variables and/or declarations"));
}
#endif
pushSTACK(aktenv.var_env); # aktuelles VAR_ENV als NEXT_ENV
pushSTACK(as_object(var_anz)); # Anzahl Bindungen
finish_frame(VAR);
}}
# Der Variablenbindungsframe ist jetzt fertig.
{var reg5 object* var_frame_ptr = STACK; # Pointer auf Variablenbindungs-Frame
# VENV-Bindungsframe aufbauen:
{ var reg4 object* top_of_frame = STACK; # Pointer übern Frame
# Zuerst DENV um die nötigen declspecs erweitern:
var reg3 object denv = aktenv.decl_env;
pushSTACK(value1); # ({form}) retten
pushSTACK(declarations);
while (mconsp(STACK_0))
{ var reg2 object declspecs = STACK_0;
STACK_0 = Cdr(declspecs);
{var reg1 object declspec = Car(declspecs); # nächstes Declspec
if (consp(declspec)) # sollte ein Cons sein
{ if (!eq(Car(declspec),S(special))) # (SPECIAL ...) haben wir schon behandelt
{ denv = augment_decl_env(declspec,denv); } # alles andere behandeln
}} }
skipSTACK(1);
{var reg1 object forms = popSTACK();
# Nun den Frame bauen:
if (eq(denv,aktenv.decl_env))
{ pushSTACK(aktenv.var_env);
finish_frame(ENV1V);
}
else
{ pushSTACK(aktenv.decl_env);
pushSTACK(aktenv.var_env);
finish_frame(ENV2VD);
aktenv.decl_env = denv;
}
# VENV-Bindungsframe ist fertig.
aktenv.var_env = make_framepointer(var_frame_ptr); # Pointer auf Variablenbindungsframe
pushSTACK(forms);
}}}}
LISPSPECFORM(let, 1,0,body)
# (LET ({varspec}) {decl} {form}), CLTL S. 110
{ # {decl} {form} trennen:
var reg6 boolean to_compile = parse_dd(STACK_0,aktenv.var_env,aktenv.fun_env); # unvollständiges var_env??
# bitte kein Docstring:
if (!nullp(value3)) { fehler_docstring(S(let),STACK_0); }
if (to_compile) # Deklaration (COMPILE) ?
# ja -> Form kompilieren:
{ skipSTACK(2); return_Values compile_eval_form(); }
else
{ skipSTACK(1);
# Variablenbindungsframe aufbauen, VAR_ENV erweitern:
{var object* bind_ptr;
var uintC bind_count;
make_variable_frame(S(let),popSTACK(),&bind_ptr,&bind_count);
# Dann die Initialisierungsformen auswerten:
{ var reg3 object* frame_pointer = bind_ptr;
var reg4 uintC count;
dotimesC(count,bind_count,
{ var reg1 object* initptr = &NEXT(frame_pointer);
var reg2 object init = *initptr; # nächstes Init
*initptr = (eq(init,unbound) ? NIL : (eval(init),value1)); # auswerten, NIL als Default
frame_pointer skipSTACKop -(varframe_binding_size-1);
});
}
# Dann die Bindungen aktivieren:
{ var reg4 object* frame_pointer = bind_ptr;
var reg5 uintC count;
dotimesC(count,bind_count,
{ frame_pointer skipSTACKop -varframe_binding_size;
{var reg1 object* markptr = &Before(frame_pointer);
if (*(oint*)(markptr) & wbit(dynam_bit_o)) # Bindung dynamisch?
{ var reg2 object symbol = *(markptr STACKop varframe_binding_sym); # Variable
var reg3 object newval = *(markptr STACKop varframe_binding_value); # neuer Wert
*(markptr STACKop varframe_binding_value) = Symbolflagged_value(symbol); # alten Wert im Frame sichern
*(oint*)(markptr) |= wbit(active_bit_o); # Bindung aktivieren
set_Symbolflagged_value_on(symbol,newval,markptr); # neuer Wert
}
else
{ *(oint*)(markptr) |= wbit(active_bit_o); } # Bindung aktivieren
}});
}
# Body abinterpretieren:
implicit_progn(popSTACK(),NIL);
# Frames auflösen:
unwind(); # VENV-Bindungsframe auflösen
unwind(); # Variablenbindungs-Frame auflösen
} }}
LISPSPECFORM(letstern, 1,0,body)
# (LET* ({varspec}) {decl} {form}), CLTL S. 111
{ # {decl} {form} trennen:
var reg7 boolean to_compile = parse_dd(STACK_0,aktenv.var_env,aktenv.fun_env); # unvollständiges var_env??
# bitte kein Docstring:
if (!nullp(value3)) { fehler_docstring(S(letstern),STACK_0); }
if (to_compile) # Deklaration (COMPILE) ?
# ja -> Form kompilieren:
{ skipSTACK(2); return_Values compile_eval_form(); }
else
{ skipSTACK(1);
# Variablenbindungsframe aufbauen, VAR_ENV erweitern:
{var object* bind_ptr;
var uintC bind_count;
make_variable_frame(S(letstern),popSTACK(),&bind_ptr,&bind_count);
# Dann die Initialisierungsformen auswerten und die Bindungen aktivieren:
{ var reg5 object* frame_pointer = bind_ptr;
var reg6 uintC count;
dotimesC(count,bind_count,
{ var reg2 object* initptr = &Next(frame_pointer);
frame_pointer skipSTACKop -varframe_binding_size;
{var reg1 object* markptr = &Before(frame_pointer);
var reg4 object init = *initptr; # nächstes Init
var reg4 object newval = (eq(init,unbound) ? NIL : (eval(init),value1)); # auswerten, NIL als Default
if (*(oint*)(markptr) & wbit(dynam_bit_o)) # Bindung dynamisch?
{ var reg3 object symbol = *(markptr STACKop varframe_binding_sym); # Variable
*initptr = Symbolflagged_value(symbol); # alten Wert im Frame sichern
*(oint*)(markptr) |= wbit(active_bit_o); # Bindung aktivieren
set_Symbolflagged_value_on(symbol,newval,markptr); # neuer Wert
}
else
{ *initptr = newval; # neuen Wert in den Frame
*(oint*)(markptr) |= wbit(active_bit_o); # Bindung aktivieren
}
}});
}
# Body abinterpretieren:
implicit_progn(popSTACK(),NIL);
# Frames auflösen:
unwind(); # VENV-Bindungsframe auflösen
unwind(); # Variablenbindungs-Frame auflösen
} }}
LISPSPECFORM(locally, 0,0,body)
# (LOCALLY {decl} {form}), CLTL2 S. 221
{ # {decl} {form} trennen:
var reg1 boolean to_compile = parse_dd(STACK_0,aktenv.var_env,aktenv.fun_env); # unvollständiges var_env??
# bitte kein Docstring:
if (!nullp(value3)) { fehler_docstring(S(locally),STACK_0); }
skipSTACK(1);
if (to_compile) # Deklaration (COMPILE) ?
# ja -> Form kompilieren:
{ return_Values compile_eval_form(); }
else
{ # Variablenbindungsframe aufbauen, VAR_ENV erweitern:
var object* bind_ptr;
var uintC bind_count;
make_variable_frame(S(locally),NIL,&bind_ptr,&bind_count);
# Body abinterpretieren:
implicit_progn(popSTACK(),NIL);
# Frames auflösen:
unwind(); # VENV-Bindungsframe auflösen
unwind(); # Variablenbindungs-Frame auflösen
} }
LISPSPECFORM(compiler_let, 1,0,body)
# (COMPILER-LET ({varspec}) {form}), CLTL S. 112
{ var reg5 object* varspecs_ = &STACK_1;
var reg3 object varspecs = *varspecs_; # Liste der Variablen
var reg7 uintL varcount = llength(varspecs); # Anzahl der Variablen
get_space_on_STACK(varcount*3*sizeof(object)); # Platz auf dem STACK verlangen
# varspecs evaluieren:
{var reg6 object* val_pointer = args_end_pointer; # Pointer über die Werte
while (consp(varspecs))
{ var reg1 object varspec = Car(varspecs);
var reg2 object symbol;
if (consp(varspec))
# varspec ist ein Cons
{ symbol = Car(varspec);
varspec = Cdr(varspec);
if (!(consp(varspec) && nullp(Cdr(varspec))))
{ pushSTACK(Car(varspecs));
pushSTACK(S(compiler_let));
//: DEUTSCH "~: ~ ist keine korrekte Variablenspezifikation."
//: ENGLISH "~: illegal variable specification ~"
//: FRANCAIS "~: ~ n'est pas une spécification de variable licite."
fehler(program_error,GETTEXT("~: illegal variable specification ~"));
}
# symbol sollte ein nichtkonstantes Symbol sein:
if (!symbolp(symbol))
{ fehler_symbol:
fehler_kein_symbol(S(compiler_let),symbol);
}
if (constantp(TheSymbol(symbol)))
{ fehler_constant:
pushSTACK(symbol);
pushSTACK(S(compiler_let));
//: DEUTSCH "~: ~ ist eine Konstante und kann nicht dynamisch gebunden werden."
//: ENGLISH "~: ~ is a constant, cannot be bound"
//: FRANCAIS "~: ~ est une constante et ne peut pas être liée."
fehler(error,GETTEXT("~: ~ is a constant, cannot be bound"));
}
pushSTACK(Cdr(varspecs));
eval_noenv(Car(varspec)); # zweites Listenelement auswerten
varspecs = STACK_0;
STACK_0 = value1; # und in den Stack
}
else
{ symbol = varspec;
if (!symbolp(symbol)) goto fehler_symbol;
if (constantp(TheSymbol(symbol))) goto fehler_constant;
pushSTACK(NIL); # NIL als Wert in den Stack
varspecs = Cdr(varspecs);
} }
varspecs = *varspecs_;
# Frame aufbauen:
{ var reg4 object* top_of_frame = STACK; # Pointer übern Frame
while (consp(varspecs))
{ var reg1 object varspec = Car(varspecs);
if (consp(varspec)) { varspec = Car(varspec); }
pushSTACK(Symbol_symvalue(varspec)); # alter Wert der Variablen
pushSTACK(varspec); # Variable
varspecs = Cdr(varspecs);
}
finish_frame(DYNBIND);
}
# Frame fertig aufgebaut, nun die Werte der Variablen verändern:
varspecs = *varspecs_;
{ var reg2 object* valptr = val_pointer;
while (consp(varspecs))
{ var reg1 object varspec = Car(varspecs);
if (consp(varspec)) { varspec = Car(varspec); }
set_Symbol_symvalue(varspec,NEXT(valptr)); # neuen Wert der Variablen zuweisen
varspecs = Cdr(varspecs);
} }
#ifdef DYNBIND_LIST
add_frame_to_binding_list(&STACK_0);
#endif
# Nun die Formen evaluieren:
implicit_progn(*(varspecs_ STACKop -1),NIL);
# Bindungsframe auflösen:
unwind();
# Stack aufräumen:
set_args_end_pointer(val_pointer);
skipSTACK(2);
}}
LISPSPECFORM(progv, 2,0,body)
# (PROGV symbollist valuelist {form}), CLTL S. 112
{ STACK_2 = (eval(STACK_2),value1); # Symbolliste auswerten
{var reg4 object valuelist = (eval(STACK_1),value1); # Wertliste auswerten
var reg3 object body = popSTACK();
var reg2 object *body_ptr;
var reg1 object varlist;
skipSTACK(1);
varlist=popSTACK();
pushSTACK(body);
body_ptr = &STACK_0;
progv(varlist,valuelist); # Frame aufbauen
implicit_progn(*body_ptr,NIL); # body auswerten
unwind(); # Frame auflösen
skipSTACK(1);
}}
# Fehlermeldung bei FLET/LABELS, wenn keine Funktionsspezifikation vorliegt.
# > caller: Aufrufer, ein Symbol
# > obj: fehlerhafte Funktionsspezifikation
nonreturning_function(local, fehler_funspec, (object caller, object obj));
local void fehler_funspec(caller,obj)
var reg2 object caller;
var reg1 object obj;
{ pushSTACK(obj);
pushSTACK(caller);
//: DEUTSCH "~: ~ ist keine Funktionsspezifikation."
//: ENGLISH "~: ~ is not a function specification"
//: FRANCAIS "~: ~ n'est pas une spécification de fonction."
fehler(program_error,GETTEXT("~: ~ is not a function specification"));
}
# UP: Beendet ein FLET/MACROLET.
# finish_flet(top_of_frame,body);
# > Stackaufbau: [top_of_frame] def1 name1 ... defn namen [STACK]
# > top_of_frame: Pointer übern Frame
# > body: Formenliste
# < mv_count/mv_space: Werte
# kann GC auslösen
local Values finish_flet (object* top_of_frame, object body);
local Values finish_flet(top_of_frame,body)
var reg2 object* top_of_frame;
var reg3 object body;
{{var reg1 uintL bindcount = # Anzahl der Bindungen
STACK_item_count(STACK,top_of_frame) / 2;
pushSTACK(aktenv.fun_env); # aktuelles FUN_ENV als NEXT_ENV
pushSTACK(as_object(bindcount));
finish_frame(FUN);
}# Funktionsbindungsframe ist fertig.
# FENV-Bindungsframe bauen:
{var reg1 object* top_of_frame = STACK; # Pointer übern Frame
pushSTACK(aktenv.fun_env);
finish_frame(ENV1F);
# FENV-Bindungsframe ist fertig.
# FUN_ENV erweitern:
# top_of_frame = Pointer auf den Funktionsbindungsframe
aktenv.fun_env = make_framepointer(top_of_frame);
}# Formen ausführen:
implicit_progn(body,NIL);
unwind(); # FENV-Bindungsframe auflösen
unwind(); # Funktionsbindungsframe auflösen
}
LISPSPECFORM(flet, 1,0,body)
# (FLET ({funspec}) {form}), CLTL S. 113
{ var reg5 object body = popSTACK(); # ({form})
var reg1 object funspecs = popSTACK(); # ({funspec})
# Funktionsbindungs-Frame aufbauen:
var reg6 object* top_of_frame = STACK; # Pointer übern Frame
while (consp(funspecs))
{ pushSTACK(body); # Formenliste retten
pushSTACK(Cdr(funspecs)); # restliche funspecs
funspecs = Car(funspecs); # nächstes funspec = (name . lambdabody)
# sollte ein Cons sein, dessen CAR ein Symbol und dessen CDR ein Cons ist:
if (!consp(funspecs)) { fehler_spec: fehler_funspec(S(flet),funspecs); }
{var reg2 object name = Car(funspecs);
var reg3 object lambdabody = Cdr(funspecs);
if (!funnamep(name)) { fehler_funsymbol(S(flet),name); }
if (!consp(lambdabody)) { goto fehler_spec; }
pushSTACK(name); # name retten
# lambdabody zu einer Closure machen:
{var reg4 object fun = get_closure(lambdabody,name,&aktenv);
name = popSTACK();
funspecs = popSTACK(); # restliche funspecs
body = popSTACK();
# in den Frame:
pushSTACK(fun); # als "Wert" die Closure
pushSTACK(name); # Name, Bindung ist automatisch aktiv
}}}
return_Values finish_flet(top_of_frame,body);
}
LISPSPECFORM(labels, 1,0,body)
# (LABELS ({funspec}) {form}), CLTL S. 113
{ # Auf den Aufbau eines Funktionsbindungs-Frames kann hier verzichtet werden,
# weil bei der Bildung der ersten Closure sowieso das Environment genestet
# und dabei dieser Funktionsbindungs-Frame in einen Vektor geschrieben würde.
# aktuelles FUN_ENV nesten:
pushSTACK(nest_fun(aktenv.fun_env));
# Anzahl der funspecs bestimmen und Syntax abtesten:
{var reg6 uintL veclength = 1; # = 2 * (Anzahl der funspecs) + 1
{ var reg2 object funspecs = STACK_(1+1);
while (consp(funspecs))
{ var reg1 object funspec = Car(funspecs);
# sollte ein Cons sein, dessen CAR ein Symbol und dessen CDR ein Cons ist:
if (!consp(funspec)) { fehler_spec: fehler_funspec(S(labels),funspec); }
{var reg3 object name = Car(funspec);
var reg4 object lambdabody = Cdr(funspec);
if (!funnamep(name)) { fehler_funsymbol(S(labels),name); }
if (!consp(lambdabody)) { goto fehler_spec; }
}
funspecs = Cdr(funspecs);
veclength += 2;
} }
# Vektor passender Länge allozieren und darin die Namen eintragen:
{var reg7 object vec = allocate_vector(veclength);
{var reg2 object* ptr = &TheSvector(vec)->data[0];
var reg1 object funspecs = STACK_(1+1);
while (consp(funspecs))
{ *ptr++ = Car(Car(funspecs)); # nächster name
ptr++; # Funktion bleibt vorerst NIL
funspecs = Cdr(funspecs);
}
*ptr++ = popSTACK(); # genestetes FUN_ENV als letztes Vektor-Element
}
{var reg5 object body = popSTACK(); # Formenliste
var reg2 object funspecs = popSTACK();
# FENV-Bindungsframe aufbauen:
{ var reg1 object* top_of_frame = STACK; # Pointer übern Frame
pushSTACK(aktenv.fun_env);
finish_frame(ENV1F);
}
# FUN_ENV erweitern:
aktenv.fun_env = vec;
# Closures erzeugen und in den Vektor stecken:
pushSTACK(body);
pushSTACK(vec);
{var reg4 uintL index = 1; # Index in den Vektor
while (consp(funspecs))
{ pushSTACK(Cdr(funspecs)); # restliche funspecs
{var reg1 object funspec = Car(funspecs);
# Closure erzeugen:
var reg3 object fun = get_closure(Cdr(funspec),Car(funspec),&aktenv);
funspecs = popSTACK();
TheSvector(STACK_0)->data[index] = fun; # in den Vektor stecken
index += 2;
} }}
skipSTACK(1); # Vektor vergessen
body = popSTACK();
# Formen ausführen:
implicit_progn(body,NIL);
unwind(); # FENV-Bindungsframe auflösen
}}}}
LISPSPECFORM(macrolet, 1,0,body)
# (MACROLET ({macrodef}) {form}), CLTL S. 113
{ var reg2 object body = popSTACK(); # ({form})
var reg1 object macrodefs = popSTACK(); # ({macrodef})
# Macrobindungs-Frame aufbauen:
var reg4 object* top_of_frame = STACK; # Pointer übern Frame
while (consp(macrodefs))
{ pushSTACK(body); # Formenliste retten
pushSTACK(Cdr(macrodefs)); # restliche macrodefs
macrodefs = Car(macrodefs); # nächstes macrodef = (name . lambdabody)
# sollte ein Cons sein, dessen CAR ein Symbol und dessen CDR ein Cons ist:
if (!consp(macrodefs))
{ fehler_spec:
pushSTACK(macrodefs);
pushSTACK(S(macrolet));
//: DEUTSCH "~: ~ ist keine Macro-Spezifikation."
//: ENGLISH "~: ~ is not a macro specification"
//: FRANCAIS "~: ~ n'est pas une spécification de macro."
fehler(program_error,GETTEXT("~: ~ is not a macro specification"));
}
{var reg3 object name = Car(macrodefs);
if (!symbolp(name))
{ pushSTACK(name);
pushSTACK(S(macrolet));
//: DEUTSCH "~: Macro-Name ~ ist kein Symbol."
//: ENGLISH "~: macro name ~ should be a symbol"
//: FRANCAIS "~: Le nom de macro ~ n'est pas un symbôle."
fehler(program_error,GETTEXT("~: macro name ~ should be a symbol"));
}
if (!mconsp(Cdr(macrodefs))) { goto fehler_spec; }
pushSTACK(name); # name retten
# Macro-Expander bauen: (SYSTEM::MAKE-MACRO-EXPANDERCONS macrodef)
pushSTACK(macrodefs); funcall(S(make_macro_expandercons),1);
name = popSTACK();
macrodefs = popSTACK(); # restliche macrodefs
body = popSTACK();
# in den Frame:
pushSTACK(value1); # als "Wert" das Cons mit dem Expander
pushSTACK(name); # Name, Bindung ist automatisch aktiv
}}
return_Values finish_flet(top_of_frame,body);
}
LISPSPECFORM(symbol_macrolet, 1,0,body)
# (SYMBOL-MACROLET ({(var expansion)}) {decl} {form}), CLTL2 S. 155
{ # {decl} {form} trennen:
var reg5 boolean to_compile = parse_dd(STACK_0,aktenv.var_env,aktenv.fun_env); # unvollständiges var_env??
# bitte kein Docstring:
if (!nullp(value3)) { fehler_docstring(S(symbol_macrolet),STACK_0); }
if (to_compile) # Deklaration (COMPILE) ?
# ja -> Form kompilieren:
{ skipSTACK(2); return_Values compile_eval_form(); }
else
{ skipSTACK(1);
# Variablenbindungsframe aufbauen, VAR_ENV erweitern:
{var object* bind_ptr;
var uintC bind_count;
make_variable_frame(S(symbol_macrolet),popSTACK(),&bind_ptr,&bind_count);
# Dann die Symbol-Macros bilden und die Bindungen aktivieren:
{ var reg3 object* frame_pointer = bind_ptr;
var reg4 uintC count;
dotimesC(count,bind_count,
{ var reg1 object* initptr = &NEXT(frame_pointer);
var reg2 object sm = allocate_symbolmacro();
TheSymbolmacro(sm)->symbolmacro_expansion = *initptr;
*initptr = sm;
frame_pointer skipSTACKop -(varframe_binding_size-1);
*(oint*)(&Before(frame_pointer)) |= wbit(active_bit_o);
});
}
# Body abinterpretieren:
implicit_progn(popSTACK(),NIL);
# Frames auflösen:
unwind(); # VENV-Bindungsframe auflösen
unwind(); # Variablenbindungs-Frame auflösen
} }}
LISPSPECFORM(if, 2,1,nobody)
# (IF test form1 [form2]), CLTL S. 115
{ eval(STACK_2); # Bedingung auswerten
{var reg1 object form;
if (!nullp(value1))
{ form = STACK_1; skipSTACK(3); } # form1 auswerten
else
{ form = STACK_0; skipSTACK(3); # form2 auswerten
if (eq(form,unbound))
{ value1 = NIL; mv_count=1; return; } # keine angegeben -> NIL
}
eval(form);
}}
LISPSPECFORM(when, 1,0,body)
# (WHEN test {form}), CLTL S. 115
{ eval(STACK_1); # Bedingung auswerten
if (!nullp(value1))
{ var reg1 object body = STACK_0;
skipSTACK(2);
implicit_progn(body,NIL);
}
else
{ skipSTACK(2);
value1 = NIL; mv_count=1;
}
}
LISPSPECFORM(unless, 1,0,body)
# (UNLESS test {form}), CLTL S. 115
{ eval(STACK_1); # Bedingung auswerten
if (nullp(value1))
{ var reg1 object body = STACK_0;
skipSTACK(2);
implicit_progn(body,NIL);
}
else
{ skipSTACK(2);
value1 = NIL; mv_count=1;
}
}
LISPSPECFORM(cond, 0,0,body)
# (COND {(bed {form})}), CLTL S. 116
{ while (mconsp(STACK_0))
{ var reg1 object clause = STACK_0; # Klausel-Liste
STACK_0 = Cdr(clause); # restliche Klauseln retten
clause = Car(clause); # nächste Klausel
if (!consp(clause)) # sollte ein Cons sein
{ pushSTACK(clause);
pushSTACK(S(cond));
//: DEUTSCH "~: Klausel ~ muß Liste sein."
//: ENGLISH "~: clause ~ should be a list"
//: FRANCAIS "~: La clause ~ doit être une liste."
fehler(program_error,GETTEXT("~: clause ~ should be a list"));
}
pushSTACK(Cdr(clause)); # Klausel-Rest retten
eval(Car(clause)); # Bedingung auswerten
if (!nullp(value1)) goto eval_clause;
skipSTACK(1); # nächste probieren
}
# keine Bedingung war erfüllt.
skipSTACK(1); value1 = NIL; mv_count=1; return;
# erfüllte Bedingung gefunden:
eval_clause:
{var reg1 object clause_rest = popSTACK(); # Klausel-Rest
skipSTACK(1);
implicit_progn(clause_rest,value1); # auswerten
}}
LISPSPECFORM(block, 1,0,body)
# (BLOCK name {form}), CLTL S. 119
{ var reg9 object body = popSTACK();
var reg9 object name = popSTACK();
if (!symbolp(name)) { fehler_symbol(name); }
{var jmp_buf returner; # Rücksprungpunkt
# Block-Frame aufbauen:
{ var reg1 object* top_of_frame = STACK; # Pointer übern Frame
pushSTACK(name); # Block-Name
pushSTACK(aktenv.block_env); # aktuelles BLOCK_ENV als NEXT_ENV
finish_entry_frame(IBLOCK,&!returner,_EMA_, goto block_return; );
}
# BENV-Frame aufbauen:
{var reg1 object* top_of_frame = STACK;
pushSTACK(aktenv.block_env);
finish_frame(ENV1B);
# BLOCK_ENV erweitern (top_of_frame = Pointer auf den Block-Frame)
aktenv.block_env = make_framepointer(top_of_frame);
}
# Body ausführen:
implicit_progn(body,NIL);
unwind(); # BENV-Bindungsframe auflösen
block_return: # Hierher wird gesprungen, wenn der BLOCK-Frame einen
# RETURN-FROM gefangen hat.
unwind(); # BLOCK-Frame auflösen
}}
# Fehler, wenn ein Block bereits verlassen wurde.
# fehler_block_left(name);
# > name: Block-Name
nonreturning_function(global, fehler_block_left, (object name));
global void fehler_block_left(name)
var reg1 object name;
{ pushSTACK(name);
pushSTACK(S(return_from));
//: DEUTSCH "~: Der Block mit Namen ~ wurde bereits verlassen."
//: ENGLISH "~: the block named ~ has already been left"
//: FRANCAIS "~: Le bloc de nom ~ a déjà été quitté."
fehler(control_error,GETTEXT("~: the block named ~ has already been left"));
}
LISPSPECFORM(return_from, 1,1,nobody)
# (RETURN-FROM name [result]), CLTL S. 120
{ var reg4 object name = STACK_1;
if (!symbolp(name)) { fehler_symbol(name); } # sollte ein Symbol sein
# BLOCK_ENV durchgehen:
{var reg1 object env = aktenv.block_env; # aktuelles BLOCK_ENV
var reg2 object* FRAME;
while (stack_env_p(env))
{ # env ist ein Frame-Pointer auf einen IBLOCK-Frame im Stack.
FRAME = TheFramepointer(env);
if (mtypecode(FRAME_(0)) & bit(nested_bit_t))
# Frame schon genestet
{ env = FRAME_(frame_next_env); break; }
if (eq(FRAME_(frame_name),name)) goto found;
env = FRAME_(frame_next_env);
}
# env ist eine Aliste.
while (consp(env))
{ var reg3 object block_cons = Car(env);
if (eq(Car(block_cons),name))
{ env = Cdr(block_cons);
if (eq(env,disabled)) # Block noch aktiv?
{ fehler_block_left(name); }
goto found;
}
env = Cdr(env);
}
# env ist zu Ende.
pushSTACK(name);
pushSTACK(S(return_from));
//: DEUTSCH "~: Es ist kein Block namens ~ sichtbar."
//: ENGLISH "~: no block named ~ is currently visible"
//: FRANCAIS "~: Aucun bloc de nom ~ n'est visible."
fehler(program_error,GETTEXT("~: no block named ~ is currently visible"));
# Block-Frame gefunden: env
found:
FRAME = uTheFramepointer(env); # Pointer auf ihn
# Werte produzieren, mit denen der Block verlassen werden soll:
{var reg5 object result = popSTACK();
skipSTACK(1);
if (!eq(result,unbound)) # result angegeben?
{ eval(result); }
else
{ value1 = NIL; mv_count=1; }
# Zum gefundenen Block-Frame springen und ihn auflösen:
unwind_upto(FRAME);
}}}
# Die Funktionen MAPCAR, MAPLIST, MAPCAN, MAPCON bauen wir in zwei Versionen:
# Die erste baut die Liste im umgekehrter Reihenfolge, muß sie dann umdrehen.
# Die zweite arbeitet vorwärtsherum, braucht dafür aber ein Cons zuviel.
#define MAP_REVERSES
#ifdef MAP_REVERSES
# Macro für MAPCAR und MAPLIST
#define MAPCAR_MAPLIST_BODY(listaccess) \
{ var reg7 object* args_pointer = rest_args_pointer STACKop 2; \
argcount++; # argcount := Anzahl der Listen auf dem Stack \
# Platz für die Funktionsaufruf-Argumente reservieren: \
get_space_on_STACK(sizeof(object)*(uintL)argcount); \
pushSTACK(NIL); # Anfang der Ergebnisliste \
{var reg6 object* ergptr = &STACK_0; # Pointer darauf \
# alle Listen parallel durchlaufen: \
loop \
{ var reg3 object* argptr = args_pointer; \
var reg5 object fun = NEXT(argptr); \
var reg4 uintC count; \
dotimespC(count,argcount, \
{ var reg2 object* next_list_ = &NEXT(argptr); \
var reg1 object next_list = *next_list_; \
if (atomp(next_list)) goto fertig; # eine Liste zu Ende -> fertig \
pushSTACK(listaccess(next_list)); # als Argument auf den Stack \
*next_list_ = Cdr(next_list); # Liste verkürzen \
}); \
funcall(fun,argcount); # Funktion aufrufen \
pushSTACK(value1); \
{var reg1 object new_cons = allocate_cons(); # neues Cons \
Car(new_cons) = popSTACK(); Cdr(new_cons) = STACK_0; \
STACK_0 = new_cons; # verlängert die Ergebnisliste \
}} \
fertig: \
value1 = nreverse(*ergptr); mv_count=1; # Ergebnisliste umdrehen \
set_args_end_pointer(args_pointer); # STACK aufräumen \
}}
#else
# Macro für MAPCAR und MAPLIST
#define MAPCAR_MAPLIST_BODY(listaccess) \
{ var reg7 object* args_pointer = rest_args_pointer STACKop 2; \
argcount++; # argcount := Anzahl der Listen auf dem Stack \
# Platz für die Funktionsaufruf-Argumente reservieren: \
get_space_on_STACK(sizeof(object)*(uintL)argcount); \
# Gesamtliste anfangen: \
{var reg1 object new_cons = allocate_cons(); # (CONS NIL NIL) \
pushSTACK(new_cons); # Gesamtliste \
pushSTACK(new_cons); # (last Gesamtliste) \
} \
{var reg6 object* ergptr = &STACK_1; # Pointer darauf \
# alle Listen parallel durchlaufen: \
loop \
{ var reg3 object* argptr = args_pointer; \
var reg5 object fun = NEXT(argptr); \
var reg4 uintC count; \
dotimespC(count,argcount, \
{ var reg2 object* next_list_ = &NEXT(argptr); \
var reg1 object next_list = *next_list_; \
if (atomp(next_list)) goto fertig; # eine Liste zu Ende -> fertig \
pushSTACK(listaccess(next_list)); # als Argument auf den Stack \
*next_list_ = Cdr(next_list); # Liste verkürzen \
}); \
funcall(fun,argcount); # Funktion aufrufen \
pushSTACK(value1); \
{var reg1 object new_cons = allocate_cons(); # neues Cons \
Car(new_cons) = popSTACK(); # new_cons = (LIST (FUNCALL ...)) \
Cdr(STACK_0) = new_cons; STACK_0 = new_cons; # verlängert Gesamtliste \
}} \
fertig: \
value1 = Cdr(*ergptr); mv_count=1; # Ergebnisliste ohne Header-Cons \
set_args_end_pointer(args_pointer); # STACK aufräumen \
}}
#endif
# Macro für MAPC und MAPL
#define MAPC_MAPL_BODY(listaccess) \
{ var reg7 object* args_pointer = rest_args_pointer STACKop 2; \
argcount++; # argcount := Anzahl der Listen auf dem Stack \
# Platz für die Funktionsaufruf-Argumente reservieren: \
get_space_on_STACK(sizeof(object)*(uintL)argcount); \
pushSTACK(BEFORE(rest_args_pointer)); # erstes Listenargument retten \
{var reg6 object* ergptr = &STACK_0; # Pointer darauf \
# alle Listen parallel durchlaufen: \
loop \
{ var reg3 object* argptr = args_pointer; \
var reg5 object fun = NEXT(argptr); \
var reg4 uintC count; \
dotimespC(count,argcount, \
{ var reg2 object* next_list_ = &NEXT(argptr); \
var reg1 object next_list = *next_list_; \
if (atomp(next_list)) goto fertig; # eine Liste zu Ende -> fertig \
pushSTACK(listaccess(next_list)); # als Argument auf den Stack \
*next_list_ = Cdr(next_list); # Liste verkürzen \
}); \
funcall(fun,argcount); # Funktion aufrufen \
} \
fertig: \
value1 = *ergptr; mv_count=1; # 1. Liste als Wert \
set_args_end_pointer(args_pointer); # STACK aufräumen \
}}
#ifdef MAP_REVERSES
# Macro für MAPCAN und MAPCON
#define MAPCAN_MAPCON_BODY(listaccess) \
{ var reg7 object* args_pointer = rest_args_pointer STACKop 2; \
argcount++; # argcount := Anzahl der Listen auf dem Stack \
# Platz für die Funktionsaufruf-Argumente reservieren: \
get_space_on_STACK(sizeof(object)*(uintL)argcount); \
pushSTACK(NIL); # Anfang der Ergebnisliste \
{var reg6 object* ergptr = &STACK_0; # Pointer darauf \
# alle Listen parallel durchlaufen: \
loop \
{ var reg3 object* argptr = args_pointer; \
var reg5 object fun = NEXT(argptr); \
var reg4 uintC count; \
dotimespC(count,argcount, \
{ var reg2 object* next_list_ = &NEXT(argptr); \
var reg1 object next_list = *next_list_; \
if (atomp(next_list)) goto fertig; # eine Liste zu Ende -> fertig \
pushSTACK(listaccess(next_list)); # als Argument auf den Stack \
*next_list_ = Cdr(next_list); # Liste verkürzen \
}); \
funcall(fun,argcount); # Funktion aufrufen \
STACK_0 = nreconc(value1,STACK_0); # Ergebnis anhängen \
} \
fertig: \
value1 = nreconc(*ergptr,NIL); mv_count=1; # Ergebnisliste umdrehen \
set_args_end_pointer(args_pointer); # STACK aufräumen \
}}
#else
# Macro für MAPCAN und MAPCON
#define MAPCAN_MAPCON_BODY(listaccess) \
{ var reg7 object* args_pointer = rest_args_pointer STACKop 2; \
argcount++; # argcount := Anzahl der Listen auf dem Stack \
# Platz für die Funktionsaufruf-Argumente reservieren: \
get_space_on_STACK(sizeof(object)*(uintL)argcount); \
# Gesamtliste anfangen: \
{var reg1 object new_cons = allocate_cons(); # (CONS NIL NIL) \
pushSTACK(new_cons); # Gesamtliste \
pushSTACK(new_cons); # (last Gesamtliste) \
} \
{var reg6 object* ergptr = &STACK_1; # Pointer darauf \
# alle Listen parallel durchlaufen: \
loop \
{ var reg3 object* argptr = args_pointer; \
var reg5 object fun = NEXT(argptr); \
var reg4 uintC count; \
dotimespC(count,argcount, \
{ var reg2 object* next_list_ = &NEXT(argptr); \
var reg1 object next_list = *next_list_; \
if (atomp(next_list)) goto fertig; # eine Liste zu Ende -> fertig \
pushSTACK(listaccess(next_list)); # als Argument auf den Stack \
*next_list_ = Cdr(next_list); # Liste verkürzen \
}); \
funcall(fun,argcount); # Funktion aufrufen \
{var reg1 object list = value1; # anzuhängende Liste \
if (consp(list)) \
{ Cdr(STACK_0) = list; # als (cdr (last Gesamtliste)) einhängen \
while (mconsp(Cdr(list))) { list = Cdr(list); } \
STACK_0 = list; # und (last Gesamtliste) := (last list) \
}} } \
fertig: \
value1 = Cdr(*ergptr); mv_count=1; # Ergebnisliste ohne Header-Cons \
set_args_end_pointer(args_pointer); # STACK aufräumen \
}}
#endif
#define Identity
LISPFUN(mapcar,2,0,rest,nokey,0,NIL)
# (MAPCAR fun list {list}), CLTL S. 128
MAPCAR_MAPLIST_BODY(Car)
LISPFUN(maplist,2,0,rest,nokey,0,NIL)
# (MAPLIST fun list {list}), CLTL S. 128
MAPCAR_MAPLIST_BODY(Identity)
LISPFUN(mapc,2,0,rest,nokey,0,NIL)
# (MAPC fun list {list}), CLTL S. 128
MAPC_MAPL_BODY(Car)
LISPFUN(mapl,2,0,rest,nokey,0,NIL)
# (MAPL fun list {list}), CLTL S. 128
MAPC_MAPL_BODY(Identity)
LISPFUN(mapcan,2,0,rest,nokey,0,NIL)
# (MAPCAN fun list {list}), CLTL S. 128
MAPCAN_MAPCON_BODY(Car)
LISPFUN(mapcon,2,0,rest,nokey,0,NIL)
# (MAPCON fun list {list}), CLTL S. 128
MAPCAN_MAPCON_BODY(Identity)
LISPSPECFORM(tagbody, 0,0,body)
# (TAGBODY {tag | statement}), CLTL S. 130
{ var reg5 object body = popSTACK();
# GENV-Frame aufbauen:
{ var reg1 object* top_of_frame = STACK; # Pointer übern Frame
pushSTACK(aktenv.go_env);
finish_frame(ENV1G);
}
# TAGBODY-Frame aufbauen:
{var reg6 object* top_of_frame = STACK; # Pointer übern Frame
# Body durchparsen und Tags im Stack ablegen:
var reg4 uintL tagcount = 0;
{ var reg3 object body_rest = body;
while (consp(body_rest))
{ var reg2 object item = Car(body_rest);
body_rest = Cdr(body_rest);
# Als Tags werden Symbole /=NIL sowie Zahlen angesehen
# (wie im Compiler), Conses sind Statements.
if (atomp(item))
{ if (numberp(item) || (symbolp(item) && (!nullp(item))))
# Marke im Stack ablegen:
{ check_STACK();
pushSTACK(body_rest); # Body-Rest nach der Marke
pushSTACK(item);
tagcount++;
}
else
{ pushSTACK(item);
pushSTACK(S(tagbody));
//: DEUTSCH "~: ~ ist weder Marke noch Statement."
//: ENGLISH "~: ~ is neither tag nor form"
//: FRANCAIS "~: ~ n'est ni un marqueur ni une forme à evaluer."
fehler(program_error,GETTEXT("~: ~ is neither tag nor form"));
}
} } }
if (tagcount>0)
{ var jmp_buf returner; # Rücksprungpunkt
pushSTACK(aktenv.go_env); # aktuelles GO_ENV als NEXT_ENV
finish_entry_frame(ITAGBODY,&!returner,_EMA_, goto go_entry; );
# GO_ENV erweitern:
aktenv.go_env = make_framepointer(STACK);
if (FALSE)
{ go_entry: # Hierher wird gesprungen, wenn dieser Frame ein GO
# gefangen hat.
body = value1; # Die Formenliste wird als value1 übergeben.
}
# Statements abarbeiten:
pushSTACK(body);
while (mconsp(STACK_0))
{ var reg1 object body_rest = STACK_0;
STACK_0 = Cdr(body_rest); # restlicher Body
body_rest = Car(body_rest); # nächstes Item
if (consp(body_rest)) { eval(body_rest); } # Form -> auswerten
}
skipSTACK(1); # Body vergessen
unwind(); # TAGBODY-Frame auflösen
unwind(); # GENV-Frame auflösen
}
else
# Body ohne Tags -> nur PROGN mit Wert NIL
{ skipSTACK(2); # GENV-Frame wieder auflösen, GENV ist unverändert
pushSTACK(body); implicit_prog();
}
value1 = NIL; mv_count=1; # Wert NIL
}}
local void fehler_control_error (object tag,object go);
local void fehler_control_error(tag,go)
var object tag;
var object go;
{
pushSTACK(tag);
pushSTACK(go);
//: DEUTSCH "~: Tagbody zur Marke ~ wurde bereits verlassen."
//: ENGLISH "~: tagbody for tag ~ has already been left"
//: FRANCAIS "~: Le TAGBODY du marqueur ~ a déjà été quitté."
fehler(control_error,GETTEXT("~: tagbody for tag ~ has already been left"));
}
LISPSPECFORM(go, 1,0,nobody)
# (GO tag), CLTL S. 133
{ var reg3 object tag = popSTACK();
if (!(numberp(tag) || (symbolp(tag) && (!nullp(tag)))))
{ pushSTACK(tag);
pushSTACK(S(go));
//: DEUTSCH "~: ~ ist keine zulässige Marke."
//: ENGLISH "~: illegal tag ~"
//: FRANCAIS "~: ~ n'est pas un marqueur permis."
fehler(program_error,GETTEXT("~: illegal tag ~"));
}
# GO_ENV durchgehen:
{var reg7 object env = aktenv.go_env; # aktuelles GO_ENV
var reg8 object* FRAME;
while (stack_env_p(env))
{ # env ist ein Frame-Pointer auf einen ITAGBODY-Frame im Stack.
FRAME = uTheFramepointer(env);
if (mtypecode(FRAME_(0)) & bit(nested_bit_t))
# Frame schon genestet
{ env = FRAME_(frame_next_env); break; }
# Tags im ungenesteten ITAGBODY-Frame absuchen:
{ var reg1 object* bind_ptr = &FRAME_(frame_bindings); # Pointer unter die Tagbindungen
var reg2 object* bindend_ptr = STACKpointable(topofframe(FRAME_(0))); # Pointer über die Tagbindungen
do { if (eql(*bind_ptr,tag)) # Tag gefunden?
{ value1 = *(bind_ptr STACKop 1); # Formenliste aus dem Frame holen
goto found;
}
bind_ptr skipSTACKop 2;
}
until (bind_ptr==bindend_ptr);
}
env = FRAME_(frame_next_env);
}
# env ist eine Aliste.
while (consp(env))
{ var reg6 object tagbody_cons = Car(env);
var reg5 object tagbody_vec = Car(tagbody_cons); # Tag-Vektor
var reg1 object* tagptr = &TheSvector(tagbody_vec)->data[0];
var reg4 uintL index = 0;
var reg2 uintL count;
dotimespL(count,TheSvector(tagbody_vec)->length,
{ if (eql(*tagptr++,tag)) # Tag gefunden?
{ env = Cdr(tagbody_cons);
if (eq(env,disabled)) # Tagbody noch aktiv?
fehler_control_error (tag,S(go));
FRAME = uTheFramepointer(env); # Pointer auf den (noch aktiven!) Frame
value1 = FRAME_(frame_bindings+2*index+1); # Formenliste
goto found;
}
index++;
});
env = Cdr(env);
}
# env ist zu Ende.
pushSTACK(tag);
pushSTACK(S(go));
//: DEUTSCH "~: Es ist keine Marke namens ~ sichtbar."
//: ENGLISH "~: no tag named ~ is currently visible"
//: FRANCAIS "~: Aucun marqueur de nom ~ n'est visible."
fehler(program_error,GETTEXT("~: no tag named ~ is currently visible"));
# Tagbody-Frame gefunden. FRAME ist ein Pointer auf ihn (ohne Typinfo),
# value1 die Liste der auszuführenden Formen.
found:
mv_count=1; # Formenliste value1 wird übergeben
# Zum gefundenen Tagbody-Frame springen und dort weitermachen:
unwind_upto(FRAME);
}}
# Fehlermeldung bei zu vielen Werten
# fehler_mv_zuviel(caller);
# > caller: Aufrufer, ein Symbol
nonreturning_function(global, fehler_mv_zuviel, (object caller));
global void fehler_mv_zuviel(caller)
var reg1 object caller;
{ pushSTACK(caller);
//: DEUTSCH "~: Zu viele Werte."
//: ENGLISH "~: too many values"
//: FRANCAIS "~: Trop de valeurs."
fehler(error,GETTEXT("~: too many values"));
}
LISPFUN(values,0,0,rest,nokey,0,NIL)
# (VALUES {arg}), CLTL S. 134
{ if (argcount >= mv_limit) { fehler_mv_zuviel(S(values)); }
STACK_to_mv(argcount);
}
LISPFUNN(values_list,1)
# (VALUES-LIST list), CLTL S. 135
{ list_to_mv(popSTACK(), fehler_mv_zuviel(S(values_list)); ); }
LISPSPECFORM(multiple_value_list, 1,0,nobody)
# (MULTIPLE-VALUE-LIST form), CLTL S. 135
{ eval(popSTACK()); # form auswerten
mv_to_list(); # Werte in Liste packen
value1 = popSTACK(); mv_count=1; # Liste als Wert
}
LISPSPECFORM(multiple_value_call, 1,0,body)
# (MULTIPLE-VALUE-CALL fun {form}), CLTL S. 135
{ var reg3 object* fun_ = &STACK_1;
*fun_ = (eval(*fun_),value1); # Funktion auswerten
{var reg1 object forms = popSTACK(); # Formenliste
var reg2 uintL argcount = 0; # Anzahl der bisherigen Argumente
while (consp(forms))
{ pushSTACK(Cdr(forms)); # restliche Formen
eval(Car(forms)); # nächste Form auswerten
forms = popSTACK();
# Deren Werte in den Stack:
argcount += (uintL)mv_count;
mv_to_STACK();
}
if (((uintL)~(uintL)0 > ca_limit_1) && (argcount > ca_limit_1))
{ pushSTACK(*fun_);
pushSTACK(S(multiple_value_call));
//: DEUTSCH "~: Zu viele Argumente für ~"
//: ENGLISH "~: too many arguments to ~"
//: FRANCAIS "~: Trop d'arguments pour ~."
fehler(error,GETTEXT("~: too many arguments to ~"));
}
funcall(*fun_,argcount); # Funktion aufrufen
skipSTACK(1);
}}
LISPSPECFORM(multiple_value_prog1, 1,0,body)
# (MULTIPLE-VALUE-PROG1 form {form}), CLTL S. 136
{ eval(STACK_1); # erste Form auswerten
{ var reg3 object body = popSTACK();
skipSTACK(1);
{var reg2 uintC mvcount = mv_count; # Wertezahl
mv_to_STACK(); # alle Werte in den Stack
pushSTACK(body); implicit_prog();
STACK_to_mv(mvcount); # alle Werte wieder aus dem Stack zurückholen
}}}
LISPSPECFORM(multiple_value_bind, 2,0,body)
# (MULTIPLE-VALUE-BIND ({var}) values-form {decl} {form}), CLTL S. 136
{ # {decl} {form} trennen:
var reg10 boolean to_compile = parse_dd(STACK_0,aktenv.var_env,aktenv.fun_env); # unvollständiges var_env??
# bitte kein Docstring:
if (!nullp(value3)) { fehler_docstring(S(multiple_value_bind),STACK_0); }
if (to_compile) # Deklaration (COMPILE) ?
# ja -> Form kompilieren:
{ skipSTACK(3); return_Values compile_eval_form(); }
else
{ var reg10 object varlist = STACK_2;
STACK_2 = STACK_1;
skipSTACK(2);
# Variablenbindungsframe aufbauen, VAR_ENV erweitern:
{var reg9 object* form_ = &STACK_0;
var object* bind_ptr;
var uintC bind_count;
make_variable_frame(S(multiple_value_bind),varlist,&bind_ptr,&bind_count);
# Stackaufbau: values-form, Variablenbindungsframe, Env-Bindungs-Frame, ({form}).
# Dann values-form auswerten:
eval(*form_);
# Macro zum Binden von Variablen im Variablenframe:
# Bindet die nächste Variable an value, erniedrigt frame_pointer um 2 bzw. 3.
#define bind_next_var(value) \
{ var reg3 object* valptr = &Next(frame_pointer); \
frame_pointer skipSTACKop -varframe_binding_size; \
{var reg2 object* markptr = &Before(frame_pointer); \
if (*(oint*)(markptr) & wbit(dynam_bit_o)) \
# dynamische Bindung aktivieren: \
{ var reg4 object sym = *(markptr STACKop varframe_binding_sym); # Variable \
*valptr = Symbolflagged_value(sym); # alten Wert in den Frame \
*(oint*)(markptr) |= wbit(active_bit_o); # Bindung aktivieren \
set_Symbolflagged_value_on(sym,value,markptr); # neuen Wert in die Wertzelle \
} \
else \
# statische Bindung aktivieren: \
{ *valptr = (value); # neuen Wert in den Frame \
*(oint*)(markptr) |= wbit(active_bit_o); # Bindung aktivieren \
} \
}}
# Die r:=bind_count Variablen an die s:=mv_count Werte binden:
# (Falls die Variablen ausgehen: restliche Werte wegwerfen;
# falls die Werte ausgehen: mit NIL auffüllen.)
# Hier r>=0 und s>=0.
{ var reg5 object* frame_pointer = bind_ptr;
var reg7 uintC r = bind_count;
var reg6 object* mv_pointer;
var reg8 uintC s = mv_count;
if (r==0) goto ok; # keine Variablen?
if (s==0) goto fill; # keine Werte?
# noch min(r,s)>0 Werte binden:
#if !defined(VALUE1_EXTRA)
mv_pointer = &mv_space[0];
#else
bind_next_var(value1);
if (--r == 0) goto ok; # keine Variablen mehr?
if (--s == 0) goto fill; # keine Werte mehr?
mv_pointer = &mv_space[1];
#endif
# noch min(r,s)>0 Werte binden:
loop
{ bind_next_var(*mv_pointer++);
if (--r == 0) goto ok; # keine Variablen mehr?
if (--s == 0) goto fill; # keine Werte mehr?
}
fill: # Noch r>0 Variablen an NIL binden
dotimespC(r,r, { bind_next_var(NIL); } );
ok: ;
}
# Body abinterpretieren:
implicit_progn(popSTACK(),NIL);
# Frames auflösen:
unwind(); # VENV-Bindungsframe auflösen
unwind(); # Variablenbindungs-Frame auflösen
skipSTACK(1);
} }}
LISPSPECFORM(multiple_value_setq, 2,0,nobody)
# (MULTIPLE-VALUE-SETQ ({var}) form), CLTL S. 136
{ {var reg2 object varlist = STACK_1;
# Variablenliste durchgehen:
while (consp(varlist))
{ var reg1 object symbol = Car(varlist); # nächste Variable
if (!symbolp(symbol)) # sollte ein Symbol
{ fehler_kein_symbol(S(multiple_value_setq),symbol); }
if (constantp(TheSymbol(symbol))) # und keine Konstante sein
{ fehler_symbol_constant(S(multiple_value_setq),symbol); }
if (sym_macrop(symbol)) # und kein Symbol-Macro
goto expand;
varlist = Cdr(varlist);
} }
if (FALSE)
{ expand:
pushSTACK(STACK_0); STACK_1 = STACK_2; STACK_2 = S(multiple_value_setf);
{var reg1 object newform = listof(3); # aus MULTIPLE-VALUE-SETQ mache MULTIPLE-VALUE-SETF
eval(newform);
}}
else
{ eval(popSTACK()); # form auswerten
{ var reg4 object varlist = popSTACK();
var reg5 object* args_end = args_end_pointer;
mv_to_STACK(); # Werte in den Stack schreiben (erleichtert den Zugriff)
# Variablenliste durchgehen:
{var reg1 object* mvptr = args_end;
var reg3 uintC count = mv_count; # Anzahl noch verfügbarer Werte
while (consp(varlist))
{ var reg2 object value;
if (count>0)
{ value = NEXT(mvptr); count--; } # nächster Wert
else
{ value = NIL; } # NIL, wenn alle Werte verbraucht
setq(Car(varlist),value); # der nächsten Variablen zuweisen
varlist = Cdr(varlist);
}
set_args_end_pointer(args_end); # STACK aufräumen
mv_count=1; # letzter value1 als einziger Wert
} }}}
LISPSPECFORM(catch, 1,0,body)
# (CATCH tag {form}), CLTL S. 139
{ STACK_1 = (eval(STACK_1),value1); # tag auswerten
# CATCH-Frame zu Ende aufbauen:
{var reg1 object body = popSTACK(); # ({form})
var reg2 object* top_of_frame = STACK STACKop 1; # Pointer übern Frame
var jmp_buf returner; # Rücksprungpunkt merken
finish_entry_frame(CATCH,&!returner,_EMA_, goto catch_return; );
# Body ausführen:
implicit_progn(body,NIL);
catch_return: # Hierher wird gesprungen, wenn der oben aufgebaute
# Catch-Frame einen Throw gefangen hat.
skipSTACK(3); # CATCH-Frame auflösen
}}
LISPSPECFORM(unwind_protect, 1,0,body)
# (UNWIND-PROTECT form {cleanup}), CLTL S. 140
{ var reg2 object cleanup = popSTACK();
var reg3 object form = popSTACK();
# UNWIND-PROTECT-Frame aufbauen:
pushSTACK(cleanup);
{var reg4 object* top_of_frame = STACK;
var jmp_buf returner; # Rücksprungpunkt
finish_entry_frame(UNWIND_PROTECT,&!returner,_EMA_, goto throw_save; );
# Protected form auswerten:
eval(form);
# Cleanup nach normaler Beendigung der Protected form:
# UNWIND-PROTECT-Frame auflösen:
skipSTACK(2);
cleanup = popSTACK();
# Werte retten:
{var reg1 uintC mvcount = mv_count;
mv_to_STACK();
# Cleanup-Formen abarbeiten:
pushSTACK(cleanup); implicit_prog();
# Werte zurückschreiben:
STACK_to_mv(mvcount);
}
return;
throw_save: # Hierher wird gesprungen, wenn der oben aufgebaute
# Unwind-Protect-Frame einen Throw aufgehalten hat.
# unwind_protect_to_save ist zu retten und am Schluß anzuspringen.
{ var reg5 restart fun = unwind_protect_to_save.fun;
var reg6 object* arg = unwind_protect_to_save.upto_frame;
# Cleanup:
# UNWIND-PROTECT-Frame auflösen:
skipSTACK(2);
cleanup = popSTACK();
# Werte retten:
{var reg1 uintC mvcount = mv_count;
mv_to_STACK();
# Cleanup-Formen abarbeiten:
pushSTACK(cleanup); implicit_prog();
# Werte zurückschreiben:
STACK_to_mv(mvcount);
}# und weiterspringen:
fun(arg);
}
}}
LISPSPECFORM(throw, 2,0,nobody)
# (THROW tag result), CLTL S. 142
{ STACK_1 = (eval(STACK_1),value1); # tag auswerten
eval(popSTACK()); # result auswerten
{var reg1 object tag = popSTACK(); # ausgewertetes Tag
throw(tag); # versuche auf dieses zu THROWen
# Nicht gelungen.
pushSTACK(tag);
pushSTACK(S(throw));
//: DEUTSCH "~: Es gibt kein CATCH zur Marke ~."
//: ENGLISH "~: there is no CATCHer for tag ~"
//: FRANCAIS "~: Il n'y a pas de CATCH correspondant au marqueur ~."
fehler(control_error,GETTEXT("~: there is no CATCHer for tag ~"));
}}
LISPFUNN(driver,1)
# (SYS::DRIVER fun) baut einen Driver-Frame auf, der jedesmal die Funktion
# fun (mit 0 Argumenten) aufruft. fun wird in einer Endlosschleife ausgeführt,
# die mit GO oder THROW abgebrochen werden kann.
{ var reg1 object* top_of_frame = STACK; # Pointer übern Frame
var DRIVER_frame_data returner_and_data; # Einsprungpunkt merken
#ifdef HAVE_NUM_STACK
returner_and_data.old_NUM_STACK_normal = NUM_STACK_normal;
#endif
finish_entry_frame(DRIVER,&!returner_and_data.returner,_EMA_,;);
# Hier ist der Einsprungpunkt.
loop { funcall(STACK_(0+2),0); } # fun aufrufen, Endlosschleife
}
LISPFUNN(unwind_to_driver,0)
# (SYS::UNWIND-TO-DRIVER) macht ein UNWIND bis zum nächsthöheren Driver-Frame.
{ reset(); }
# Überprüft ein optionales Macroexpansions-Environment in STACK_0.
# > STACK_0: Argument
# < STACK_0: Macroexpansions-Environment #(venv fenv)
# kann GC auslösen
local void test_env (void);
local void test_env()
{ var reg1 object arg = STACK_0;
if (eq(arg,unbound))
{ STACK_0 = allocate_vector(2); } # Vektor #(nil nil) als Default
elif (!(simple_vector_p(arg) && (TheSvector(arg)->length == 2)))
{ pushSTACK(arg); # Wert für Slot DATUM von TYPE-ERROR
pushSTACK(O(type_svector2)); # Wert für Slot EXPECTED-TYPE von TYPE-ERROR
pushSTACK(arg);
//: DEUTSCH "Argument ~ ist kein Macroexpansions-Environment."
//: ENGLISH "Argument ~ is not a macroexpansion environment"
//: FRANCAIS "L'argument ~ n'est pas un environnement pour macros"
fehler(type_error,GETTEXT("Argument ~ is not a macroexpansion environment"));
} }
LISPFUNN(macro_function,1)
# (MACRO-FUNCTION symbol), CLTL S. 144
{ var reg3 object symbol = popSTACK();
if (!symbolp(symbol)) { fehler_symbol(symbol); }
{var reg2 object fundef = Symbol_function(symbol); # globale Funktionsdefinition
if (fsubrp(fundef))
# ein FSUBR -> Propertyliste absuchen: (GET symbol 'SYS::MACRO)
{ var reg1 object got = get(symbol,S(macro)); # suchen
if (eq(got,unbound)) goto nil; # nichts gefunden?
value1 = got;
}
elif (consp(fundef) && eq(Car(fundef),S(macro))) # (SYS::MACRO . expander) ?
{ value1 = Cdr(fundef); }
else # SUBR/Closure/#<UNBOUND> -> keine Macrodefinition
{ nil: value1 = NIL; }
mv_count=1;
}}
LISPFUN(macroexpand,1,1,norest,nokey,0,NIL)
# (MACROEXPAND form [env]), CLTL S. 151
{ test_env();
{var reg1 object env = popSTACK();
var reg2 object form = STACK_0;
STACK_0 = env; # env retten
macroexp0(form,env); # expandieren
if (!nullp(value2)) # was getan?
# ja -> zu Tode expandieren:
{ do { macroexp0(value1,STACK_0); } until (nullp(value2));
value2 = T;
}
mv_count=2; skipSTACK(1);
}}
LISPFUN(macroexpand_1,1,1,norest,nokey,0,NIL)
# (MACROEXPAND-1 form [env]), CLTL S. 151
{ test_env();
{var reg1 object env = popSTACK();
var reg2 object form = popSTACK();
macroexp0(form,env); # 1 mal expandieren
mv_count=2;
}}
LISPSPECFORM(declare, 0,0,body)
# (DECLARE {decl-spec}), CLTL S. 153
{ # ({decl-spec}) bereits in STACK_0
//: DEUTSCH "Deklarationen ~ an dieser Stelle nicht erlaubt."
//: ENGLISH "declarations ~ are not allowed here"
//: FRANCAIS "Les déclarations ~ ne sont pas permises à cet endroit."
fehler(program_error,GETTEXT("declarations ~ are not allowed here"));
}
LISPSPECFORM(the, 2,0,nobody)
# (THE value-type form), CLTL S. 161
{ eval(STACK_0); # form auswerten
mv_to_list(); # Werteliste bilden und retten
# Stackaufbau: value-type, form, values.
# zum Typ-Check (SYS::%THE values value-type) aufrufen:
pushSTACK(STACK_0); pushSTACK(STACK_(2+1)); funcall(S(pthe),2);
if (nullp(value1))
# Typ-Check mißlang
{ pushSTACK(STACK_(2+0)); # value-type
pushSTACK(STACK_(0+1)); # values
pushSTACK(STACK_(1+2)); # form
pushSTACK(S(the));
//: DEUTSCH "~: Die Form ~ produzierte die Werte ~, nicht vom Typ ~"
//: ENGLISH "~: ~ evaluated to the values ~, not of type ~"
//: FRANCAIS "~: La forme ~ a produit les valeurs ~ qui ne sont pas de type ~."
fehler(error, GETTEXT("~: ~ evaluated to the values ~, not of type ~")); # type_error ??
}
# Typ-Check OK -> Werte zurückgeben:
list_to_mv(popSTACK(), { fehler_mv_zuviel(S(the)); } );
skipSTACK(2);
}
LISPFUNN(proclaim,1)
# (PROCLAIM decl-spec)
{ var reg3 object declspec = popSTACK();
if (!consp(declspec))
{ pushSTACK(declspec);
pushSTACK(S(proclaim));
//: DEUTSCH "~: Falsche Deklaration: ~"
//: ENGLISH "~: bad declaration ~"
//: FRANCAIS "~: Mauvaise déclaration : ~"
fehler(error,GETTEXT("~: bad declaration ~"));
}
{var reg4 object decltype = Car(declspec); # Deklarationstyp
if (eq(decltype,S(special))) # SPECIAL
{ while (consp( declspec = Cdr(declspec) ))
{ var reg1 object symbol = Car(declspec);
if (!symbolp(symbol)) { fehler_symbol(symbol); }
if (!keywordp(symbol)) { clear_const_flag(TheSymbol(symbol)); }
set_special_flag(TheSymbol(symbol));
} }
elif (eq(decltype,S(declaration))) # DECLARATION
{ while (consp( declspec = Cdr(declspec) ))
{ var reg2 object symbol = Car(declspec);
if (!symbolp(symbol)) { fehler_symbol(symbol); }
# (PUSHNEW symbol (cdr declaration-types)) :
{ var reg1 object list = Cdr(O(declaration_types));
while (consp(list))
{ if (eq(Car(list),symbol)) goto not_adjoin;
list = Cdr(list);
} }
pushSTACK(declspec); pushSTACK(symbol);
{var reg1 object new_cons = allocate_cons();
var reg2 object list = O(declaration_types);
Car(new_cons) = popSTACK(); Cdr(new_cons) = Cdr(list);
Cdr(list) = new_cons;
declspec = popSTACK();
}
not_adjoin: ;
} }
elif (eq(decltype,S(inline)) || eq(decltype,S(notinline))) # INLINE, NOTINLINE
{ pushSTACK(decltype);
while (consp( declspec = Cdr(declspec) ))
{ var reg2 object symbol = Car(declspec);
if (!funnamep(symbol)) { fehler_kein_symbol(S(proclaim),symbol); }
# (SYS::%PUT (SYS::GET-FUNNAME-SYMBOL symbol) 'SYS::INLINABLE decltype) :
pushSTACK(declspec);
pushSTACK(symbol); funcall(S(get_funname_symbol),1); pushSTACK(value1);
pushSTACK(S(inlinable));
pushSTACK(STACK_(1+2));
funcall(L(put),3);
declspec = popSTACK();
}
skipSTACK(1);
}
# Alles restliche wird ignoriert.
value1 = NIL; mv_count=1;
}}
LISPFUNN(eval,1)
# (EVAL form), CLTL S. 321
{ eval_noenv(popSTACK()); } # form im leeren Environment auswerten
LISPSPECFORM(load_time_value, 1,1,nobody)
# (LOAD-TIME-VALUE form [read-only-p]), CLTL2 S. 680
{ var reg1 object form = STACK_1;
skipSTACK(2); # read-only-p ignorieren
eval_noenv(form); # form im leeren Environment auswerten
mv_count=1;
}
# UP: Überprüft ein optionales Environment-Argument für EVALHOOK und APPLYHOOK.
# test_optional_env_arg(&env5);
# > subr_self: Aufrufer (ein SUBR)
# < env5: 5 Komponenten des Environments
# erhöht STACK um 1
local void test_optional_env_arg (environment* env5,object env);
local void test_optional_env_arg(env5,env)
var reg2 environment* env5;
var reg1 object env;
{ if (eq(env,unbound)) # nicht angegeben -> leeres Environment
{ env5->var_env = NIL;
env5->fun_env = NIL;
env5->block_env = NIL;
env5->go_env = NIL;
env5->decl_env = O(top_decl_env);
}
elif (simple_vector_p(env) && (TheSvector(env)->length == 5))
# ein Simple-Vector der Länge 5
{ env5->var_env = TheSvector(env)->data[0];
env5->fun_env = TheSvector(env)->data[1];
env5->block_env = TheSvector(env)->data[2];
env5->go_env = TheSvector(env)->data[3];
env5->decl_env = TheSvector(env)->data[4];
}
else
{ pushSTACK(env); # Wert für Slot DATUM von TYPE-ERROR
pushSTACK(O(type_svector5)); # Wert für Slot EXPECTED-TYPE von TYPE-ERROR
pushSTACK(env);
pushSTACK(TheSubr(subr_self)->name);
//: DEUTSCH "~: ~ ist nicht als Environment geeignet."
//: ENGLISH "~: ~ may not be used as an environment"
//: FRANCAIS "~: ~ ne peut pas être utilisé comme environnement."
fehler(type_error,GETTEXT("~: ~ may not be used as an environment"));
} }
LISPFUN(evalhook,3,1,norest,nokey,0,NIL)
# (EVALHOOK form evalhookfn applyhookfn [env]), CLTL S. 323
{ var reg4 object applyhookfn = STACK_1;
var reg3 object evalhookfn = STACK_2;
var reg2 object *form_ptr = &STACK_3;
var reg5 object *env_ptr = &STACK_0;
var environment env5;
bindhooks(evalhookfn,applyhookfn);
test_optional_env_arg(&env5,*env_ptr); # env-Argument nach env5
# Environment-Frame aufbauen:
make_ENV5_frame();
# aktuelle Environments setzen:
aktenv = env5;
# form unter Umgehung von *EVALHOOK* und *APPLYHOOK* auswerten:
eval_no_hooks(*form_ptr);
unwind(); # Environment-Frame auflösen
unwind(); # Bindungsframe für *EVALHOOK* / *APPLYHOOK* auflösen
skipSTACK(4);
}
LISPFUN(applyhook,4,1,norest,nokey,0,NIL)
# (APPLYHOOK function args evalhookfn applyhookfn [env]), CLTL S. 323
{ var environment env5;
var reg2 object args;
var reg6 object applyhookfn = STACK_1;
var reg5 object evalhookfn = STACK_2;
var reg1 object *args_ptr = &STACK_3;
var reg7 object *fun_ptr = &STACK_4;
var reg8 object *env_ptr = &STACK_0;
bindhooks(evalhookfn,applyhookfn);
test_optional_env_arg(&env5,*env_ptr); # env-Argument nach env5
args = *args_ptr;
# Environment-Frame aufbauen:
make_ENV5_frame();
# aktuelle Environments setzen:
aktenv = env5;
# fun retten:
{ # Argumente einzeln auswerten und auf dem Stack ablegen:
var reg2 uintC argcount = 0;
while (consp(args))
{ pushSTACK(Cdr(args)); # restliche Argumentliste
eval_no_hooks(Car(args)); # nächstes arg auswerten
args = STACK_0; STACK_0 = value1; # Wert im Stack ablegen
argcount++;
if (argcount==0) # Überlauf?
{ pushSTACK(*fun_ptr);
pushSTACK(S(applyhook));
//: DEUTSCH "~: Zu viele Argumente für ~"
//: ENGLISH "~: too many arguments given to ~"
//: FRANCAIS "~: Trop d'arguments fournis à ~."
fehler(error,GETTEXT("~: too many arguments given to ~"));
} }
funcall(*fun_ptr,argcount); # Funktion anwenden
}
unwind(); # Environment-Frame auflösen
unwind(); # Bindungsframe für *EVALHOOK* / *APPLYHOOK* auflösen
skipSTACK(5);
}
LISPFUNN(constantp,1)
# (CONSTANTP expr), CLTL S. 324
{ var reg1 object arg = popSTACK();
switch (typecode(arg))
{ case_cons: # Cons
if (eq(Car(arg),S(quote))) goto ja; else goto nein;
case_symbol: # Symbol
if (constantp(TheSymbol(arg))) goto ja; else goto nein;
case_number: # Zahl
case_char: # Character
case_string: # String
case_bvector: # Bit-Vektor
goto ja;
default:
goto nein;
}
ja: value1 = T; mv_count=1; return;
nein: value1 = NIL; mv_count=1; return;
}
LISPFUNN(function_name_p,1)
# (SYS::FUNCTION-NAME-P expr) erkennt Funktionsnamen
{ var reg1 object arg = popSTACK();
value1 = (funnamep(arg) ? T : NIL); mv_count=1;
}
LISPFUN(parse_body,1,2,norest,nokey,0,NIL)
# (SYS::PARSE-BODY body [docstring-allowed [env]])
# parst body, erkennt Deklarationen, liefert 3 Werte:
# 1. body-rest, alle Formen nach den Deklarationen,
# 2. Liste der aufgetretenen declspecs
# 3. docstring (nur falls docstring-allowed=T war) oder NIL.
# (docstring-allowed sollte = NIL oder T sein,
# env sollte ein Function-Environment sein.)
{ test_env();
{var reg5 boolean docstring_allowed = (!eq(STACK_1,unbound) && !nullp(STACK_1)); # Docstrings erlaubt?
var reg2 object body = STACK_2; # body = ({decl|doc} {form})
STACK_1 = NIL; # Noch war kein Doc-String da
pushSTACK(NIL); # Anfang decl-spec-Liste
# Stackaufbau: body, docstring, env, declspecs.
while (consp(body))
{ pushSTACK(body); # body retten
{ var reg1 object form = Car(body); # nächste Form
# evtl. macroexpandieren (ohne FSUBRs, Symbole zu expandieren):
do { var reg1 object env = STACK_(1+1);
macroexp(form,TheSvector(env)->data[0],TheSvector(env)->data[1]);
form = value1;
}
until (nullp(value2));
body = popSTACK();
{var reg4 object body_rest = Cdr(body); # body verkürzen
if (stringp(form)) # Doc-String gefunden?
{ if (atomp(body_rest)) # an letzter Stelle der Formenliste?
goto fertig; # ja -> letzte Form kann kein Doc-String sein!
if (!docstring_allowed) # kein Doc-String erlaubt?
{ pushSTACK(STACK_3); # ganzer body
//: DEUTSCH "Hier sind keine Doc-Strings erlaubt: ~"
//: ENGLISH "no doc-strings allowed here: ~"
//: FRANCAIS "Les chaînes de documentation ne sont pas permises ici : ~"
fehler(program_error,GETTEXT("no doc-strings allowed here: ~"));
}
if (!nullp(STACK_2)) # schon ein Doc-String dagewesen?
# ja -> mehr als ein Doc-String ist zuviel:
{ pushSTACK(STACK_3); # ganzer body
//: DEUTSCH "In ~ kommen zu viele Doc-Strings vor."
//: ENGLISH "Too many documentation strings in ~"
//: FRANCAIS "Trop de chaînes de documentation apparaîssent dans ~."
fehler(program_error,GETTEXT("Too many documentation strings in ~"));
}
STACK_2 = form; # neuer Doc-String
body = body_rest;
}
elif (consp(form) && eq(Car(form),S(declare))) # Deklaration (DECLARE ...) ?
{ # neue decl-specs einzeln auf STACK_0 consen:
pushSTACK(body_rest); # body_rest retten
pushSTACK(Cdr(form)); # Liste der neuen decl-specs
while (mconsp(STACK_0))
{ # Diese Deklaration auf STACK_(0+2) consen:
var reg3 object new_cons = allocate_cons();
Car(new_cons) = Car(STACK_0);
Cdr(new_cons) = STACK_(0+2);
STACK_(0+2) = new_cons;
# zum nächsten decl-spec:
STACK_0 = Cdr(STACK_0);
}
skipSTACK(1);
body = popSTACK(); # body := alter body_rest
}
else
{ fertig: # fertig mit Durchlaufen der Formenliste
#if 0 # Im Interpreter zwar eine gute Idee, aber der Compiler
# wird dadurch behindert, weil er dann CASE und HANDLER-BIND
# nicht so gut compilieren kann.
if (!eq(form,Car(body))) # Sofern die Form expandiert wurde,
# ersetze body durch (cons form (cdr body)) :
{ pushSTACK(body_rest); pushSTACK(form);
body = allocate_cons();
Car(body) = popSTACK(); # form
Cdr(body) = popSTACK(); # body_rest
}
#endif
break;
}
}}}
value1 = body;
value2 = nreverse(popSTACK()); # decl-spec-Liste
skipSTACK(1);
value3 = popSTACK(); # Doc-String
skipSTACK(1);
mv_count=3; # 3 Werte: ({form}), declspecs, doc
}}
LISPFUNN(keyword_test,2)
# (SYSTEM::KEYWORD-TEST arglist kwlist)
# stellt fest, ob in der Argumentliste arglist (eine paarige Keyword/Value -
# Liste) alle Keywords in der Liste kwlist vorkommen oder aber
# ein Keyword/Value-Paar :ALLOW-OTHER-KEYS mit value /= NIL vorkommt.
# Wenn nein, Error.
{ var reg4 object arglist = STACK_1;
# Argumente-Zahl überprüfen:
{ var reg1 uintL argcount = llength(arglist);
if (!((argcount%2) == 0))
{ pushSTACK(arglist);
//: DEUTSCH "Keyword-Argumentliste ~ hat ungerade Länge."
//: ENGLISH "keyword argument list ~ has an odd length"
//: FRANCAIS "La liste de mots clé ~ est de longueur impaire."
fehler(error,GETTEXT("keyword argument list ~ has an odd length"));
} }
# Suche, ob :ALLOW-OTHER-KEYS kommt:
{ var reg1 object arglistr = arglist;
while (consp(arglistr))
{ if (eq(Car(arglistr),S(Kallow_other_keys)) && !nullp(Car(Cdr(arglistr))))
goto fertig;
arglistr = Cdr(Cdr(arglistr));
} }
# Suche, ob alle angegebenen Keywords in kwlist vorkommen:
{ var reg3 object arglistr = arglist;
while (consp(arglistr))
{ var reg2 object key = Car(arglistr);
var reg1 object kwlistr = STACK_0;
while (consp(kwlistr))
{ if (eq(Car(kwlistr),key)) goto found;
kwlistr = Cdr(kwlistr);
}
# nicht gefunden
pushSTACK(Car(Cdr(arglistr)));
pushSTACK(key);
//: DEUTSCH "Unzulässiges Keyword/Wert-Paar ~, ~ in einer Argumentliste. Die erlaubten Keywords sind ~"
//: ENGLISH "illegal keyword/value pair ~, ~ in argument list. The allowed keywords are ~"
//: FRANCAIS "Paire mot-clé - valeur ~, ~ illicite dans une liste d'arguments. Les mots-clé permis sont ~"
fehler(error,GETTEXT("illegal keyword/value pair ~, ~ in argument list. The allowed keywords are ~"));
found: # gefunden. Weiter:
arglistr = Cdr(Cdr(arglistr));
} }
fertig:
skipSTACK(2);
value1 = NIL; mv_count=0; # keine Werte
}
LISPSPECFORM(and, 0,0,body)
# (AND {form}), CLTL S. 82
{ var reg1 object body = popSTACK();
if (atomp(body))
{ value1 = T; mv_count=1; } # (AND) -> T
else
loop
{ pushSTACK(Cdr(body));
eval(Car(body)); # form auswerten
body = popSTACK();
if (atomp(body)) break; # am Schluß: Werte der letzten Form zurück
if (nullp(value1)) { mv_count=1; break; } # vorzeitig: 1 Wert NIL
}
}
LISPSPECFORM(or, 0,0,body)
# (OR {form}), CLTL S. 83
{ var reg1 object body = popSTACK();
if (atomp(body))
{ value1 = NIL; mv_count=1; } # (OR) -> NIL
else
loop
{ pushSTACK(Cdr(body));
eval(Car(body)); # form auswerten
body = popSTACK();
if (atomp(body)) break; # am Schluß: Werte der letzten Form zurück
if (!nullp(value1)) { mv_count=1; break; } # vorzeitig: 1 Wert /=NIL
}
}
# Ab jetzt hat der Tabellenmacro eine andere Verwendung:
#undef LISPSPECFORM
# Tabelle aller Fsubr-Funktionen:
global struct fsubr_tab_ fsubr_tab =
{
#define LISPSPECFORM LISPSPECFORM_D
#include "fsubr.c"
#undef LISPSPECFORM
};