1015 lines
32 KiB
Scheme
1015 lines
32 KiB
Scheme
(module dynamic mzscheme
|
|
|
|
(require-for-syntax (lib "stx.ss" "syntax")
|
|
"private/ops.ss"
|
|
"private/util.ss"
|
|
(lib "kerncase.ss" "syntax"))
|
|
|
|
(begin-for-syntax
|
|
|
|
(define kernel-forms (kernel-form-identifier-list #'here))
|
|
|
|
(define (top-block-context? ctx) (memq ctx '(top-block)))
|
|
(define (return-block-context? ctx) (memq ctx '(return-block)))
|
|
(define (block-context? ctx) (memq ctx '(top-block block return-block)))
|
|
(define (expression-context? ctx) (memq ctx '(expression)))
|
|
(define (type-context? ctx) (memq ctx '(type)))
|
|
|
|
(define block-context 'block)
|
|
(define return-block-context 'return-block)
|
|
(define top-block-context 'top-block)
|
|
(define expression-context 'expression)
|
|
(define type-context 'type)
|
|
|
|
;; --------------------------------------------------------
|
|
;; Transformer procedure property and basic struct
|
|
|
|
(define-values (prop:honu-transformer honu-transformer? honu-transformer-ref)
|
|
(make-struct-type-property 'honu-transformer))
|
|
|
|
|
|
(define-values (struct:honu-trans make-honu-trans honu-trans? honu-trans-ref honu-trans-set!)
|
|
(make-struct-type 'honu-trans #f 1 0 #f
|
|
(list (list prop:honu-transformer #t))
|
|
(current-inspector) 0))
|
|
|
|
(define (make-honu-transformer proc)
|
|
(unless (and (procedure? proc)
|
|
(procedure-arity-includes? proc 2))
|
|
(raise-type-error
|
|
'define-honu-syntax
|
|
"procedure (arity 2)"
|
|
proc))
|
|
(make-honu-trans proc))
|
|
|
|
;; --------------------------------------------------------
|
|
;; Type
|
|
|
|
(define-values (struct:honu-type make-h-type honu-type? honu-type-ref honu-type-set!)
|
|
(make-struct-type 'honu-type #f 3 0 #f null (current-inspector) 0))
|
|
|
|
(define (honu-type-stx v) (honu-type-ref v 0))
|
|
(define (honu-type-pred-def-stx v) (honu-type-ref v 1))
|
|
(define (honu-type-pred-stx v) (honu-type-ref v 2))
|
|
|
|
;; --------------------------------------------------------
|
|
;; Parsing blocks
|
|
|
|
(define operator?
|
|
(let ([sym-chars (string->list "+-_=?:<>.!%^&*/~|")])
|
|
(lambda (stx)
|
|
(and (identifier? stx)
|
|
(let ([str (symbol->string (syntax-e stx))])
|
|
(and (positive? (string-length str))
|
|
(memq (string-ref str 0) sym-chars)))))))
|
|
|
|
(define (get-transformer stx)
|
|
(or (and (stx-pair? stx)
|
|
(identifier? (stx-car stx))
|
|
(let ([v (syntax-local-value (stx-car stx) (lambda () #f))])
|
|
(and (honu-transformer? v) v)))
|
|
(and (stx-pair? stx)
|
|
(let ([first (stx-car stx)])
|
|
(and (stx-pair? first)
|
|
(identifier? (stx-car first))
|
|
(module-identifier=? #'#%parens (stx-car first))
|
|
;; If the stx-car is a list with just one operator symbol,
|
|
;; try using the operator as a transformer
|
|
(let ([l (cdr (stx->list first))])
|
|
(let loop ([l l])
|
|
(cond
|
|
[(null? l) #f]
|
|
[(operator? (car l))
|
|
(if (ormap operator? (cdr l))
|
|
#f
|
|
(let ([v (syntax-local-value (car l) (lambda () #f))])
|
|
(and (honu-transformer? v)
|
|
v)))]
|
|
[else (loop (cdr l))]))))))))
|
|
|
|
;; --------------------------------------------------------
|
|
;; Parsing blocks
|
|
|
|
(define parse-an-expr #f)
|
|
(define parse-a-tail-expr #f)
|
|
|
|
(define (parse-block-one ctx body k done-k)
|
|
(cond
|
|
[(stx-null? body) (done-k)]
|
|
[(get-transformer body)
|
|
=> (lambda (transformer)
|
|
(let-values ([(code rest) (transformer body ctx)])
|
|
(k code rest)))]
|
|
[(syntax-case body (#%braces)
|
|
[((#%braces . block) . rest) (cons #'block #'rest)]
|
|
[_else #f])
|
|
=> (lambda (b+r)
|
|
(k #`(honu-unparsed-block #f void-type #f #,(return-block-context? ctx)
|
|
#,@(car b+r))
|
|
(cdr b+r)))]
|
|
[else (let-values ([(expr-stxs after-expr) (extract-until body (list #'\;))])
|
|
(unless expr-stxs
|
|
(raise-syntax-error
|
|
#f
|
|
"expected a semicolon to terminate form"
|
|
(stx-car body)))
|
|
(let ([code ((if (return-block-context? ctx)
|
|
parse-a-tail-expr
|
|
parse-an-expr)
|
|
expr-stxs)])
|
|
(k ((if (top-block-context? ctx)
|
|
(lambda (x) `(printf "~s\n" ,x))
|
|
values)
|
|
code)
|
|
(stx-cdr after-expr))))]))
|
|
|
|
(define (parse-block stx ctx)
|
|
(let loop ([stx stx])
|
|
(parse-block-one ctx
|
|
stx
|
|
(lambda (code rest)
|
|
(cons code (loop rest)))
|
|
(lambda ()
|
|
null))))
|
|
|
|
;; --------------------------------------------------------
|
|
;; Parsing expressions
|
|
|
|
(define parse-expr
|
|
(let ()
|
|
(define (parse-expr-seq stx)
|
|
(define (start-expr stx)
|
|
(let ([trans (get-transformer stx)])
|
|
(if trans
|
|
(let-values ([(expr rest) (trans stx expression-context)])
|
|
(if (stx-null? rest)
|
|
(list expr)
|
|
(cons expr (start-operator rest))))
|
|
(syntax-case stx (#%parens #%braces)
|
|
[(v)
|
|
(or (number? (syntax-e #'v))
|
|
(identifier? #'v)
|
|
(string? (syntax-e #'v)))
|
|
(if (operator? #'v)
|
|
(raise-syntax-error
|
|
#f
|
|
"operator alone is not an expression"
|
|
#'v)
|
|
(list #'v))]
|
|
[((#%parens . pexpr))
|
|
(if (stx-null? #'pexpr)
|
|
(raise-syntax-error
|
|
#f
|
|
"missing expression inside parentheses"
|
|
(stx-car stx))
|
|
(list (parse-expr #'pexpr)))]
|
|
[((#%braces . pexpr))
|
|
(if (stx-null? #'pexpr)
|
|
(raise-syntax-error
|
|
#f
|
|
"missing expression inside braces"
|
|
(stx-car stx))
|
|
(list #'(honu-unparsed-block #f void-type #f #f . pexpr)))]
|
|
[(op . more)
|
|
(and (identifier? #'op)
|
|
(ormap (lambda (uop)
|
|
(module-identifier=? #'op uop))
|
|
unary-prefix-ops))
|
|
(cons (make-prefix (stx-car stx)) (start-expr #'more))]
|
|
[(expr then . more)
|
|
(append (start-expr (list #'expr))
|
|
(start-operator #'(then . more)))]
|
|
[(bad . rest)
|
|
(raise-syntax-error
|
|
'expression
|
|
"unknown expression form"
|
|
#'bad)]))))
|
|
(define (start-operator stx)
|
|
(unless (or (and (stx-pair? (stx-car stx))
|
|
(or (module-identifier=? #'#%brackets (stx-car (stx-car stx)))
|
|
(module-identifier=? #'#%parens (stx-car (stx-car stx)))))
|
|
(and (identifier? (stx-car stx))
|
|
(hash-table-get op-table
|
|
(syntax-e (stx-car stx))
|
|
(lambda () #f))))
|
|
(raise-syntax-error
|
|
'expression
|
|
"expected an operator, but found something else"
|
|
(stx-car stx)))
|
|
;; Check for postfix operator, first
|
|
(cond
|
|
[(stx-pair? (stx-car stx))
|
|
;; Convert vector index or application to a binary operator:
|
|
(let ([opl (if (module-identifier=? #'#%brackets (stx-car (stx-car stx)))
|
|
(let ([index-expr (parse-expr (stx-cdr (stx-car stx)))])
|
|
(list (make-infix (stx-car (stx-car stx)))
|
|
index-expr))
|
|
(let ([arg-exprs (parse-arg-list (stx-cdr (stx-car stx)))])
|
|
(list (make-infix (stx-car (stx-car stx)))
|
|
arg-exprs)))])
|
|
(if (stx-null? (stx-cdr stx))
|
|
opl
|
|
(append opl (start-operator (stx-cdr stx)))))]
|
|
[(or (module-identifier=? #'++ (stx-car stx))
|
|
(module-identifier=? #'-- (stx-car stx)))
|
|
(if (null? (stx-cdr stx))
|
|
(list (make-postfix (stx-car stx)))
|
|
(cons (make-postfix (stx-car stx))
|
|
(start-operator (stx-cdr stx))))]
|
|
[else
|
|
;; Otherwise, must be infix
|
|
(cons (make-infix (stx-car stx))
|
|
(start-expr (stx-cdr stx)))]))
|
|
(start-expr stx))
|
|
|
|
(define (parse-expr stx)
|
|
(let group ([seq (parse-expr-seq stx)])
|
|
;; seq is a list that mixes exprs with ops.
|
|
;; Find leftmost oper with maximal precedence
|
|
(if (null? (cdr seq))
|
|
(car seq)
|
|
(let loop ([seq seq][before null][op #f][since null])
|
|
(cond
|
|
[(null? seq)
|
|
(cond
|
|
[(prefix? op)
|
|
(group (append (reverse (cdr before))
|
|
(list (quasisyntax/loc (op-id op)
|
|
(#,(op-id op) #,(car before))))
|
|
(reverse since)))]
|
|
[(postfix? op)
|
|
(let ([after (reverse since)])
|
|
(group (append (reverse before)
|
|
(list (quasisyntax/loc (op-id op)
|
|
(#,(op-id op) #,(car after))))
|
|
(cdr after))))]
|
|
[(infix? op)
|
|
(let ([after (reverse since)])
|
|
(group (append (reverse (cdr before))
|
|
(list (quasisyntax/loc (op-id op)
|
|
(#,(op-id op) #,(car before) #,(car after))))
|
|
(cdr after))))]
|
|
[else (error "not an op!: " op)])]
|
|
[(not (op? (stx-car seq)))
|
|
(loop (cdr seq) before op (cons (car seq) since))]
|
|
[(> (hash-table-get precedence-table (prec-key (car seq)) (lambda () 0))
|
|
(hash-table-get precedence-table (prec-key op) (lambda () 0)))
|
|
(loop (cdr seq)
|
|
(if op
|
|
(append since (list op) before)
|
|
since)
|
|
(car seq) null)]
|
|
[else
|
|
(loop (cdr seq) before op (cons (car seq) since))])))))
|
|
|
|
(define (parse-arg-list stxs)
|
|
(if (stx-null? stxs)
|
|
stxs
|
|
(let-values ([(val-stxs after-expr) (extract-until stxs (list #'\,))])
|
|
(when (and val-stxs
|
|
(stx-null? (stx-cdr after-expr)))
|
|
(raise-syntax-error
|
|
'procedure\ call
|
|
"missing expression after comma"
|
|
(stx-car after-expr)))
|
|
(when (null? val-stxs)
|
|
(raise-syntax-error
|
|
'procedure\ call
|
|
"missing expression before token"
|
|
(stx-car after-expr)))
|
|
(if val-stxs
|
|
(cons (parse-expr val-stxs)
|
|
(parse-arg-list (stx-cdr after-expr)))
|
|
(list (parse-expr stxs))))))
|
|
|
|
parse-expr))
|
|
|
|
(define (parse-tail-expr expr-stxs)
|
|
(syntax-case expr-stxs (honu-return #%parens)
|
|
[(honu-return expr ...)
|
|
(let ([exprs #'(expr ...)])
|
|
(when (stx-null? exprs)
|
|
(raise-syntax-error
|
|
#f
|
|
"missing expression"
|
|
(stx-car expr-stxs)))
|
|
(parse-expr exprs))]
|
|
[((#%parens expr0 expr ...))
|
|
(let ([exprs #'(expr0 expr ...)])
|
|
(parse-tail-expr exprs))]
|
|
[_else
|
|
(parse-expr expr-stxs)]))
|
|
|
|
(set! parse-an-expr parse-expr)
|
|
(set! parse-a-tail-expr parse-tail-expr)
|
|
|
|
;; --------------------------------------------------------
|
|
;; Parsing declarations (which always start with a type)
|
|
|
|
(define (parse-one-argument proc-id type id k)
|
|
(cons (list id
|
|
(honu-type-stx type)
|
|
(honu-type-pred-def-stx type)
|
|
(honu-type-pred-stx type))
|
|
(k)))
|
|
|
|
(define (parse-arguments orig-args-stx proc-id)
|
|
(if (stx-null? orig-args-stx)
|
|
null
|
|
(let loop ([args-stx orig-args-stx]
|
|
[where "at start of argument sequence"]
|
|
[where-stx orig-args-stx])
|
|
(let ([trans (get-transformer args-stx)])
|
|
(let-values ([(type rest-stx) (if trans
|
|
(trans args-stx type-context)
|
|
(values #f #f))])
|
|
(unless (honu-type? type)
|
|
(raise-syntax-error
|
|
'|procedure declaration|
|
|
(format "expected a type ~a" where)
|
|
where-stx))
|
|
(syntax-case rest-stx ()
|
|
[(id)
|
|
(identifier? #'id)
|
|
(parse-one-argument proc-id type #'id
|
|
(lambda () null))]
|
|
[(id comma . rest)
|
|
(and (identifier? #'id)
|
|
(identifier? #'comma)
|
|
(module-identifier=? #'comma #'\,))
|
|
(parse-one-argument proc-id type #'id
|
|
(lambda ()
|
|
(loop #'rest
|
|
"after comma"
|
|
#'comma)))]
|
|
[(id something . rest)
|
|
(identifier? #'id)
|
|
(raise-syntax-error
|
|
'procedure\ declaration
|
|
"expected a comma after identifier name"
|
|
#'something)]
|
|
[_else
|
|
(raise-syntax-error
|
|
'procedure\ declaration
|
|
"expected an argument identifier"
|
|
(car rest-stx))]))))))
|
|
|
|
(define (make-honu-type pred-id mk-pred-def)
|
|
(make-honu-trans
|
|
(lambda (orig-stx ctx)
|
|
(let* ([pred-id (or pred-id
|
|
(car (generate-temporaries '(type-pred))))]
|
|
[pred-def (if mk-pred-def
|
|
(mk-pred-def pred-id orig-stx)
|
|
#'(begin))])
|
|
(cond
|
|
[(block-context? ctx)
|
|
(with-syntax ([pred-id pred-id]
|
|
[type-name (stx-car orig-stx)])
|
|
(let loop ([stx (stx-cdr orig-stx)]
|
|
[after (stx-car orig-stx)]
|
|
[after-what "type name"]
|
|
[parens-ok? #t])
|
|
(syntax-case stx ()
|
|
[(id . rest)
|
|
(begin
|
|
(unless (identifier? #'id)
|
|
(raise-syntax-error 'declaration
|
|
(format "expected a identifier after ~a" after-what)
|
|
(stx-car orig-stx)
|
|
#'id))
|
|
(if (and (identifier? (stx-car #'rest))
|
|
(module-identifier=? #'set! (stx-car #'rest)))
|
|
;; -- Non-procedure declaration
|
|
(let-values ([(val-stxs after-expr) (extract-until (stx-cdr #'rest)
|
|
(list #'\; #'\,))])
|
|
(unless val-stxs
|
|
(raise-syntax-error
|
|
'declaration
|
|
"missing semicolon or comma after initializing assignment"
|
|
(stx-car #'rest)))
|
|
(when (null? val-stxs)
|
|
(raise-syntax-error
|
|
'declaration
|
|
"missing expression initializing assignment"
|
|
(stx-car #'rest)))
|
|
(let ([def #`(define-typed id #f type-name pred-id
|
|
(check-expr #f 'id type-name pred-id
|
|
(honu-unparsed-expr #,@val-stxs)))])
|
|
(if (module-identifier=? #'\; (stx-car after-expr))
|
|
(values #`(begin #,pred-def #,def) (stx-cdr after-expr))
|
|
(let-values ([(defs remainder kind) (loop (stx-cdr after-expr) (stx-car after-expr) "comma" #f)])
|
|
(values #`(begin #,pred-def #,def #,defs) remainder)))))
|
|
;; -- Procedure declaration
|
|
(syntax-case #'rest (#%parens \;)
|
|
[((#%parens . prest) (#%braces . body) . rest)
|
|
parens-ok?
|
|
(let ([args (parse-arguments #'prest #'id)])
|
|
(with-syntax ([((arg arg-type arg-pred-def arg-pred-id) ...) args]
|
|
[(temp-id ...) (generate-temporaries (map car args))])
|
|
(values #`(begin
|
|
#,pred-def
|
|
arg-pred-def ...
|
|
(define-typed-procedure id
|
|
((arg arg-type arg-pred-id) ...)
|
|
(lambda (temp-id ...)
|
|
(define-typed arg id arg-type arg-pred-id temp-id) ...
|
|
(honu-unparsed-block id type-name pred-id #t . body))))
|
|
#'rest)))]
|
|
;; --- Error handling ---
|
|
[((#%parens . prest) . bad-rest)
|
|
parens-ok?
|
|
(begin
|
|
(parse-arguments #'prest #'id)
|
|
(raise-syntax-error
|
|
'|procedure declaration|
|
|
"braces for function body after parenthesized arguments"
|
|
(stx-car #'rest)
|
|
#'id))]
|
|
[_else
|
|
(raise-syntax-error
|
|
'|declaration|
|
|
(if parens-ok?
|
|
"expected either = (for variable intialization) or parens (for function arguments)"
|
|
"expected = (for variable initialization)")
|
|
#'id)])))]
|
|
[_else
|
|
(raise-syntax-error #f
|
|
(format "expected a identifier after ~a" after-what)
|
|
after
|
|
#'id)])))]
|
|
[(type-context? ctx)
|
|
(values (make-h-type (stx-car orig-stx) pred-def pred-id) (stx-cdr orig-stx))]
|
|
[(expression-context? ctx)
|
|
(raise-syntax-error #f
|
|
"illegal in an expression context"
|
|
(stx-car orig-stx))])))))
|
|
|
|
(define (make-proc-predicate name form)
|
|
;; Form start with a operator-transformer sequence
|
|
(let-values ([(args-stx -> result-stx)
|
|
(let loop ([stx (stx-cdr (stx-car form))][args null])
|
|
(if (and (identifier? (stx-car stx))
|
|
(module-identifier=? #'-> (stx-car stx)))
|
|
(values (reverse args) (stx-car stx) (stx-cdr stx))
|
|
(loop (stx-cdr stx) (cons (stx-car stx) args))))])
|
|
(when (stx-null? result-stx)
|
|
(raise-type-error
|
|
#f
|
|
"missing type for result"
|
|
->))
|
|
(let ([arg-types
|
|
(let loop ([args-stx args-stx])
|
|
(if (stx-null? args-stx)
|
|
null
|
|
(let ([trans (get-transformer args-stx)])
|
|
(unless trans
|
|
(raise-type-error '->
|
|
"non-type within a procedure-type construction"
|
|
(stx-car args-stx)))
|
|
(let-values ([(type rest-stx) (trans args-stx type-context)])
|
|
(cons type (loop rest-stx))))))]
|
|
[result-type
|
|
(let ([trans (get-transformer result-stx)])
|
|
(unless trans
|
|
(raise-type-error '->
|
|
"non-type in result position for procedure-type construction"
|
|
(stx-car result-stx)))
|
|
(let-values ([(type rest-stx) (trans result-stx type-context)])
|
|
(unless (stx-null? rest-stx)
|
|
(raise-type-error '->
|
|
"extra tokens following result for procedure-type construction"
|
|
(stx-car rest-stx)))
|
|
type))])
|
|
(with-syntax ([(arg ...) (generate-temporaries arg-types)]
|
|
[(arg-type ...) (map honu-type-stx arg-types)]
|
|
[(arg-pred-def ...) (map honu-type-pred-def-stx arg-types)]
|
|
[(arg-pred-id ...) (map honu-type-pred-stx arg-types)]
|
|
[result-type (honu-type-stx result-type)]
|
|
[result-pred-def (honu-type-pred-def-stx result-type)]
|
|
[result-pred-id (honu-type-pred-stx result-type)]
|
|
[n (length arg-types)])
|
|
#`(begin
|
|
arg-pred-def ...
|
|
result-pred-def
|
|
(define (#,name v)
|
|
(if (and (procedure? v)
|
|
(procedure-arity-includes? v n))
|
|
(values #t (lambda (arg ...)
|
|
(check-expr
|
|
#f #t result-type result-pred-id
|
|
(v (check-expr #f #f arg-type arg-pred-id arg) ...))))
|
|
(values #f #f))))))))
|
|
|
|
(define (compatible-type? val-expr val-type target-type)
|
|
(and (identifier? target-type)
|
|
(identifier? val-type)
|
|
(or (module-identifier=? val-type target-type)
|
|
(module-identifier=? #'obj target-type)
|
|
(and (number? (syntax-e val-expr))
|
|
(module-identifier=? #'num target-type))
|
|
(and (integer? (syntax-e val-expr))
|
|
(exact? (syntax-e val-expr))
|
|
(module-identifier=? #'int target-type))
|
|
(and (real? (syntax-e val-expr))
|
|
(module-identifier=? #'real target-type))
|
|
(and (string? (syntax-e val-expr))
|
|
(module-identifier=? #'string-type target-type))))))
|
|
|
|
(define (check proc who type-name pred val)
|
|
(let-values ([(tst new-val) (pred val)])
|
|
(unless tst
|
|
(raise
|
|
(make-exn:fail:contract
|
|
(string->immutable-string
|
|
(format "~a: expected ~a value for ~a, got something else: ~e"
|
|
(or proc (if (eq? who #t) #f who) "procedure")
|
|
type-name
|
|
(cond
|
|
[(eq? who #t) "result"]
|
|
[else (if proc
|
|
(format "~a argument" who)
|
|
(if who
|
|
"initialization"
|
|
"argument"))])
|
|
val))
|
|
(current-continuation-marks))))
|
|
new-val))
|
|
|
|
(define-syntax (check-expr stx)
|
|
(syntax-case stx ()
|
|
[(_ proc who type-name pred val)
|
|
;; Avoid the check if the static types are consistent
|
|
(let ([v (local-expand
|
|
#'val
|
|
'expression
|
|
(cons #'honu-typed
|
|
kernel-forms))])
|
|
(syntax-case v (honu-typed)
|
|
[(honu-typed val val-type)
|
|
(compatible-type? #'val #'val-type #'type-name)
|
|
;; No run-time check:
|
|
#'val]
|
|
[_else
|
|
;; Even without a type for v, we might see a literal,
|
|
;; or maybe the declaration is simply obj
|
|
(if (compatible-type? v #'obj #'type-name)
|
|
;; No run-time check:
|
|
#'val
|
|
;; Run-time check:
|
|
#'(check proc who 'type-name pred val))]))]))
|
|
|
|
(define-syntax (define-typed stx)
|
|
(syntax-case stx ()
|
|
[(_ id proc-name type-name pred-id val)
|
|
(with-syntax ([gen-id (car (generate-temporaries (list #'id)))])
|
|
#'(begin
|
|
(define gen-id val)
|
|
(define-syntax id
|
|
(make-set!-transformer
|
|
(lambda (stx)
|
|
(syntax-case stx (set!)
|
|
[(set! id rhs)
|
|
#'(set! gen-id (check-expr set! id type-name pred-id rhs))]
|
|
[(id arg (... ...))
|
|
#'(#%app (honu-typed gen-id type-name) arg (... ...))]
|
|
[id
|
|
#'(honu-typed gen-id type-name)]))))))]))
|
|
|
|
(define-syntax (define-typed-procedure stx)
|
|
(syntax-case stx ()
|
|
[(_ id arg-spec val)
|
|
(with-syntax ([gen-id (car (generate-temporaries (list #'id)))])
|
|
#'(begin
|
|
(define gen-id val)
|
|
(define-syntax id
|
|
(with-syntax ([((arg arg-type pred-id) (... ...)) (quote-syntax arg-spec)])
|
|
(make-set!-transformer
|
|
(lambda (stx)
|
|
(syntax-case stx (set!)
|
|
[(set! id rhs)
|
|
(raise-syntax-error #f
|
|
"cannot assign to procedure name"
|
|
stx
|
|
#'id)]
|
|
[(id actual-arg (... ...))
|
|
(let ([actual-args (syntax->list #'(actual-arg (... ...)))]
|
|
[formal-args (syntax->list #'(arg (... ...)))])
|
|
(unless (= (length actual-args)
|
|
(length formal-args))
|
|
(raise-syntax-error
|
|
'id
|
|
(format "expects ~a arguments, provided ~a"
|
|
(length formal-args)
|
|
(length actual-args))
|
|
stx))
|
|
#'(#%app (honu-typed gen-id type-name)
|
|
(check-expr 'id 'arg arg-type pred-id actual-arg)
|
|
(... ...)))]
|
|
[id
|
|
#'(honu-typed (let ([id (lambda (arg (... ...))
|
|
(id arg (... ...)))])
|
|
id)
|
|
type-name)])))))))]))
|
|
|
|
(define-syntax honu-typed
|
|
(syntax-rules ()
|
|
[(_ expr type) expr]))
|
|
|
|
(require-for-syntax (lib "context.ss" "syntax"))
|
|
(define-syntax (honu-block stx)
|
|
;; A block can have mixed exprs and defns. Wrap expressions with
|
|
;; `(define-values () ... (values))' as needed, and add a (void)
|
|
;; at the end if needed. Also, wrap the final expression with
|
|
;; a type check as needed.
|
|
(let ([proc-id (stx-car (stx-cdr stx))]
|
|
[result-type-name (stx-car (stx-cdr (stx-cdr stx)))]
|
|
[result-pred-id (stx-car (stx-cdr (stx-cdr (stx-cdr stx))))]
|
|
[exprs (let loop ([exprs (cddddr (syntax->list stx))])
|
|
(apply
|
|
append
|
|
(map (lambda (expr)
|
|
(let ([expr (local-expand
|
|
expr
|
|
(generate-expand-context)
|
|
kernel-forms)])
|
|
(syntax-case expr (begin)
|
|
[(begin . rest)
|
|
(loop (syntax->list #'rest))]
|
|
[else
|
|
(list expr)])))
|
|
exprs)))])
|
|
#`(let ()
|
|
#,@(let loop ([exprs exprs][prev-defns null][prev-exprs null])
|
|
(cond
|
|
[(null? exprs) (append
|
|
(reverse prev-defns)
|
|
(if (and (pair? prev-exprs)
|
|
proc-id
|
|
(syntax-e proc-id))
|
|
(reverse (cons
|
|
#`(check-expr '#,proc-id #t
|
|
#,result-type-name
|
|
#,result-pred-id
|
|
#,(car prev-exprs))
|
|
(cdr prev-exprs)))
|
|
(begin
|
|
(unless (or (not proc-id)
|
|
(not (syntax-e proc-id))
|
|
(module-identifier=? #'type-name #'void-type))
|
|
(error "no expression for type check; should have been "
|
|
"caught earlier"))
|
|
(reverse prev-exprs)))
|
|
(if (null? prev-exprs)
|
|
(list #'(void))
|
|
null))]
|
|
[(and (stx-pair? (car exprs))
|
|
(or (module-identifier=? #'define-values (stx-car (car exprs)))
|
|
(module-identifier=? #'define-syntaxes (stx-car (car exprs)))))
|
|
(loop (cdr exprs)
|
|
(cons (car exprs)
|
|
(append
|
|
(map (lambda (expr)
|
|
#`(define-values () (begin #,expr (values))))
|
|
prev-exprs)
|
|
prev-defns))
|
|
null)]
|
|
[else
|
|
(loop (cdr exprs) prev-defns (cons (car exprs) prev-exprs))])))))
|
|
|
|
(define-syntax (honu-unparsed-block stx)
|
|
(syntax-case stx (void)
|
|
[(_ proc-id result-type-name result-pred-id return-context? . body)
|
|
#`(honu-block proc-id result-type-name result-pred-id #,@(parse-block
|
|
#'body
|
|
(if (syntax-e #'return-context?)
|
|
return-block-context
|
|
block-context)))]))
|
|
|
|
(define-syntax (honu-unparsed-expr stx)
|
|
(syntax-case stx ()
|
|
[(_ v ...) (parse-expr (syntax->list #'(v ...)))]))
|
|
|
|
(define-syntax (h-return stx)
|
|
(syntax-case stx ()
|
|
[(_ expr) #'expr]))
|
|
|
|
(define-syntax (#%parens stx)
|
|
(syntax-case stx ()
|
|
[(_ rator (rand ...)) (syntax/loc #'rator (rator rand ...))]))
|
|
|
|
;; --------------------------------------------------------
|
|
;; Defining a new transformer or new type
|
|
|
|
(require-for-syntax (lib "define.ss" "syntax"))
|
|
(define-syntax (define-honu-syntax stx)
|
|
(let-values ([(id rhs) (normalize-definition stx #'lambda #f)])
|
|
(with-syntax ([id id]
|
|
[rhs rhs])
|
|
#'(define-syntax id (make-honu-transformer rhs)))))
|
|
|
|
(define-syntax (define-type stx)
|
|
(syntax-case stx ()
|
|
[(_ id pred-expr)
|
|
(identifier? #'id)
|
|
(with-syntax ([pred-id (car (generate-temporaries '(pred)))])
|
|
#'(begin
|
|
(define pred-id (let ([pred pred-expr])
|
|
(lambda (v)
|
|
(values (pred v) v))))
|
|
(define-syntax id (make-honu-type #'pred-id #f))))]))
|
|
|
|
(define-syntax (define-type-constructor stx)
|
|
(syntax-case stx ()
|
|
[(_ id generator-expr)
|
|
(identifier? #'id)
|
|
#'(define-syntax id (make-honu-type #f generator-expr))]))
|
|
|
|
;; ----------------------------------------
|
|
;; Pre-defined types and forms
|
|
|
|
(define (exact-integer? v)
|
|
(and (integer? v) (exact? v)))
|
|
|
|
(define-type int exact-integer?)
|
|
(define-type real real?)
|
|
(define-type num number?)
|
|
(define-type obj (lambda (x) #t))
|
|
(define-type string-type string?)
|
|
|
|
(define-type-constructor -> make-proc-predicate)
|
|
|
|
(define-for-syntax parse-comma-separated
|
|
(lambda (body empty-case parse-one combine)
|
|
(syntax-case body (\;)
|
|
[(\;) (empty-case)]
|
|
[_else
|
|
(let loop ([body body][accum null][prev-comma #f])
|
|
(syntax-case body (\, \;)
|
|
[(\, . rest)
|
|
(let-values ([(one) (parse-one (reverse accum) prev-comma (stx-car body))]
|
|
[(other rest) (loop #'rest null (stx-car body))])
|
|
(values (combine one other) rest))]
|
|
[(\; . rest)
|
|
(identifier? #'id)
|
|
(values (parse-one (reverse accum) prev-comma (stx-car body)) #'rest)]
|
|
[(x . rest)
|
|
(loop #'rest (cons #'x accum) #f)]))])))
|
|
|
|
(define-honu-syntax honu-provide
|
|
(lambda (body ctx)
|
|
(unless (top-block-context? ctx)
|
|
(raise-syntax-error #f "not allowed outside the top level" (stx-car body)))
|
|
(parse-comma-separated
|
|
(stx-cdr body)
|
|
(lambda () #'(begin))
|
|
(lambda (stxes prev-comma-stx term-stx)
|
|
(syntax-case stxes ()
|
|
[(id)
|
|
(identifier? #'id)
|
|
#`(provide id)]
|
|
[else
|
|
(raise-syntax-error
|
|
#f
|
|
"unknown provide form"
|
|
(stx-car body)
|
|
(car stxes))]))
|
|
(lambda (p decls)
|
|
#`(begin #,p #,decls)))))
|
|
|
|
(define-honu-syntax honu-require
|
|
(lambda (body ctx)
|
|
(define (check-empty rest after-what)
|
|
(unless (stx-null? rest)
|
|
(raise-syntax-error
|
|
#f
|
|
(format "expect a comma or semicolon after ~a" after-what)
|
|
(stx-car body)
|
|
(stx-car rest))))
|
|
(unless (top-block-context? ctx)
|
|
(raise-syntax-error #f "not allowed outside the top level" (stx-car body)))
|
|
(parse-comma-separated
|
|
(stx-cdr body)
|
|
(lambda () #'(begin))
|
|
(lambda (stxes prev-comma-stx term-stx)
|
|
#`(require
|
|
#,(let ()
|
|
(define (parse-module-name stxes)
|
|
(syntax-case stxes (lib file #%parens)
|
|
[(fn . rest)
|
|
(string? (syntax-e #'fn))
|
|
(begin
|
|
(check-empty #'rest "path string")
|
|
#'fn)]
|
|
[(lib (#%parens names ...) . rest)
|
|
(let ([names (let loop ([names #'(names ...)])
|
|
(syntax-case names (\,)
|
|
[() null]
|
|
[(name . rest)
|
|
(begin
|
|
(unless (string? (syntax-e #'name))
|
|
(raise-syntax-error
|
|
#f
|
|
"expected a string for a library path"
|
|
(car stxes)
|
|
#'name))
|
|
(syntax-case #'rest (\,)
|
|
[() (list #'name)]
|
|
[(\, . rest)
|
|
(cons #'name (loop #'rest))]
|
|
[else
|
|
(raise-syntax-error
|
|
#f
|
|
"expected a comma"
|
|
(stx-car stxes)
|
|
(stx-car #'rest))]))]
|
|
[(\,)
|
|
(raise-syntax-error
|
|
#f
|
|
"expected a string before comma"
|
|
(car stxes)
|
|
(stx-car names))]
|
|
[_else
|
|
(raise-syntax-error
|
|
#f
|
|
"expected a string for a library path"
|
|
(car stxes)
|
|
(stx-car names))]))])
|
|
(when (null? names)
|
|
(raise-syntax-error
|
|
#f
|
|
"expected at least one string for the library path"
|
|
(cadr stxes)))
|
|
(check-empty #'rest "library path")
|
|
(syntax-local-introduce #`(lib #,@names)))]
|
|
[(lib . rest)
|
|
(raise-syntax-error
|
|
#f
|
|
"expected a parenthesized sequence of strings after `lib' keyword"
|
|
(car stxes)
|
|
(stx-car body))]
|
|
[(file (#%parens name) . rest)
|
|
(string? (syntax-e #'name))
|
|
(begin
|
|
(check-empty #'rest "file name")
|
|
(syntax-local-introduce #`(file name)))]
|
|
[(file . rest)
|
|
(raise-syntax-error
|
|
#f
|
|
"expected a parenthesized string after `file' keyword"
|
|
(car stxes)
|
|
(stx-car body))]
|
|
[(fn)
|
|
(identifier? #'fn)
|
|
#'fn]
|
|
[else
|
|
(raise-syntax-error
|
|
#f
|
|
"unknown require form"
|
|
(stx-car body)
|
|
(car stxes))]))
|
|
(define (parse-module-spec stxes)
|
|
(syntax-case stxes (rename #%parens \,)
|
|
[(rename (#%parens spec0 spec ... \, local-id \, remote-id) . rest)
|
|
(begin
|
|
(unless (identifier? #'local-id)
|
|
(raise-syntax-error
|
|
#f
|
|
"expected an identifier"
|
|
(stx-car stxes)
|
|
#'local-id))
|
|
(unless (identifier? #'remote-id)
|
|
(raise-syntax-error
|
|
#f
|
|
"expected an identifier"
|
|
(stx-car stxes)
|
|
#'remote-id))
|
|
(begin0
|
|
#`(rename #,(parse-module-name
|
|
(syntax->list #'(spec0 spec ...)))
|
|
local-id
|
|
remote-id)
|
|
(check-empty #'rest "rename")))]
|
|
[(rename . rest)
|
|
(raise-syntax-error
|
|
#f
|
|
"expected a parenthesized id, id, and require spec `rename' keyword"
|
|
(car stxes)
|
|
(stx-car body))]
|
|
[_else (parse-module-name stxes)]))
|
|
(parse-module-spec stxes))))
|
|
(lambda (p decls)
|
|
#`(begin #,p #,decls)))))
|
|
|
|
(define-honu-syntax honu-return
|
|
(lambda (stx ctx)
|
|
(unless (return-block-context? ctx)
|
|
(raise-syntax-error #f "allowed only in a tail position" (stx-car stx)))
|
|
(let-values ([(val-stxs after-expr) (extract-until (stx-cdr stx)
|
|
(list #'\;))])
|
|
(unless val-stxs
|
|
(raise-syntax-error
|
|
#f
|
|
"missing semicolon"
|
|
(stx-car stx)))
|
|
(when (null? val-stxs)
|
|
(raise-syntax-error
|
|
#f
|
|
"missing expression"
|
|
(stx-car stx)))
|
|
(with-syntax ([expr (parse-expr val-stxs)])
|
|
(unless (stx-null? (stx-cdr after-expr))
|
|
(raise-syntax-error
|
|
#f
|
|
"not at a block end"
|
|
(stx-car stx)))
|
|
(values
|
|
(syntax/loc (stx-car stx)
|
|
(h-return expr))
|
|
null)))))
|
|
|
|
(define-honu-syntax honu-if
|
|
(lambda (stx ctx)
|
|
(define (get-block-or-statement kw rest)
|
|
(syntax-case rest (#%braces)
|
|
[((#%braces then ...) . rest)
|
|
(values #`(honu-unparsed-block #f void-type #f #,(return-block-context? ctx) then ...)
|
|
#'rest)]
|
|
[else
|
|
(let-values ([(val-stxs rest) (extract-until rest
|
|
(list #'\;))])
|
|
(unless val-stxs
|
|
(raise-syntax-error
|
|
#f
|
|
"expected a braced block or a terminating semicolon"
|
|
kw))
|
|
(when (null? val-stxs)
|
|
(raise-syntax-error
|
|
#f
|
|
"expected an expression before semicolon"
|
|
kw
|
|
(stx-car rest)))
|
|
(if (return-block-context? ctx)
|
|
(values (parse-tail-expr val-stxs) (stx-cdr rest))
|
|
(values (parse-expr val-stxs) (stx-cdr rest))))]))
|
|
|
|
(syntax-case stx (#%parens)
|
|
[(_ (#%parens test ...) . rest)
|
|
(let ([test-expr (parse-expr (syntax->list #'(test ...)))])
|
|
(let-values ([(then-expr rest) (get-block-or-statement (stx-car stx) #'rest)])
|
|
(syntax-case rest (else)
|
|
[(else . rest2)
|
|
(let-values ([(else-expr rest) (get-block-or-statement (stx-car rest) #'rest2)])
|
|
(values #`(if #,test-expr #,then-expr #,else-expr)
|
|
rest))]
|
|
[_else
|
|
(values #`(if #,test-expr #,then-expr) rest)])))]
|
|
[_else
|
|
(raise-syntax-error
|
|
#f
|
|
"expected a parenthesized test after `if' keyword"
|
|
(stx-car stx))])))
|
|
|
|
;; ----------------------------------------
|
|
;; Main compiler loop
|
|
|
|
(define-syntax (honu-unparsed-begin stx)
|
|
(syntax-case stx ()
|
|
[(_) #'(begin)]
|
|
[(_ . body) (let-values ([(code rest) (parse-block-one top-block-context
|
|
#'body
|
|
values
|
|
(lambda ()
|
|
(values #'(void) null)))])
|
|
#`(begin
|
|
#,code
|
|
(honu-unparsed-begin #,@rest)))]))
|
|
|
|
(define-syntax (#%dynamic-honu-module-begin stx)
|
|
#`(begin
|
|
#,(syntax-local-introduce #'(require (lib "dynamic.ss" "honu-module")))
|
|
(honu-unparsed-begin #,@(stx-cdr stx))))
|
|
|
|
(define-syntax (\; stx) (raise-syntax-error '\; "out of context" stx))
|
|
|
|
(define true #t)
|
|
(define false #f)
|
|
|
|
(provide int real obj (rename string-type string) ->
|
|
\;
|
|
(rename set! =)
|
|
(rename honu-return return)
|
|
(rename honu-if if)
|
|
+ - * / (rename modulo %)
|
|
(rename string->number stringToNumber)
|
|
(rename number->string numberToString)
|
|
true false
|
|
display write newline
|
|
#%datum
|
|
#%top
|
|
#%parens
|
|
#%dynamic-honu-module-begin
|
|
define-honu-syntax
|
|
(rename honu-provide provide)
|
|
(rename honu-require require)))
|