(eval-when (compile load eval)
(define grammar-input
(lambda (x)
(if (or (pair? x) (null? x))
x
#f)))
(define grammar-try (lambda (element input cfa)
(if (procedure? element)
(let ((trial (element input)))
(or (and (grammar-input trial) (cfa trial))
trial))
(and (pair? input)
(equal? element (car input))
(cfa (cdr input))))))
(define grammar-predicate (lambda (pred)
(lambda (input)
(and (pair? input)
(pred (car input))
(cdr input)))))
(define grammar-alt (lambda elements
(lambda (input)
(let loop ((elements elements))
(and (not (null? elements))
(or (grammar-try (car elements) input (lambda (x) x))
(loop (cdr elements))))))))
(define grammar-seq (lambda elements
(let loop ((elements elements))
(lambda (input)
(if (null? elements)
input
(grammar-try (car elements) input
(loop (cdr elements))))))))
(define grammar-lst (lambda elements
(lambda (input)
(and (pair? input)
(list? (car input))
(grammar-try (apply grammar-seq elements)
(car input)
(lambda (trial)
(and (null? trial) (cdr input))))))))
(define grammar-star (lambda (element)
(lambda (input)
(if (null? input) input
(let ((trial (grammar-try element input (lambda (x) x))))
(if (grammar-input trial)
((grammar-star element) trial)
(or trial input)))))))
(define grammar-plus (lambda (element)
(grammar-seq element (grammar-star element))))
(define grammar-opt (lambda (element)
(grammar-alt (grammar-seq element) (grammar-seq))))
(define grammar-dot (lambda (prefix-element suffix-element)
(lambda (input)
(and (pair? input)
(pair? (car input))
(not (list? (car input)))
(let loop ((object (car input)) (ls '()))
(if (pair? object)
(loop (cdr object) (cons (car object) ls))
(and ((grammar-seq prefix-element) (reverse ls))
((grammar-seq suffix-element) (list object))
(cdr input))))))))
(define grammar-report-if-bad (lambda (name cfa) (lambda (input)
(or (grammar-input (cfa input))
(parameterize ((print-level 2))
(if (pair? input) (printf "Bad ~s: ~s~n" name (car input)))
#t)))))
(define grammar-cfa->predicate
(lambda (cfa)
(lambda (object)
(and (null? (cfa (list object))) #t))))
)
(define-syntax grammar
(let ()
(define own-grammar
(let ((variable (grammar-predicate symbol?))
(datum (grammar-predicate (lambda (object) #t)))
(delayed-non-terminal 'ignored))
(let ((non-terminal (lambda (x) (delayed-non-terminal x)))
(terminal (grammar-lst 'quote datum)))
(let ((element (grammar-alt terminal non-terminal)))
(set! delayed-non-terminal
(grammar-report-if-bad 'non-terminal
(grammar-alt variable
(grammar-lst 'alt (grammar-star element))
(grammar-lst 'seq (grammar-star element))
(grammar-lst 'lst (grammar-star element))
(grammar-lst 'star element)
(grammar-lst 'plus element)
(grammar-lst 'opt element)
(grammar-lst 'dot element element)
(grammar-lst 'predicate datum)
(grammar-lst 'cfa datum)
(grammar-lst 'report-if-bad datum non-terminal))))
(grammar-cfa->predicate
(grammar-report-if-bad 'grammar
(grammar-lst 'grammar variable
(grammar-plus
(grammar-report-if-bad 'production
(grammar-lst variable (grammar-star element)))))))))))
(lambda (x)
(syntax-case x ()
((_ start (i v ...) ...)
(and (memq (syntax-object->datum (syntax start))
(syntax-object->datum (syntax (i ...))))
(own-grammar (syntax-object->datum x)))
(with-syntax
(((t ...) (generate-temporaries (syntax (i ...))))
((id ...) (map (lambda (id)
(datum->syntax-object (syntax start) id))
'(alt seq lst star plus opt dot predicate
cfa report-if-bad))))
(syntax ((lambda (id ...)
(let ((t #f) ...)
(let ((i (lambda (x) (t x))) ...)
(set! t (grammar-seq v ...)) ...
(grammar-cfa->predicate start))))
grammar-alt grammar-seq grammar-lst grammar-star
grammar-plus grammar-opt grammar-dot grammar-predicate
(lambda (cfa) cfa) grammar-report-if-bad))))))))