unofficial mirror of guile-devel@gnu.org 
 help / color / mirror / Atom feed
* [PATCH] Implement R7RS 'syntax-error'
@ 2013-12-19 18:38 Mark H Weaver
  2014-01-08 11:48 ` Ludovic Courtès
  0 siblings, 1 reply; 2+ messages in thread
From: Mark H Weaver @ 2013-12-19 18:38 UTC (permalink / raw)
  To: guile-devel

[-- Attachment #1: Type: text/plain, Size: 925 bytes --]

This patch implements the R7RS 'syntax-error' macro, which supports
improved error reporting from within 'syntax-rules' macros.

For 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 ...))))
  
  (define (foo x)
    (simple-let ((y (* x x))
                 ((z1 z2) (values x x)))
      (+ y 1)))
  =>
  While compiling expression:
  ERROR: Syntax error:
  unknown location: simple-let: expected an identifier but got (z1 z2) in form (simple-let ((y (* x x)) ((z1 z2) (values x x))) (+ y 1))

This patch assumes that my earlier "custom ellipses" patch has already
been applied.  I'd like to push these to stable-2.0.

Comments and suggestions welcome.

      Mark



[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: [PATCH] Implement R7RS 'syntax-error' --]
[-- Type: text/x-patch, Size: 13245 bytes --]

From 9f4f8641d0218525a2fc58ef6f8c6728145f0def Mon Sep 17 00:00:00 2001
From: Mark H Weaver <mhw@netris.org>
Date: Thu, 19 Dec 2013 13:22:50 -0500
Subject: [PATCH] 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 docs.

* test-suite/tests/syntax.test: Add tests.
---
 doc/ref/api-macros.texi      |   24 +++++++++++
 module/ice-9/psyntax-pp.scm  |   90 +++++++++++++++++++++++++++++++++--------
 module/ice-9/psyntax.scm     |   38 ++++++++++++++++-
 test-suite/tests/syntax.test |   41 +++++++++++++++++++
 4 files changed, 172 insertions(+), 21 deletions(-)

diff --git a/doc/ref/api-macros.texi b/doc/ref/api-macros.texi
index 61137f0..72dd0df 100644
--- a/doc/ref/api-macros.texi
+++ b/doc/ref/api-macros.texi
@@ -363,6 +363,30 @@ Cast into this form, our @code{when} example is significantly shorter:
   (if c (begin e ...)))
 @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
 
 When writing macros that generate macro definitions, it is convenient to
diff --git a/module/ice-9/psyntax-pp.scm b/module/ice-9/psyntax-pp.scm
index a9015b2..e2c6b00 100644
--- a/module/ice-9/psyntax-pp.scm
+++ b/module/ice-9/psyntax-pp.scm
@@ -2583,18 +2583,85 @@
                       "source expression failed to match any pattern"
                       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
   (make-syntax-transformer
     'syntax-rules
     'macro
     (lambda (xx)
       (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)
-             (let ((tmp-1 (list keys docstrings clauses)))
-               (let ((tmp ($sc-dispatch tmp-1 '(each-any each-any #(each ((any . any) any))))))
+             (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)) each-any))))
                  (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))
                                              (cons '(#(syntax-object x ((top)) (hygiene guile)))
                                                    (append
@@ -2607,20 +2674,7 @@
                                                                    pattern))
                                                            (cons '#(syntax-object syntax-case ((top)) (hygiene guile))
                                                                  (cons '#(syntax-object x ((top)) (hygiene guile))
-                                                                       (cons k
-                                                                             (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))))))))))
+                                                                       (cons k clause)))))))))
                               (let ((form tmp))
                                 (if dots
                                   (let ((tmp dots))
diff --git a/module/ice-9/psyntax.scm b/module/ice-9/psyntax.scm
index 143d4c7..1ec6498 100644
--- a/module/ice-9/psyntax.scm
+++ b/module/ice-9/psyntax.scm
@@ -2839,21 +2839,53 @@
           #'(syntax-case (list in ...) ()
               ((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 ...)
+       (and (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
   (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)
       (with-syntax
           (((k ...) keys)
            ((docstring ...) docstrings)
-           ((((keyword . pattern) template) ...) clauses))
+           ((((keyword . pattern) template) ...) clauses)
+           ((clause ...) (map expand-clause clauses)))
         (with-syntax
             ((form #'(lambda (x)
                        docstring ...        ; optional docstring
                        #((macro-type . syntax-rules)
                          (patterns pattern ...)) ; embed patterns as procedure metadata
                        (syntax-case x (k ...)
-                         ((dummy . pattern) #'template)
-                         ...))))
+                         clause ...))))
           (if dots
               (with-syntax ((dots dots))
                 #'(with-ellipsis dots form))
diff --git a/test-suite/tests/syntax.test b/test-suite/tests/syntax.test
index 142e2e5..093453b 100644
--- a/test-suite/tests/syntax.test
+++ b/test-suite/tests/syntax.test
@@ -1211,6 +1211,47 @@
       (define-syntax bar (foo x y z))
       (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"
   
   (pass-if-syntax-error "duplicate pattern variable"
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 2+ messages in thread

* Re: [PATCH] Implement R7RS 'syntax-error'
  2013-12-19 18:38 [PATCH] Implement R7RS 'syntax-error' Mark H Weaver
@ 2014-01-08 11:48 ` Ludovic Courtès
  0 siblings, 0 replies; 2+ messages in thread
From: Ludovic Courtès @ 2014-01-08 11:48 UTC (permalink / raw)
  To: guile-devel

Mark H Weaver <mhw@netris.org> skribis:

> From 9f4f8641d0218525a2fc58ef6f8c6728145f0def Mon Sep 17 00:00:00 2001
> From: Mark H Weaver <mhw@netris.org>
> Date: Thu, 19 Dec 2013 13:22:50 -0500
> Subject: [PATCH] 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 docs.
>
> * test-suite/tests/syntax.test: Add tests.

Looks good to me.  The log should mention the new section in
api-macros.texi.

> +@subsubsection Reporting syntax errors in macros

Should be capitalized.

Thanks!

Ludo’.




^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2014-01-08 11:48 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-12-19 18:38 [PATCH] Implement R7RS 'syntax-error' Mark H Weaver
2014-01-08 11:48 ` Ludovic Courtès

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).