(module compile mzscheme (require (lib "match.ss") (lib "struct.ss")) (require (only (lib "lex.ss" "parser-tools") position struct:position make-position position? position-offset position-line position-col set-position-offset! set-position-line! set-position-col!)) ;; any * position * position (define-struct/properties region (source start end) ([prop:custom-write (lambda (r port write?) (fprintf port "#" (position-line (region-start r)) (position-col (region-start r)) (position-line (region-end r)) (position-col (region-end r))))])) ;; region * symbol (define-struct id (loc name) #f) ;; region (define-struct Expr (loc) #f) ;; id * Expr (define-struct (FunExpr Expr) (var body) #f) ;; integer (define-struct (IntExpr Expr) (value) #f) ;; Expr * Expr (define-struct (AppExpr Expr) (rator rand) #f) ;; id (define-struct (VarExpr Expr) (name) #f) (define (current-position in) (let-values ([(line col offset) (port-next-location in)]) (make-position offset line col))) ;; parse-expression : input-port -> (optional Expr) (define (parse-expression in) (skip-whitespace in) (and (not (eof-object? (peek-char in))) (begin0 (cond [(regexp-match-peek-positions #rx"^fun([ \t\r\n]|$)" in) => (lambda (match) (let ([start (current-position in)]) (read-string 3 in) (skip-whitespace in) (let ([x (parse-identifier in)]) (skip-whitespace in) (parse-literal "." in) (skip-whitespace in) (let ([body (parse-expression in)] [loc (make-region (object-name in) start (current-position in))]) (make-FunExpr loc x body)))))] [(regexp-match-peek-positions #rx"^[0-9]+" in) => (lambda (match) (let ([start (current-position in)] [num (string->number (read-string (cdar match) in))]) (make-IntExpr (make-region (object-name in) start (current-position in)) num)))] [(regexp-match-peek-positions #rx"^\\(" in) => (lambda (match) (let ([start (current-position in)]) (read-char in) (let ([rator (parse-expression in)]) (skip-whitespace in) (let ([rand (parse-expression in)]) (skip-whitespace in) (parse-literal ")" in) (make-AppExpr (make-region (object-name in) start (current-position in)) rator rand)))))] [else (let ([start (current-position in)] [x (parse-identifier in)]) (make-VarExpr (make-region (object-name in) start (current-position in)) x))]) (skip-whitespace in)))) (define (parse-identifier in) (let ([match (regexp-match-peek-positions #rx"^[a-zA-Z]+" in)]) (unless match (raise-syntax-error 'parse "invalid identifier")) (let ([start (current-position in)] [name (string->symbol (read-string (cdar match) in))]) (make-id (make-region (object-name in) start (current-position in)) name)))) (define (parse-literal lit in) (let ([next (peek-string (string-length lit) 0 in)]) (unless (equal? next lit) (raise-syntax-error 'parse "expected ~a" lit)) (read-string (string-length lit) in))) (define (skip-whitespace in) (cond [(regexp-match-peek-positions #rx"^[ \t\r\n]+" in) => (lambda (match) (read-string (cdar match) in) (void))])) (define stx-for-original-property (read-syntax #f (open-input-string "here"))) ;; build-syntax : any * region -> syntax (define (build-syntax expr loc) (datum->syntax-object #f expr (and loc (location->syntax loc)) stx-for-original-property)) ;; location->syntax : region -> syntax (define (location->syntax loc) (let ([start (region-start loc)] [end (region-end loc)]) (datum->syntax-object #f 'source-location (list (region-source loc) (position-line start) (position-col start) (position-offset start) (- (position-offset end) (position-offset start))) stx-for-original-property))) ;; id->syntax : id -> syntax (define (id->syntax id) (build-syntax (id-name id) (id-loc id))) ;; compile-expression : (optional Expr) -> syntax (define (compile-expression expr) (match expr [($ FunExpr loc var body) (with-syntax ([x (id->syntax var)] [body-e (compile-expression body)]) (build-syntax #'(lambda (x) body-e) loc))] [($ IntExpr loc value) (build-syntax value loc)] [($ AppExpr loc rator rand) (with-syntax ([rator-e (compile-expression rator)] [rand-e (compile-expression rand)] [(v) (generate-temporaries '(v))]) (build-syntax #'(let ([v rator-e]) (if (procedure? v) (v rand-e) (error 'app "not a procedure"))) loc))] [($ VarExpr loc id) (build-syntax (id->syntax id) loc)] [#f #'(void)])) (provide parse-expression compile-expression))