[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
4.1 Einführung in den Parser | ||
4.2 Vorbereitende Funktionen und Makros | ||
4.3 Funktionen und Makros des Parsers | ||
4.4 Definition der Operatoren | ||
4.5 Nutzerdefinierte Operatoren |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
In Implementierung der Funktion maxima-toplevel-loop
wurde eine
Lisp-read
-eval
-Schleife eingeführt, um Eingaben des Nutzers
zu lesen und auszuwerten. Die Lisp-Funktion eval
ist durch die Funktion
meval
ersetzt worden, siehe Implementierung des ersten Evaluators.
In diesem Kapitel wird nun der Parser implementiert. Die Funktion mread
ist die zentrale Funktion des Parsers, die die Lisp-Funkion read
ersetzt.
mread
liest mathematische Ausdrücke, Kommandos, Programme und setzt
diese in kMaxima-Ausdrücke um. Die hier vorgestellten Funktionen entsprechen
im wesentlichen dem Code des Originals Maxima.
Die folgende Tabelle zeigt die Operatoren, die kMaxima zu Beginn kennt, den dazu gehörenden Operator sowie einen Ausdruck, der den Operator anwendet.
Name Symbole Beispiel
"+" $+ mplus a+b ((mplus) $A $B) "-" $- mminus -a ((mminus) $A) "*" $+ mtimes a*b ((mtimes) $A $B) "^" $^ mexpt a^b ((mexpt) $A $B) "<" $< mlessp a<b ((mlessp) $A $B) "=" $= mequal a=b ((mequal) $A $B) ">" $> mgreaterp a>b ((mqreaterp) $A $B) "(" $( mprogn (a,b) ((mprogn) $A $B) ")" $) "[" $[ mlist [a,b] ((mlist $A $B) "]" $] "," $, $ev a,b (($ev) $A $B) ":" $: msetq a:b ((msetq) $A $B) "!" $! mfactorial a! ((mfactorial) $A) "#" $# mnotequal a#b ((mnotequal) $A $B) "'" $' mquote 'a ((mquote) $A) ";" $; displayinput "$" $$ nodisplayinput "**" $** mexpt a**b ((mexpt) $A $B) "^^" $^^ mncexpt a^^b ((mncexpt) $A $B) ":=" $:= mdefine a:=b ((mdefine) $A $B) "::" $:: mset a::b ((mset) $A $B) "<=" $<= mleqp a<=b ((mleqp) $A $B) ">=" $>= mgeqp a>=b ((mgeqp) $A $B) "''" $'' tritt in der Ausgabe nicht auf "&&" $&& tritt in der Ausgabe nicht auf "::=" $::= mdefmacro a::=b ((mdefmacro) $A $B)
Im Unterschied zum Original Maxima sind die Operatoren &
und !!
in kMaxima nicht definiert. Der Operator ''
tritt in der Ausgabe nicht
auf, da dieser vom Parser sofort angewendet wird. Zum Unterschied zwischen dem
Namen und den Symbolen, die einen Operator bezeichnen siehe
Namen der Operatoren.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
4.2.1 Namen der Operatoren | ||
4.2.2 Liste der Operatoren | ||
4.2.3 Definition der Syntax des Parsers | ||
4.2.4 Fehlerbehandlung des Parsers | ||
4.2.5 Weitere Funktionen des Parsers |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Operatoren haben verschiedene Bezeichnungen in Maxima. In der internen
Darstellung eines Ausdrucks hat zum Beispiel der Operator für die Addition
+
das Symbol mplus
als Bezeichnung. Wird der Operator vom Parser
eingelesen, wird er zunächst durch das Symbol $+
repräsentiert.
Zuletzt kennt Maxima noch die Zeichenkette "+"
, die den Namen
des Operators für die Addition repräsentiert.
Mit der Funktion getopr
wird der Name eines Operators als Zeichenkette
zurückgegeben. (getopr 'mplus)
hat also das Ergebnis "+"
.
Umgekehrt gibt die Funktion getop
das interne Symbol zu einem Namen
zurück. (getop "+")
hat das Ergebnis mplus
. Die Namen der
Operatoren werden zum Indikator op
in der Eigenschaftsliste des
dazugehörenden internen Symbols abgelegt. Da Zeichenketten keine
Eigenschaftsliste haben, werden die Namen der Operatoren mit ihrem
dazugehörenden internen Symbol in eine Hash-Tabelle abgelegt, die lokal zu
den Funktionen getopr0
, putopr
und remopr
definiert ist.
Mit den folgenden Funktionen werden die Namen oder Symbole von Operatoren geholt, in die Eigenschaftsliste oder in eine Hash-Tabelle eingetragen oder wieder entfernt.
Die Funktion getopr0
holt das interne Symbol, das einen Operator
bezeichnet. Das Argument x ist der Name des Operators, der als eine
Zeichenkette angegeben werden muss. Die Rückgabe ist das interne Symbol,
das den Operator bezeichnet, oder nil
, wenn das Argument x keinen
Operator bezeichnet.
opr-table
ist eine zu den Funktionen getopr0
, putopr
und
remopr
lokale Hash-Tabelle, die zu den Namen der Operatoren die
entsprechenden internen Symbole der Operatoren enthält.
Siehe die Funktion putopr
,
um zu einem Namen das interne Symbol
des Operators abzulegen und die Funktion remopr
,
um einen Eintrag
zum Namen eines Operators zu entfernen. Mit der Funktion getop
wird
umgekehrt zu einem internen Bezeichner eines Operators der Name als
Zeichenkette zurückgegeben.
Beispiele:
* (getopr0 "+") MPLUS T * (getopr0 "if") MCOND T
Bemerkung:
Die Funktion akzeptiert auch ein Symbol als Argument x. In diesem Fall
prüft getopr0
, ob der Name des Operators zum Indikator opr
auf
der Eigenschaftsliste abgelegt ist. Das Original Maxima und kMaxima haben keine
Operatoren, die mit einem Symbol als Namen bezeichnet werden und einen Eintrag
in der Eigenschaftsliste haben. Möglicherweise sollte die zusätzliche
Funktionalität entfernt werden. Operatoren müssen dann auf jeden Fall eine
Zeichenkette als Namen haben.
Quelltext:
(defun getopr0 (x) (or (getprop x 'opr) (and (stringp x) (gethash x opr-table))))
Die Funktion putopr
legt zum Namen eines Operators x, der eine
Zeichenkette ist, das Symbol y in einer zu der Funktion lokalen
Hash-Tabelle opr-table
ab. Die Funktion wird intern von kMaxima
aufgerufen, um die Namen der vorhandenen kMaxima-Operatoren zu initialisieren.
Definiert der Nutzer einen Operator, wird putopr
von der Funktion
op-setup
aufgerufen, um den Namen des Operators zu initialisieren.
Siehe auch die Funktionen getopr0
,
getopr
und
remopr
.
Bemerkung:
Wie die Funktionen getopr0
und remopr
akzeptiert auch
putopr
ein Symbol als den Namen eines Operators. Dies wird im Original
und in kMaxima nicht genutzt. Der Name eines Operators ist immer eine
Zeichenkette.
Quelltext:
(defun putopr (x y) (or (and (symbolp x) (putprop x y 'opr)) (and (stringp x) (setf (gethash x opr-table) y))))
Entfernt den Eintrag zum Argument x aus der Hash-Tabelle opr-table
.
Die Hash-Tabelle opr-table
ist lokal zu den Funktionen remopr
,
putopr
und getopr0
definiert. Das Argument x ist eine
Zeichenkette, die der Name eines Operators ist.
Siehe auch die Funktionen putopr
,
getopr0
,
getopr
und getop
.
Beispiel:
In diesem Beispiel wird der Eintrag zum Namen des Additionsoperators "+"
entfernt.
* (getopr "+") MPLUS * (remopr "+") T * (getopr0 "+") NIL
Bemerkung:
Wie die Funktionen getopr0
und putopr
akzeptiert auch
remopr
ein Symbol als den Namen eines Operators. Dies wird im Original
und in kMaxima nicht genutzt. Der Name eines Operators ist immer eine
Zeichenkette.
Quelltext:
(defun remopr (x) (or (and (symbolp x) (remprop x 'opr)) (and (stringp x) (remhash x opr-table))))
Wie die Funktion getopr0
holt die Funktion das interne Symbol, das einen
Operator bezeichnet. Das Argument x ist der Name des Operators. Der Name
ist eine eine Zeichenkette. Die Rückgabe ist das interne Symbol, das den
Operator bezeichnet. Im Unterschied zu der Funktion getopr0
wird das
Argument x zurückgegeben, wenn zum Argument x kein Operator
vorhanden ist.
Quelltext:
(defun getopr (x) (or (getopr0 x) x))
Die Funktion getop
gibt zu dem Symbol x, das einen Operator
bezeichnet, den dazugehörenden Namen als eine Zeichenkette zurück.
Beispiel:
Der Multiplikationsoperator hat das interne Symbol mtimes
. Der Name des
Operators ist "*"
.
* (getop 'mtimes) "*"
Quelltext:
(defun getop (x) (or (getprop x 'op) x))
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Wenn der Parser Eingaben liest, dann enthalten diese verschiedene Operatoren
wie zum Beispiel das Zeichen "+" für die Addition. Intern wird die Addition
mit dem Operator mplus
dargestellt. Eine Eingabe wie zum Beispiel "a+b"
wird vom Parser in den internen Ausdruck ((mplus) $A $B)
umgewandelt.
Die zu Beginn definierten Symbole sind in der globalen Variablen
*symbols-defined*
enthalten. Mit dem Makro define-initial-symbols
wird eine globale Liste *maxima-operators*
mit den Zeichen aufgebaut, die
einen Operator definieren. Der Parser liest diese Liste, um Operatoren zu
interpretieren.
Das folgende Makro define-initial-symbols
wird mit einer Liste der
Operatoren aufgerufen, die definiert werden sollen. Die Operatoren werden dabei
als Symbole übergeben, zum Beispiel als Liste '(|+| |-| ...)
. Das
Makro ruft die Funktion define-initial-symbols*
auf und initialisiert
die globalen Variablen *symbols-defined*
und *maxima-operators*
.
Standardwert: nil
Eine globale Liste mit den Symbolen, die vom Parser als Operator eingelesen
werden. Die Liste wird von der Funktion define-initial-symbols*
initialisiert. Weiterhin wird diese Liste verwendet, um die globale
Liste *maxima-operators*
zu initialisieren.
Hinweis:
Nicht alle Operatoren werden der Liste *symbols-defined*
hinzugefügt.
In der Liste sind nur solche Operatoren enthalten, die nicht-alphabetische
Zeichen enthalten. Der Parser prüft beim Einlesen mit der Funktion
scan-operator-token
,
ob ein Operator mit nicht-alphabetischen
Zeichen vorliegt.
Siehe auch die Funktion def-operator
mit der neue Operatoren definiert
werden, die der Liste *symbols-defined*
hinzugefügt werden, wenn diese
nicht-alphabetische Zeichen enthalten.
Beispiel:
Nach dem Start von kMaxima enthält *symbols-defined*
die folgenden
Symbole.
* *symbols-defined* (+ - * ^ < = > |(| |)| [ ] |,| |:| ! |#| |'| $ |;| ** ^^ |:=| |::| <= >= |''| && |::=|)
Standardwert: nil
Die globale Variable *maxima-operators*
ist eine verschachtelte Liste
mit den Operatoren, die der Parser als Operatoren einliest. Die Struktur der
Liste wird der Funktion cstrsetup
mit Hilfe der Symbole der Liste
*symbols-defined*
aufgebaut.
Beispiel:
Nach dem Starten von kMaxima hat *maxima-operators*
den folgenden Wert.
* *maxima-operators* (NIL (ANS NIL) (#\+ (ANS $+)) (#\- (ANS $-)) (#\* (ANS $*) (#\* (ANS $**))) (#\^ (ANS $^) (#\^ (ANS $^^))) (#\< (ANS $<) (#\= (ANS $<=))) (#\= (ANS $=)) (#\> (ANS $>) (#\= (ANS $>=))) (#\( (ANS |$(|)) (#\) (ANS |$)|)) (#\[ (ANS $[)) (#\] (ANS $])) (#\, (ANS |$,|)) (#\: (ANS |$:|) (#\= (ANS |$:=|)) (#\: (ANS |$::|) (#\= (ANS |$::=|)))) (#\! (ANS $!)) (#\# (ANS |$#|)) (#\' (ANS |$'|) (#\' (ANS |$''|))) (#\$ (ANS $$)) (#\; (ANS |$;|)) (#\& #\& (ANS $&&)))
Bemerkung:
Wie für die Liste *symbols-defined*
gilt auch für
*maxima-operators*
, dass nicht alle von kMaxima definierten Operatoren
in der Liste enthalten sind. So fehlen zum Beispiel die logischen
Operatoren. Dieser Unterschied muss noch einmal ausgearbeitet werden.
Das Makro define-initial-symbols
wird beim Laden von kMaxima aufgerufen
und initialisiert die globalen Variablen *symbols-defined*
sowie
*maxima-operators*
.
Die eigentliche Initialisierung wird von der
Funktion define-initial-symbols*
ausgeführt.
Bemerkung:
Die Wirkungsweise der let
-Umgebung im Makro muss noch erarbeitet werden.
Quelltext:
(defmacro define-initial-symbols (&rest l) (let ((*symbols-defined* nil) (*maxima-operators* nil)) (define-initial-symbols* l) `(progn (setq *symbols-defined* (copy-list ',*symbols-defined*)) (setq *maxima-operators* (subst () () ',*maxima-operators*)))))
Die Funktion define-initial-symbols*
sortiert die Liste der Symbole,
die als Argument l übergeben wird, nach der Größe und ruft dann
die Funktion cstrsetup
auf, die die Liste mit den Zeichen der Operatoren
für den Parser generiert.
Quelltext:
(defun define-initial-symbols* (l) (setq *symbols-defined* (sort (copy-list l) #'(lambda (x y) (< (length (exploden x)) (length (exploden y)))))) (setq *maxima-operators* (cstrsetup *symbols-defined*)))
Die Funktion cstrsetup
generiert die Liste mit den Zeichen die vom
Parser als Operator eingelesen werden. cstrsetup
wird von der Funktion
define-initial-symbols*
mit der Liste *symbols-defined*
als Argument aufgerufen und generiert die Liste *maxima-operators*
.
Quelltext:
(defun cstrsetup (arg) (labels ((add2cstr (x tree ans) (add2cstr1 (nconc (exploden x) (cons (list 'ans ans) nil)) tree)) (add2cstr1 (x tree) (cond ((null tree) x) ((atom (car tree)) (cond ((equal (car tree) (car x)) (rplacd tree (add2cstr1 (cdr x) (cdr tree)))) (t (list tree (cond ((atom (car x)) x) ((equal (caar x) 'ans) (car x)) (t x)))))) ((equal (caar tree) (car x)) (rplacd (car tree) (add2cstr1 (cdr x) (cdar tree))) tree) ((null (cdr tree)) (rplacd tree (list x)) tree) (t (rplacd tree (add2cstr1 x (cdr tree))) tree)))) (do ((arg arg (cdr arg)) (tree nil)) ((null arg) (list* () '(ans ()) tree)) (if (atom (car arg)) (setq tree (add2cstr (car arg) tree (symbolconc '$ (if (stringp (car arg)) (maybe-invert-string (car arg)) (car arg))))) (setq tree (add2cstr (caar arg) tree (cadar arg)))))))
Die Funktion define-symbol
fügt der Liste *symbols-defined*
den Operator x hinzu und aktualisiert die globale Variable
*maxima-operators*
.
Das Argument x
ist eine Zeichenkette mit
dem Namen des Operators. Die Rückgabe ist ein Maxima-Symbol, das den Operator
bezeichnet.
Beispiel:
In diesem Beispiel wird ein Operator mit dem Namen "grad"
definiert.
Die Rückgabe ist das Symbol $grad
. Während die Operatoren als
Symbole in die Liste eingetragen sind, werden neue Operatoren mit ihrem Namen
als eine Zeichenkette eingetragen.
* (define-symbol "grad") $GRAD :INTERNAL (+ - * ^ < = > |(| |)| [ ] |,| |:| ! |#| |'| $ |;| ** ^^ |:=| |::| <= >= |''| && |::=| "grad")
Quelltext:
(defun define-symbol (x) (define-initial-symbols* (cons x *symbols-defined*)) (symbolconc '$ (maybe-invert-string x)))
Mit der Funktion undefine-symbol
wird der Operator opr von den
Listen *symbols-defined*
und *maxima-operators*
entfernt. Das
Argument opr ist der Name des Operators, der als eine Zeichenkette
angegeben werden muss.
Quelltext:
(defun undefine-symbol (opr) (define-initial-symbols* (delete opr *symbols-defined* :test #'equal)))
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Die Makros expandieren zu '(led)
oder '(nud)
. Sie werden nur
von der Funktion make-parser-fun-def
gebraucht, die mit der Funktion
symbolconc
die Makros aus den Symbolen led
, nud
und
propl
zusammensetzt und die Expansion des Makros als Argument an die
Funktion inherit-propl
verwendet.
Beispiel:
Das Beispiel zeigt die Expansion des Makros led-propl
.
* (macroexpand '(led-propl)) '(LED) T
Bemerkung:
Im Original Maxima werden diese Makros noch an weiteren Stellen verwendet. In kMaxima wurde jeweils die Expansion der Makros eingesetzt, um den Quelltext zu vereinfachen.
Quelltext:
(defmacro led-propl () ''(led)) (defmacro nud-propl () ''(nud))
Übernimmt die Eigenschaft getl und den dazugehörigen Wert des Operators op-from und trägt diese in die Liste des Operators op-to ein.
Quelltext:
(defun inherit-propl (op-to op-from getl) (let ((propl (getpropl op-from getl))) (if propl (progn (remprop op-to (car propl)) (putprop op-to (cadr propl) (car propl))) (merror "has no ~a properties. ~a ~a" getl op-from 'wrng-type-arg))))
Die Rückgabe der Funktion make-parser-fun-def
ist ein Makro, dass die
Definition einer led
-Funktion ist, wenn das Argument p das Symbol
led
ist und eine nud
-Funktion, wenn das Argument p das
Symbol nud
ist. Das Argument op ist der Operator, für den die
led
- oder nud
-Funktion definiert werden soll. Das Argument
bvl
ist eine Liste mit den Argumenten der zu definierenden Funktion.
Für eine led
-Funktion ist bvl
eine Liste mit zwei Argumenten
und für eine nud
-Funktion eine Liste mit genau einem Argument.
Das Makro, das zurückgegeben wird, ist selbst ein Makro mit der Definition
defun-prop
. Dieses expandiert in einen Ausdruck der die Funktion
zum Indikator p auf die Eigenschaftsliste des Symbols op ablegt.
Beispiel:
Das Beispiel zeigt die einzelnen Stufen der Expansion, wenn für den Operator
$^
eine led-Funktion
definiert wird.
* (make-parser-fun-def '$^ 'led '(op left) 'body) (DEF-LED-FUN $^ (OP LEFT) OP . BODY) * (macroexpand-1 (make-parser-fun-def '$^ 'led '(op left) 'body)) (DEFUN-PROP ($^ LED) (OP LEFT) OP . BODY) T * (macroexpand (make-parser-fun-def '$^ 'led '(op left) 'body)) (LET* ((#:G629 '$^) (#:G630 'LED)) (MULTIPLE-VALUE-BIND (#:G632) #'(LAMBDA (OP LEFT) OP . BODY) (SB-KERNEL:%PUT #:G629 #:G630 #:G632))) T
Quelltext:
(defun make-parser-fun-def (op p bvl body) (if (not (consp op)) `(,(symbolconc 'def- p '-fun) ,op ,bvl ,(car bvl) . ,body) `(progn ,(make-parser-fun-def (car op) p bvl body) ,@(mapcar #'(lambda (x) `(inherit-propl ',x ',(car op) (,(symbolconc p '-propl)))) (cdr op)))))
Das Makro legt zum Indikator nud
die nud
-Funktion, die mit dem
Argument equiv übergeben wird, auf der Eigenschaftsliste des Symbols
op ab. Das Symbol op bezeichnet einen Operator wie er vom Parser
eingelesen wird. So liest der Parser zum Beispiel den Operator der Addition
als $+
ein.
Siehe auch das Makro def-led-equiv
,
um die led
-Funktion auf
der Eigenschaftsliste eines Operators abzulegen.
Beispiel:
Das Beispiel zeigt die Expansion des Makros, wenn für den Operator der
Addition $+
die Funktion parse-prefix
als nud
-Funktion
definiert wird.
* (macroexpand-1 '(def-nud-equiv |$+| parse-prefix)) (PUTPROP '$+ #'PARSE-PREFIX 'NUD) T
Quelltext:
(defmacro def-nud-equiv (op equiv) (list 'putprop (list 'quote op) (list 'function equiv) (list 'quote 'nud)))
Das Makro legt zum Indikator led
die led
-Funktion, die mit dem
Argument equiv übergeben wird, auf der Eigenschaftsliste des Symbols
op ab. Das Symbol op bezeichnet einen Operator wie er vom Parser
eingelesen wird. So liest der Parser zum Beispiel den Operator der Addition
als $+
ein.
Siehe auch das Makro def-nud-equiv
,
um die nud
-Funktion auf
der Eigenschaftsliste eines Operators abzulegen.
Beispiel:
Das Beispiel zeigt die Expansion des Makros, wenn für den Operator der
Multiplikation $*
die Funktion parse-nary
als led
-Funktion
definiert wird.
* (macroexpand-1 '(def-led-equiv |$*| parse-nary)) (PUTPROP '$* #'PARSE-NARY 'LED) T
Quelltext:
(defmacro def-led-equiv (op equiv) (list 'putprop (list 'quote op) (list 'function equiv) (list 'quote 'led)))
Setzt den linksseitigen Vorrang lbp und den rechtsseitigen Vorrang rbp des Operators op auf der Eigenschaftsliste des Operators. Das Argument op kann auch eine Liste mit Operatoren sein. In diesem Fall erhalten alle Operatoren der Liste die angegebenen links- und rechtsseitigen Vorränge.
Ist für den angegeben Operator op oder einer der Operatoren, falls eine Liste mit Operatoren angegeben wird, bereits ein links- oder rechtsseitiger Vorrang definiert, muss das entsprechende Argument lbp oder rbp den gleichen Wert haben. Ansonsten bricht die Funktion mit einer Fehlermeldung ab.
Die Funktion set-lbp-and-rbp
wird von den Makros def-nud
und
def-led
genutzt, um jeweils die Definition einer nud
- oder
led
-Funktion sowie die dazugehörenden Vorränge auf der
Eigenschaftsliste abzulegen. Ansonsten wird diese Funktion vom Parser nicht
weiter genutzt.
Beispiele:
Die folgenden Beispiele zeigen wie die Funktion verwendet werden kann.
* (set-lbp-and-rbp '$/ 120 120) NIL * (set-lbp-and-rbp '($+ $-) 100 134) (NIL NIL)
Bemerkung:
Für die Definition des links- und rechtsseitigen Vorrangs wird bei der
Definition der Operatoren nicht die Funktion set-lbp-and-rbp
genutzt,
sondern die Makros def-rbp
und def-lbp
.
Quelltext:
(defun set-lbp-and-rbp (op lbp rbp) (cond ((not (consp op)) (let ((existing-lbp (getprop op 'lbp)) (existing-rbp (getprop op 'rbp))) (cond ((not lbp)) ((not existing-lbp) (putprop op lbp 'lbp)) ((not (eql existing-lbp lbp)) (merror "Incompatible LBP's defined for operator ~a" op))) (cond ((not rbp)) ((not existing-rbp) (putprop op rbp 'rbp)) ((not (eql existing-rbp rbp)) (merror "Incompatible RBP's defined for operator ~a" op))))) (t (mapcar #'(lambda (x) (set-lbp-and-rbp x lbp rbp)) op))))
Quelltext:
(defmacro def-nud-fun (op-name op-l . body) (list* 'defun-prop (list* op-name 'nud 'nil) op-l body))
Quelltext:
(defmacro def-nud ((op . lbp-rbp) bvl . body) (let ((lbp (nth 0 lbp-rbp)) (rbp (nth 1 lbp-rbp))) `(progn 'compile ,(make-parser-fun-def op 'nud bvl body) (set-lbp-and-rbp ',op ',lbp ',rbp))))
Quelltext:
(defmacro def-led-fun (op-name op-l . body) (list* 'defun-prop (list* op-name 'led 'nil) op-l body))
Quelltext:
(defmacro def-led ((op . lbp-rbp) bvl . body) (let ((lbp (nth 0 lbp-rbp)) (rbp (nth 1 lbp-rbp))) `(progn 'compile ,(make-parser-fun-def op 'led bvl body) (set-lbp-and-rbp ',op ',lbp ',rbp))))
Mit den Makros def-lbp
und def-rbp
werden der linksseitige oder
rechtsseitige Vorrang val eines Operators op definiert.
Siehe auch die Funktionen lbp
und rbp
,
um den Vorrang eines
Operators zu ermitteln.
Quelltext:
(defmacro def-lbp (op val) `(defprop ,op ,val lbp)) (defmacro def-rbp (op val) `(defprop ,op ,val rbp))
Die Funktionen lbp
und rbp
ermitteln den Vorrang eines Operators
op. Siehe auch die Makros def-lbp
und def-rbp
,
um
den Vorrang eines Operators zu definieren.
Quelltext:
(defun lbp (op) (cond ((getprop op 'lbp)) (t 200))) (defun rbp (op) (cond ((getprop op 'rbp)) (t 200)))
Quelltext:
(defmacro def-match (x m) `(defprop ,x ,m match))
Die Makros def-pos
, def-lpos
und def-rpos
erlauben die
Definition der Wortart für das Ergebnis eines Operators sowie der Wortarten
für ein linksseitiges oder ein rechtsseitiges Argument.
Siehe auch die Funktionen pos
,
lpos
und
rpos
,
um die Wortarten eines Operators zu ermitteln.
Quelltext:
(defmacro def-pos (op pos) `(defprop ,op ,pos pos)) (defmacro def-lpos (op pos) `(defprop ,op ,pos lpos)) (defmacro def-rpos (op pos) `(defprop ,op ,pos rpos))
Die Funktionen pos
, lpos
und rpos
geben die Wortart für
das Ergebnis eines Operators sowie die Wortarten für ein linksseitiges oder
ein rechtsseitiges Argument zurück.
Siehe auch die Makros def-pos
,
def-lpos
und
def-rpos
,
um die Wortarten für einen Operator zu definieren.
Quelltext:
(defun pos (op) (cond ((getprop op 'pos)) (t '$any))) (defun lpos (op) (cond ((getprop op 'lpos)) (t '$any))) (defun rpos (op) (cond ((getprop op 'rpos)) (t '$any)))
Die Symbole $any
, $clause
und $expr
bezeichnen die
Möglichkeiten Wortarten des Parsers. Ist für einen Operator die Wortart
eines Argumentes festgelegt und wird beim Einlesen von der Eingabe eine davon
verschiedene Wortart festgestellt, dann bricht das Einlesen mit einer
Fehlermeldung ab.
Siehe auch die Makros def-pos
,
def-lpos
und
def-rpos
,
um die Wortarten für einen Operator zu definieren und
die Funktionen pos
,
lpos
sowie rpos
,
um die
Wortarten eines Operators zu ermitteln.
Beispiel:
Die Wortart des Operators not
ist $clause
für das Ergebnis und
das Argument. Ein algebraischer Ausdruck der Wortart $expr
führt zu
einer Fehlermeldung und zum Abbruch des Einlesens.
(%i1) not a^2; incorrect syntax: Found algebraic expression where logical expression expected not a^2; ^
Bemerkung:
Der Mechanismus die Syntax eines Ausdrucks mit der Wortart zu prüfen, ist kaum implementiert. Viele Ausdrücke werden nicht als ungültig erkannt. Die Implementation ist genauer zu prüfen und gegebenenfalls zu verbessern und zu erweitern.
Quelltext:
(defprop $any "untyped" english) (defprop $clause "logical" english) (defprop $expr "algebraic" english)
Quelltext:
(defun mheader (op) (add-lineinfo (or (getprop op 'mheader) (list op))))
Quelltext:
(defmacro def-mheader (op header) `(defprop ,op ,header mheader))
Quelltext:
(defmacro def-collisions (op &rest alist) (let ((keys (do ((i 1 (ash i 1)) (lis alist (cdr lis)) (nl () (cons (cons (caar lis) i) nl))) ((null lis) nl)))) `(progn 'compile (defprop ,op ,(let nil (copy-tree keys )) keys) ,@(mapcar #'(lambda (data) `(defprop ,(car data) ,(do ((i 0 (logior i (cdr (assoc (car lis) keys :test #'eq)))) (lis (cdr data) (cdr lis))) ((null lis) i)) ,op)) alist))))
Quelltext:
(defun collision-lookup (op active-bitmask key-bitmask) (let ((result (logand active-bitmask key-bitmask))) (if (not (zerop result)) (do ((l (get op 'keys) (cdr l))) ((null l) (parse-bug-err 'collision-check)) (if (not (zerop (logand result (cdar l)))) (return (caar l)))))))
Quelltext:
(defun collision-check (op active-bitmask key) (let ((key-bitmask (get key op))) (if (not key-bitmask) (mread-synerr "~A is an unknown keyword in a ~A statement." (mopstrip key) (mopstrip op))) (let ((collision (collision-lookup op active-bitmask key-bitmask))) (if collision (if (eq collision key) (mread-synerr "This ~A's ~A slot is already filled." (mopstrip op) (mopstrip key)) (mread-synerr "A ~A cannot have a ~A with a ~A field." (mopstrip op) (mopstrip key) (mopstrip collision)))) (logior (cdr (assoc key (get op 'keys) :test #'eq)) active-bitmask))))
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Die Funktion mopstrip
wird von den Funktionen für die Fehlerbehandlung
des Parsers aufgerufen. Das Argument x ist ein Ausdruck, der auf einfache
Weise formatiert wird. Die Rückgabe ist das Ergebnis der Formatierung.
So werden die booleschen Konstanten t
und nil
zu $true
und
$false
formatiert, von Maxima-Bezeichnern werden die führenden Zeichen
$
oder %
entfernt und Symbole die einen Reverse-Alias haben,
werden durch diesen ersetzt.
Siehe auch die Funktion mread-synerr
für die Ausgabe von Fehlern beim
Parsen eines Ausdrucks.
Quelltext:
(defun mopstrip (x) (cond ((null x) 'false) ((or (eq x t) (eq x 't)) 'true) ((numberp x) x) ((symbolp x) (or (getprop x 'reversealias) (let ((name (symbol-name x))) (if (member (char name 0) '(#\$ #\%) :test #'char=) (subseq name 1) name)))) (t x)))
Tritt ein Fehler beim Parsen eines Ausdrucks von dem Stream
*parse-stream*
auf, wird von den Funktionen des Parsers die Funktion
mread-synerr
aufgerufen. Liest der Parser aus einer Datei, wird der
Dateiname ermittelt und ausgegeben. Dann wird die mit dem Argument
format-string übergebenen Fehlermeldung mit den Argumenten der Liste
l ausgegeben. Zuletzt wird ein Teil der fehlerhaften Eingabe ausgegeben
und die Stelle markiert, an der der Fehler festgestellt wurde. Dazu werden die
letzten Zeichen der Eingabe vom Parser in der globalen Liste
*parse-window*
gespeichert.
Die Funktionen parse-err
,
parse-bug-err
,
parse-delim-err
,
parse-erb-err
und
parse-premterm-err
sind spezialisierte Funktionen für bestimmte
Fehler, die die Funktion mread-synerr
aufrufen.
Quelltext:
(defun mread-synerr (format-string &rest l) (let (tem *errset* (file "stdin")) (errset (setq tem (file-position *parse-stream*)) (setq file (namestring *parse-stream*))) (when tem (format t "~%~a:~a:" file tem)) (format t "incorrect syntax: ") (apply 'format t format-string (mapcar #'(lambda (x) (if (symbolp x) (print-invert-case x) x)) l)) (when (eql *parse-stream* *standard-input*) (let ((n *parse-window-length*) some ch) (loop for i from (1- n) downto (- n 20) while (setq ch (nth i *parse-window*)) do (cond ((eql ch #\newline) (push #\n some) (push #\\ some)) ((eql ch #\tab) (push #\t some) (push #\\ some)) (t (push ch some)))) (format t "~%~{~c~}~%~vt^" some (- (length some) 2)) (read-line *parse-stream* nil nil))) (terpri) (throw 'maxima-continue t)))
Die Funktion parse-err
wird für eine allgemeine Fehler aufgerufen,
die beim Parsen einer Eingabe auftreten.
Siehe auch die Funktion mread-synerr
.
Quelltext:
(defun parse-err () (mread-synerr "Syntax error"))
Die Funktion parse-bug-err
wird vom Parser aufgerufen, wenn ein
interner Fehler beim Parsen einer Eingabe aufgetreten ist.
Siehe auch die Funktion mread-synerr
.
Quelltext:
(defun parse-bug-err (op) (mread-synerr "Parser bug in ~A. Please report this to the Maxima maintainers,~ ~%including the characters you just typed which caused the error. Thanks." (mopstrip op)))
Die Funktion parse-delim-err
wird vom Parser aufgerufen, wenn beim
Einlesen der Eingabe ein Fehler beim Lesen eines Begrenzungszeichen auftritt.
Siehe auch die Funktion mread-synerr
.
Quelltext:
(defun parse-delim-err (op) (mread-synerr "Illegal use of delimiter ~A" (mopstrip op)))
Die Funktion parse-erb-err
wird vom Parser aufgerufen, wenn beim
Lesen der Eingabe eine schließende Klammer auftritt und die öffnende
Klammer fehlt.
Siehe auch die Funktion mread-synerr
.
Quelltext:
(defun parse-erb-err (op l) (declare (ignore l)) (mread-synerr "Too many ~A's" (mopstrip op)))
Die Funktion parse-premterm-err
wird vom Parser aufgerufen, wenn eine
Eingabe vorzeitig abbricht.
Siehe auch die Funktion mread-synerr
.
Quelltext:
(defun parse-premterm-err (op) (mread-synerr "Premature termination of input at ~A." (mopstrip op)))
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Prüft, ob das Argument lex ein Symbol ist, das auf der Eigenschaftsliste
entweder eine led
- oder nud
-Funktion als Eintrag hat. In diesem
Fall ist die Rückgabe t
und ansonsten nil
.
Siehe auch die Funktion operatorp1
,
die zusätzlich prüft, ob ein
Vorrang zum Symbol auf der Eigenschaftsliste abgelegt ist.
Quelltext:
(defun operatorp (lex) (and (symbolp lex) (getpropl lex '(nud led))))
Prüft, ob das Argument lex ein Symbol ist, das auf der Eigenschaftsliste
entweder eine led
-, nud
-Funktion, einen links- oder
rechtsseitigen Vorrang als Eintrag hat. In diesem Fall ist die Rückgabe
t
und ansonsten nil
.
Siehe auch die Funktion operatorp
,
die nur prüft, ob eine
led
- oder nud
-Funktion auf der Eigenschaftsliste abgelegt ist.
Quelltext:
(defun operatorp1 (lex) (and (symbolp lex) (getpropl lex '(lbp rbp nud led))))
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
4.3.1 Einlesen von Zeichen | ||
4.3.2 Einlesen der Token | ||
4.3.3 Parsen der Eingabe |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Die fünf Routinen tyi
, parse-tyi-init
, parse-tyi
,
parse-tyipeek
und unparse-tyi
sind für das Lesen von Zeichen
von einem Stream verantwortlich. Diese Routinen nutzen für die Wahrnehmung
ihrer Aufgaben die folgenden globalen Variablen.
Standardwert: nil
Puffer der Funktionen parse-tyi
,
parse-tyipeek
und
unparse-tyi
.
Die Funktion unparse-tyi
stellt Zeichen in den
Puffer zurück. Die Funktionen parse-tyi
und parse-tyipeek
prüfen zuerst, ob ein Zeichen im Puffer vorliegt und geben, wenn vorhanden,
dieses zurück. Ansonsten wird das nächste Zeichen mit der Funktion
tyi
vom Stream geholt.
Standardwert: nil
Der Stream von dem die Funktionen Zeichen lesen.
Standardwert: -1
Das Zeichen, um auf das Ende eines Streams zu testen. Die Variable hat den Wert
-1
. Unklar ist, warum hier nicht das der Wert *mread-eof-obj*
verwendet wird.
Standardwert: nil
Ringpuffer für die letzten eingegebenen Zeichen. Die Zeichen in diesem Ringpuffer werden von der Fehlerbehandlung genutzt, um dem Nutzer die Stelle in der Eingabe anzuzeigen, wo ein Fehler beim Parsen aufgetreten ist.
Die Größe des Ringpuffers. Der Wert wird zu 25 initialisiert.
Standardwert: nil
Kann von der Funktion tyi
kein Zeichen gelesen werden, kontrolliert
die Variable, ob der Nutzer aufgefordert wird, eine Eingabe von der Tastatur
vorzunehmen.
Standardwert: nil
Das Prompt-Zeichen für die Eingabeaufforderung, wenn der Nutzer zum Eingeben von Zeichen aufgefordert wird.
Die elementare Routine für das Lesen eines Zeichens vom Stream
*parse-stream*
ist die Funktion tyi
. Die Argumente der Funktion
sind optional. Wird die Funktion ohne Argumente aufgerufen, werden die Zeichen
von der Standardeingabe *standard-input*
gelesen und das
EOF
-Zeichen ist nil
. Die Funktion tyi
hat drei
Hilfsfunktionen, die lokal zur Funktion definiert sind. Diese sind
tyi-raw
, backslash-check
und eat-continuations
.
Die Hilfsfunktion tyi-raw
versucht zunächst ein Zeichen zu lesen. Ist
dies nicht möglich und hat die Variable *prompt-on-read-hang*
den Wert
true
, wird der Prompt *read-hang-prompt*
ausgegeben, um ein
Zeichen von der Tastatur einzulesen.
Die Hilfsfunktionen backslash-check
und eat-continuations
testen,
ob ein Backslash #\
eingelesen wurde. Mit dem Backslash-Zeichen werden
Zeilen bei der Eingabe aus einer Datei fortgesetzt. Alle #\return
und
#\newline
-Zeichen die dem Backslash-Zeichen folgen, werden dann aus dem
Stream entfernt.
Quelltext:
(let ((previous-tyi #\a)) (defun tyi (&optional (stream *standard-input*) eof) (labels ((tyi-raw () (let ((ch (read-char-no-hang stream nil eof))) (if ch ch (progn (when (and *prompt-on-read-hang* *read-hang-prompt*) (princ *read-hang-prompt*) (force-output *standard-output*)) (read-char stream nil eof))))) (backslash-check (ch) (if (eq previous-tyi #\\ ) (progn (setq previous-tyi #\a) ch) (setq previous-tyi (if (eq ch #\\ ) (let ((next-char (peek-char nil stream nil eof))) (if (or (eq next-char #\newline) (eq next-char #\return)) (eat-continuations ch) ch)) ch)))) (eat-continuations (ch) (setq ch (tyi-raw)) (do () ((not (or (eq ch #\newline) (eq ch #\return)))) (let ((next-char (peek-char nil stream nil eof))) (if (and (eq ch #\return) (eq next-char #\newline)) (tyi-raw))) (setq ch (tyi-raw)) (let ((next-char (peek-char nil stream nil eof))) (if (and (eq ch #\\ ) (or (eq next-char #\return) (eq next-char #\newline))) (setq ch (tyi-raw)) (return-from eat-continuations ch)))) ch)) (let ((ch (tyi-raw))) (if (eq ch eof) ch (backslash-check ch))))))
Die Funktion parse-tyi-init
wird immer dann von der Funktion
parse-tyi
aufgerufen, wenn das nächste Zeichen vom Stream zu lesen ist.
Ist die globale Variable *parse-window*
noch nicht initialisiert, wird
dies erledigt. *parse-window*
wird dazu eine Liste der Länge
*parse-window-length*
zugewiesen und mit dem Aufruf nonc
zu
einer zirkulären Liste initialisiert. parse-tyi-init
holt sodann mit
der Funktion tyi
das nächste Zeichen vom Stream, schreibt das Zeichen
in die zirkuläre Liste *parse-window*
und gibt das Zeichen eingelesene
zurück.
Quelltext:
(defun parse-tyi-init (stream eof) (or *parse-window* (progn (setq *parse-window* (make-list *parse-window-length*)) (nconc *parse-window* *parse-window*))) (let ((tem (tyi stream eof))) (setf (car *parse-window*) tem *parse-window* (cdr *parse-window*)) tem))
Der Parser ruft die Funktion parse-tyi-init
direkt auf, sondern
die Funktion parse-tyi
. Die Funktion prüft zunächst, ob ein Zeichen
im Puffer *parse-tyi*
vorliegt. Ist dies der Fall, wird das Zeichen
zurückgegeben und der Puffer entsprechend korrigiert. Ansonsten wird mit der
Funktion parse-tyi-init
das nächste Zeichen vom Stream
*parse-stream*
geholt.
Quelltext:
(defun parse-tyi () (let ((tem *parse-tyi*)) (cond ((null tem) (parse-tyi-init *parse-stream* *parse-stream-eof*)) ((atom tem) (setq *parse-tyi* nil) tem) (t (setq *parse-tyi* (cdr tem)) (car tem)))))
Die Funktion parse-tyipeek
holt das nächste Zeichen vom Stream, ohne
es vom Stream zu entfernen. Entweder ist noch ein Zeichen im Puffer
*parse-stream*
vorhanden, welches zurückgegeben wird, oder es wird mit
der Funktion parse-tyi-init
ein Zeichen vom Stream gelesen und in den
Puffer geschrieben.
Quelltext:
(defun parse-tyi-peek () (let ((tem *parse-tyi*)) (cond ((null tem) (setq *parse-tyi* (parse-tyi-init *parse-stream* *parse-stream-eof*))) ((atom tem) tem) (t (car tem)))))
Mit der Funktion unparse-tyi
wird ein Zeichen ch wieder in
in den Puffer *parse-tyi*
zurückgeschrieben.
Quelltext:
(defun unparse-tyi (ch) (let ((tem *parse-tyi*)) (if (null tem) (setq *parse-tyi* ch) (setq *parse-tyi* (cons ch tem)))))
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Jetzt werden die Funktionen definiert, um ein Token einzulesen. kMaxima unterscheidet die folgenden Token:
Operatoren sind zum Beispiel die Zeichen für die Addition "+" oder
Multiplikation "*". Operatoren werden von der Funktion
scan-operator-token
eingelesen. Die Operatoren die von dieser Funktion
behandelt werden, sind in der Liste *maxima-operators*
enthalten.
kMaxima liest ganze Zahlen oder Gleitkommazahlen mit den Funktionen
scan-number-before-dot
,
scan-number-after-dot
,
scan-number-exponent
und scan-number-rest
ein. Weitere
Funktionen sind scan-digits
und make-number
.
Zeichenketten werden von den Funktionen scan-string
eingelesen.
Liest einen Maxima-Bezeichner mit der Funktion scan-maxima-token
ein.
Maxima-Bezeichner erhalten einen $
-Zeichen als Präfix.
Lisp-Bezeichner werden von der Funktion scan-lisp-token
eingelesen. Im
Unterschied zu kMaxima Token erhalten Lisp Token keinen Präfix.
Ein Lisp-Keyword hat den Präfix :
und wird von der Funktion
scan-keyword-token
eingelesen.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Standardwert: (list nil)
kMaxima liest mit der Funktion scan-one-token
die Token von einem Stream.
Mit der Funktion peek-one-token
wird das nächste Token gelesen, ohne
es vom Stream zu entfernen. Dazu hat kMaxima den Puffer
*scan-buffered-token*
. Wird die Funktion peek-one-token
aufgerufen und es liegt kein Token im Puffer vor, wird das nächste Token aus
dem Stream gelesen und in dem Puffer abgelegt.
Die Funktion peek-one-token
prüft zunächst, ob im Puffer
*scan-buffered-token*
ein Token vorliegt. Ist dies der Fall wird das
Token zurückgegeben. Ansonsten wird mit der Funktion
scan-one-token
ein Token vom Stream gelesen, in den Puffer geschrieben
und als Ergebnis zurückgegeben.
Bemerkung:
Der Originalcode hat einen Fehler. Wird wiederholt mit der Funktion
peek-one-token
versucht ein Token von einem Stream zu lesen, der kein
weiteres Zeichen enthält, wird die globale Variable
*scan-buffered-token*
auf den Wert (t)
gesetzt. Die Folge ist,
dass immer der Wert nil
und nicht wie erwartet der Wert des Argumentes
eof zurückgegeben wird. Es ist unklar, ob dieser Fehler Konsequenzen
in Maxima hat.
Quelltext:
(defun peek-one-token (&optional (eof-p nil) (eof nil) &aux token) (cond ((car *scan-buffered-token*) (cdr *scan-buffered-token*)) (t (cond ((eq eof (setq token (scan-one-token eof-p eof))) eof) (t (rplacd *scan-buffered-token* token) (cdr (rplaca *scan-buffered-token* t)))))))
Die Funktion scan-one-token
liest ein einzelnes Token vom Stream.
Zunächst prüft die Funktion jedoch, ob bereits ein Token im Puffer
*scan-buffered-token*
vorliegt. Liegt kein Token im Puffer vor, geht die
Funktion folgendermaßen vor. Zuerst wird mit der Funktion
scan-operator-token
geprüft, ob ein Operator im Stream vorliegt. Dann
wird geprüft, ob der Stream noch Zeichen enthält. Ist das nächste Zeichen
im Stream #\/
, wird mit der Funktion gooble-comment
ein Kommentar
vom Stream gelesen. Mit dem Zeichen #\.
beginnt eine Gleitkommazahl,
die mit der Funktion scan-number-after-dot
vom Stream gelesen wird. Als
nächste wird geprüft, ob das Zeichen #\"
vom Stream gelesen wurde.
In diesem Fall wird die Funktion scan-string
aufgerufen, um eine
Zeichenkette vom Stream zu lesen. Mit dem Zeichen #\?
wird eine
Lisp-Zeichenkette, ein Lisp-Schlüsselwort oder ein Lisp-Bezeichner
eingeleitet. In diesem Fällen werden die entsprechenden Funktionen
scan-string
,
scan-keyword-token
oder
scan-lisp-token
aufgerufen. Liegt jetzt eine Ziffer vor, dann wird mit
der Funktion scan-number-before-dot
die Zahl vom Stream gelesen. Zuletzt
wird die Funktion aufgerufen, um einen kMaxima-Bezeichner vom Stream zu lesen.
Quelltext:
(defun scan-one-token (&optional (eof-p nil) (eof nil) &aux test) (cond ((car *scan-buffered-token*) (rplaca *scan-buffered-token* nil) (cdr *scan-buffered-token*)) ((scan-operator-token *maxima-operators*)) ((eql (setq test (parse-tyi-peek)) *parse-stream-eof*) (parse-tyi) (if eof-p eof (merror "parser: end of file while scanning expression."))) ((eql test #\/ ) (parse-tyi) (cond ((char= (parse-tyi-peek) #\* ) (parse-tyi) (gobble-comment) (scan-one-token eof-p eof)) (t '$/))) ((eql test #\. ) (parse-tyi) (if (digit-char-p (parse-tyi-peek) 10) (scan-number-after-dot (list (list #\. ) nil)) '|$.|)) ((eql test #\" ) (parse-tyi) (scan-string)) ((eql test #\? ) (parse-tyi) (cond ((char= (parse-tyi-peek) #\" ) (parse-tyi) (scan-string)) ((char= (parse-tyi-peek) #\: ) (scan-keyword-token)) (t (scan-lisp-token)))) ((digit-char-p test 10) (scan-number-before-dot nil)) (t (scan-maxima-token))))
Die Funktion gooble-comment
liest einen Kommentar vom Stream, der von den
Zeichen /*
und */
eingeschlossen ist. Kommentare können
verschachtelt sein.
Quelltext:
(defun gobble-comment () (do ((depth 1) (ch (parse-tyi-peek) (parse-tyi-peek))) ((eql 0 depth) t) (cond ((eql ch *parse-stream-eof*) (merror "Parser: end of file in comment.")) ((char= ch #\* ) (parse-tyi) (cond ((char= (parse-tyi-peek) #\/ ) (parse-tyi) (decf depth)))) ((char= ch #\/ ) (parse-tyi) (cond ((char= (parse-tyi-peek) #\*) (parse-tyi) (incf depth)))) (t (parse-tyi)))))
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Standardwert: (#\tab #\space #\linefeed #\return #\page #\newline)
Liste mit Zeichen, die vom Parser als Whitespace-Zeichen gelesen werden.
Wird von der Funktion scan-one-token
aufgerufen, um zu prüfen, ob
das nächste Token ein Operator ist, der in der Liste obj
enthalten ist.
Als Argument wird die Liste der kMaxima-Operatoren *maxima-operators*
von der Funktion scan-one-token
übergeben. Die Funktion liest
Whitespace-Zeichen vom Stream und ruft dann die Funktion
scan-operator-token-aux
auf, die die eigentliche Aufgabe hat, ein
eventuell vorhandenen Operator vom Stream zu lesen.
Siehe die globale Variable *whitespaces*
für die Liste von Zeichen,
die von kMaxima als Whitespace-Zeichen betrachtet werden.
Quelltext:
(defun scan-operator-token (obj) (do ((ch (parse-tyi-peek) (parse-tyi-peek))) ((not (member ch *whitespaces*))) (parse-tyi)) (scan-operator-token-aux obj))
Wird von der Funktion scan-operator-token
aufgerufen, um einen
eventuell auf dem Stream vorhandenen Operator einzulesen. Das Argument
obj
ist eine Liste mit kMaxima-Operatoren. Die Funktion wird mit der
Liste *maxima-operators*
oder rekursiv mit einem Teil dieser Liste
aufgerufen. Ist kein Operator auf dem Stream vorhandenen oder hat der Stream
keine weiteren Zeichen, wird als Ergebnis der Wert nil
zurückgegeben.
Wird ein Operator vom Stream gelesen, dann ist die Rückgabe der Funktion
ein kMaxima-Bezeichner für den Operator. Zum Beispiel hat der Operator
+
den kMaxima-Bezeichner $+
.
Quelltext:
(defun scan-operator-token-aux (obj) (labels ((parser-assoc (ch lis) (do ((v lis (cdr v))) ((null v)) (cond ((consp (car v)) (if (eql (caar v) ch) (return (car v)))) ((eql (car v) ch) (return v)))))) (let* ((ch (parse-tyi-peek)) (lis (if (eql ch *parse-stream-eof*) nil (parser-assoc ch obj))) result) (cond ((null lis) nil) (t (parse-tyi) (cond ((atom (cadr lis)) (setq result (scan-operator-token-aux (list (cdr lis))))) ((null (cddr lis)) (setq result (and (eql (car (cadr lis)) 'ans) (or (not (alphabetp (cadr (exploden (cadadr lis))))) (member (parse-tyi-peek) *whitespaces*)) (cadr (cadr lis))))) (t (let ((res (and (eql (car (cadr lis)) 'ans) (cadadr lis))) (token (scan-operator-token-aux (cddr lis)))) (setq result (or token res (scan-operator-token-aux (list (cadr lis)))))))) (or result (unparse-tyi ch)) result)))))
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Lese ein kMaxima-Token vom Stream. Die Rückgabe ist ein Symbol mit den
Zeichen, die vom Stream gelesen wurden, denen ein $
-Zeichen vorangestellt
wird.
Die Zeichen werden mit der Funktion scan-token
vom Stream gelesen.
scan-token
gibt eine Liste mit den eingelesenen Zeichen zurück. Der
Liste wird das $
-Zeichen vorangestellt. Mit der Funktion
implode
wird die Liste der Zeichen in ein internes kMaxima-Symbol
umgewandelt und mit der Funktion getalias
geprüft, ob zu dem Symbol
ein Alias-Name vorliegt.
Quelltext:
(defun scan-maxima-token () (getalias (implode (cons '#\$ (scan-token t)))))
Lese ein Lisp-Token vom Stream. Ein Lisp-Symbol beginnt mit einem
?
-Zeichen.
Quelltext:
(defun scan-lisp-token () (let ((charlist (scan-token nil))) (if charlist (implode charlist) (mread-synerr "Lisp symbol expected."))))
Lese ein Lisp-Schlüsselwort vom Stream. Das Schlüsselwort beginnt mit
einem ?
-Zeichen, dem ein :
-Zeichen folgt.
Quelltext:
(defun scan-keyword-token () (let ((charlist (cdr (scan-token nil)))) (if charlist (let ((*package* (find-package :keyword))) (implode charlist)) (mread-synerr "Lisp keyword expected."))))
Lese ein kMaxima-, Lisp- oder Keyword-Token vom Stream. Hat das Argument
flag
den Wert t
, dann wird ein kMaxima-Token vom Stream gelesen,
ansonsten wird ein Lisp-Token vom Stream gelesen.
Ein Token kann alle Zeichen enthalten, die alphabetisch sind. Das sind die
Zeichen A ... Z
und a ... z
sowie alle Zeichen die in der
globalen Liste *alphabet*
enthalten sind. Siehe auch die Funktion
alphabetp
,
mit der getestet wird, ob ein Zeichen alphabetisch ist.
Die Funktion wird von den Funktionen scan-maxima-token
,
scan-lisp-token
und scan-keyword-token
aufgerufen.
Quelltext:
(defun scan-token (flag) (do ((ch (parse-tyi-peek) (parse-tyi-peek)) (l () (cons ch l))) ((or (eql ch *parse-stream-eof*) (and flag (not (or (digit-char-p ch (max 10 *read-base*)) (alphabetp ch) (char= ch #\\ ))))) (nreverse (or l (list (parse-tyi))))) (when (char= (parse-tyi) #\\ ) (setq ch (parse-tyi))) (setq flag t)))
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Lese eine Zeichenkette vom Stream.
Quelltext:
(defun scan-string () (let ((buf (make-array 50 :element-type '#.(array-element-type "a") :fill-pointer 0 :adjustable t))) (do ((ch (parse-tyi-peek) (parse-tyi-peek))) ((cond ((eql ch *parse-stream-eof*)) ((char= ch #\") (parse-tyi) t)) (copy-seq buf)) (if (char= (parse-tyi) #\\ ) (setq ch (parse-tyi))) (vector-push-extend ch buf))))
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Standardwert: #\D
Enthält das Zeichen, das von kMaxima als das Exponent-Zeichen verwendet wird.
Standardwert: (#\E #\e #\F #\f #\B #\b #\D #\d #\S #\s )
Enthält Liste der Zeichen, die von kMaxima als ein Zeichen für den Exponenten erkannt werden.
Standardwert: t
Standardwert: 100000
Standardwert: 0
Wandelt das Argument x rationale Zahlen von einem Lisp-Format in das kMaxima-Format um. Das Argument x kann auch eine ganze Zahl sein, die unverändert zurückgegeben wird.
Quelltext:
(defun cl-rat-to-maxima (x) (if (integerp x) x (list '(rat simp) (numerator x) (denominator x))))
Das Argument data ist eine Liste mit den Zeichen der Zahl, die der Parser eingelesen hat. Die Liste data enthält Teillisten mit den Teil der Zahl vor einem Komma, dem Teil nach einem Komma und dem Exponenten der Zahl. Die einzelnen Elemente einer Zahl sind in einer umgekehrten Reihenfolge im Argument data enthalten. Den Aufbau der Liste zeigt das folgende Beispiel:
12.45e34 -> '((#\4 #\5) (#\+) (#\E) (#\3 #\4) (#\.) (#\1 #\2))
Die Rückgabe ist eine Zahl in der internen Darstellung von kMaxima. Diese sind ganze Zahlen, rationale Zahlen, Gleitkommazahlen oder große Gleitkommazahlen.
Quelltext:
(defun make-number (data) (setq data (nreverse data)) (let ((marker (car (nth 3 data)))) (unless (eql marker +flonum-exponent-marker+) (when (member marker '(#\E #\F #\S #\D #\L )) (setf (nth 3 data) (list +flonum-exponent-marker+))))) (if (not (equal (nth 3 data) '(#\B))) (read-from-string (coerce (apply #'append data) 'string)) (let ((int-part (read-from-string (coerce (or (first data) '(#\0)) 'string))) (frac-part (read-from-string (coerce (or (third data) '(#\0)) 'string))) (frac-len (length (third data))) (exp-sign (first (fifth data))) (expo (read-from-string (coerce (sixth data) 'string)))) (if (and $fast_bfloat_conversion (> (abs expo) $fast_bfloat_threshold)) (let* ((extra-prec (+ *fast-bfloat-extra-bits* (ceiling (log expo 2d0)))) (fpprec (+ fpprec extra-prec)) (mant (+ (* int-part (expt 10 frac-len)) frac-part)) (bf-mant (bcons (intofp mant))) (p (power (bcons (intofp 10)) (- (if (char= exp-sign #\- ) (- expo) expo) frac-len))) (result (mul bf-mant p))) (let ((fpprec (- fpprec extra-prec))) (check-bigfloat result))) (let ((ratio (* (+ int-part (* frac-part (expt 10 (- frac-len)))) (expt 10 (if (char= exp-sign #\-) (- expo) expo))))) ($bfloat (cl-rat-to-maxima ratio)))))))
Quelltext:
(defun scan-digits (data continuation? continuation &optional exponent-p) (do ((ch (parse-tyi-peek) (parse-tyi-peek)) (l () (cons ch l))) ((not (and (characterp ch) (digit-char-p ch (max 10 *read-base*)))) (cond ((member ch continuation?) (funcall continuation (list* (list (char-upcase (parse-tyi))) (nreverse l) data))) ((and (null l) exponent-p) (merror "parser: incomplete number; missing exponent?")) (t (make-number (cons (nreverse l) data))))) (parse-tyi)))
Quelltext:
(defun scan-number-exponent (data) (push (list (if (or (char= (parse-tyi-peek) #\+ ) (char= (parse-tyi-peek) #\- )) (parse-tyi) #\+ )) data) (scan-digits data nil nil t))
Quelltext:
(defun scan-number-rest (data) (let ((ch (caar data))) (cond ((member ch '(#\.)) (scan-number-after-dot data)) ((member ch *exponent-chars*) (setf data (push (list #\. ) (rest data))) (push (list #\0 ) data) (push (list ch ) data) (scan-number-exponent data)))))
Quelltext:
(defun scan-number-before-dot (data) (scan-digits data (push #\. *exponent-chars*) #'scan-number-rest))
Quelltext:
(defun scan-number-after-dot (data) (scan-digits data *exponent-chars* #'scan-number-exponent))
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
nud
-FunktionEin Prefix-Operator hat das Argument auf der rechten Seite des Operators.
Trifft der Parser auf einen Prefix-Operator wird seine nud-Funktion aufgerufen,
um das Argument auf der rechten Seite zu lesen. Vordefinierte nud-Funktionen
sind parse-prefix
, parse-matchfix
und parse-nofix
.
led
-FunktionPostfix-Operatoren haben ihr Argument auf der linken Seite. Infix-Operatoren
auf der linken und rechten Seite. Um das Argument auf der linken Seite zu holen
wird vom Parser die led-Funktion des Operators aufgerufen. Vordefinierte
led-Funktionen sind parse-postfix
, parse-infix
und
parse-nary
.
Die rechte Bindungskraft des Operators. Ist die rechte Bindungskraft eines
Operators größer als die linke Bindungskraft des folgenden Operators, zum
Beispiel ist im Ausdruck 2*x+3
die rechte Bindungskraft der
Multiplikation größer als die linke Bindungskraft der Addition, wird die
gesamte linke Seite als Argument des nachfolgenden Operators betrachtet. In
diesem Beispiel ist 2*x
das linke Argument der Addition. Der Ausdruck
wird also als (2*x)+3
gelesen.
Die linke Bindungskraft des Operators. Solange die linke Bindungskraft des
nachfolgenden Operators größer ist als die rechte Bindungskraft des
vorhergehenden Operators, fährt der Parser mit dem Einlesen von Token fort.
In dem Beispiel 3+2*x
wird die Ziffer 2
nicht als nachfolgendes
Argument der Addition eingelesen, da die linksseitige Bindungskraft der
Multiplikation größer als die rechtsseitige Bindungskraft der Addition.
Der Parser fährt fort und liest den Ausdruck 2*x
vollständig ein.
Der Ausdruck wird also 3+(2*x)
gelesen.
pos
meint "Part of Speach" was mit Wortart übersetzt werden kann.
Damit kann der Typ des Operators festgelegt werden. Es werden die Wortarten
any
für einen beliebigen Typ, clause
für einen logischen Typ
und expr
für einen Ausdruck unterschieden. Anhand der Wortart kann der
Parser feststellen, ob der eingelesene Ausdruck die korrekte Syntax hat. Diese
Funktionalität ist jedoch nur sehr begrenzt implementiert.
lpos
ist die Wortart des Arguments die auf der linken Seiten des
Operators vom Parser erwartet wird.
rpos
ist die Wortart des Arguments die auf der linken Seiten des
Operators vom Parser erwartet wird.
Der Parser definiert die folgenden Operatoren:
Operator nud led header lbp rbp pos lpos rpos ------------------------------------------------------------------------ [ matchfix function (mlist) 200 any any ] delim-err erb-err 5 ( function function (mprogn) ) delim-err erb-err 5 ' function (mquote) '' function : infix (msetq) 180 20 any any any :: infix (mset) 180 20 any any any := infix (mdefine) 180 20 any any any ::= infix (mdefmacro) 180 20 any any any ! postfix (mfactorial) 160 expr expr !! function ($genfact) 160 ^ function (mexpt) 140 139 expr expr expr ^^ function (mncexpt) 140 139 expr expr expr . infix (mnctimes) 130 129 expr expr expr * nary (mtimes) 120 expr ** / prefix (mquotient) 120 120 expr expr expr + prefix function (mplus) 100 134 expr expr - prefix (mminus) 100 134 expr expr = infix (mequal) 80 80 clause expr expr > infix (mgreaterp) 80 80 clause expr expr >= infix (mgeqp) 80 80 clause expr expr < infix (mlessp) 80 80 clause expr expr <= infix (mleqp) 80 80 clause expr expr $ premterm-err (nodisplay) -1 ; premterm-err (display) -1 && delim-err -1 # infix (mnotequal) 80 80 clause expr expr , nary ($ev) 10 any any not prefix (mnot) 70 clause clause clause and nary (mand) 65 clause clause or nary (mor) 60 clause clause then delim-err 5 25 else delim-err 5 25 elseif delim-err 5 45 any clause if function (mcond) 200 45 any clause do parse-$do (mdo) 25 25 any for parse-$do 25 200 any from parse-$do 25 95 any in 95 step parse-$do 25 95 expr next parse-$do 25 45 any thru parse-$do 25 95 expr unless parse-$do 25 45 clause while parse-$do 25 45 clause
Standardwert: nil
Der Prompt der Funktion mread
.
Hat *mread-prompt*
einen von
nil
verschiedenen Wert, erwartet mread
die Eingabe von der
Tastatur.
Standardwert: nil
Wird von der Funktion mread
an einen Wert gebunden.
mread
ist die Hauptfunktion des Parsers und liest einen kMaxima-Ausdruck
von einem Stream stream ein. Das Argument stream kann die Eingabe
von der Tastatur, aus einer Datei oder irgendeinem anderen Stream bezeichnen.
Das Argument eof ist Optional und gibt das Zeichen an, mit dem
signalisiert wird, das der Stream keine weitere Zeichen enthält.
Die Rückgabe ist ein kMaxima-Ausdruck in der internen Darstellung. Wird die
Eingabe eines Ausdrucks mit ;
abgeschlossen, dann wird dem Ausdruck
der Operator displayinput
vorangestellt. Damit wird der aufrufenden
Funktion signalisiert, dass das Ergebnis ausgegeben werden soll. Wird die
Eingabe dagegen mit $
abgeschlossen, dann wird dem eingelesenen Ausdruck
der Operator nodisplayinput
vorangestellt.
Hat die globale Variable *mread-prompt*
einen von nil
verschiedenen Wert, dann wird von der Tastatur eingelesen. In diesem Fall wird
überprüft, ob die globale Variable *parse-window*
initialisiert ist.
Ist dies der Fall, dann wird das Symbol nil
in die zirkuläre Liste
*parse-window*
geschrieben, um die folgende Eingabe von der letzten
Eingabe zu trennen. Dann wird der Prompt *mread-prompt*
ausgegeben.
Beispiel:
a+b; ((DISPLAYINPUT) NIL ((MPLUS) $A $B)) * (mread *standard-input*) a+b$ ((NODISPLAYINPUT) NIL ((MPLUS) $A $B))
Quelltext:
(defun mread (stream &optional eof) (let ((*parse-stream* stream) (*mread-eof-obj* eof) (*scan-buffered-token* (list nil)) (*parse-tyi* nil)) (when *mread-prompt* (when *parse-window* (setf (car *parse-window*) nil *parse-window* (cdr *parse-window*))) (princ *mread-prompt*) (force-output)) (if (eq *mread-eof-obj* (peek-one-token t *mread-eof-obj*)) *mread-eof-obj* (do ((labels ()) (input (parse '$any 0) (parse '$any 0))) (nil) (case (peek-one-token) ((|$;| |$$| ) (return (list (mheader (scan-one-token)) (if labels (cons (mheader '|$[| ) (nreverse labels))) input))) ((|$&&|) (scan-one-token) (if (symbolp input) (push input labels) (mread-synerr "Invalid && tag. Tag must be a symbol"))) (t (parse-bug-err 'mread)))))))
Die Funktion parse
wird von mread
aufgerufen, um die Token vom
Stream zu lesen. mread
ruft parse
mit den Argumenten $any
für mode und 0
für rbp auf. Wird parse
rekursiv
von den Funktionen des Parsers aufgerufen, enthält das Argument mode
die erwartete Wortart und das Argument rbp den aktuellen rechtsseitigen
Vorrang.
Wird ein Operator eingelesen, dann wird die entsprechende led
- oder
nud
-Funktion von den lokalen Funktionen led-call
oder
nud-call
geholt und aufgerufen.
Das Einlesen vom Stream wird abgebrochen, wenn der letzte rechtsseitige Vorrang
größer oder gleich dem linksseitigen Vorrang des nächsten Operators auf
dem Stream ist. Die Operatoren ;
, $
und &&
haben einen
negativen linksseitigen Vorrang von -1
und beenden die Eingabe immer.
Quelltext:
(defun parse (mode rbp) (labels ((led-call (op l) (let ((tem (getprop op 'led)) res) (setq res (if (null tem) (mread-synerr "~A is not an infix operator" (mopstrip op)) (funcall tem op l))) res)) (nud-call (op) (let ((tem (getprop op 'nud)) res) (setq res (if (null tem) (if (operatorp op) (mread-synerr "~A is not a prefix operator" (mopstrip op)) (cons '$any op)) (funcall tem op))) res))) (do ((left (nud-call (scan-one-token)) (led-call (scan-one-token) left))) ((>= rbp (lbp (peek-one-token))) (convert left mode)))))
Ist ein Ausdruck vollständig eingelesen, wird der Ausdruck als Argument
item an die Funktion convert
übergegeben. Die Funktion prüft,
ob der Ausdruck item die Wortart mode
hat. Ist dies der Fall wird
der eingelesene Ausdruck zurückgegeben. Ansonsten wird ein Parser-Fehler
generiert wird.
Quelltext:
(defun convert (item mode) (if (or (eq mode (car item)) (eq '$any mode) (eq '$any (car item))) (cdr item) (mread-synerr "Found ~A expression where ~A expression expected" (getprop (car item) 'english) (getprop mode 'english))))
Das Argument op ist ein Prefix-Operator. Die Funktion parse-prefix
gibt eine Liste zurück, die als erstes Element die Wortart des Operators
op und als zweites Element das Symbol des Operators bzw. den Header
enthält. Um das Argument des Operators zu holen, wird die Funktion
parse
mit der Wortart des rechtsseitigen Arguments sowie des
rechtsseitigen Vorrang aufgerufen.
Quelltext:
(defun parse-prefix (op) (list (pos op) (mheader op) (parse (rpos op) (rbp op))))
Das Argument op ist ein Postfix-Operator und l enthält das auf
der linken Seite des Operators eingelesene Argument. Die Funktion
parse-postfix
gibt als Ergebnis eine Liste zurück, die als erstes
Element die Wortart des Operators op und als zweites Element das Symbol
des Operators enthält. Das dritte Element ist die Rückgabe der Funktion
convert
mit dem Argumenten l und der linksseitigen Wortart des
Operators op, die mit der Funktion lpos
ermittelt wird.
Quelltext:
(defun parse-postfix (op l) (list (pos op) (mheader op) (convert l (lpos op))))
Das Argument op ist ein Infix-Operator und l enthält das auf der
linken Seite des Operators eingelesene Argument. Die Funktion
parse-infix
gibt als Ergebnis eine Liste zurück, die als erstes Element
die Wortart des Operators op und als zweites Element das Symbol des
Operators enthält. Das dritte Element der Liste ist das Argument das auf der
linken Seite des Operators eingelesen wurde. Das vierte Element ist das
rechte Argument des Infix-Operators, das mit der Funktion parse
eingelesen wird. parse
wird mit der rechtsseitigen Wortart und dem
rechtsseitigen Vorrang des Operators op aufgerufen.
Quelltext:
(defun parse-infix (op l) (list (pos op) (mheader op) (convert l (lpos op)) (parse (rpos op) (rbp op))))
Das Argument op ist ein Nofix-Operator. Die Rückgabe der Funktion
parse-nofix
ist eine Liste mit der Wortart des Operators als erstes
Element und einer Liste als zweites Element, die als Element die interne
Bezeichnung des Operators hat.
Siehe auch die Funktion $nofix
,
um einen Nofix-Operator zu
definieren.
Beispiel:
kMaxima kennt keinen Nofix-Operator. Daher wird zunächst als Beispiel
ein Nofix-Operator quit
definiert.
(%i1) nofix(quit); (%o1) quit (%i2) quit; 0 * (parse-nofix '$quit) ($ANY ($QUIT))
Quelltext:
(defun parse-nofix (op) (list (pos op) (mheader op)))
Das Argument op ist ein Nary-Operator, der eine beliebige Anzahl an
Argumenten haben kann. Das Argument l ist das auf der linken Seite
des Operators eingelesene Argument. Die Rückgabe ist eine Liste mit der
Wortart des Operators als erstes Argument, dem Symbol des Operators als
zweites Argument und dem bisher eingelesenen Argument als drittes Argument.
Mit der Funktion prsnary
werden die weiteren Argumente vom Parser
eingelesenen. Die Funktion wird mit dem Operator op, der linksseitigen
Wortart und dem linksseitigen Vorrang des Operators op aufgerufen.
Quelltext:
(defun parse-nary (op l) (list* (pos op) (mheader op) (convert l (lpos op)) (prsnary op (lpos op) (lbp op))))
Wird von der Funktion parse-nary
aufgerufen, um alle verbleibenden
rechtsseitigen Argumente des Operators op vom Stream zu lesen. Die
Argumente müssen die Wortart mode haben. Das Argument rbp
ist der rechtsseitige Vorrang des zuletzt gelesenen Operators.
Bemerkung:
In kMaxima und im Original Maxima wird die Funktion nur von der Funktion
parse-nary
. Die Funktion könnte daher lokal definiert werden.
Quelltext:
(defun prsnary (op mode rbp) (do ((nl (list (parse mode rbp)) (cons (parse mode rbp) nl))) ((not (eq op (peek-one-token))) (nreverse nl)) (scan-one-token)))
Das Argument op ist ein Matchfix-Operator. Es wird eine Liste
zurückgegeben, deren erstes Element die Wortart des Matchfix-Operators ist.
Das zweite Argument ist der Header des Operators. Mit der Funktion
prsmatch
werden sodann die Operanden des Operators geholt.
Quelltext:
(defun parse-matchfix (op) (list* (pos op) (mheader op) (prsmatch (getprop op 'match) (lpos op))))
Lese die rechtsseitigen Argumente eines Matchfix-Operators vom Stream. Die Argumente sind mit Kommata voneinander getrennt.
Bemerkung:
Der rechtsseitige Vorrang jedes Matchfix-Operators wird hier immer mit dem
Wert 10
definiert. Dies könnte vermieden werden, wenn der
für einen Matchfix-Operator, wir für andere Operator auch, ein
rechtsseitiger Vorrang definiert wird.
Quelltext:
(defun prsmatch (match mode) (cond ((eq match (peek-one-token)) (scan-one-token) nil) (t (do ((nl (list (parse mode 10)) (cons (parse mode 10) nl))) ((eq match (peek-one-token)) (scan-one-token) (nreverse nl)) (if (eq '|$,| (peek-one-token)) (scan-one-token) (mread-synerr "Missing ~A" (mopstrip match)))))))
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Die Operatoren für die Definition einer Liste.
Beispiel:
* (caddr (mread *standard-input*)) [a, b, c]; ((MLIST) $A $B $C)
Quelltext:
(def-nud-equiv |$[| parse-matchfix) (def-match |$[| |$]|) (def-lbp |$[| 200) (def-pos |$[| $any) (def-lpos |$[| $any) (def-mheader |$[| (mlist)) (def-led (|$[| 200) (op left) (setq left (convert left '$any)) (if (numberp left) (parse-err)) (let ((header (if (atom left) (list (amperchk left) 'array) '(mqapply array))) (right (prsmatch '|$]| '$any))) (cond ((null right) (mread-synerr "No subscripts given")) ((atom left) (setq right (cons header right)) (cons '$any (getalias right))) (t (cons '$any (cons header (cons left right))))))) (def-nud-equiv |$]| delim-err) (def-led-equiv |$]| erb-err) (def-lbp |$]| 5)
Operatoren für die Definition einer Liste mit Argumenten oder einer
Block-Anweisung. Mit der led
-Funktion des Operators $(
wird
die Liste der Argumente einer Funktion eingelesen. Die nud
-Funktion
des Operators $(
liest dagegen eine Block-Anweisung ein.
Beispiel:
* (caddr (mread *standard-input*)) f(a,b,c); (($F) $A $B $C) * (caddr (mread *standard-input*)) (a,b,c); ((MPROGN) $A $B $C)
Quelltext:
(def-mheader |$(| (mprogn)) (def-nud (|$(| 200) (op) (let ((right) (hdr (mheader '|$(|))) (cond ((eq '|$)| (peek-one-token)) (parse-err)) ((or (null (setq right (prsmatch '|$)| '$any))) (cdr right)) (cons '$any (cons hdr right))) (t (cons '$any (car right)))))) (def-led (|$(| 200) (op left) (setq left (convert left '$any)) (if (numberp left) (parse-err)) (let ((hdr (and (atom left) (mheader (amperchk left)))) (r (prsmatch '|$)| '$any))) (cons '$any (cond ((atom left) (cons hdr r)) (t (cons '(mqapply) (cons left r))))))) (def-nud-equiv |$)| delim-err) (def-led-equiv |$)| erb-err) (def-lbp |$)| 5)
Definition des Quote-Operators.
Die Rückgabe ist ein Ausdruck ((mquote) expr)
, wenn expr
ein
Atom, eine Liste oder eine Block-Anweisung ist. Erhält eine Funktion den
Quote-Operator wie zum Beispiel in 'f(x)
, dann wird ein Ausdruck mit der
Substantivform der Funktion zurückgegeben. Dies gilt auch für
Array-Funktionen wie zum Beispiel 'f[a](x)
. Siehe auch die Beispiele
für die einzelnen Fälle.
Beispiele:
* (caddr (mread *standard-input*)) '(expr); ((MQUOTE) $EXPR) * (caddr (mread *standard-input*)) 'atom; ((MQUOTE) $ATOM) * (caddr (mread *standard-input*)) '[a,b,c]; ((MQUOTE) ((MLIST) $A $B $C)) * (caddr (mread *standard-input*)) '(a,b,c); ((MQUOTE) ((MPROGN) $A $B $C)) * (caddr (mread *standard-input*)) 'f(x); ((%F) $X) * (caddr (mread *standard-input*)) '(f(x)); ((MQUOTE) (($F) $X)) * (caddr (mread *standard-input*)) 'f[a](x); ((MQAPPLY) ((%F ARRAY) $A) $X)
Quelltext:
(def-mheader |$'| (mquote)) (def-nud (|$'|) (op) (let (right) (cond ((eq '|$(| (peek-one-token)) (list '$any (mheader '|$'|) (parse '$any 190))) ((or (atom (setq right (parse '$any 190))) (member (caar right) '(mquote mlist mprog mprogn lambda) :test #'eq)) (list '$any (mheader '|$'|) right)) ((eq 'mqapply (caar right)) (cond ((eq (caaadr right) 'lambda) (list '$any (mheader '|$'|) right)) (t (rplaca (cdr right) (cons (cons ($nounify (caaadr right)) (cdaadr right)) (cdadr right))) (cons '$any right)))) (t (cons '$any (cons (cons ($nounify (caar right)) (cdar right)) (cdr right)))))))
Definition des Quote-Quote-Operators.
Der Quote-Quote-Operator ist nicht im eingelesenen Ausdruck enthalten. Wird der Operator auf einen Ausdruck angewendet, wird dieser sofort ausgewertet. Funktionen werden in ihre Verbform umgewandelt.
Beispiele:
* (setq $a 99) 99 * (caddr (mread *standard-input*)) ''a; 99 * (caddr (mread *standard-input*)) ''f(x); (($F) $X) * (caddr (mread *standard-input*)) ''f[a](x); ((MQAPPLY) (($F ARRAY) $A) $X)
Quelltext:
(def-nud (|$''|) (op) (let (right) (cons '$any (cond ((eq '|$(| (peek-one-token)) (meval (parse '$any 190))) ((atom (setq right (parse '$any 190))) (meval right)) ((eq 'mqapply (caar right)) (rplaca (cdr right) (cons (cons ($verbify (caaadr right)) (cdaadr right)) (cdadr right))) right) (t (cons (cons ($verbify (caar right)) (cdar right)) (cdr right)))))))
Definition des Operators für die Zuweisung.
Quelltext:
(def-led-equiv |$:| parse-infix) (def-lbp |$:| 180) (def-rbp |$:| 20) (def-pos |$:| $any) (def-rpos |$:| $any) (def-lpos |$:| $any) (def-mheader |$:| (msetq))
Quelltext:
(def-led-equiv |$::| parse-infix) (def-lbp |$::| 180) (def-rbp |$::| 20) (def-pos |$::| $any) (def-rpos |$::| $any) (def-lpos |$::| $any) (def-mheader |$::| (mset))
Quelltext:
(def-led-equiv |$:=| parse-infix) (def-lbp |$:=| 180) (def-rbp |$:=| 20) (def-pos |$:=| $any) (def-rpos |$:=| $any) (def-lpos |$:=| $any) (def-mheader |$:=| (mdefine))
Quelltext:
(def-led-equiv |$::=| parse-infix) (def-lbp |$::=| 180) (def-rbp |$::=| 20) (def-pos |$::=| $any) (def-rpos |$::=| $any) (def-lpos |$::=| $any) (def-mheader |$::=| (mdefmacro))
Quelltext:
(def-led-equiv |$!| parse-postfix) (def-lbp |$!| 160) (def-pos |$!| $expr) (def-lpos |$!| $expr) (def-mheader |$!| (mfactorial))
Quelltext:
(def-lbp |$^| 140) (def-rbp |$^| 139) (def-pos |$^| $expr) (def-lpos |$^| $expr) (def-rpos |$^| $expr) (def-mheader |$^| (mexpt)) (def-led ((|$^| |$^^|)) (op left) (cons '$expr (getalias (list (mheader op) (convert left (lpos op)) (parse (rpos op) (rbp op)))))) (mapc #'(lambda (prop) ; Make $** like $^ (let ((propval (get '$^ prop))) (if propval (putprop '$** propval prop)))) '(lbp rbp pos rpos lpos mheader)) (inherit-propl '$** '$^ (led-propl))
Quelltext:
(def-lbp |$^^| 140) (def-rbp |$^^| 139) (def-pos |$^^| $expr) (def-lpos |$^^| $expr) (def-rpos |$^^| $expr) (def-mheader |$^^| (mncexpt))
Quelltext:
(def-led-equiv |$.| parse-infix) (def-lbp |$.| 130) (def-rbp |$.| 129) (def-pos |$.| $expr) (def-lpos |$.| $expr) (def-rpos |$.| $expr) (def-mheader |$.| (mnctimes))
Quelltext:
(def-led-equiv |$*| parse-nary) (def-lbp |$*| 120) (def-pos |$*| $expr) (def-lpos |$*| $expr) (def-mheader |$*| (mtimes))
Quelltext:
(def-led-equiv $/ parse-infix) (def-lbp $/ 120) (def-rbp $/ 120) (def-pos $/ $expr) (def-rpos $/ $expr) (def-lpos $/ $expr) (def-mheader $/ (mquotient))
Quelltext:
(def-nud-equiv |$+| parse-prefix) (def-lbp |$+| 100) (def-rbp |$+| 134) (def-pos |$+| $expr) (def-rpos |$+| $expr) (def-mheader |$+| (mplus)) (def-led ((|$+| |$-|) 100) (op left) (setq left (convert left '$expr)) (do ((nl (list (if (eq op '$-) (list (mheader '$-) (parse '$expr 100)) (parse '$expr 100)) left) (cons (parse '$expr 100) nl))) ((not (member (first-c) '($+ $-) :test #'eq)) (list* '$expr (mheader '$+) (nreverse nl))) (if (eq (first-c) '$+) (pop-c))))
Quelltext:
(def-nud-equiv |$-| parse-prefix) (def-lbp |$-| 100) (def-rbp |$-| 134) (def-pos |$-| $expr) (def-rpos |$-| $expr) (def-mheader |$-| (mminus))
Definition des Operators für eine Gleichung.
Quelltext:
(def-led-equiv |$=| parse-infix) (def-lbp |$=| 80) (def-rbp |$=| 80) (def-pos |$=| $clause) (def-rpos |$=| $expr) (def-lpos |$=| $expr) (def-mheader |$=| (mequal))
Definition des Operators für eine Ungleichung.
Quelltext:
(def-led-equiv |$#| parse-infix) (def-lbp |$#| 80) (def-rbp |$#| 80) (def-pos |$#| $clause) (def-rpos |$#| $expr) (def-lpos |$#| $expr) (def-mheader |$#| (mnotequal))
Quelltext:
(def-led-equiv |$>| parse-infix) (def-lbp |$>| 80) (def-rbp |$>| 80) (def-pos |$>| $clause) (def-rpos |$>| $expr) (def-lpos |$>| $expr) (def-mheader |$>| (mgreaterp))
Quelltext:
(def-led-equiv |$>=| parse-infix) (def-lbp |$>=| 80) (def-rbp |$>=| 80) (def-pos |$>=| $clause) (def-rpos |$>=| $expr) (def-lpos |$>=| $expr) (def-mheader |$>=| (mgeqp))
Quelltext:
(def-led-equiv |$<| parse-infix) (def-lbp |$<| 80) (def-rbp |$<| 80) (def-pos |$<| $clause) (def-rpos |$<| $expr) (def-lpos |$<| $expr) (def-mheader |$<| (mlessp))
Quelltext:
(def-led-equiv |$<=| parse-infix) (def-lbp |$<=| 80) (def-rbp |$<=| 80) (def-pos |$<=| $clause) (def-rpos |$<=| $expr) (def-lpos |$<=| $expr) (def-mheader |$<=| (mleqp))
Quelltext:
(def-nud-equiv $not parse-prefix) (def-rbp $not 70) (def-pos $not $clause) (def-rpos $not $clause) (def-lpos $not $clause) (def-mheader $not (mnot))
Quelltext:
(def-led-equiv $and parse-nary) (def-lbp $and 65) (def-pos $and $clause) (def-lpos $and $clause) (def-mheader $and (mand))
Quelltext:
(def-led-equiv $or parse-nary) (def-lbp $or 60) (def-pos $or $clause) (def-lpos $or $clause) (def-mheader $or (mor))
Der Komma-Operator trennt die Elemente einer Liste.
Quelltext:
(def-led-equiv |$,| parse-nary) (def-lbp |$,| 10) (def-pos |$,| $any) (def-lpos |$,| $any) (def-mheader |$,| ($ev))
Operatoren für die Definition von Programmverzweigungen.
Quelltext:
(def-rbp $if 45) (def-pos $if $any) (def-rpos $if $clause) (def-mheader $if (mcond)) (def-nud ($if) (op) (list* (pos op) (mheader op) (parse-condition op))) (defun parse-condition (op) (list* (parse (rpos op) (rbp op)) (if (eq (peek-one-token) '$then) (parse '$any (rbp (scan-one-token))) (mread-synerr "Missing `then'")) (case (peek-one-token) (($else) (list t (parse '$any (rbp (scan-one-token))))) (($elseif) (parse-condition (scan-one-token))) (t (list t '$false))))) (def-nud-equiv $then delim-err) (def-lbp $then 5) (def-rbp $then 25) (def-nud-equiv $else delim-err) (def-lbp $else 5) (def-rbp $else 25) (def-nud-equiv $elseif delim-err) (def-lbp $elseif 5) (def-rbp $elseif 45) (def-pos $elseif $any) (def-rpos $elseif $clause)
Operatoren für die Definition von Programmschleifen.
Quelltext:
(defmacro make-mdo () '(list (list 'mdo) nil nil nil nil nil nil nil)) (defmacro mdo-op (x) `(car (car ,x))) (defmacro mdo-for (x) `(second ,x)) (defmacro mdo-from (x) `(third ,x)) (defmacro mdo-step (x) `(fourth ,x)) (defmacro mdo-next (x) `(fifth ,x)) (defmacro mdo-thru (x) `(sixth ,x)) (defmacro mdo-unless (x) `(seventh ,x)) (defmacro mdo-body (x) `(eighth ,x)) (def-nud-equiv $do parse-$do) (def-nud-equiv $for parse-$do) (def-nud-equiv $from parse-$do) (def-nud-equiv $step parse-$do) (def-nud-equiv $next parse-$do) (def-nud-equiv $thru parse-$do) (def-nud-equiv $unless parse-$do) (def-nud-equiv $while parse-$do) (defun parse-$do (lex &aux (left (make-mdo))) (setf (car left) (mheader 'mdo)) (do ((op lex (scan-one-token)) (active-bitmask 0)) (nil) (if (eq op '|$:|) (setq op '$from)) (setq active-bitmask (collision-check '$do active-bitmask op)) (let ((data (parse (rpos op) (rbp op)))) (case op ($do (setf (mdo-body left) data) (return (cons '$any left))) ($for (setf (mdo-for left) data)) ($from (setf (mdo-from left) data)) ($in (setf (mdo-op left) 'mdoin) (setf (mdo-from left) data)) ($step (setf (mdo-step left) data)) ($next (setf (mdo-next left) data)) ($thru (setf (mdo-thru left) data)) (($unless $while) (if (eq op '$while) (setq data (list (mheader '$not) data))) (setf (mdo-unless left) (if (null (mdo-unless left)) data (list (mheader '$or) data (mdo-unless left))))) (t (parse-bug-err '$do)))))) (def-lbp $do 25) (def-lbp $for 25) (def-lbp $from 25) (def-lbp $step 25) (def-lbp $next 25) (def-lbp $thru 25) (def-lbp $unless 25) (def-lbp $while 25) (def-rbp $do 25) (def-rbp $for 200) (def-rbp $from 95) (def-rbp $in 95) (def-rbp $step 95) (def-rbp $next 45) (def-rbp $thru 95) (def-rbp $unless 45) (def-rbp $while 45) (def-rpos $do $any) (def-rpos $for $any) (def-rpos $from $any) (def-rpos $step $expr) (def-rpos $next $any) (def-rpos $thru $expr) (def-rpos $unless $clause) (def-rpos $while $clause) (def-mheader $do (mdo)) (def-collisions $do ($do . ()) ($for . ($for)) ($from . ($in $from)) ($in . ($in $from $step $next)) ($step . ($in $step $next)) ($next . ($in $step $next)) ($thru . ($in $thru)) ;$IN didn't used to get checked for ($unless . ()) ($while . ()))
Der Operator $;
beendet die Eingabe eines Ausdrucks. Dem eingelesenen
Ausdruck wird displayinput
vorangestellt. Damit wird kMaxima mitgeteilt,
dass der Ausdruck auf der Ausgabe angezeigt werden soll.
Siehe auch den Operator $$
.
Beispiel:
* (mread *standard-input*) a; ((DISPLAYINPUT) NIL $A)
Quelltext:
(def-mheader |$;| (displayinput)) (def-nud-equiv |$;| premterm-err) (def-lbp |$;| -1)
Der Operator $$
beendet die Eingabe eines Ausdrucks. Dem eingelesenen
Ausdruck wird nodisplayinput
vorangestellt. Damit wird kMaxima
mitgeteilt, dass der Ausdruck nicht angezeigt werden soll.
Siehe auch den Operator $;
.
Beispiel:
* (mread *standard-input*) a$ ((NODISPLAYINPUT) NIL $A)
Quelltext:
(def-mheader |$$| (nodisplayinput)) (def-nud-equiv |$$| premterm-err) (def-lbp |$$| -1)
Definition des Operators für eine Marke. Der Operator $&&
erlaubt die
Syntax label && a+b;
, wobei in diesem Beispiel label
eine Marke
ist.
Beispiel:
* (mread *standard-input*) label && a+b; ((DISPLAYINPUT) ((MLIST) $LABEL) ((MPLUS) $A $B))
Quelltext:
(def-nud-equiv |$&&| delim-err) (def-lbp |$&&| -1)
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Nutzerdefinierte Operatoren erlauben die Erweiterung der Syntax des Parsers.
Der Parser unterscheidet Prefix-, Postfix-, Infix- Nary-, Matchfix- und
Nofix-Operatoren und bietet dem Nutzer für jeden Typ eine entsprechende
Funktion $prefix
,
$postfix
,
$infix
,
$nary
,
$matchfix
und $nofix
an, um einen Operator mit
geeigneten Standardwerten zu definieren. Alle diese Funktionen rufen die
Funktion def-operator
auf, die die eigentliche Definition des Operators
ausführt. Weiterhin können mit den vorgenannten Funktionen bereits
vorhandene, einschließlich der von kMaxima definierten Operatoren,
umdefiniert werden.
Quelltext:
(defun $prefix (operator &optional (rbp 180) (rpos '$any) (pos '$any)) (def-operator operator pos () () rbp rpos () t '(nud . parse-prefix) 'msize-prefix 'dimension-prefix ()) operator)
Quelltext:
(defun $postfix (operator &optional (lbp 180) (lpos '$any) (pos '$any)) (def-operator operator pos lbp lpos () () t () '(led . parse-postfix) 'msize-postfix 'dimension-postfix ()) operator)
Quelltext:
(defun $infix (operator &optional (lbp 180) (rbp 180) (lpos '$any) (rpos '$any) (pos '$any)) (def-operator operator pos lbp lpos rbp rpos t t '(led . parse-infix) 'msize-infix 'dimension-infix ()) operator)
Quelltext:
(defun $nary (operator &optional (bp 180) (argpos '$any) (pos '$any)) (def-operator operator pos bp argpos bp () t t '(led . parse-nary) 'msize-nary 'dimension-nary ()) operator)
Quelltext:
(defun $matchfix (operator match &optional (argpos '$any) (pos '$any)) (def-operator operator pos () argpos () () () () '(nud . parse-matchfix) 'msize-matchfix 'dimension-match match) operator)
Mit der Funktion $nofix
wird ein Operator definiert, der kein Argument
hat. $nofix
ruft die Funktion def-operator
mit Standardwerten
für die meisten Argumente auf. Die folgende Liste zeigt, mit welchen
Werten die Funktion def-operator
von $nofix
aufgerufen wird, um
den Operator zu initialisieren.
op
Als Bezeichner des neuen Operators wird das Argument operator übergeben, das ein Symbol oder eine Zeichenkette sein kann.
pos
Die Wortart der Rückgabe des Operators ist das optionale Argument pos,
das den Standardwert any
hat.
lbp
Der linksseitige Vorrang des Operators erhält den Wert nil
.
lpos
Die Wortart des linksseitigen Argumentes des Operators erhält den Wert
nil
.
rbp
Der rechtsseitige Vorrang des Operators erhält den Wert nil
.
rpos
Die Wortart des rechtsseitigen Argumentes des Operators erhält den Wert
nil
.
sp1
Hat sp1
einen von nil
verschiedenen Wert, wird dem Operator in
der Anzeige ein Leerzeichen vorangestellt. Ein Nofix-Operator wird mit dem
Wert nil
initialisiert.
sp2
Hat sp2
einen von nil
verschiedenen Wert, wird dem Operator in
der Anzeige ein Leerzeichen nachgestellt. Ein Nofix-Operator wird mit dem
Wert nil
initialisiert.
parse-data
Eine Liste deren car
entweder led
oder nud
ist und deren
cdr
die entsprechende led-
oder nud
-Funktion bezeichnet.
Ein Nofix-Operator wird mit '(nud . parse-nofix)
initialisiert.
Siehe die Funktion parse-nofix
.
grind-fn
Eine Funktion, die den Operator und seine Operanden für die lineare Anzeige
formatiert. Ein Nofix-Operator wird mit dem Werg 'msize-nofix
initialisiert. Siehe die Funktion msize-nofix
.
dim-fn
Eine Funktion, die den Operator und seine Operanden für die zweidimensionale
Anzeige formatiert. Ein Nofix-Operator wird mit dem Wert
'dimension-nofix
initialisiert. Siehe die Funktion
dimension-nofix
.
match
Wenn op
ein $matchfix
-Operator ist, bezeichnet match
das
Symbol, mit dem die Eingabe beendet wird. Ein Nofix-Operator wird mit dem
Wert nil
initialisiert.
kMaxima hat keinen eigenen Nofix-Operator.
Beispiel:
Ein Beispiel ist die Definition eines Nofix-Operators quit
, der die
Funktion $quit
aufruft.
(%i1) quit; (%o1) quit (%i2) nofix(quit); (%o2) quit (%i3) quit; 0
Der Operator hat den Namen "quit"
, der von der Funktion getop
zurückgegeben wird. Umgekehrt gibt die Funktion getopr
zum Namen
"quit"
das interne kMaxima-Symbol $quit
als Ergebnis zurück.
* (getop '$quit) "quit" * (getopr "quit") $QUIT
Sowohl die Eigenschaftsliste des Symbols $quit
als auch die Liste der
Noun-Form %quit
erhalten Einträge, die für das Parsen der Eingabe
und der Ausgabe des Operators notwendig sind.
* (symbol-plist '$quit) (DISSYM (#\q #\u #\i #\t) DIMENSION DIMENSION-NOFIX GRIND MSIZE-NOFIX NUD PARSE-NOFIX POS $ANY VERB %QUIT OP "quit") * (symbol-plist '%quit) (DISSYM (#\q #\u #\i #\t) DIMENSION DIMENSION-NOFIX NOUN $QUIT)
Quelltext:
(defun $nofix (operator &optional (pos '$any)) (def-operator operator pos () () () () () () '(nud . parse-nofix) 'msize-nofix 'dimension-nofix ()) operator)
Die Funktion def-operator
wird von den Funktionen $prefix
,
$postfix
,
$infix
,
$nary
,
$matchfix
und
$nofix
mit Standardwerten für die einzelnen Argumente aufgerufen, um
einen nutzerdefinierten Operator zu definieren oder einen vorhandenen Operator
neu zu definieren. Die Argumente werden von den genannten Funktionen mit
geeigneten Standardwerten belegt und haben die folgende Bedeutung:
op
Der Bezeichner des neuen Operators, der ein Symbol oder eine Zeichenkette sein kann.
pos
Die Wortart der Rückgabe des Operators.
lbp
Der linksseitige Vorrang des Operators.
lpos
Die Wortart des linksseitigen Argumentes des Operators.
rbp
Der rechtsseitige Vorrang des Operators.
rpos
Die Wortart des rechtsseitigen Argumentes des Operators.
sp1
Hat sp1
einen von nil
verschiedenen Wert, wird dem Operator in
der Anzeige ein Leerzeichen vorangestellt.
sp2
Hat sp2
einen von nil
verschiedenen Wert, wird dem Operator in
der Anzeige ein Leerzeichen nachgestellt.
parse-data
Eine Liste deren car
entweder led
oder nud
ist und deren
cdr
die entsprechende led-
oder nud
-Funktion bezeichnet.
grind-fn
Eine Funktion, die den Operator und seine Operanden für die lineare Anzeige formatiert.
dim-fn
Eine Funktion, die den Operator und seine Operanden für die zweidimensionale Anzeige formatiert.
match
Wenn op
ein $matchfix
-Operator ist, bezeichnet match
das
Symbol, mit dem die Eingabe beendet wird.
Hinweis:
Im Unterschied zum Original Maxima werden in kMaxima Namen von Operatoren nur
dann der Liste *symbols-defined*
hinzugefügt, wenn der Name eine
Zeichenkette ist und diese nicht-alphabetische Zeichen enthält, da Namen mit
nicht-alphabetischen Zeichen auf besondere Weise eingelesen werden müssen.
Wird eine Zeichenkette als Argument op übergeben, die nur alphabetische
Zeichen enthält, wird die Zeichenkette in ein Symbol umgewandelt, das nicht
der Liste *symbols-defined*
hinzugefügt wird.
Quelltext:
(defun def-operator (op pos lbp lpos rbp rpos sp1 sp2 parse-data grind-fn dim-fn match) (let ((x)) (if (or (and rbp (not (integerp (setq x rbp)))) (and lbp (not (integerp (setq x lbp))))) (merror "syntax extension: binding powers must be integers; found: ~A" x)) (when (stringp op) (cond ((not (every 'alphabetp (coerce op 'list))) (setq op (define-symbol op))) (t (setq op (symbolconc '$ (maybe-invert-string op)))))) (when (not (symbolp op)) (merror "syntax extension: first argument must be a string or a symbol;~ found: ~A" op)) (op-setup op) (let ((noun ($nounify op)) (dissym (cdr (exploden op)))) (cond ((not match) (setq dissym (append (if sp1 '(#\space)) dissym (if sp2 '(#\space))))) (t (if (stringp match) (setq match (define-symbol match))) (op-setup match) (putprop op match 'match) (putprop match 5 'lbp) (setq dissym (cons dissym (cdr (exploden match)))))) (putprop op pos 'pos) (putprop op (cdr parse-data) (car parse-data)) (putprop op grind-fn 'grind) (putprop op dim-fn 'dimension) (putprop noun dim-fn 'dimension) (putprop op dissym 'dissym) (putprop noun dissym 'dissym) (when rbp (putprop op rbp 'rbp) (putprop noun rbp 'rbp)) (when lbp (putprop op lbp 'lbp) (putprop noun lbp 'lbp)) (when lpos (putprop op lpos 'lpos)) (when rpos (putprop op rpos 'rpos)) (getopr op))))
Bemerkung:
Der linksseitige Vorrang für einen Matchfix-Operator wird in der Funktion
def-operator
mit dem Wert 5
initialisiert. Die Zahl 5
fällt hier vom Himmel und verhindert gegebenenfalls eine komplette
Umdefinition der Vorränge aller Operatoren oder der Definition von
Matchfix-Operatoren mit einem besonderen linken Vorrang.
Standardwert: nil
Die globale Variable *mopl*
enthält die Namen der internen Operatoren,
die vom Nutzer modifiziert wurden. Operatoren, die in dieser Liste enthalten
sind, werden nicht gelöscht.
Siehe die Funktion op-setup
,
die die Namen von internen Operatoren
in die Liste *mopl*
schreibt.
Definiert der Nutzer einen neuen Operator oder ändert der Nutzer die
Definition eines vorhandenen Operators werden von der Funktion op-setup
die Operatoreigenschaft op
auf der Eigenschaftsliste und der Name des
Operators initialisiert. Das Argument op ist ein Symbol, das den Operator
bezeichnet. Die Rückgabe ist der Wert der Funktion add2lnc
nachdem
der Name des Operators der Informationsliste $props
hinzugefügt wurde.
Die Funktion prüft, ob es sich bei dem Operator op um einen internen
Operator handelt. In diesem Fall wird der Operator der globalen Liste
*mopl*
hinzugefügt. Dieser Eintrag verhindert, dass ein interner
Operator gelöscht wird.
op-setup
wird von der Funktion def-operator
aufgerufen, um einen
Operator zu definieren oder die Definition eines vorhandenen Operators zu
modifizieren.
Seiteneffekte:
op-setup
modifiziert die globalen Variablen $props
und
*mopl*
.
Quelltext:
(defun op-setup (op) (declare (special *mopl* $props)) (let ((opr (or (getprop op 'op) (coerce (makestring1 op) 'string)))) (putprop op opr 'op) (putopr opr op) (if (and (operatorp1 op) (not (member opr (cdr $props) :test #'eq))) (push opr *mopl*)) (add2lnc opr $props)))
[ << ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
This document was generated by Dieter Kaiser on Dezember, 13 2011 using texi2html 1.76.