meeting -*- Outline -*- * abstract syntax (2.2.2) ------------------------------------------ ABSTRACT SYNTAX (2.2.2) Idea: identify each rule in the grammar with a record that a. b. def: *abstract syntax* ignores details of def: *concrete syntax* gives details needed for humans to write and read. CONCRETE SYNTAX OF LAMBDA CALCULUS ::= | (lambda () ) | ( ) AN ABSTRACT SYNTAX (define-datatype expression expression? (var-exp (id symbol?)) (lambda-exp (id symbol?) (body expression?)) (app-exp (rator expression?) (rand expression?))) ------------------------------------------ a... identifies the rule (a tag) b... provides access to components ... the characters used to write programs (parenthesis, etc.) ... (var-exp (id symbol?)) (lambda-exp (id symbol?) (body expression?)) (app-exp (rator expression?) (rand expression?))) Q: Can you define an abstract syntax for if expressions? literals? ------------------------------------------ PARSING AND UNPARSING parse concrete ---------> abstract ^ / \----------/ unparse Examples of parse (parse 'x) = (var-exp 'x) (parse '(f x)) = (app-exp (var-exp 'f) (var-exp 'x)) ------------------------------------------ ------------------------------------------ CODE FOR PARSE (deftype parse-expression (-> (datum) expression)) (define parse-expression (lambda (datum) (cond ((symbol? datum) (var-exp datum)) ((and (list? datum) (= 3 (length datum)) (eq? 'lambda (car datum))) (lambda-exp (caadr datum) (parse-expression (caddr datum)))) ((and (list? datum) (= 2 (length datum))) (app-exp (parse-expression (car datum)) (parse-expression (cadr datum)))) (else (eopl:error 'parse-expression "Invalid concrete syntax ~s" datum))))) ------------------------------------------ Q: why is the error case there? does that catch all errors? it's defensive, yes, in this case, but not the one in the book. Use the following to *explain cases* unparse is a convenience in displaying output of transformations. ------------------------------------------ UNPARSE USING CASES (deftype unparse (-> (expression) datum)) (define unparse-expression (lambda (exp) (cases expression exp ------------------------------------------ ... (var-exp (id) id) (lambda-exp (id body) (list 'lambda (list id) (unparse-expression body))) (app-exp (rator rand) (list (unparse-expression rator) (unparse-expression rand)))))) Now can write free-vars over the parsed, abstract syntax ------------------------------------------ FOR YOU TO DO (free-vars (parse-expression '(lambda (x) (car x)))) ==> (car) (load "set-ops.scm") ;;; use set-of, set-remove, set-union (deftype free-vars (-> (expression) (set-of symbol))) (define free-vars (lambda (exp) (cases expression exp (var-exp (id) ------------------------------------------ ... (set-of id)) (lambda-exp (id body) (set-remove id (free-vars body))) (app-exp (rator rand) (set-union (free-vars rator) (free-vars rand)))))) ** handling Kleene star or plus (p. 51ff) ------------------------------------------ ABSTRACT SYNTAX FOR KLEENE STAR To represent a starred production, use a list. Concrete syntax: ::= | | (if ) | (lambda ({}*) ) | ( {}*) Abstract syntax: (define-datatype expression expression? (lib-exp (datum number?)) (var-exp (id symbol?)) (if-exp (test-exp expression) (true-exp expression) (false-exp expression)) (lambda-exp (app-exp -------------------------------------- ... (ids (list-of symbol)) (body expression)) ... (rator expression) (rands (list-of expression))) -------------------------------------- EXAMPLE USING THIS GRAMMAR (deftype free-vars (-> (expression) (set symbol))) (define free-vars (lambda (exp) (cases expression exp (lit-exp (datum) the-empty-set) (var-exp (id) (set-of id)) (if-exp (test-exp true-exp false-exp) (set-union (free-vars test-exp) (set-union (free-vars true-exp) (free-vars false-exp)))) (lambda-exp (ids body) ------------------------------------------ ... (set-minus (free-vars body) (apply set ids))) ; or (list->set ids), and write list->set (app-exp (rator rands) (set-union (free-vars rator) (set-union-list (map free-vars rands)))))))