#lang scheme/base
(provide
 make-constant
 constants)
(require
 "../tools.ss"
 "../scat.ss"
 "../target.ss"
 "macro-syntax.ss"
 "macro-eval.ss"
 )

;; Constants.

;; A constant is a macro that compiles a single literal value.

;; It's convenient to have constant names bound also in the scheme
;; namespace for asm pattern specification. There it represents a
;; value.

;; However, as the PIC18 case has illustrated, it is best to keep
;; constants late bound: they are part of the compiler specialization
;; which fills in some macro hooks (redefines the stubs that throw
;; errors).

;; For the scheme names to work, they need to refer to a delayed
;; target-value that refers to the macro to obtain its value.


;; Cyclic implementation:
;;  - the macro compiles a literal, with value = the tval
;;  - the tval extracts a constant from the macro

;; The cycle is broken in 2 ways:
;;  - macro is redefined with a real constant
;;  - the tval gets evaluated at assembly time, and produces an error

;;  The macro compiles a literal, this literal is the target-value
;;  that refers to the macro struct. This allows the macro to behave
;;  as a constant before it is actually defined with a value.

;;  The evaluator checks if the macro evaluates to itself, end only
;;  then raises an error. As long as the macro is redefined before
;;  assembly time, things are ok.

(define (constant->value macro)
  (macro->data macro 'qw))

(define (make-constant name)
  (define macro (macro: ',tval))
  (define tval  (target-value-delay (tval-thunk) name))
  (define tval-thunk
    (lambda ()
      (let ((value (constant->value macro)))
        (if (eq? value tval)
            (error 'undefined-parameter "~a" name)
            value))))
  macro)


(define-sr (constants name ...)
  (begin
    (define-ns (macro) name (make-constant 'name)) ...
    (define name (constant->value (ns (macro) name))) ...))


