;;; ==================================================================
;;; This deals with incremental compilation as used by the command interface.
;;; The basic theory is to create new modules which import the entire
;;; symbol table of an existing module.
;;; This adds a new module to the extension environment. This env is an alist
;;; of module names & extended modules.
(define *extension-env* '())
(define (extend-module mod-name new-ast)
(push (tuple mod-name new-ast) *extension-env*))
;;; This cleans out extensions for a module.
(define (remove-extended-modules mod-name)
(setf *extension-env* (rem-ext1 *extension-env* mod-name)))
(define (rem-ext1 env name)
(cond ((null? env)
'())
((eq? (tuple-2-1 (car env)) name)
(rem-ext1 (cdr env) name))
(else
(cons (car env) (rem-ext1 (cdr env) name)))))
(define (clear-extended-modules)
(setf *extension-env* '()))
;;; This retrieves the current extension to a module (if any).
(define (updated-module name)
(let ((name+mod (assq name *extension-env*)))
(if (not (eq? name+mod '#f))
(tuple-2-2 name+mod)
(let ((mod-in-table (table-entry *modules* name)))
(cond ((eq? mod-in-table '#f)
(signal-module-not-ready name))
((eq? (module-type mod-in-table) 'interface)
(signal-cant-eval-interface name))
(else mod-in-table))))))
(define (signal-module-not-ready name)
(fatal-error 'module-not-ready
"Module ~A is not loaded and ready."
name))
(define (signal-cant-eval-interface name)
(fatal-error 'no-evaluation-in-interface
"Module ~A is an interface: evaluation not allowed."
name))
(define (compile-fragment module str filename)
(let ((mod-ast (updated-module module)))
(dynamic-let
((*printers* (if (memq 'extension *printers*) *printers* '()))
(*abort-phase* '#f))
(mlet (((t-code new-ast) (compile-fragment1 module mod-ast str filename)))
(cond ((eq? t-code 'error)
'error)
(else
(eval t-code)
new-ast))))))
(define (compile-fragment1 mod-name mod-ast str filename)
(let/cc x
(dynamic-let ((*abort-compilation* (lambda () (funcall x 'error '()))))
(let* ((mods (parse-from-string
(format '#f "module ~A where~%~A~%" mod-name str)
(function parse-module-list)
filename))
(new-mod (car mods)))
(when (not (null? (cdr mods)))
(signal-module-decl-in-extension))
(when (not (null? (module-imports new-mod)))
(signal-import-decl-in-extension))
(fragment-initialize new-mod mod-ast)
(values (modules->lisp-code mods) new-mod)))))
(define (signal-module-decl-in-extension)
(fatal-error 'module-decl-in-extension
"Module declarations are not allowed in extensions."))
(define (signal-import-decl-in-extension)
(fatal-error 'import-decl-in-extension
"Import declarations are not allowed in extensions."))
;;; Copy stuff into the fragment module structure from its parent module.
;;; The inverted symbol table is not necessary since the module contains
;;; no imports.
(define (fragment-initialize new old)
(setf (module-name new) (gensym))
(setf (module-type new) 'extension)
(setf (module-unit new) (module-unit old))
(setf (module-uses-standard-prelude? new)
(module-uses-standard-prelude? old))
(setf (module-inherited-env new) old)
(setf (module-fixity-table new)
(copy-table (module-fixity-table old)))
(setf (module-default new) (module-default old)))
;;; This code deals with the actual evaluation of Haskell code.
;;; This decides whether a variable has type `Dialogue'.
(define (io-type? var)
(let ((type (var-type var)))
(when (not (gtype? type))
(error "~s is not a Gtype." type))
(and (null? (gtype-context type))
(is-dialogue? (gtype-type type)))))
(define (is-dialogue? type)
(let ((type (expand-ntype-synonym type)))
(and (ntycon? type)
(eq? (ntycon-tycon type) (core-symbol "Arrow"))
(let* ((args (ntycon-args type))
(a1 (expand-ntype-synonym (car args)))
(a2 (expand-ntype-synonym (cadr args))))
(and
(ntycon? a1)
(eq? (ntycon-tycon a1) (core-symbol "SystemState"))
(ntycon? a2)
(eq? (ntycon-tycon a2) (core-symbol "IOResult")))))))
(define (is-list-of? type con)
(and (ntycon? type)
(eq? (ntycon-tycon type) (core-symbol "List"))
(let ((arg (expand-ntype-synonym (car (ntycon-args type)))))
(and (ntycon? arg) (eq? (ntycon-tycon arg) con)))))
(define (apply-exec var)
(initialize-io-system)
(mlet (((_ sec)
(time-execution
(lambda ()
(let/cc x
(setf *runtime-abort* (lambda () (funcall x 'error)))
(let ((fn (eval (fullname var))))
(unless (var-strict? var)
(setf fn (force fn)))
(funcall fn (box 'state))))))))
(say "~%")
(when (memq 'time *printers*)
(say "Execution time: ~A seconds~%" sec)))
'done)
(define (eval-module mod)
(dolist (v (module-vars mod))
(when (io-type? v)
(when (not (string-starts? "temp_" (symbol->string (def-name v))))
(say/ne "~&Evaluating ~A.~%" v))
(apply-exec v))))
(define (run-program name)
(compile/load name)
(let ((main-mod (table-entry *modules* '|Main|)))
(if main-mod
(let ((main-var (table-entry (module-symbol-table main-mod) '|main|)))
(if main-var
(apply-exec main-var)
(error "Variable main missing")))
(error "module Main missing"))))
|