#lang scheme/base
(require
"../tools.ss"
"../tools-tx.ss"
syntax/stx
(for-template
scheme/base))
(provide
rpn-lambda rpn-next
rpn-immediate rpn-function
rpn-program rpn-quoted rpn-quasiquoted
rpn-map-identifier
rpn-default-lambda rpn-close-expression
rpn-current-close
rpn-init
rpn-compile rpn-state rpn-context rpn-tx->syntax rpn-lex-mapper rpn-wrap
transformer
)
(define (immediate object expr) #`(#,object #,expr))
(define (function object expr) #`(apply #,object #,expr))
(define (rpn-default-lambda body)
#`(lambda (#,(rpn-state)) #,body))
(define (rpn-init thunk)
(parameterize
((rpn-current-close (rpn-lambda)))
(thunk)))
(define (rpn-compile code)
(rpn-init
(lambda ()
(let-values (((code+ body)
((rpn-next) code (rpn-state))))
(if (stx-null? code+)
(rpn-close-expression body)
(raise-syntax-error #f "garbage at end of code" code+))))))
(define rpn-current-close (make-parameter #f))
(define (rpn-close-expression expr)
(let ((result ((rpn-current-close) expr)))
(rpn-current-close (rpn-lambda))
result))
(define (map-identifier/expand id)
(let ((stx ((rpn-map-identifier) id)))
(if (identifier? stx)
stx
(let ((exp-stx (expand-syntax-once stx)))
'(printf "EXPANDING: ~a ~a ~a\n"
(syntax->datum id)
(syntax->datum stx)
(syntax->datum exp-stx))
exp-stx))))
(define (next source expr)
(syntax-case source ()
(()
(values source expr))
((id . source+)
(let ((datum (syntax->datum #'id)))
((or
(and
(procedure? datum)
(lambda () (datum source expr)))
(and
(identifier? #'id)
(let*
((mid (map-identifier/expand #'id))
(tx (transformer mid)))
(and tx
(lambda ()
(tx source expr)))))
(lambda ()
((rpn-next)
#'source+
(combine #'id expr)))))))))
(define (transformer id-stx)
(let ((binding
(syntax-local-value
id-stx (lambda () #f))))
binding))
(define (quoted object expr)
(define (lex/uq stx)
(syntax-case stx (unquote)
((unquote atom)
#'(unquote atom))
((car . cdr)
#`(#,(lex/uq #'car) . #,(lex/uq #'cdr)))
(atom
#'atom)))
((rpn-immediate)
#`(quasiquote #,(lex/uq object))
expr))
(define (quasiquoted object expr)
(define (uq stx)
(syntax-case stx (unquote)
((unquote expr)
#`(unquote
#,(rpn-compile #'expr))) ((car . cdr)
#`(#,(uq #'car) . #,(uq #'cdr)))
(atom
#'atom)))
((rpn-immediate)
#`(quasiquote #,(uq object))
expr))
(define (program p expr)
((rpn-immediate)
(rpn-compile p) expr))
(define (quoter fn stx expr)
(syntax-case stx ()
((atom) (fn #'atom expr))
(other (raise-syntax-error
'arity-error
"quoter/unquoter takes a single argument"))))
(define (combine thing expr)
(syntax-case thing (quote quasiquote unquote)
((quote . a) (quoter (rpn-quoted) #'a expr))
((quasiquote . a) (quoter (rpn-quasiquoted) #'a expr))
((unquote . a) (quoter (rpn-function) #'a expr))
((atoms ...)
((rpn-program) #'(atoms ...) expr))
(x
(if (identifier? #'x)
((rpn-function)
((rpn-map-identifier) #'x) expr)
((rpn-immediate) #'x expr)))))
(define (undefined-function name expr)
(raise-syntax-error
#f
"scat-undefined"
name))
(define (rpn-tx->syntax tx)
(let ((with-rpn-context (rpn-context)))
(lambda (stx)
(printf "WITH: ~a\n" (object-name with-rpn-context))
(with-rpn-context
(lambda ()
(tx (stx-cdr stx)))))))
(define rpn-immediate (make-parameter immediate))
(define rpn-function (make-parameter function))
(define rpn-quoted (make-parameter quoted))
(define rpn-quasiquoted (make-parameter quasiquoted))
(define rpn-program (make-parameter program))
(define rpn-state (make-parameter #'*state*))
(define rpn-lambda (make-parameter rpn-default-lambda))
(define rpn-next (make-parameter next))
(define rpn-map-identifier (make-parameter (lambda (id) id)))
(define rpn-context (make-parameter (lambda (thunk) (thunk))))
(define (rpn-lex-mapper fn-lex [fn-no-lex (lambda (x) x)])
(lambda (stx-lst)
(map
(lambda (stx)
(if
(and (identifier? stx)
(lexical-binding? stx))
(fn-lex stx)
(fn-no-lex stx)))
stx-lst)))
(define (make-wrapper fn)
(lambda (code expr)
(syntax-case code ()
((_ . code+)
((rpn-next) #'code+ (fn expr))))))
(define-sr (rpn-wrap (expr) . body)
(make-wrapper (lambda (expr) . body)))