[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4. Parser


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.1 Einführung in den Parser

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 Vorbereitende Funktionen und Makros


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.2.1 Namen der Operatoren

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.

Funktion: getopr0 x

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))))

Funktion: putopr x y

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))))

Funktion: remopr x

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))))

Funktion: getopr x

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))

Funktion: getop 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] [ ? ]

4.2.2 Liste der Operatoren

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*.

Globale Variable: *symbols-defined*

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*
(+ - * ^ < = > |(| |)| [ ] |,| |:| ! |#| |'| $ |;| ** ^^ |:=| |::| <= >= |''|
   && |::=|)

Globale Variable: *maxima-operators*

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.

Makro: define-initial-symbols &rest l

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*)))))

Funktion: define-initial-symbols* l

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*)))

Funktion: cstrsetup arg

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)))))))

Funktion: define-symbol x

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)))

Funktion: undefine-symbol opr

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] [ ? ]

4.2.3 Definition der Syntax des Parsers

Makro: led-propl
Makro: nud-propl

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))

Funktion: inherit-propl op-to op-from getl

Ü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))))

Funktion: make-parser-fun-def op p bvl body

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)))))

Makro: def-nud-equiv op equiv

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)))

Makro: def-led-equiv op equiv

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)))

Funktion: set-lbp-and-rbp op lbp rbp

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))))

Makro: def-nud-fun op-name op-l . body

Quelltext:

  (defmacro def-nud-fun (op-name op-l . body)
    (list* 'defun-prop (list* op-name 'nud 'nil) op-l body))

Makro: def-nud (op . lbp-rpb) bvl . 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))))

Makro: def-led-fun op-name op-l . body

Quelltext:

  (defmacro def-led-fun (op-name op-l . body)
    (list* 'defun-prop (list* op-name 'led 'nil) op-l body))

Makro: def-led (op . lbp-rpp) bvl . 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))))

Makro: def-lbp op val
Makro: def-rbp op val

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))

Funktion: lbp op
Funktion: rbp op

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)))

Makro: def-match x m

Quelltext:

(defmacro def-match (x m) 
  `(defprop ,x ,m match))

Makro: def-pos op pos
Makro: def-lpos op pos
Makro: def-rpos op pos

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))

Funktion: pos op
Funktion: lpos op
Funktion: rpos op

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)))

Symbol: $any
Symbol: $clause
Symbol: $expr

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)

Funktion: mheader op

Quelltext:

(defun mheader (op)
  (add-lineinfo (or (getprop op 'mheader) (list op))))

Makro: def-mheader op header

Quelltext:

(defmacro def-mheader (op header) `(defprop ,op ,header mheader))

Makro: def-collisions op &rest alist

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))))

Funktion: collision-lookup op active-bitmask key-bitmask

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)))))))

Funktion: collision-check op active-bitmask key

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] [ ? ]

4.2.4 Fehlerbehandlung des Parsers

Funktion: mopstrip x

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)))

Funktion: mread-synerr format-string &rest l

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)))

Funktion: parse-err

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"))

Funktion: parse-bug-err op

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)))

Funktion: parse-delim-err 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)))

Funktion: parse-erb-err op l

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)))

Funktion: parse-premterm-err 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] [ ? ]

4.2.5 Weitere Funktionen des Parsers

Funktion: operatorp lex

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))))

Funktion: operatorp1 lex

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 Funktionen und Makros des Parsers


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.3.1 Einlesen von Zeichen

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.

Globale Variable: *parse-tyi*

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.

Globale Variable: *parse-stream*

Standardwert: nil

Der Stream von dem die Funktionen Zeichen lesen.

Globale Variable: *parse-stream-eof*

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.

Globale Variable: *parse-window*

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.

Globale Variable: *parse-window-length*

Die Größe des Ringpuffers. Der Wert wird zu 25 initialisiert.

Globale Variable: *prompt-on-read-hang*

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.

Globale Variable: *read-hang-prompt*

Standardwert: nil

Das Prompt-Zeichen für die Eingabeaufforderung, wenn der Nutzer zum Eingeben von Zeichen aufgefordert wird.

Funktion: tyi &optional (stream *standard-input*) eof

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))))))

Funktion: parse-tyi-init stream eof

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))

Funktion: parse-tyi

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)))))

Funktion: parse-tyipeek

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)))))

Funktion: unparse-tyi ch

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] [ ? ]

4.3.2 Einlesen der Token


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.3.2.1 Einführung in Einlesen der Token

Jetzt werden die Funktionen definiert, um ein Token einzulesen. kMaxima unterscheidet die folgenden Token:

Operator

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.

Zahlen

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.

Zeichenkette

Zeichenketten werden von den Funktionen scan-string eingelesen.

kMaxima Token

Liest einen Maxima-Bezeichner mit der Funktion scan-maxima-token ein. Maxima-Bezeichner erhalten einen $-Zeichen als Präfix.

