;; File: parser.rkt ;; Author: You! ;; A starting point for the unilang parser ;; We need to make QLs here. So either include your make-UQL function ;; or just load your whole unicalc API here. (define (make-UQL quant err num den) (list (list quant err) num den)) (load "symbol-definitions.rkt") (load "tokenizer.rkt") ;; An interactive read-eval-print loop just for the parser. It's ;; not necessary, but it's sort of fun. (define (read-eval-print) (begin (prompt) (let* ( (expression (read-line)) ) (if (equal? expression "quit") expression ;; exit (begin (display (unicalc-parse expression)) (read-eval-print) )) ) )) ;; Prompt the user to enter an expression. (define (prompt) (newline) (display prompt-string)) ;; Function: unicalc-parse ;; input: a string to parse ;; Output: a list of three elements, the same as the parse and parse-input ;; functions ;; Also prints the results to the screen (define (unicalc-parse exp) (let ((output (parse exp))) (display result-label) (display (first output)) (display "\n") (display syntax-tree-label) (display (second output)) (display "\n") output)) ;; Function begins-with? ;; input: an element and a list ;; output: #t if the list L begins with the element elem, #f otherwise ;; This function is also defined in the tokenizer, which is included ;; above, but we include it again in case you write your own tokenizer ;; and don't implement this function. (define (begins-with? elem L) (if (null? L) #f (equal? elem (first L)))) ;; Function: parse, the top-level parsing function ;; input: ;; expression - a string of input from the user ;; ouput: a list of three elements: ( parse-outcome parse-result residual ) ;; parse-outcome: #t if the input was successfully parsed, and #f if it was not ;; parse-result: the parse tree ;; residual: the rest of the tokens in the input list that are not included ;; in the parse tree (or an empty list, if all tokens were accounted for). (define (parse expression) (parse-input (tokenize expression))) ;; Parse the following grammar ;; ;; I -> V = E | % varname | varname ;; E -> S | Val ;; S -> P + S | P - S | P ;; P -> Pow * P | Pow / P | Pow ;; Pow -> Sub^Amt | Sub ;; Sub -> ( S ) | varname ;; Val -> Amt +- Amt UL | Amt UL ;; Amt -> number | - number ;; UL -> Units / Units | Units ;; Units -> empty | unitname Units ;; ;; function parse-input. ;; Input: tokens, a list of tokens ;; output: a list of three elements: ( parse-outcome parse-result residual ) ;; parse-outcome: #t if the input was successfully parsed, and #f if it was not ;; parse-result: the parse tree ;; residual: the rest of the tokens in the input list that are not included ;; in the parse tree (or an empty list, if all tokens were accounted for). ;; Parse a top level expression using recursive descent parsing. ;; For now this function calls parse-val (which itself is only partly implemented, ;; but make sure to update it as you implement your grammar. (define (parse-input tokens) (let* ( (triple (parse-val tokens)) (parseok (outcome triple)) (val (result triple)) (after-val (residual triple)) ) (if parseok (succeed val after-val) (fail tokens)))) ;; function: parse-val ;; input: input, a list of tokens ;; output: the same as parse-input ;; Parse a value according to the rule ;; Val -> Amt +- Amt UL | Amt UL ;; NOTE: THIS FUNCTION IS ONLY PARTIALLY IMPLEMENTED. IT CURRENTLY IMPLEMENTS: ;; Val -> Amt UL (define (parse-val input) (let* ( (triple (parse-amt input)) (have-amt (outcome triple)) (amt (result triple)) (after-amt (residual triple)) ) (if have-amt (let* ( (triple2 (parse-ul after-amt)) (have-ul (outcome triple2)) (unit-list (result triple2)) (after-ul (residual triple2)) ) (if have-ul (succeed (make-UQL amt 0 (first unit-list) (second unit-list)) after-ul) (fail input))) (fail input)))) ;; parse-amt ;; input: a list of tokens ;; output: the same as parse-input ;; Parse an ammount according to the rule ;; Amt -> number | - number ;; NOTE: THIS FUNCTION IS ONLY PARTIALLY IMPLEMENTED. IT CURRENTLY IMPLEMENTS: ;; Amt -> number (define (parse-amt input) (cond ((number? (first input)) (succeed (first input) (rest input))) (else (fail input)))) ;; parse a unit-list. ;; input: a list of tokens ;; output: the same as parse-input ;; UL -> Units / Units | Units (define (parse-ul input) (let* ( (triple (parse-units input)) (have-units (outcome triple)) (units (result triple)) (after-units (residual triple)) ) (if have-units (if (begins-with? divide-char after-units) (let* ( (triple2 (parse-units (rest after-units))) (have-denom (outcome triple2)) (denom-units (result triple2)) (after-denom (residual triple2)) ) (if have-denom (succeed (list units denom-units) after-denom) (succeed (list units '()) (rest after-units)))) (succeed (list units '()) after-units)) (fail input)))) ;; parse-units ;; input: a list of tokens ;; output: the same as parse-input ;; Parse a list of Units according to the rule ;; Units -> empty | unitname Units (define (parse-units input) (parse-units-help input '())) ;; parse-units ;; input: ;; input: a list of tokens ;; accum: The units accumulated so far ;; output: the same as parse-input ;; Parse a list of Units according to the rule ;; Units -> empty | unitname Units (define (parse-units-help input accum) (cond ((null? input) (succeed (map string->symbol (reverse accum)) '())) ((string? (first input)) (parse-units-help (rest input) (cons (first input) accum))) (else (succeed (map string->symbol (reverse accum)) input)))) ;; Helper functions you might find useful ;; char->num ;; input: a character representing a number ;; output: the number represented by the input character (define (char->number c) (string->number (list->string (list c)))) ;; string->char ;; input: a string of length 1 ;; output: a character representation of the single character in the input string. (define (string->char s) (first (string->list s))) ;; char->symbol ;; input: a character ;; output: a symbol representation of the same character (define (char->symbol char) (string->symbol (list->string (list char)))) ;; Define parser succeed and fail functions ;; Both return 3-lists of the form ;; ( ) (define (succeed result residue) (list #t result residue)) (define (fail residue) (list #f () residue)) (define (outcome triple) (first triple)) (define (result triple) (second triple)) (define (residual triple) (third triple)) (load "tester.rkt") ;; Tests for the parser. You should add at least 2 more. (test (parse "x = 42+-0 meter") (list #t (list '= 'x (make-UQL 42 0 '(meter) '())) '())) (test (parse "% x") '(#t (% x) ())) (test (parse "z = y + g - x") '(#t (= z (+ y (- g x))) ())) (test (parse "x = 3.42 +- .75 foot foot / second") (list #t (list '= 'x (make-UQL 3.42 0.75 '(foot foot) '(second))) '())) (test (parse "var = 3") (list #t (list '= 'var (make-UQL 3 0 '() '())) '())) (test (parse "var = -2.5 meter / second") (list #t (list '= 'var (make-UQL -2.5 0 '(meter) '(second))) '())) (test (parse "x = (y+x)^-2") '(#t (= x (^ (+ y x) -2)) () )) (test (parse "x = y+x^2") '(#t (= x (+ y (^ x 2))) ())) (test (parse "x = y+(x^2-(z/y))") '(#t (= x (+ y (- (^ x 2) (/ z y)))) ())) (test (parse "x = y+(x^2-z/y)") '(#t (= x (+ y (- (^ x 2) (/ z y)))) ())) (test (parse "a = 3.5 +- .2 / cm") (list #t (list '= 'a (make-UQL 3.5 0.2 '() '(cm))) '()) ) (test (parse "a") '(#t a ())) (tester 'show)