git.fiddlerwoaroof.com
command-interface/incremental-compiler.scm
4e987026
 ;;; ==================================================================
 
 ;;; 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"))))