; .EnTete "Le-Lisp (c) version 15.2" " " "Compatibilite' Maclisp" ; .EnPied "defun.ll" "%" " " ; .SuperTitre "lexpr et defun" ; ; .Auteur "Alain Be`ges" ; ; .Centre "*****************************************************************" ; .Centre " (c) Le-Lisp est une marque de'pose'e de l'INRIA " ; .Centre "*****************************************************************" ; ; .Centre "$Header: defun.ll,v 4.1 88/01/13 12:34:52 kuczynsk Rel $" (unless (>= (version) 15.2) (error 'load 'erricf 'defun)) (de pkg-gensym (pkg) (symbol pkg (gensym))) (de vect-to-cons (start len vect) (let ((end (sub1 (add start len))) (res ())) (while (le start end) (newr res (vref vect start)) (incr start)) res)) ; .Section "presentation" ; Ce fichier re'alise la compatibilite' maclisp/lelisp sur ; les points suivants : ; 1) Traitement des lexpr ie fonctions arg, setarg, listify. ; 2) Fonction defun et les commodite's qui lui sont attache'es ; (les &keywords) ; .SSection "les lexpr maclisp" ; Elles diffe'rent des lexpr lelisp en ce qui suit : ; le corps de la lexpr peut contenir une re'fe'rence a` l'argument ; sous les formes suivantes : ; - (arg n) fourni le n-ieme e'le'ment de la valeur de l'argument; ; - (setarg n val) affecte la valeur val au n-ieme e'le'ment de ... ; ; - (listify n) fourni la liste des n premiers e'le'ments, ou des n ; derniers si n est ne'gatif. ; .SSection "le defun" ; Il remplace les "de" "df" "dm" de Le←Lisp ; sa syntaxe est : ;- (defun <name> macro <bvl> . <body>) ; .br ;- (defun <name> fexpr <bvl> . <body>) ; .br ;- (defun <name> expr <bvl> . <body>) ou pour cette dernie`re ... ; .br ;- (defun <name> <bvl> . <body>) ; Il est en outre dote' des commodite's suivantes, relatives a` la ; liste des variables : ; On peut, dans la liste des variables (bvl), faire apparaitre les symboles ; &optional, &key, &rest, &aux (dans cet ordre) ... ; 1) &optional ; Toutes les variables suivants ce mot-clef ne sont pas obligatoires lors ; de l'appel de la fonction ... ; (defun foo (x &optional a1 (a2 v2) (a3 v3 p3)) ...) ; De'finit une expr dont la variable x est obligatoire et les variables ; a1 a2 a3 sont optionnelles ; ; - si a1 n'est pas lie'e par l'appel, elle sera lie'e a` nil; ; - si a2 n'est pas lie'e par l'appel, elle sera lie'e a` sa valeur par ; de'faut, qui est le resultat de l'e'valuation de v2 (v2 peut faire ; re'fe'rence aux variables pre'ce'demnts lie'es ; les liaisons ; se font se'quentiellement); ; - en ce qui concerne a3 sa valeur par de'faut est bien su↑r v3, mais on ; dispose en plus de la variable p3, qui vaudra t si v3 est lie'e par ; l'appel, et nil sinon. ; 2) &key ; Il doit suivre &optional ; Toutes les variables introduites pre'ce'dement, optionnelles ou non, ; e'taient positionnelles, en ce sens que c'e'tait la place de l'argument ; actuel dans le corps d'appel de la fonction qui de'cidait de l'argument ; formel (de la variable) auquel il devait e↑tre lie'; ; .br ; pour tourner cette restriction on introduit les variables introduites ; par mot clef : ; (... &key (a x) (b y) ...) ; les mots clef sont ici a et b, et les variables correspondantes x et y. ; Le corps de la fonction (soit foo) pourra contenir les parametres x et y ; (et non a et b !), mais ces parametres seront lie's par un appel du genre : ; (foo ... 'b <valeur a` affecter a` y> ...) ; Il est important de remarquer que ; (1) on peut lier y avant x (l'ordre n'a pas d'importance : c'est la seule ; raison d'e↑tre de ce type de variable !); ; (2) ces variables peuvent ou non e↑tre lie'es par l'appel, elle sont ; toujours conside're'es comme optionnelles. ; Finissons en donnant les syntaxes possibles : ; - (... &key x ...) ou (... &key (x x) ...) ; .br ; la variable et le mot clef correspondant sont ici confondu; la valeur par ; de'faut de la variable x est nil; ; - (... &key (a x) ...) ; .br ; la variable est x et le mot clef correspondant est a; la valeur par defaut ; de x est nil; ; - (... &key ((a x) vx) ...) ou (... &key ((x) vx) ...) ; .br ; introduit la variable x (avec ou sans mot clef distinct de x); la valeur ; par de'faut de x sera (le produit de l'e'valuation de) vx; ; - (... &key ((a x) vx px) ...) ou (... &key ((x) vx px) ...) ; .br ; on dispose en plus, par rapport a` l'exemple pre'ce'dent, de la variable ; px qui sera lie'e a` t si x est li'ee par l'appel et nil sinon. ; 3) &rest ; Ce mot-clef doit suivre &optional et &key (si pre'sents); ; la liste de variable (a b c &rest v) est e'quivalente a` la liste de ; variables Le←Lisp ; (a b c . v) ; &rest ne doit e↑tre suivi que d'un seul symbole ; 4) &aux ; Il doit suivre &optional, &key et &rest (si ils sont pre'sents) ; ; ( ... &aux a (aa vv) ...) ; introduit simplement des variables locales : ; - a initialise'e a` nil et ; - aa initialise'e a` vv (ou` vv est une expression pouvant faire toutes ; les re'fe'rences avants possibles). ; Ceci est simule' par un slet. ; .SSection "exemple" ; ? (defun foo (x &optional y &key ((ka a) 1 pa) &rest r &aux (q 1) (w 2)) ; . <body>) ; = (de foo (x . #:maclisp:defun:g160) ; (slet ((y (if (lt 0 (length #:maclisp:defun:g160)) ; (nth 0 #:maclisp:defun:g160) ; ())) ; (#:maclisp:defun:g160 (nthcdr 1 #:maclisp:defun:g160))) ; (let ((a ()) (pa ())) ; (#:maclisp:defun:kwd-bind-key ; '#:maclisp:defun:g160 ; '(((ka a) 1 pa))) ; (setq r #:maclisp:defun:g160) ; (slet ((q 1) (w 2)) . <body>)))) ; .Section "lambda revisite'" ; 1) traitement des lexpr ... (defvar #:maclisp:lexpr:len 70) ; pourquoi pas (defvar #:maclisp:lexpr:bvl-vector (makevector #:maclisp:lexpr:len ())) (de #:maclisp:lexpr:init-bvl-vector (n) (setq #:maclisp:lexpr:len n) (setq #:maclisp:lexpr:bvl-vector (makevector n ()))) (de listify (n) ; n est un fix positif ou negatif (if (ge n 0) (vect-to-cons 0 n #:maclisp:lexpr:bvl-vector) (vect-to-cons (add1 (add #:maclisp:lexpr:len n)) (mul n -1) #:maclisp:lexpr:bvl-vector))) (de arg (n) (vref #:maclisp:lexpr:bvl-vector (sub1 n))) (de setarg (n val) (vset #:maclisp:lexpr:bvl-vector (sub1 n) val)) (de #:maclisp:lexpr:aux (a) ; l'atome a doit avoir une liste pour valeur (let ((l (length a)) (i 0)) (when (gt l #:maclisp:lexpr:len) (#:maclisp:lexpr:init-bvl-vector l)) (while (lt i l) (vset #:maclisp:lexpr:bvl-vector i (nextl a)) (incr i)))) (de #:maclisp:lexpr (symb body) `(lambda ,symb (#:maclisp:lexpr:aux ,symb) . ,body)) ; l'ecriture de la fonction lambda elle-me↑me est repousse'e a` plus tard. ; 2) dans certaines incarnations de maclisp nil peut e↑tre present dans la ; la liste des variables (bvl) d'une lambda ... ; pour parer cette botte on substitue au(x) nil(s) pre'sents dans la bvl ; des variables #:maclisp:lexpr:nil:gensym qui seront lie'es en pure perte. ; On garde ainsi bonne contenance (ie on ne touche pas a` l'e'valuateur). (de #:maclisp:lexpr:subst-nil-to-gensym (bvl) (cond ((null bvl) (pkg-gensym '#:maclisp:lexpr:nil)) ((atom bvl) bvl) (t (rplac bvl (#:maclisp:lexpr:subst-nil-to-gensym (car bvl)) (if (null (cdr bvl)) () (#:maclisp:lexpr:subst-nil-to-gensym (cdr bvl))))))) (de #:maclisp:lexpr:nil-in-bvl (bvl body) `(lambda ,(#:maclisp:lexpr:subst-nil-to-gensym bvl) . ,body)) ; 3) la fonction lambda elle-me↑me (df lambda (bvl . body) (if (atom bvl) ; lexpr donc ... (#:maclisp:lexpr bvl body) (#:maclisp:lexpr:nil-in-bvl bvl body))) ; .Section "le defun" (defvar #:maclisp:&keywords '(&optional &rest &aux &key)) (de #:maclisp:defun:kwd-input (kwd dvl) ; quoi est a` qui ? (let ((aux (cdr (memq kwd dvl))) (res ())) (when aux ; sans c/a c'est pas la peine (while (not (or (null aux) (memq (car aux) #:maclisp:&keywords))) (newr res (nextl aux)))) res)) (de #:maclisp:defun:kwd-optl (var optl) ; traitement des variables introduites par &optional; ; on fabrique la liste de liaison du slet correspondant ... ; (la valeur de) var est le nom de la variable qui servira a` lier les ; variables introduites apres &optional &rest et &key . (let ((i 0) (l-optl (length optl)) (res ())) (while (lt i l-optl) (let ((elm (nth i optl))) (cond ; premier cas : une variable seule ((symbolp elm) (newr res `(,elm (if (lt ,i (length ,var)) (nth ,i ,var) ())))) ; deuxieme cas : un couple var. val. initiale par defaut ((eq 2 (length elm)) (newr res `(,(car elm) (if (lt ,i (length ,var)) (nth ,i ,var) ,(cadr elm))))) ; troisieme cas : un triplet ((eq 3 (length elm)) (newr res `(,(car elm) (if (lt ,i (length ,var)) (nth ,i ,var) ,(cadr elm)))) (newr res `(,(caddr elm) (if (lt ,i (length ,var)) t ()))))) (incr i))) (newr res `(,var (nthcdr ,i ,var))) res)) (de #:maclisp:defun:kwd-key-aux (kwd) ; petit pre'traitement de la liste des variables du type &key pour lui ; donner une syntaxe uniforme (... ((kx x) vx px) ...) (let ((res ())) (unless (null kwd) (while (consp kwd) (newr res (let ((x (car kwd))) (cond ((symbolp x) `((,x ,x) () ())) ; de la forme x ((symbolp (car x)) `(,x () ())) ; de la forme (kx x) (t `(,(if (null (cdar x)) `(,(caar x) ,(caar x)) (car x)) ,(cadr x) ,(caddr x)))))) (nextl kwd))) res)) (de #:maclisp:defun:kwd-key (key) ; fabrique la liste du let correspondant apres traitement de key ; par #:maclisp:defun:kwd-key-aux ; ; on lie les variables a` nil : le seul interet de ceci est de "localiser" ; les variables pour pouvoir les lier a` l'aide de setq a` l'interieur de ; ce let (cf fonction suivante et exemples) (let ((res ())) (while key (let ((k (car key))) (newr res `(,(cadar k) ())) (when (caddr k) (newr res `(,(caddr k) ()))) (nextl key))) res)) (de #:maclisp:defun:kwd-bind-key (var key) ; va faire les liaisons a` l'aide de setq(s) mais sera appelle'e a` ; l'interieur d'un let; ; la valeur de var est le nom de la variable (cre'e par un gensym) dont la ; valeur correspond a` la partie &key et &rest des arguments ; actuels ; ; (la valeur de) key est une liste de la forme (... ((kx x) vx px) ...) ou` ; vx et px peuvent e↑tre e'gaux a` nil. ; apres l'appel de cette fonction les x et px seront lie's et var contiendra ; ce que doit contenir la variable de &rest si elle existe (while key (letvq ((kx x) vx px) (car key) (let ((yes (memq kx (eval var)))) ; on en a un (if yes (progn (set x (cadr yes)) (set var (delete (car yes) (eval var))) (set var (delete (cadr yes) (eval var))) (when px (set px t))) (set x (eval vx))))) (nextl key))) (de #:maclisp:defun:kwd-aux (aux) ; fabrique la liste du slet correspondant (if aux (mapcar (lambda (x) (if (symbolp x) (list x) x)) aux) ())) (de #:maclisp:defun:dvl (dvl body) ; dvl pour defun variables list (if (symbolp dvl) `(,dvl (#:maclisp:lexpr:aux ,dvl) . ,body) ; c'etait une lexpr (let ((tvar ()) ; pour l'instant (optl (#:maclisp:defun:kwd-input '&optional dvl)) (rest (car (#:maclisp:defun:kwd-input '&rest dvl))) (key (#:maclisp:defun:kwd-key-aux (#:maclisp:defun:kwd-input '&key dvl))) (aux (#:maclisp:defun:kwd-input '&aux dvl)) (var (pkg-gensym '#:maclisp:defun))) ; on remplie la liste des vraies variables tvar (while (not (or (memq (car dvl) #:maclisp:&keywords) (null dvl))) (newr tvar (nextl dvl))) ; puis on commence par l'interieur : d'abord les "aux" (unless (null aux) (setq body `((slet ,(#:maclisp:defun:kwd-aux aux) . ,body)))) ; puis le "rest" (que l'on "localise" avec un let) (unless (null rest) (setq body `((let ((,rest ,var)) . ,body)))) ; puis les "key" (unless (null key) (setq body `((let ,(#:maclisp:defun:kwd-key key) (#:maclisp:defun:kwd-bind-key ,(kwote var) ,(kwote key)) . ,body)))) ; puis les "optl" (unless (null optl) (setq body `((slet ,(#:maclisp:defun:kwd-optl var optl) . ,body)))) ; puis on rajoute les variables (if (and (null optl) (null rest) (null key) (null aux)) (newl body tvar) (newl body (nconc tvar var)))))) (dmd defun l (cond ((eq 'expr (cadr l)) `(de ,(car l) . ,(#:maclisp:defun:dvl (caddr l) (cdddr l)))) ((eq 'fexpr (cadr l)) `(df ,(car l) . ,(#:maclisp:defun:dvl (caddr l) (cdddr l)))) ((eq 'macro (cadr l)) `(dm ,(car l) . ,(#:maclisp:defun:dvl (caddr l) (cdddr l)))) (t `(de ,(car l) . ,(#:maclisp:defun:dvl (cadr l) (cddr l)))))) ; c'est fini