;;; Code stored as: {turing}<3-lisp.problem-sets>arith-parser.3-lisp
;;; {phylum}<3-lisp>course>problem-sets>arith-parser.3-lisp
;;;==========================================================================
;;; Simple ARITH Expression Parser
;;;==========================================================================
;;;
;;; Parses ARITH expressions.
;;; Built on top of the Lexical Analyser
;;; ------------------------------------
;;; Assumed grammar: (<token> is the primitive lexical item)
;;;
;;; <expression> ::= <numeral> | <complex>
;;;
;;; <complex> ::= <operator> "(" <expression> "," <expression> ")"
;;;
;;; <operator> ::= "+" | "-" | "*" | "/"
;;; ARITH-PARSE Main parsing routine. Returns a parse tree, or else.
;;; =========== the string "rejected" if not a well-formed ARITH expression.
(define ARITH-PARSE
(lambda [string]
(let [[parse (dynamic-catch
(arith-parse-expression (lexical-analysis string)))]]
(if (null (second parse))
(first parse)
"rejected"))))
;;; Ancillary parsing functions:
;;;
;;; The following procedures call ARITH-ERROR-IN-PARSE immediately upon
;;; finding a grammatical error of any sort, which does not return (since
;;; the procedures below are not designed to do anything sensible about
;;; continuing from errors), but instead does a DYNAMIC-THROW (q.v.).
;;; Each of the parsing routines will return a parse/tail pair;
;;; ARITH-PARSE-EXPRESSION Attempts to parse an arbitrary ARITH expression.
;;; ======================
(define ARITH-PARSE-EXPRESSION
(lambda [tail]
(append-results ['expression]
(cond [(member (first tail) ["+" "*" "-" "/"])
(arith-parse-args ['complex ['operator (first tail)]]
(rest tail))]
[(numeral-expression (first tail))
[['numeral-expression (first tail)] (rest tail)]]
[$true (arith-error-in-parse)]))))
;;; ARITH-PARSE-ARGS attempts to recognize an ARITH args expression
;;; ====================
(define ARITH-PARSE-ARGS
(lambda [new tail]
(letseq [[parse (append-results new (arith-parse-token "(" tail))]
[parse (append-results (first parse) (arith-parse-expression (second parse)))]
[parse (append-results (first parse) (arith-parse-token "," (second parse)))]
[parse (append-results (first parse) (arith-parse-expression (second parse)))]
[parse (append-results (first parse) (arith-parse-token ")" (second parse)))]]
parse)))
(define APPEND-RESULTS
(lambda [new result]
[(append new [(first result)]) (second result)]))
(define ARITH-PARSE-TOKEN
(lambda [token tail]
(if (and (not (null tail))
(= token (first tail)))
[token (rest tail)]
(arith-error-in-parse))))
;;; ARITH-ERROR-IN-PARSE Signals an error by sending back the string "rejected".
;;; ====================
(define ARITH-ERROR-IN-PARSE
(lambda []
(dynamic-throw [[] ["something"]])))
;;; ========================================================================
;;; DIFFERENT VERSION USING OBJECTS
;;; ========================================================================
;;; A2-PARSE Main parsing routine. Returns "rejected" if <string> is not
;;; ======== a well-formed ARITH expression; otherwise a parse tree.
(define A2-PARSE
(lambda [string]
(letseq [[s (lexan-object string)]
[answer (dynamic-catch (a2-parse-expression s))]]
(if (and (= (next-token s) 'eot)
(sequence answer))
answer
"Rejected"))))
;;; Ancillary parsing functions:
;;;
;;; The following procedures call A2-ERROR-IN-PARSE immediately upon
;;; finding a grammatical error of any sort, which does not return (since
;;; the procedures below are not designed to do anything sensible about
;;; continuing from errors), but instead does a DYNAMIC-THROW (q.v.).
;;; A2-PARSE-EXPRESSION Attempts to parse an arbitrary ARITH expression.
;;; =================== .
(define A2-PARSE-EXPRESSION
(lambda [s]
['expression
(cond [(member (next-token s) ["+" "*" "-" "/"])
(cons 'complex
(cons ['operator (get-token s)]
(a2-parse-args s)))]
[(numeral-expression (next-token s))
['numeral-expression (get-token s)]]
[$true (a2-error-in-parse)])]))
;;; A2-PARSE-ARGS Attempts to parse an ARITH args expression
;;; =================
(define A2-PARSE-ARGS
(lambda [s]
[(a2-parse-token "(" s)
(a2-parse-expression s)
(a2-parse-token "," s)
(a2-parse-expression s)
(a2-parse-token ")" s)]))
;;; A2-PARSE-TOKEN Ensures that the next token in s is <token>; returns it
;;; ============== as the parse.
(define A2-PARSE-TOKEN
(lambda [token s]
(if (not (= token (get-token s)))
(a2-error-in-parse)
token)))
;;; A2-ERROR-IN-PARSE Signals an error by sending back something which isn't
;;; ================= a sequence, and therefore cannot be a valid parse.
(define A2-ERROR-IN-PARSE
(lambda []
(dynamic-throw 'not-a-sequence)))