Adaish

Die Funktion adaish erlaubt es, ein Codeschnippsel in einer Algol/Pascal/Ada-artigen Notation zu formulieren, in Lisp zu übersetzen und dann auszuführen. Sie basiert auf den Prinzipien von EBNF (siehe Wörter von formalen Sprachen erkennen). Die verwendeten Regeln sind:

  NUMBER = <Ganzzahl, Folge von Ziffern an deren Anfang nicht die 0 steht>
  NAME = <Bezeichner, Folge von Kleinbuchstaben>
  ARGUMENTS = EXPRESSION { "," EXPRESSION }
  CALL = NAME "(" ARGUMENTS ")"
  BRACKETED-EXPRESSION = "(" EXPRESSION ")"
  FACTOR = BRACKETED-EXPRESSION | NUMBER | CALL | NAME
  PRODUCT = FACTOR { "*" FACTOR }
  QUOTIENT = FACTOR "/" FACTOR
  PRODUCT-OR-QUOTIENT = PRODUCT | QUOTIENT
  SUM = PRODUCT-OR-QUOTIENT { "+" PRODUCT-OR-QUOTIENT }
  DIFFERENCE = PRODUCT-OR-QUOTIENT "-" PRODUCT-OR-QUOTIENT
  EXPRESSION = DIFFERENCE | SUM

  COMPARISON-OPERATOR = "<=" | ">=" | "<" | ">" | "="
  ATOM = EXPRESSION COMPARISON-OPERATOR EXPRESSION
  NEGATION = "NOT" LITERAL
  BRACKETED-DISJUNCTION = "(" DISJUNCTION ")"
  LITERAL = BRACKETED-DISJUNCTION | NEGATION | ATOM
  CONJUNCTION = LITERAL { "AND" LITERAL }
  DISJUNCTION = CONJUNCTION { "OR" CONJUNCTION }
  PREDICATE = DISJUNCTION

  PARAMETERS = NAME { "," NAME }
  VARIABLE-DEFINITIONS = "VAR" PARAMETERS
  SEQUENCE = STATEMENT { ";" STATEMENT }
  BLOCK = [ VARIABLE-DEFINITIONS ] "BEGIN" SEQUENCE "END"
  IF = "IF" PREDICATE "THEN" SEQUENCE [ ELSE-BRANCH ]
  IF-OR-SEQUENCE = IF | SEQUENCE
  ELSE-BRANCH = "ELSE" IF-OR-SEQUENCE
  CONDITIONAL = "IF" PREDICATE "THEN" STATEMENT [ ELSE-BRANCH ] "END" "IF"
  FOR = "FOR" NAME "IN" EXPRESSION
  WHILE = "WHILE" PREDICATE
  LOOP = [ FOR ] [ WHILE ] "LOOP" SEQUENCE "END" "LOOP"
  EXIT-WHEN = "EXIT "WHEN" PREDICATE
  ASSIGNMENT = NAME ":=" EXPRESSION
  RETURN = "RETURN" EXPRESSION
  STATEMENT = BLOCK | CONDITIONAL | LOOP | EXIT-WHEN | ASSIGNMENT | RETURN

Die Regeln finden sich im Programm als Funktionen wieder. Die Funktion translate-statement repräsentiert dabei die letzte Regel. Sie wird von der Funktion adaish-to-lisp aufgerufen.

(defproc adaish-to-lisp (source)
  (quasi-quote
    (catch
      (quote adaish-return)
      (unquote
        (first
          (translate-statement
            (tokenize-string
              source
              (concatenate (code-char 10) (code-char 13) " (),;:=+-*/<>")
              t)))))))

Diese Funktion liefert beispielsweise für die Zeichenkette

VAR p
BEGIN
  p := 1;
  FOR i IN list(2, 3, 4, 5) WHILE p < 20 LOOP
    p := p * i
  END LOOP;
  RETURN p
END

den übersetzten Lisp-Ausdruck

(catch (quote adaish-return)
  (let
    ((p nil))
    (progn
      (setq p 1)
      (catch (quote adaish-break)
        (dolist (i (list 2 3 4 5))
          (unless (< p 20)
            (throw (quote adaish-break) nil))
          (setq p (* p i))))
      (throw (quote adaish-return) p))))

Die Funktion adaish nimmt den von adaish-to-lisp erzeugten Ausdruck und wertet ihn aus.

(defproc adaish (source)
  (eval (adaish-to-lisp source)))

Für das Beispiel

VAR i, ps, rps
BEGIN
  i := 2;
  WHILE length(ps) < 20 LOOP
    VAR c
    BEGIN
      FOR p IN ps WHILE c = nil AND p * p <= i LOOP
        IF integer?(i / p) = t THEN
          c := t
        END IF
      END LOOP;
      IF c = nil THEN
        rps := cons(i, rps);
        ps := reverse(rps)
      END IF
    END;
    i := i + 1
  END LOOP;
  RETURN ps
END

liefert ein Aufruf von adaish die Liste

(2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71)

als Ergebnis.

Die unten verlinkte Datei adaish.sheet.xml enthält den vollständigen Quelltext und weitere Beispiele. Die Datei kann mit dem Programm Calc ausgeführt werden.


adaish.sheet.xml