; XMLScheme (c) Markus Lumpe & EOPL2 (parts)
(define scanner-spec
'(
(white-sp
(whitespace) skip)
(comment
("%" (arbno (not #\newline))) skip)
(identifier
(letter (arbno (or letter digit "?"))) symbol)
(number
(digit (arbno digit)) number)
(verbatim
("$" (arbno (not #\$)) "$") string)
)
)
;
; Basic syntax:
;
; program ::= expression
;
; declarations ::=
; "" expression "/>"}* "/>"
;
; i-vars ::= ""
;
; methods ::= ""
;
; expression ::=
; ""
; ""
; "" expression "/>"
; "
; " expression "/>"
; ""
; ""
; ""
; "" expression arguments "/>"
; ""
; ""
; ""
; ""
; ""
; ""
; ""
; "<" prim-op arguments "/>"
;
; prim-op ::=
; "add"
; "sub"
; "mul"
; "inc"
; "dec"
; ; new operations :
; "div" | "equal" | "less" | "greater" | "not" | "and" | "or"
;
; arguments ::=
; ""
;
; formals ::=
; ""
;
; param ::=
; ""
; ""
;
(define expression-spec
'(
(expression
("") loop-exp)
(expression
("") class-exp)
(expression
(""
expression "/>") assign-exp)
(expression
("") seq-exp)
(expression
("") proc-exp)
(expression
(""
expression
"" "/>") send-exp)
(expression
("" "/>") app-exp)
(expression
("") reclet-exp)
(expression
("") let-exp)
(expression
(""
""
"" "/>") if-exp)
(expression
("") lit-exp)
(expression
("") var-exp)
(expression
("") extern-exp)
; we require at least one argument now
(expression
("<" prim-op "" "/>") primapp-exp)
(expression
("") verbatim-exp)
(i-vars
() empty-ivars)
(i-vars
(""
expression "/>")
"/>") ivar-decls)
(methods
() empty-methods)
(methods
(""
formals
expression "/>")
"/>") method-decls)
(prim-op
("add") add-prim)
(prim-op
("sub") sub-prim)
(prim-op
("mul") mult-prim)
(prim-op
("inc") inc-prim)
(prim-op
("dec") dec-prim)
(prim-op
("div") div-prim)
(prim-op
("equal") equal-prim)
(prim-op
("less") less-prim)
(prim-op
("greater") greater-prim)
(prim-op
("not") not-prim)
(prim-op
("and") and-prim)
(prim-op
("or") or-prim)
(formals
("")
"/>") param-decls)
(formal-mode
("value") mode-value)
(formal-mode
("byref") mode-byref)
(declarations
(""
expression "/>")
"/>") let-decls)
(opt-declarations
() empty-decl-list)
(opt-declarations
(declarations) decl-list)
(opt-conditions
() empty-exp-list)
(opt-conditions
("") exp-list)
(opt-increments
() empty-exp-list)
(opt-increments
("") exp-list)
)
)
(define grammar-spec
'(
(program
(expression) a-program)
(formals
("")
"/>") param-decls)
(formal-mode
("value") mode-value)
(formal-mode
("byref") mode-byref)
(i-vars
() empty-ivars)
(i-vars
(""
expression "/>")
"/>") ivar-decls)
(methods
() empty-methods)
(methods
(""
formals
expression "/>")
"/>") method-decls)
(declarations
(""
expression "/>")
"/>") let-decls)
(opt-declarations
() empty-decl-list)
(opt-declarations
(declarations) decl-list)
(opt-conditions
() empty-exp-list)
(opt-conditions
("") exp-list)
(opt-increments
() empty-exp-list)
(opt-increments
("") exp-list)
(expression
("") loop-exp)
(expression
("") class-exp)
(expression
("") seq-exp)
(expression
(""
expression "/>") assign-exp)
(expression
("") extern-exp)
(expression
("") proc-exp)
(expression
(""
expression
"" "/>") send-exp)
(expression
("" "/>") app-exp)
(expression
("") reclet-exp)
(expression
("") let-exp)
(expression
(""
""
"" "/>") if-exp)
(expression
("") lit-exp)
(expression
("") var-exp)
; we require at least one argument now
(expression
("<" prim-op "" "/>") primapp-exp)
(expression
("") verbatim-exp)
(prim-op
("add") add-prim)
(prim-op
("sub") sub-prim)
(prim-op
("mul") mult-prim)
(prim-op
("inc") inc-prim)
(prim-op
("dec") dec-prim)
(prim-op
("div") div-prim)
(prim-op
("equal") equal-prim)
(prim-op
("less") less-prim)
(prim-op
("greater") greater-prim)
(prim-op
("not") not-prim)
(prim-op
("and") and-prim)
(prim-op
("or") or-prim)
)
)
;; build the scanner and parser
(define front-end
(sllgen:make-string-parser scanner-spec grammar-spec))
;; build the expression parser (XMLScheme5)
(define expression-parser
(sllgen:make-string-parser scanner-spec expression-spec))
;; build the define-datatype definitions
(define-datatype program program?
(a-program
(exp expression?))
)
(define-datatype pdeclarations pdeclarations?
(param-decls
(modes (list-of pmode?))
(ids (list-of symbol?)))
)
(define-datatype pmode pmode?
(mode-value)
(mode-byref)
)
(define-datatype declarations declarations?
(let-decls
(ids (list-of symbol?))
(rands (list-of expression?)))
)
(define-datatype declaration-list declaration-list?
(empty-decl-list)
(decl-list
(decls declarations?))
)
(define-datatype expression-list expression-list?
(empty-exp-list)
(exp-list
(exp expression?)
(tail (list-of expression?)))
)
(define-datatype i-vars i-vars?
(empty-ivars)
(ivar-decls
(ids (list-of symbol?))
(rands (list-of expression?)))
)
(define-datatype methods methods?
(empty-methods)
(method-decls
(ids (list-of symbol?))
(params (list-of pdeclarations?))
(bodies (list-of expression?)))
)
(define-datatype expression expression?
(seq-exp
(exp expression?)
(exps (list-of expression?)))
(verbatim-exp
(code string?))
(class-exp
(params pdeclarations?)
(i-decls i-vars?)
(m-decls methods?))
(assign-exp
(id symbol?)
(exp expression?))
(loop-exp
(decls declaration-list?)
(conds expression-list?)
(incrs expression-list?)
(body expression?))
(reclet-exp
(decls declarations?)
(body expression?))
(extern-exp
(unit symbol?))
(proc-exp
(ids pdeclarations?)
(body expression?))
(send-exp
(mid symbol?)
(obj expression?)
(rands (list-of expression?)))
(app-exp
(rator expression?)
(rands (list-of expression?)))
(let-exp
(decls declarations?)
(body expression?))
(if-exp
(test-exp expression?)
(then-exp expression?)
(else-exp expression?))
(lit-exp
(datum number?))
(var-exp
(id symbol?))
(primapp-exp
(prim primitive?)
(hd expression?)
(rands (list-of expression?)))
)
(define-datatype primitive primitive?
(add-prim)
(sub-prim)
(mult-prim)
(inc-prim)
(dec-prim)
(div-prim)
(equal-prim)
(less-prim)
(greater-prim)
(not-prim)
(and-prim)
(or-prim)
)
(load "ref-environment.scm")
(define init-env
(lambda ()
(extend-env '(i v x) '(1 5 10) (empty-env))
)
)
(define eval-program
(lambda (pgm)
(cases program pgm
(a-program (body)
(eval-expression body (init-env)))
)
)
)
(define is-true?
(lambda (x)
(not (zero? x))
)
)
(define get-rands
(lambda (decls)
(cases declarations decls
(let-decls (ids rands)
rands)
)
)
)
(define get-ids
(lambda (decls)
(cond
((declarations? decls)
(cases declarations decls
(let-decls (ids rands)
ids)))
((pdeclarations? decls)
(get-param-ids decls))
)
)
)
(define get-param-ids
(lambda (ps)
(cases pdeclarations ps
(param-decls (modes ids) ids)
)
)
)
(define get-param-modes
(lambda (ps)
(cases pdeclarations ps
(param-decls (modes ids) modes)
)
)
)
;; use colosured to allow for classes
(load "closured.scm")
(define build-rec-proc
(lambda (v rec-ids)
(cases procval v
(closure (params body env)
(closure-rec params body rec-ids env))
(else v) ;; we should never reach this case
)
)
)
;; load target handling
(load "targetsb.scm")
(define eval-exp-sequence
(lambda (exp exps env)
; while loop
(let continue ((res (eval-expression exp env))
(tail exps))
;(display res) (display "\n")
(if (null? tail)
res
(continue (eval-expression (car tail) env)
(cdr tail))
)
)
)
)
(define eval-declaration-list
(lambda (decl-lst env)
(cases declaration-list decl-lst
(empty-decl-list () '())
(decl-list (decls)
(zip (get-ids decls)
(map (lambda (e) (eval-expression e env)) (get-rands decls))))
)
)
)
(define make-exp-list
(lambda (exps)
(cases expression-list exps
(empty-exp-list () '())
(exp-list (exp tail) (cons exp tail))
)
)
)
(define eval-loop
(lambda (loop-decls conditions increments body env)
(let ((p (unzip loop-decls))
; map conditions to or-prim
(loop-test (primapp-exp (or-prim) (car conditions) (cdr conditions))))
; loop
(let
continue
;; loop var denote direct targets
((new-env (extend-env (car p) (map direct-target (cadr p)) env)) ; env
(res 0)) ; res #f
;; do loop
(if (is-true? (eval-expression loop-test new-env))
(let ((new-res (eval-expression body new-env)) ; eval body => res
(step-res (map (lambda (e) (eval-expression e new-env)) increments)))
;; next iteration, direct targets
(continue (extend-env (car p) (map direct-target step-res) env) new-res)
)
res
)
)
)
)
)
;; objects
(define-datatype objectval objectval?
(object
(ivars environment?)
(methods environment?)
(image environment?))
)
(define lookup-method
(lambda (obj mid)
(cases objectval obj
(object (ivars methods image) (apply-env methods mid))
)
)
)
(define self
(lambda (obj)
(cases objectval obj
(object (ivars methods image) image)
)
)
)
;; load file system utilities
(require (lib "file.ss"))
;; create a visible verbatim environment
(define verbatim-env 0)
(define evaluate-verbatim
(lambda (code env)
;; create temporary file (DrScheme supports optional arguments)
(let* ((temp-file (open-output-file "eval-verbatim.temp" 'text 'truncate)))
;; write file
(let loop ((contents (string->list code)))
(if (null? contents)
(close-output-port temp-file)
(begin
(write-char (car contents) temp-file)
(loop (cdr contents))))
)
;; Load the contents and evaluate it in the current context
;; Load does not return a value. However, we have access to
;; set verbatim environment
(set! verbatim-env (extend-env '(__RESULT__) (list (direct-target 0)) env))
;; execute program
(setref! (apply-env-ref verbatim-env '__RESULT__) (load "eval-verbatim.temp") )
;; delete file
(delete-directory/files "eval-verbatim.temp")
;; return value of verbatim expression
(apply-env verbatim-env '__RESULT__)
)
)
)
(define eval-expression
(lambda (exp env)
(cases expression exp
(extern-exp (unit-name)
(let ((contents (read-file
(string-append (symbol->string unit-name) ".xml"))))
;; no change
(eval-expression (expression-parser contents) env)))
(verbatim-exp (code)
(evaluate-verbatim (substring code 1 (- (string-length code) 1)) env))
(seq-exp (exp exps)
(eval-exp-sequence exp exps env))
(class-exp (params i-decls m-decls)
(let ((vtable (evaluate-methods m-decls env)))
(closure-class params i-decls (car vtable) (cadr vtable) env)))
(assign-exp (id r-value)
(let ((val (eval-expression r-value env)))
; maps val to direct-target
(setref! (apply-env-ref env id) val)
val))
(loop-exp (decls conds incrs body)
(let ((new-decls (eval-declaration-list decls env)); list of pairs
(conditions (make-exp-list conds))
(increments (make-exp-list incrs)))
;; check for correct increments arity
(if (= (length new-decls) (length increments))
(eval-loop new-decls conditions increments body env)
;; arity error
(eopl:error 'eval-expression
"Arity mismatch in loop increments" )
)
))
(proc-exp (params body) (closure params body env))
(send-exp (mid obj rands)
(let (;; evaluate object
(receiver (eval-expression obj env)))
(if (objectval? receiver)
(let* (;; find method
(mproc (lookup-method receiver mid))
;; evaluate arguments
(args (eval-proc-rands
(get-param-modes (closure-params mproc)) rands env)))
;; call method using 'self' as calling env
(apply-method mproc args (self receiver))
)
(eopl:error 'eval-expression "Receiver is not an object")
)))
(app-exp (rator rands)
;; check procval first in order to extract parameter modes
(let ((proc (eval-expression rator env)))
(if (procval? proc)
;; we need parameter modi here
(let ((args (eval-proc-rands
(get-param-modes (closure-params proc)) rands env)))
;; add calling-env to resolve occurring recursive procedures
(apply-procval proc args env))
(eopl:error 'eval-expression
"Attempt to apply non-procedure ~s" proc))))
(reclet-exp (decls body)
(let* ((args (eval-rands (get-rands decls) env))
;; filter recursive procedure ids
(rec-proc-ids (map car (filter (lambda (p) (procval? (cadr p)))
(zip (get-ids decls) args))))
;; now change closure to closure-rec
(new-args (map (lambda (v)
(if (procval? v)
(build-rec-proc v rec-proc-ids)
v)) args))
;; reclet: binder is direct-target
(new-d-args (map (lambda (v) (direct-target v)) new-args)))
(eval-expression body (extend-env (get-ids decls) new-d-args env))))
(let-exp (decls body)
(let ((args (eval-let-rands (get-rands decls) env)))
(eval-expression body (extend-env (get-ids decls) args env))))
(if-exp (test-exp true-exp false-exp)
(if (is-true? (eval-expression test-exp env))
(eval-expression true-exp env)
(eval-expression false-exp env)))
(lit-exp (datum) datum)
(var-exp (id) (apply-env env id))
(primapp-exp (prim hd rands)
(let ((args (eval-rands (cons hd rands) env)))
(apply-primitive prim args)))
)
)
)
;; procedure arguments
(define eval-proc-rands
(lambda (modes rands env)
(if (= (length rands) (length modes))
(map (lambda (p) (eval-proc-rand p env)) (zip modes rands))
(eopl:error 'eval-proc-rands "Parameter mismatch")
)
)
)
(define eval-proc-rand
(lambda (argument env)
(cases pmode (car argument)
(mode-value ()
(direct-target (eval-expression (cadr argument) env)))
(mode-byref ()
(cases expression (cadr argument)
(var-exp (id)
;; build new reference
(indirect-target (let ((ref (apply-env-ref env id)))
(cases target (primitive-deref ref)
(direct-target (val) ref) ;; return new reference
(indirect-target (a-ref) a-ref))))) ;; return old reference
(else (direct-target (eval-expression (cadr argument) env)))
))
)
)
)
;; let (binder is direct-target: call-by-value)
(define eval-let-rands
(lambda (rands env)
(map (lambda (x) (eval-let-rand x env)) rands)
)
)
(define eval-let-rand
(lambda (rand env)
(direct-target (eval-expression rand env) )
)
)
;; primapp-exp
(define eval-rands
(lambda (rands env)
(map (lambda (x) (eval-rand x env)) rands)
)
)
(define eval-rand
(lambda (rand env)
(eval-expression rand env)
)
)
(define apply-primitive
(lambda (prim args)
(cases primitive prim
;; we use a more reliable approach to evaluate primitive expressions
(add-prim () (eval (append '(+) args)))
(sub-prim () (eval (append '(-) args)))
(mult-prim () (eval (append '(*) args)))
(inc-prim () (car (reverse (map (lambda (n) (+ n 1)) args)))); return last res
(dec-prim () (car (reverse (map (lambda (n) (- n 1)) args)))); return last res
(div-prim () (eval (append '(/) args)))
(equal-prim ()
(let cont ((tail (cdr args)))
(if (null? tail)
1 ; identity ==> true
(if (eqv? (car args) (car tail))
(cont (cdr tail))
0; not all elements are equal
)
)
))
(less-prim () (b->n (eval (append '(<) args))))
(greater-prim () (b->n (eval (append '(>) args))))
(not-prim ()
(b->n (car (reverse (map (lambda (v)
(if (number? v)
(not (is-true? v))
(not v))) args))))); return last res
; 'and' and 'or' in order to work correctly have to be applied to booleans
(and-prim () (b->n (eval (append '(and) (map is-true? args)))))
(or-prim () (b->n (eval (append '(or) (map is-true? args)))))
)
)
)
(define b->n (lambda (b) (if b 1 0)))
(define interpreter
(lambda (string)
(eval-program (front-end string))
)
)
(define read-eval-loop
(sllgen:make-rep-loop
"$ "
eval-program
(sllgen:make-stream-parser scanner-spec grammar-spec)))
(load "readfile.scm")
(define run-from-file
(lambda (fname)
(interpreter (read-file fname))
)
)
(define parse-from-file
(lambda (fname)
(front-end (read-file fname))
)
)