Lisp Token

Lisp-Bezeichner werden von der Funktion scan-lisp-token eingelesen. Im Unterschied zu kMaxima Token erhalten Lisp Token keinen Präfix.

Lisp Keyword

Ein Lisp-Keyword hat den Präfix : und wird von der Funktion scan-keyword-token eingelesen.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.3.2.2 Hauptroutine für das Einlesen der Token

Globale Variable: *scan-buffered-token*

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.

Funktion: peek-one-token &optional (eof-p nil) (eof nil)

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)))))))

Funktion: scan-one-token &optional (eof-p nil) (eof nil)

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))))

Funktion: gooble-comment

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] [ ? ]

4.3.2.3 Einlesen von Operatoren

Globale Variable: *whitespaces*

Standardwert: (#\tab #\space #\linefeed #\return #\page #\newline)

Liste mit Zeichen, die vom Parser als Whitespace-Zeichen gelesen werden.

Funktion: scan-operator-token obj

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))

Funktion: 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] [ ? ]

4.3.2.4 Einlesen von Bezeichnern

Funktion: scan-maxima-token

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)))))

Funktion: scan-lisp-token

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."))))

Funktion: scan-keyword-token

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."))))

Funktion: scan-token flag

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] [ ? ]

4.3.2.5 Einlesen von Zeichenketten

Funktion: scan-string

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] [ ? ]

4.3.2.6 Einlesen von Zahlen

Konstante: +flonum-exponent-marker+

Standardwert: #\D

Enthält das Zeichen, das von kMaxima als das Exponent-Zeichen verwendet wird.

Globale Variable: *exponent-chars*

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.

Optionsvariable: $fast_bfloat_conversion

Standardwert: t

Optionsvariable: $fast_bfloat_threshold

Standardwert: 100000

Optionsvariable: *fast-bfloat-extra-bits*

Standardwert: 0

Funktion: cl-rat-to-maxima x

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))))

Funktion: make-number data

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)))))))

Funktion: scan-digits data continuation? continuation &optional exponent-p

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)))

Funktion: scan-number-exponent data

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))

Funktion: scan-number-rest data

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)))))

Funktion: scan-number-before-dot data

Quelltext:

(defun scan-number-before-dot (data)
  (scan-digits data (push #\. *exponent-chars*) #'scan-number-rest))

Funktion: scan-number-after-dot data

Quelltext:

(defun scan-number-after-dot (data)
  (scan-digits data *exponent-chars* #'scan-number-exponent))

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.3.3 Parsen der Eingabe

nud-Funktion

Ein 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-Funktion

Postfix-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.

rbp

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.

lbp

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

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

lpos ist die Wortart des Arguments die auf der linken Seiten des Operators vom Parser erwartet wird.

rpos

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

Globale Variable: *mread-prompt*

Standardwert: nil

Der Prompt der Funktion mread. Hat *mread-prompt* einen von nil verschiedenen Wert, erwartet mread die Eingabe von der Tastatur.

Globale Variable: *mread-eof-obj*

Standardwert: nil

Wird von der Funktion mread an einen Wert gebunden.

Funktion: mread stream &optional eof

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)))))))

Funktion: parse mode rbp

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)))))

Funktion: convert item 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))))

Funktion: parse-prefix op

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))))

Funktion: parse-postfix op l

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))))

Funktion: parse-infix op l

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))))

Funktion: parse-nofix 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)))

Funktion: parse-nary op l

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))))

Funktion: prsnary op mode rbp

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)))

Funktion: parse-matchfix op

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))))

Funktion: prsmatch match mode

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] [ ? ]

4.4 Definition der Operatoren

Operator: $[
Operator: $]

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)

Operator: $(
Operator: $)

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)

Operator: $'

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)))))))

Operator: $''

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)))))))

Operator: $:

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))

Operator: $::

Quelltext:

(def-led-equiv |$::| parse-infix)
(def-lbp       |$::| 180)
(def-rbp       |$::|  20)
(def-pos       |$::| $any)
(def-rpos      |$::| $any)
(def-lpos      |$::| $any)
(def-mheader   |$::| (mset))

Operator: $:=

Quelltext:

(def-led-equiv |$:=| parse-infix)
(def-lbp       |$:=| 180)
(def-rbp       |$:=|  20)
(def-pos       |$:=| $any)
(def-rpos      |$:=| $any)
(def-lpos      |$:=| $any)
(def-mheader   |$:=| (mdefine))

Operator: $::=

Quelltext:

(def-led-equiv |$::=| parse-infix)
(def-lbp       |$::=| 180)
(def-rbp       |$::=|  20)
(def-pos       |$::=| $any)
(def-rpos      |$::=| $any)
(def-lpos      |$::=| $any)
(def-mheader   |$::=| (mdefmacro))

