#lang racket
(require (for-syntax syntax/parse))
(require (for-syntax racket/list))
(require (for-syntax racket/bool))
(require "../utilities.rkt")
(require (for-syntax "../utilities.rkt"))
(require "first-order.rkt")
(require racket/stxparam)
(define-for-syntax (return->error stx)
(raise-syntax-error #f
"return cannot be used outside the context of a `def` form"
stx))
(define-syntax-parameter return return->error)
(provide return)
(define-syntax (make-a-define stx)
(syntax-parse stx
[(_ mac-name:id no-args-allowed:boolean define-to-use:expr)
(syntax/loc stx
(define-syntax (mac-name stx)
(define (identifier-is-bound? id)
(or (identifier-binding id)
(and (namespace-variable-value (syntax-e id) #t (lambda () #f)) #t)))
(define (check-top-level-not-defined id)
(when (identifier-is-bound? id)
(raise-syntax-error #f
(string-append "this name was defined previously, "
"and cannot be re-defined")
id)))
(syntax-parse stx
[(_ var-name:id body:expr)
(if (not (identifier/non-kw? (syntax var-name)))
(raise-pyret-error/stx
"keyword names cannot be re-defined"
(syntax var-name))
(begin
(check-top-level-not-defined (syntax var-name))
(syntax/loc stx
(define var-name body))))]
[(_ (fun-name:id args:id (... ...)) body:expr ...+)
(begin
(unless (identifier/non-kw? (syntax fun-name))
(raise-pyret-error/stx
"keywords cannot be used as function names"
(syntax fun-name)))
(check-top-level-not-defined (syntax fun-name))
(let ([args-list (syntax->list (syntax (args (... ...))))])
(when (not no-args-allowed)
(when (empty? args-list)
(raise-pyret-error/stx
(string-append
"functions must have at least one argument")
(second (syntax->list stx)))))
(for-each
(lambda (s)
(when (symbol=? (syntax-e (syntax fun-name)) (syntax-e s))
(raise-pyret-error/stx
(string-append
"the name of the function must not be used as the "
"name of an argument")
s)))
args-list)
(let loop ([seen empty]
[togo args-list])
(cond
[(empty? togo) (void)]
[else
(let ([f (first togo)]
[r (rest togo)])
(let ([id (syntax-e f)])
(when (findf (lambda (c) (symbol=? c id)) seen)
(raise-pyret-error/stx
(string-append
"found an argument name that has been used more "
"than once")
f))
(loop (cons id seen) r)))]))
(with-syntax ([num-expected-args (length args-list)])
(syntax/loc stx
(define-to-use fun-name
(lambda args-given
(let ([the-fun
(lambda (args (... ...)) body (... ...))])
(let ([num-args-given (length args-given)])
(if (equal? num-args-given num-expected-args)
(apply the-fun args-given)
(let ([the-locs (app-locations-first)])
(unless the-locs
(error-no-marks 'semantics/define))
(let ([srcvec (app-locs->total-loc the-locs)])
(raise-pyret-error
(format
(string-append
"function ~a expects ~a argument(s) "
"but was given ~a")
(quote fun-name)
num-expected-args
num-args-given)
srcvec))))))))))))])))]))
(make-a-define beginner-semantic-define #f define-first-order)
(make-a-define intermediate-semantic-define #f define)
(make-a-define advanced-semantic-define #t define)
(define-syntax (make-fun-and-def stx)
(syntax-parse stx
[(_ fun-name:id def-name:id mult-exprs-allowed:boolean semantics:id)
(syntax/loc stx
(begin
(begin-for-syntax
(define-syntax-class define-form
#:literals (fun-name def-name)
(pattern (def-name name:id value:expr))
(pattern (fun-name (name:id args:id (... ...))
locals:define-form (... ...)
body:expr ...+))))
(define-syntax (fun-name stx)
(syntax-parse stx
[(_ (name:id args:id (... ...))
locals:define-form (... ...)
body:expr ...+)
(let ([body-list (syntax->list (syntax (body (... ...))))])
(when (and (not mult-exprs-allowed) (> (length body-list) 1))
(raise-pyret-error
(format
(string-append "there must only be one expression "
"in the body of a function; found ~a")
(length body-list))
(app-locs->total-loc (map syntax->vector body-list))))
(syntax/loc stx
(semantics (name args (... ...))
(syntax-parameterize ([return return->error])
locals (... ...)
body (... ...)))))]))
(define-syntax (def-name stx)
(syntax-parse stx
[(_ name:id value:expr)
(syntax/loc stx
(semantics name value))]))))]))
(make-fun-and-def beginner-fun beginner-def #f beginner-semantic-define)
(make-fun-and-def intermediate-fun intermediate-def #f intermediate-semantic-define)
(provide beginner-def intermediate-def)
(provide beginner-fun intermediate-fun)