#lang scheme/base

;; These are basic utility functions to be used in base.ss, but not to
;; be re-exported: base.ss should only export base.* names.

(provide (all-defined-out))

(require "rep.ss"
         "ns.ss"
         "stack.ss"
         )

;; Convenience macro for primitive definitions.
(define-syntax define-word
  (syntax-rules ()
    ((_ name proto . body)
     (define-ns (scat) name 
       (make-word
        (stack-lambda proto . body))))))


;; Like scheme's lambda, but with specified annotation. This is
;; useful for temporary words that have no source representation.
  
;; (define-syntax pn-lambda-annotate
;;   (syntax-rules ()
;;     ((_ annotation formals body ...)
;;      (make-word 
;;       annotation
;;       (lambda formals body ...)))))
        








;; Direct stack access. Use these functions to give proper error
;; handling.

(define (need-pair s)
  (unless (pair? s) (error 'stack-underflow)))

(define (stack-car s) (need-pair s) (car s))
(define (stack-cdr s) (need-pair s) (car s))





;; General state accumulation with proper tail recursion. Maybe this
;; should be called fold? Not really, since it can't express right
;; fold..

;; - abstract interpretation of an abstract list
;; - the last element is called in tail position

;; it's easier to test zero case up front

;; NOTE: symbols are used more than once, so best to use variables.


(define (interpret-list
         interpret         ;; abstract code interpretation
         car cdr null?     ;; abstract list access
         lst               ;; code sequence
         state)            ;; state accumulator
  (if (null? lst)
      state                            ;; nop
      (let next ((l lst)
                 (s state))
        (let ((kar (car l))
              (kdr (cdr l)))
          (if (null? kdr)
              (interpret kar s)        ;; tail call
              (next kdr                ;; recursive call
                    (interpret kar s)))))))


(define (->string x) (format "~a" x))


(define cpm-mark
  (let ((ms 0))
    (lambda ()
      (let*
          ((now   (current-process-milliseconds))
           (delta (- now ms)))
        (set! ms now)
        delta))))
