rpn/test-rpn.ss
#lang scheme/base
(require "main.ss"
         "../ns.ss"
         "../forth/forth-lex.ss"
         (for-syntax scheme/base
                     "main.ss"
                     "../ns.ss"
                     "../tools.ss"
                     "../forth/forth-tx.ss"  ;; also perform some forth tests
                     "parse-tx.ss")
         (lib "78.ss" "srfi") ;; check
         scheme/pretty)
          

(provide (all-defined-out)
         (all-from-out "main.ss"))

(check-set-mode! 'report-failed)


;; The RPN language serves mostly as a test for the rpn-parse macro,
;; and an example or prototype for building different language
;; semantics on top of the basic syntax.

;; This file implements a simple RPN language on top of scheme.  It is
;; extended by the scat: and macro: languages, and serves as a test
;; and concrete example of how to use the rpn parser to build modified
;; syntax/semantics for a concatenative language.
;;
;; The easiest way to see how this work is to take the forms used in
;; the test at the end of this file and observe the successive macro
;; expansion steps in DrScheme's macro stepper.
;;
;;
;;
;; The parser has 2 distinct translation stages to facilitate Forth's
;; incremental compilation model.
;;
;;   * TOKENS -> DICTIONARY
;;
;;      During this stage a flat stream of tokens (coming from the
;;      lexer) is parsed by the 'rpn-parse macro, a non-nested parser
;;      loop implementing basic RPN language elements.
;;
;;      The ``semantics macros'' passed to 'rpn-parse will end up in
;;      the dictionary, associated with the proper token types as recognised by the parser.
;;
;;      I.e. for the rpn: and rpn-begin forms below tokens get mapped
;;      as:
;;
;;         token-stx -> tagged-instruction-stx = (list semantics-stx expr-stx)
;;         ---------------------------------------
;;         123       -> (p-push 123)
;;         foo       -> (p-apply rpn/foo)
;;         (1 2)     -> (p-push (rpn: 1 2))
;;         '(123)    -> (p-push `(,123))
;;         ...
;;
;;      The dictionary format is:
;;        (list-of (cons name-stx (list-of tagged-instruction-stx)))
;;        (list-of (cons name-stx (list-of (list semantics-stx expr-stx))))
;;
;;      This stage is extensible by binding instances of the
;;      rpn-transformer struct to mapped identifers
;;      (syntax-local-value), which will be executed by the parser
;;      loop.  In addition token streams can contain rpn-transformer
;;      instances.
;;
;;
;;   * DICTIONARY -> SCHEME
;;
;;      The second phase is the conversion from dictionary format to
;;      scheme binding forms and lambda expressions.
;;
;;      Examples of this conversion are 'rpn-begin-mk, 'rpn:-ml.
;;      These will pass the conversion on to 'rpn-lambda.
;;
;;      The macro 'rpn-parse-test when passed with a 'quote form will
;;      give a symbolic representation of the dictionary as
;;      constructed by rpn-parse.
;;
;;      For the rpn: language the tagged-instruction syntax gets
;;      transformed to single-assignment form (nested let expressions)
;;      in the rpn-lambda macro.  See p-apply and p-push for
;;      individual function and immediate semantics.
;;
;;
;;




;; Namespace macros.
(define-syntax-rule (rpn form) (ns (rpn) form))
                      
;; Macro bound to macros implementing semantics.
(define-syntax-rule (rpn->dict dict-mk init-dict code ...)
  (rpn-parse (dict-mk       ;; dictionary form transformer (macro continuation)
              (rpn)         ;; namespace of function references
              p-apply       ;; function semantics (= execute)
              p-push        ;; immediate smenatics (= push)
              p-push        ;; quoted program semantics (= push)
              rpn:          ;; anonymous program quoter for recursive parse
              init-dict     ;; initial dictionary line
              ) code ...))

;; Now use this to create 2 forms: a definer mapping a dictionary to
;; an expression containing defining forms, ...
(define-syntax-rule (rpn-begin code ...)
  (rpn->dict begin (rpn-no-anon) code ...))
(define-syntax-rule (rpn-no-anon) (begin))


;; Syntax checker: no anonymous code allowed on the first dict line.
;; (define-syntax-rule (rpn-no-anonymous) (begin))


;; rpn-parse output testing: quotes the dictionary.
(define-syntax-rule (rpn-begin-test code ...)
  (rpn->dict quote* () code ...))


;; ... and an anonymous code compiler mapping a dictionary to a single
;; lamda expression.
(define-syntax-rule (rpn: code ...)
  (rpn->dict rpn:-compile (rpn-lambda) code ...))




;; P (parameter) stack is abstract.
(define-syntax-rule (p-car p) (car p))
(define-syntax-rule (p-cadr p) (cadr p))
(define-syntax-rule (p-cdr p) (cdr p))
(define-syntax-rule (p-cddr p) (cddr p))
(define-syntax-rule (p-cons x p) (cons x p))
(define-syntax-rule (p->list p) p)
(define-syntax-rule (list->p l) l)


;; Semantics for the locals construct.
(define-syntax-rule (p-locals . a)
  (rpn-let-locals ((rpn)
                   rpn:
                   p-pop-values) . a))




(define (p-pop-values p n)
  (let next ((n n)
             (v '())
             (p p))
    (if (zero? n)
        (apply values (cons p v))
        (next (sub1 n)
              (cons (p-car p) v)
              (p-cdr p)))))

;; Expand to fast primitives.
(define-syntax-rule (op-2->1 op)
  (lambda (p)
    (p-cons (op (p-car p)
                (p-cadr p))
            (p-cddr p))))

(define-syntax-rule (op-1->1 op)
  (lambda (p)
    (p-cons (op (p-car p))
            (p-cdr p))))

(define-syntax-rule (snarfs snarfer (op ...))
  (begin
    (ns (rpn) (define op (snarfer op)))
    ...))

(define (rpn-print-stack p)
  (let ((s (p->list p)))
    (printf "<~s>" (length s))
    (for ((e (reverse s))) (printf " ~a" e))
    (newline)))

;; Basic expression compilers.  The code is right folded in
;; rpn-lambda, so we use nested let expressions here.  Read this as:

;; p <- (p-cons val p)
;; p <- (fn p)

;; The assignment is implemented by shadowing p with a new binding.


(define-syntax-rule (p-push  val p sub) (let ((p (p-cons val p))) sub))
(define-syntax-rule (p-apply fn  p sub) (let ((p (fn p))) sub))
(define-syntax-rule (p-prog  pr  p sub) (let ((p (p-cons (rpn: . pr) p))) sub))

(define-syntax-rule (p-word  name compile code ...)
  (ns (rpn) (define name (compile code ...))))

;; Prefix parsing words.
(ns (rpn) (define-syntax : (make-rpn-definition-transformer
                            (lambda (name d) ;; compile
                              (let* ((d (d-compile #'p-word d))
                                     (d (d-compile name d))
                                     (d (d-compile #'rpn-lambda d))) d)))))
    
(ns (rpn) (define-syntax \| (make-rpn-locals-transformer #'p-locals)))
(ns (rpn) (define-syntax \[  ;; nested quoted programs from flat Forth syntax.
            (let ((open (string->symbol "["))
                  (close (string->symbol "]")))
              (make-rpn-sexp-transformer
               open close
               (lambda (expr dict)
                 (d-compile #`(p-prog #,expr) dict))))))

(ns (rpn) (define-syntax include  ;; nested files
            (make-rpn-include-transformer
             file->syntax-list
             ;; syntax->datum  ;; normal scheme reader
             (lambda (it)
               (let* ((it (syntax->datum it))
                      (it (if (symbol? it) (symbol->string it) it))) it))
             (lambda () '()))))  ;; no search path


;; s-expressions are dumped as a single dictionary entry = toplevel expression.
(ns (rpn) (define-syntax  \{
            (make-rpn-sexp-transformer
             (string->symbol "{")
             (string->symbol "}")
             rpn-compile-toplevel)))

(ns (rpn) (define-syntax tick
            (rpn-syntax-rules () ((_ atom) ('atom)))))




;; Printing + functionality for testing.
(define-syntax-rule (rpn->stack code ...)
  ((rpn: code ...) (list->p '())))

;; read-eval-print
(define-syntax-rule (rpn> code ...)
  (rpn-print-stack (rpn->stack code ...)))

;; read-eval-print with Forth syntax
(define-syntax-rule (forth-command str)
  (forth-lex-string/cps rpn> str))
(define-syntax-rule (forth-compile str)
  (pretty-print (forth-lex-string/cps rpn-begin-test str)))

(snarfs op-2->1 (+ - * /))
(snarfs op-1->1 (sin cos car cdr))

(ns (rpn) (define (i p) ((p-car p) (p-cdr p))))


;; Tests for all syntactic elements.
(define-syntax-rule (rpn-test code ...)
  (reverse (rpn->stack code ...)))
(define-syntax-rule (rpn-checks ((code ...) => list) ...)
  (begin (check (rpn-test code ...) => list) ...))



(rpn-begin : add3 + +     ;; definitions
           : add4 + + +)

(let ((inc (rpn: 1 +))
      (two 2))
  (rpn-checks
   ((1 2 3) => '(1 2 3))                     ;; immediate + stack order
   ((1 2 +) => '(3))                         ;; immediate + function
   ((1 ,inc) => '(2))                        ;; unquote
   ((1 ',two +) => '(3))                     ;; quote+unquote
   ((1 (2 +) i) => '(3))                     ;; code quotation + interpretation
   ((1 (,inc) i) => '(2))                    ;; code quotation + interpretation + function unquote
   ((`(1 2 ,+)) => `((1 2 ,(ns (rpn) +))))   ;; quasiquotation + identifier unquote
   ((1 2 3 `(,(+ +)) car i) => '(6))         ;; quasiquotation + program unquote
   ((1 2 \| a b \| a b a a) => '(1 2 1 1))   ;; locals
   ((1 2 3 add3) => '(6))                    ;; definitions
   ((1 2 3 4 add4) => '(10))
   ))