mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-05-20 11:40:18 +02:00
Implement R7RS 'syntax-error'.
* module/ice-9/psyntax.scm (syntax-error): New macro. (syntax-rules): Handle 'syntax-error' templates specially for improved error reporting. * module/ice-9/psyntax-pp.scm: Regenerate. * doc/ref/api-macros.texi (Syntax Rules): Add new subsection "Reporting Syntax Errors in Macros". * test-suite/tests/syntax.test: Add tests.
This commit is contained in:
parent
1624e149f7
commit
0e18163366
4 changed files with 172 additions and 21 deletions
|
@ -363,6 +363,30 @@ Cast into this form, our @code{when} example is significantly shorter:
|
||||||
(if c (begin e ...)))
|
(if c (begin e ...)))
|
||||||
@end example
|
@end example
|
||||||
|
|
||||||
|
@subsubsection Reporting Syntax Errors in Macros
|
||||||
|
|
||||||
|
@deffn {Syntax} syntax-error message [arg ...]
|
||||||
|
Report an error at macro-expansion time. @var{message} must be a string
|
||||||
|
literal, and the optional @var{arg} operands can be arbitrary expressions
|
||||||
|
providing additional information.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@code{syntax-error} is intended to be used within @code{syntax-rules}
|
||||||
|
templates. For example:
|
||||||
|
|
||||||
|
@example
|
||||||
|
(define-syntax simple-let
|
||||||
|
(syntax-rules ()
|
||||||
|
((_ (head ... ((x . y) val) . tail)
|
||||||
|
body1 body2 ...)
|
||||||
|
(syntax-error
|
||||||
|
"expected an identifier but got"
|
||||||
|
(x . y)))
|
||||||
|
((_ ((name val) ...) body1 body2 ...)
|
||||||
|
((lambda (name ...) body1 body2 ...)
|
||||||
|
val ...))))
|
||||||
|
@end example
|
||||||
|
|
||||||
@subsubsection Specifying a Custom Ellipsis Identifier
|
@subsubsection Specifying a Custom Ellipsis Identifier
|
||||||
|
|
||||||
When writing macros that generate macro definitions, it is convenient to
|
When writing macros that generate macro definitions, it is convenient to
|
||||||
|
|
|
@ -2585,18 +2585,85 @@
|
||||||
"source expression failed to match any pattern"
|
"source expression failed to match any pattern"
|
||||||
tmp)))))))))))
|
tmp)))))))))))
|
||||||
|
|
||||||
|
(define syntax-error
|
||||||
|
(make-syntax-transformer
|
||||||
|
'syntax-error
|
||||||
|
'macro
|
||||||
|
(lambda (x)
|
||||||
|
(let ((tmp-1 x))
|
||||||
|
(let ((tmp ($sc-dispatch tmp-1 '(_ (any . any) any . each-any))))
|
||||||
|
(if (if tmp
|
||||||
|
(apply (lambda (keyword operands message arg)
|
||||||
|
(string? (syntax->datum message)))
|
||||||
|
tmp)
|
||||||
|
#f)
|
||||||
|
(apply (lambda (keyword operands message arg)
|
||||||
|
(syntax-violation
|
||||||
|
(syntax->datum keyword)
|
||||||
|
(string-join
|
||||||
|
(cons (syntax->datum message)
|
||||||
|
(map (lambda (x) (object->string (syntax->datum x))) arg)))
|
||||||
|
(if (syntax->datum keyword) (cons keyword operands) #f)))
|
||||||
|
tmp)
|
||||||
|
(let ((tmp ($sc-dispatch tmp-1 '(_ any . each-any))))
|
||||||
|
(if (if tmp
|
||||||
|
(apply (lambda (message arg) (string? (syntax->datum message))) tmp)
|
||||||
|
#f)
|
||||||
|
(apply (lambda (message arg)
|
||||||
|
(cons '#(syntax-object syntax-error ((top)) (hygiene guile))
|
||||||
|
(cons '(#f) (cons message arg))))
|
||||||
|
tmp)
|
||||||
|
(syntax-violation
|
||||||
|
#f
|
||||||
|
"source expression failed to match any pattern"
|
||||||
|
tmp-1)))))))))
|
||||||
|
|
||||||
(define syntax-rules
|
(define syntax-rules
|
||||||
(make-syntax-transformer
|
(make-syntax-transformer
|
||||||
'syntax-rules
|
'syntax-rules
|
||||||
'macro
|
'macro
|
||||||
(lambda (xx)
|
(lambda (xx)
|
||||||
(letrec*
|
(letrec*
|
||||||
((expand-syntax-rules
|
((expand-clause
|
||||||
|
(lambda (clause)
|
||||||
|
(let ((tmp-1 clause))
|
||||||
|
(let ((tmp ($sc-dispatch
|
||||||
|
tmp-1
|
||||||
|
'((any . any)
|
||||||
|
(#(free-id #(syntax-object syntax-error ((top)) (hygiene guile)))
|
||||||
|
any
|
||||||
|
.
|
||||||
|
each-any)))))
|
||||||
|
(if (if tmp
|
||||||
|
(apply (lambda (keyword pattern message arg)
|
||||||
|
(string? (syntax->datum message)))
|
||||||
|
tmp)
|
||||||
|
#f)
|
||||||
|
(apply (lambda (keyword pattern message arg)
|
||||||
|
(list (cons '#(syntax-object dummy ((top)) (hygiene guile)) pattern)
|
||||||
|
(list '#(syntax-object syntax ((top)) (hygiene guile))
|
||||||
|
(cons '#(syntax-object syntax-error ((top)) (hygiene guile))
|
||||||
|
(cons (cons '#(syntax-object dummy ((top)) (hygiene guile)) pattern)
|
||||||
|
(cons message arg))))))
|
||||||
|
tmp)
|
||||||
|
(let ((tmp ($sc-dispatch tmp-1 '((any . any) any))))
|
||||||
|
(if tmp
|
||||||
|
(apply (lambda (keyword pattern template)
|
||||||
|
(list (cons '#(syntax-object dummy ((top)) (hygiene guile)) pattern)
|
||||||
|
(list '#(syntax-object syntax ((top)) (hygiene guile)) template)))
|
||||||
|
tmp)
|
||||||
|
(syntax-violation
|
||||||
|
#f
|
||||||
|
"source expression failed to match any pattern"
|
||||||
|
tmp-1))))))))
|
||||||
|
(expand-syntax-rules
|
||||||
(lambda (dots keys docstrings clauses)
|
(lambda (dots keys docstrings clauses)
|
||||||
(let ((tmp-1 (list keys docstrings clauses)))
|
(let ((tmp-1 (list keys docstrings clauses (map expand-clause clauses))))
|
||||||
(let ((tmp ($sc-dispatch tmp-1 '(each-any each-any #(each ((any . any) any))))))
|
(let ((tmp ($sc-dispatch
|
||||||
|
tmp-1
|
||||||
|
'(each-any each-any #(each ((any . any) any)) each-any))))
|
||||||
(if tmp
|
(if tmp
|
||||||
(apply (lambda (k docstring keyword pattern template)
|
(apply (lambda (k docstring keyword pattern template clause)
|
||||||
(let ((tmp (cons '#(syntax-object lambda ((top)) (hygiene guile))
|
(let ((tmp (cons '#(syntax-object lambda ((top)) (hygiene guile))
|
||||||
(cons '(#(syntax-object x ((top)) (hygiene guile)))
|
(cons '(#(syntax-object x ((top)) (hygiene guile)))
|
||||||
(append
|
(append
|
||||||
|
@ -2609,20 +2676,7 @@
|
||||||
pattern))
|
pattern))
|
||||||
(cons '#(syntax-object syntax-case ((top)) (hygiene guile))
|
(cons '#(syntax-object syntax-case ((top)) (hygiene guile))
|
||||||
(cons '#(syntax-object x ((top)) (hygiene guile))
|
(cons '#(syntax-object x ((top)) (hygiene guile))
|
||||||
(cons k
|
(cons k clause)))))))))
|
||||||
(map (lambda (tmp-1 tmp)
|
|
||||||
(list (cons '#(syntax-object
|
|
||||||
dummy
|
|
||||||
((top))
|
|
||||||
(hygiene guile))
|
|
||||||
tmp)
|
|
||||||
(list '#(syntax-object
|
|
||||||
syntax
|
|
||||||
((top))
|
|
||||||
(hygiene guile))
|
|
||||||
tmp-1)))
|
|
||||||
template
|
|
||||||
pattern))))))))))
|
|
||||||
(let ((form tmp))
|
(let ((form tmp))
|
||||||
(if dots
|
(if dots
|
||||||
(let ((tmp dots))
|
(let ((tmp dots))
|
||||||
|
|
|
@ -2841,21 +2841,53 @@
|
||||||
#'(syntax-case (list in ...) ()
|
#'(syntax-case (list in ...) ()
|
||||||
((out ...) (let () e1 e2 ...)))))))
|
((out ...) (let () e1 e2 ...)))))))
|
||||||
|
|
||||||
|
(define-syntax syntax-error
|
||||||
|
(lambda (x)
|
||||||
|
(syntax-case x ()
|
||||||
|
;; Extended internal syntax which provides the original form
|
||||||
|
;; as the first operand, for improved error reporting.
|
||||||
|
((_ (keyword . operands) message arg ...)
|
||||||
|
(string? (syntax->datum #'message))
|
||||||
|
(syntax-violation (syntax->datum #'keyword)
|
||||||
|
(string-join (cons (syntax->datum #'message)
|
||||||
|
(map (lambda (x)
|
||||||
|
(object->string
|
||||||
|
(syntax->datum x)))
|
||||||
|
#'(arg ...))))
|
||||||
|
(and (syntax->datum #'keyword)
|
||||||
|
#'(keyword . operands))))
|
||||||
|
;; Standard R7RS syntax
|
||||||
|
((_ message arg ...)
|
||||||
|
(string? (syntax->datum #'message))
|
||||||
|
#'(syntax-error (#f) message arg ...)))))
|
||||||
|
|
||||||
(define-syntax syntax-rules
|
(define-syntax syntax-rules
|
||||||
(lambda (xx)
|
(lambda (xx)
|
||||||
|
(define (expand-clause clause)
|
||||||
|
;; Convert a 'syntax-rules' clause into a 'syntax-case' clause.
|
||||||
|
(syntax-case clause (syntax-error)
|
||||||
|
;; If the template is a 'syntax-error' form, use the extended
|
||||||
|
;; internal syntax, which adds the original form as the first
|
||||||
|
;; operand for improved error reporting.
|
||||||
|
(((keyword . pattern) (syntax-error message arg ...))
|
||||||
|
(string? (syntax->datum #'message))
|
||||||
|
#'((dummy . pattern) #'(syntax-error (dummy . pattern) message arg ...)))
|
||||||
|
;; Normal case
|
||||||
|
(((keyword . pattern) template)
|
||||||
|
#'((dummy . pattern) #'template))))
|
||||||
(define (expand-syntax-rules dots keys docstrings clauses)
|
(define (expand-syntax-rules dots keys docstrings clauses)
|
||||||
(with-syntax
|
(with-syntax
|
||||||
(((k ...) keys)
|
(((k ...) keys)
|
||||||
((docstring ...) docstrings)
|
((docstring ...) docstrings)
|
||||||
((((keyword . pattern) template) ...) clauses))
|
((((keyword . pattern) template) ...) clauses)
|
||||||
|
((clause ...) (map expand-clause clauses)))
|
||||||
(with-syntax
|
(with-syntax
|
||||||
((form #'(lambda (x)
|
((form #'(lambda (x)
|
||||||
docstring ... ; optional docstring
|
docstring ... ; optional docstring
|
||||||
#((macro-type . syntax-rules)
|
#((macro-type . syntax-rules)
|
||||||
(patterns pattern ...)) ; embed patterns as procedure metadata
|
(patterns pattern ...)) ; embed patterns as procedure metadata
|
||||||
(syntax-case x (k ...)
|
(syntax-case x (k ...)
|
||||||
((dummy . pattern) #'template)
|
clause ...))))
|
||||||
...))))
|
|
||||||
(if dots
|
(if dots
|
||||||
(with-syntax ((dots dots))
|
(with-syntax ((dots dots))
|
||||||
#'(with-ellipsis dots form))
|
#'(with-ellipsis dots form))
|
||||||
|
|
|
@ -1211,6 +1211,47 @@
|
||||||
(define-syntax bar (foo x y z))
|
(define-syntax bar (foo x y z))
|
||||||
(bar a b c))))
|
(bar a b c))))
|
||||||
|
|
||||||
|
(with-test-prefix "syntax-error"
|
||||||
|
|
||||||
|
(pass-if-syntax-error "outside of macro without args"
|
||||||
|
"test error"
|
||||||
|
(eval '(syntax-error "test error")
|
||||||
|
(interaction-environment)))
|
||||||
|
|
||||||
|
(pass-if-syntax-error "outside of macro with args"
|
||||||
|
"test error x \\(y z\\)"
|
||||||
|
(eval '(syntax-error "test error" x (y z))
|
||||||
|
(interaction-environment)))
|
||||||
|
|
||||||
|
(pass-if-equal "within macro"
|
||||||
|
'(simple-let
|
||||||
|
"expected an identifier but got (z1 z2)"
|
||||||
|
(simple-let ((y (* x x))
|
||||||
|
((z1 z2) (values x x)))
|
||||||
|
(+ y 1)))
|
||||||
|
(catch 'syntax-error
|
||||||
|
(lambda ()
|
||||||
|
(eval '(let ()
|
||||||
|
(define-syntax simple-let
|
||||||
|
(syntax-rules ()
|
||||||
|
((_ (head ... ((x . y) val) . tail)
|
||||||
|
body1 body2 ...)
|
||||||
|
(syntax-error
|
||||||
|
"expected an identifier but got"
|
||||||
|
(x . y)))
|
||||||
|
((_ ((name val) ...) body1 body2 ...)
|
||||||
|
((lambda (name ...) body1 body2 ...)
|
||||||
|
val ...))))
|
||||||
|
(define (foo x)
|
||||||
|
(simple-let ((y (* x x))
|
||||||
|
((z1 z2) (values x x)))
|
||||||
|
(+ y 1)))
|
||||||
|
foo)
|
||||||
|
(interaction-environment))
|
||||||
|
(error "expected syntax-error exception"))
|
||||||
|
(lambda (k who what where form . maybe-subform)
|
||||||
|
(list who what form)))))
|
||||||
|
|
||||||
(with-test-prefix "syntax-case"
|
(with-test-prefix "syntax-case"
|
||||||
|
|
||||||
(pass-if-syntax-error "duplicate pattern variable"
|
(pass-if-syntax-error "duplicate pattern variable"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue