(module parser mzscheme
(require (lib "etc.ss")
(lib "match.ss")
(lib "contract.ss")
(planet "inspector.ss" ("dherman" "inspector.plt" 1 0))
(planet "contract-utils.ss" ("cobbe" "contract-utils.plt" 1 0))
"utils.ss"
"ast.ss")
(provide/contract (parse-program (-> sexp/c program?))
(struct (exn:cj:parse exn:fail:contract)
([message string?]
[continuation-marks continuation-mark-set?]
[src any/c])))
(provide expand-parse-exn)
(with-public-inspector
(define-struct (exn:cj:parse exn:fail:contract) (src))
(define-struct temp-class (name superclass fields methods)))
(set! make-temp-class
(let ([old-ctor make-temp-class])
(lambda (n s f m)
(unless (class-name? n)
(error 'make-temp-class "expected class-name, got ~a" n))
(unless (or (class-name? s) (not s))
(error 'make-temp-class
"expected class-name or false, got ~a" s))
(unless (and (list? f) (andmap field? f))
(error 'make-temp-class "expected list of field, got ~a" f))
(unless (and (list? m) (andmap method? m))
(error 'make-temp-class "expected list of method, got ~a" m))
(old-ctor n s f m))))
(define-syntax expand-parse-exn
(syntax-rules ()
[(_ expr)
(with-handlers ([exn:cj:parse? struct->vector])
expr)]))
(define parse-init-program
(lambda (src)
(unless (and (list? src)
(not (null? src)))
(raise (make-exn:cj:parse "bad program" (current-continuation-marks)
src)))
(let ([table (make-hash-table)])
(hash-table-put! table 'Object
(make-temp-class 'Object #f null null))
(recur loop ([src src])
(cond
[(null? (cdr src)) (values table (parse-expr (car src)))]
[else
(add-to-table! (parse-defn (car src)) table)
(loop (cdr src))])))))
(define add-to-table!
(lambda (cdefn table)
(when (hash-table-get table
(temp-class-name cdefn)
(lambda () #f))
(raise (make-exn:cj:parse "duplicate class definition"
(current-continuation-marks)
cdefn)))
(hash-table-put! table (temp-class-name cdefn) cdefn)))
(define parse-program
(lambda (src)
(let-values ([(temp-table main) (parse-init-program src)])
(make-program (make-final-classes temp-table) main))))
(define make-final-classes
(lambda (temp-table)
(let ([final-table (make-hash-table)])
(hash-table-for-each temp-table
(patch-superclass temp-table final-table))
final-table)))
(define patch-superclass
(lambda (temp-table final-table)
(lambda (name class0)
(recur loop ([name name]
[class class0]
[history null])
(when (memq class history)
(raise (make-exn:cj:parse "inheritance cycle"
(current-continuation-marks)
class0)))
(unless (hash-table-get final-table name (lambda () #f))
(let* ([parent-name (temp-class-superclass class)]
[parent
(if parent-name
(hash-table-get temp-table parent-name
(lambda ()
(raise (make-exn:cj:parse
"parent class doesn't exist"
(current-continuation-marks)
class))))
#f)])
(when parent (loop parent-name parent (cons class history)))
(let ([final-parent (if parent-name
(hash-table-get final-table
parent-name)
#f)])
(hash-table-put!
final-table name
(make-class (make-class-type name)
final-parent
(temp-class-fields class)
(temp-class-methods class))))))))))
(define parse-expr
(match-lambda
['null (make-nil)]
[(? integer? i) (make-num-lit i)]
['true (make-bool-lit #t)]
['false (make-bool-lit #f)]
[(? id? x) (make-var-ref x)]
['this (make-var-ref 'this)]
[('new (? class-name? cname))
(make-new (make-class-type cname))]
[('ref obj (? field-name? fd)) (make-ref (parse-expr obj) fd)]
[('set obj (? field-name? fd) rhs)
(make-set (parse-expr obj) fd (parse-expr rhs))]
[('send obj (? method-name? md) args ...)
(make-send (parse-expr obj) md (map parse-expr args))]
[('super (? method-name? md) args ...)
(make-super md (map parse-expr args))]
[('cast (? class-name? cname) obj)
(make-cast (make-class-type cname) (parse-expr obj))]
[('let (? id? id) rhs body)
(make-cj-let id (parse-expr rhs) (parse-expr body))]
[((? binary-prim-name? op) rand1 rand2)
(make-binary-prim op (parse-expr rand1) (parse-expr rand2))]
[((? unary-prim-name? op) rand)
(make-unary-prim op (parse-expr rand))]
[('if e1 e2 e3) (make-if-expr (parse-expr e1)
(parse-expr e2)
(parse-expr e3))]
[bogus (raise (make-exn:cj:parse "bad expression"
(current-continuation-marks)
bogus))]))
(define parse-defn
(match-lambda
[('class
(? defn-name? name)
(? class-name? superclass)
(fields ...)
methods ...)
(make-temp-class name superclass
(map (parse-field (make-class-type name)) fields)
(map parse-method methods))]
[bogus (raise (make-exn:cj:parse "bad definition"
(current-continuation-marks)
bogus))]))
(define parse-field
(lambda (declaring-class)
(match-lambda
[((? type-name? type) (? field-name? fd))
(make-field (parse-type type) declaring-class fd)]
[bogus (raise (make-exn:cj:parse "bad field definition"
(current-continuation-marks)
bogus))])))
(define parse-type
(match-lambda
['int (make-ground-type 'int)]
['bool (make-ground-type 'bool)]
[(? class-name? cname) (make-class-type cname)]
[bogus (raise (make-exn:cj:parse "bad type" (current-continuation-marks)
bogus))]))
(define parse-method
(match-lambda
[((? type-name? type) (? method-name? name) (args ...) body)
(let-values ([(names types) (mv-map parse-arg args)])
(make-method (parse-type type) name names types
(parse-expr body)))]
[bogus (raise (make-exn:cj:parse "bad method definition"
(current-continuation-marks)
bogus))]))
(define parse-arg
(match-lambda
[((? type-name? type) (? arg-name? name))
(values name (parse-type type))]
[bogus (raise (make-exn:cj:parse "bad argument definition"
(current-continuation-marks)
bogus))])))