#lang scheme/base

;; Handling different languages is done by associating a fresh
;; namespace to each instantiated target code module. Such a namespace
;; is called a ``project''.

;; This module provides some instantiation/sharing help and provides
;; communication between namespaces using 'eval'.

;; (define-syntax-rule (delegated-live-macros: name ...)
;;   (begin
;;     (provide name ...)
;;     (define-syntax-rule (name . args)
;;       (prj-eval `(with-console (lambda () (name . args))))) ...))

(define-syntax-rule (delegated-functions: name ...)
  (begin
    (provide name ...)
    (define (name . args)
      (prj-eval `(apply name ',args))) ...))

;; Special case: to not limit composition, provide macros as
;; functions. This is for constructs that need to be macros in the
;; compiler namespace because they expand to toplevel forms.
(define-syntax-rule (delegated-toplevel: name ...)
  (begin
    (provide name ...)
    (define (name arg)
      (prj-eval `(name ,arg))) ...))


(provide
 make-prj-namespace           ;; create namespace with sharing
 current-prj                  ;; parameter with current prj namespace
;; prj-require                  ;; require into prj namespace
 prj-eval                     ;; evaluate form in prj namespace
 prj                          ;; shortcut macro for multiple quoted forms

 forth-debug       ;; string -> print compiled code

 enter-prj
 
 words!
 console!
 pointers!

 make-prj              
 init-prj)

(delegated-toplevel:

 forth-command      ;; string -> target interaction command
 
 forth-compile      ;; string -> compiled code
 forth-load/compile ;; file -> compiled code
 forth-path         ;; add a search directory for load

 )


(delegated-functions:

 repl

 tfind             ;; lookup target word
 
 prog              ;; program a .hex file using an external programmer like piklab-prog

 with-console
 
 current-console   ;; target's console port + baud rate
 pointers          ;; memory allocation pointers  FIXME: make same as asm's current-pointers
 
 print-code        ;; print it
 kill-code!        ;; delete accumulated code
 
 save-ihex         ;; intel hex output
 save-dict         ;; save base app state
 load-dict         ;; load ..


)

;(delegated-live-macros:
; scat>             ;; scat host<->target interaction
; target>           ;; simulated target-local view of scat>
;
;)



(define make-prj (make-parameter (lambda () (error 'no-prj-registered))))
(define (init-prj)
  (current-prj ((make-prj))))


;; Create a namespace with shared and private module instances.
(define (shared/initial-namespace src-ns shared private)
  (let ((dst-ns (make-base-namespace)))

    ;; See PLT 4.0 guide, section 16.3
    ;; Reflection and Dynamic Evaluation -> Sharing Data and Code Across Namespaces
    (define (load-shared mod)
      (parameterize ((current-namespace src-ns))  ;; make sure it's there
        (dynamic-require mod #f)
        (namespace-require mod))

      (namespace-attach-module src-ns mod dst-ns) ;; get instance from here
      (parameterize ((current-namespace dst-ns))  ;; create bindings
        (namespace-require mod))
      )
    
    (define (load-private mod)
      (parameterize ((current-namespace dst-ns))
        (dynamic-require mod #f)
        (namespace-require mod)))
    
    (for-each load-shared shared)
    (for-each load-private private)

    dst-ns))

;; Create an initialized namespace with structure sharing. Make sure
;; the initial module names are all in canonical form, otherwise the
;; reps won't be shared.

(require scheme/runtime-path)
(define-runtime-path shared "shared.ss")
;; (printf "shared: ~a\n" shared)

(define (make-prj-namespace [initial '()]
                            [src (current-namespace)])
  (shared/initial-namespace
   src
   ;; Data structures are shared, so we can lift code out of the
   ;; namespace. This should include some more code that is common to
   ;; all compilers.
   `(,shared)

   ;; Populate more.
   `(,@initial)))
     


;; To simplify put the current namspace in a parameter.
(define current-prj (make-parameter #f))
(define (prj-eval expr)
  (unless (current-prj) (init-prj)) ;; lazy instantiation
  (parameterize
      ;; Make sure code inside the namespace can use 'eval' for
      ;; reflective operations.
      ((current-namespace (current-prj)))
    (eval expr)))

;; (define (prj-require spec)
;;   (parameterize ((current-namespace (current-prj)))
;;     (namespace-require spec)))


(define-syntax-rule (prj forms ...)
  (prj-eval '(begin forms ...)))


(define (forth-debug str)
  (prj-eval
   `(begin
      (asm-off!)
      (forth-compile ,str)
      (asm-on!))))


;; Loading project files.
(define (words! x)    (prj-eval `(set-target-words! ',x)))
(define (console! x)  (prj-eval `(current-console ',x)))
(define (pointers! x) (prj-eval `(pointers ',x)))


;; Entering/leaving project namespaces.

(define (enter-prj)
  (let ((top (current-namespace)))
    (prj-eval `(define (leave-prj) (current-namespace ,top)))
    (current-namespace (current-prj))))