Operator: $!

Quelltext:

(def-led-equiv |$!| parse-postfix)
(def-lbp       |$!| 160)
(def-pos       |$!| $expr)
(def-lpos      |$!| $expr)
(def-mheader   |$!| (mfactorial))

Operator: $^

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))

Operator: $^^

Quelltext:

(def-lbp       |$^^| 140)
(def-rbp       |$^^| 139)
(def-pos       |$^^| $expr)
(def-lpos      |$^^| $expr)
(def-rpos      |$^^| $expr)
(def-mheader   |$^^| (mncexpt))

Operator: $.

Quelltext:

(def-led-equiv |$.| parse-infix)
(def-lbp       |$.| 130)
(def-rbp       |$.| 129)
(def-pos       |$.| $expr)
(def-lpos      |$.| $expr)
(def-rpos      |$.| $expr)
(def-mheader   |$.| (mnctimes))

Operator: $*

Quelltext:

(def-led-equiv |$*| parse-nary)
(def-lbp       |$*| 120)
(def-pos       |$*| $expr)
(def-lpos      |$*| $expr)
(def-mheader   |$*| (mtimes))

Operator: $/

Quelltext:

(def-led-equiv $/  parse-infix)
(def-lbp       $/  120)
(def-rbp       $/  120)
(def-pos       $/  $expr)
(def-rpos      $/  $expr)
(def-lpos      $/  $expr)
(def-mheader   $/  (mquotient))

Operator: $+

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))))

Operator: $-

Quelltext:

(def-nud-equiv |$-| parse-prefix)
(def-lbp       |$-| 100)
(def-rbp       |$-| 134)
(def-pos       |$-| $expr)
(def-rpos      |$-| $expr)
(def-mheader   |$-| (mminus))

Operator: $=

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))

Operator: $#

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))

Operator: $>

Quelltext:

(def-led-equiv |$>| parse-infix)
(def-lbp       |$>| 80)
(def-rbp       |$>| 80)
(def-pos       |$>| $clause)
(def-rpos      |$>| $expr)
(def-lpos      |$>| $expr)
(def-mheader   |$>| (mgreaterp))

Operator: $>=

Quelltext:

(def-led-equiv |$>=| parse-infix)
(def-lbp       |$>=| 80)
(def-rbp       |$>=| 80)
(def-pos       |$>=| $clause)
(def-rpos      |$>=| $expr)
(def-lpos      |$>=| $expr)
(def-mheader   |$>=| (mgeqp))

Operator: $<

Quelltext:

(def-led-equiv |$<| parse-infix)
(def-lbp       |$<| 80)
(def-rbp       |$<| 80)
(def-pos       |$<| $clause)
(def-rpos      |$<| $expr)
(def-lpos      |$<| $expr)
(def-mheader   |$<| (mlessp))

Operator: $<=

Quelltext:

(def-led-equiv |$<=| parse-infix)
(def-lbp       |$<=| 80)
(def-rbp       |$<=| 80)
(def-pos       |$<=| $clause)
(def-rpos      |$<=| $expr)
(def-lpos      |$<=| $expr)
(def-mheader   |$<=| (mleqp))

Operator: $not

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))

Operator: $and

Quelltext:

(def-led-equiv $and parse-nary)
(def-lbp       $and 65)
(def-pos       $and $clause)
(def-lpos      $and $clause)
(def-mheader   $and (mand))

Operator: $or

Quelltext:

(def-led-equiv $or parse-nary)
(def-lbp       $or 60)
(def-pos       $or $clause)
(def-lpos      $or $clause)
(def-mheader   $or (mor))

Operator: $,

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))

Operator: $if
Operator: $then
Operator: $else
Operator: $elseif

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)

Operator: $do
Operator: $for
Operator: $from
Operator: $in
Operator: $step
Operator: $next
Operator: $thru
Operator: $unless
Operator: $while

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  . ()))

Operator: $;

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)

Operator: $$

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)

Operator: $&&

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] [ ? ]

4.5 Nutzerdefinierte Operatoren

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.

Funktion: $prefix operator &optional rbp rpos pos

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)

Funktion: $postfix operator &optional lbp lpos pos

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)

Funktion: $infix operator &optional lbp rpb lpos rpos pos

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)

Funktion: $nary operator &optional bp argpos pos

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)

Funktion: $matchfix operator match &optional argpos pos

Quelltext:

(defun $matchfix (operator match &optional (argpos '$any) (pos '$any))
  (def-operator operator pos () argpos () () () ()
                '(nud . parse-matchfix)
                'msize-matchfix 
                'dimension-match match)
  operator)

Funktion: $nofix operator &optional pos

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)

Funktion: def-operator op pos lbp lpos rbp rpos sp1 sp2 parse-data grind-fn dim-fn match

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.

Globale Variable: *mopl*

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.

Funktion: op-setup op

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.