add sex-error reporting
This commit is contained in:
@@ -145,6 +145,12 @@ return Sex code.
|
|||||||
...)
|
...)
|
||||||
#+end_src
|
#+end_src
|
||||||
|
|
||||||
|
** Compile-time type information
|
||||||
|
Sex has a number of type reflection features, aiming to help with
|
||||||
|
macro writing. During the compilation, all type info is collected, and
|
||||||
|
is accessible during macro expansion. This allows us to write things
|
||||||
|
like providing auto serialization, adding meta information, and so on.
|
||||||
|
|
||||||
** Use an established environment for development
|
** Use an established environment for development
|
||||||
As Sex is S-expressions, you always have Emacs with paredit as your
|
As Sex is S-expressions, you always have Emacs with paredit as your
|
||||||
best option.
|
best option.
|
||||||
|
|||||||
@@ -175,7 +175,7 @@ forms, and what remains."
|
|||||||
(define (walk-generic-toplevel form)
|
(define (walk-generic-toplevel form)
|
||||||
(cond ((atom? form) (atom-to-fmt-c form))
|
(cond ((atom? form) (atom-to-fmt-c form))
|
||||||
((list? form) (map walk-generic-toplevel form))
|
((list? form) (map walk-generic-toplevel form))
|
||||||
(else (error "Malformed form " form))))
|
(else (sex-error form "malformed form" form))))
|
||||||
|
|
||||||
(define (walk-expr form)
|
(define (walk-expr form)
|
||||||
(match form
|
(match form
|
||||||
@@ -281,7 +281,7 @@ forms, and what remains."
|
|||||||
(('fn arglist ret-type)
|
(('fn arglist ret-type)
|
||||||
`(%fun ,(walk-type ret-type) ,(walk-arglist arglist)))
|
`(%fun ,(walk-type ret-type) ,(walk-arglist arglist)))
|
||||||
(('fn . _)
|
(('fn . _)
|
||||||
(assert #f "Malformed function type form"))
|
(sex-error form "malformed function type" form))
|
||||||
|
|
||||||
;; Special case: nested structs/unions
|
;; Special case: nested structs/unions
|
||||||
((or ('struct . _)
|
((or ('struct . _)
|
||||||
@@ -363,7 +363,7 @@ forms, and what remains."
|
|||||||
`(,type ,(atom-to-fmt-c name)
|
`(,type ,(atom-to-fmt-c name)
|
||||||
,(process-struct-fields fields)
|
,(process-struct-fields fields)
|
||||||
. ,(tree-map atom-to-fmt-c attrs)))
|
. ,(tree-map atom-to-fmt-c attrs)))
|
||||||
(else (error "Malformed aggregate definition " form))))
|
(else (sex-error form "malformed aggregate definition" form))))
|
||||||
|
|
||||||
(define (walk-enum form)
|
(define (walk-enum form)
|
||||||
(match form
|
(match form
|
||||||
@@ -379,7 +379,7 @@ forms, and what remains."
|
|||||||
(list 'extern (walk-function form)))
|
(list 'extern (walk-function form)))
|
||||||
(('var . _)
|
(('var . _)
|
||||||
(list 'extern (walk-var form)))
|
(list 'extern (walk-var form)))
|
||||||
(else (error "Extern what?"))))
|
(else (sex-error form "extern must be followed by fn or var" form))))
|
||||||
|
|
||||||
(define (walk-public form)
|
(define (walk-public form)
|
||||||
(match form
|
(match form
|
||||||
@@ -400,7 +400,7 @@ forms, and what remains."
|
|||||||
;; ignore here, used in generating public interface
|
;; ignore here, used in generating public interface
|
||||||
(process-toplevel-form form))
|
(process-toplevel-form form))
|
||||||
(else
|
(else
|
||||||
(error "Pub what?" (cadr form)))))
|
(sex-error form "pub must be followed by a definition" form))))
|
||||||
|
|
||||||
(define (process-toplevel-form form)
|
(define (process-toplevel-form form)
|
||||||
(match form
|
(match form
|
||||||
|
|||||||
@@ -85,7 +85,7 @@
|
|||||||
('pub 'typedef new-type target))
|
('pub 'typedef new-type target))
|
||||||
(process-typedef sex-form new-type target acc))
|
(process-typedef sex-form new-type target acc))
|
||||||
|
|
||||||
(else (assert #f (fmt #f "Unknown top level form " sex-form)))))
|
(else (sex-error sex-form "unknown top level form" sex-form))))
|
||||||
|
|
||||||
(define (process-imports module-public-forms acc)
|
(define (process-imports module-public-forms acc)
|
||||||
;; Recursively process imports: register public macros, cons all
|
;; Recursively process imports: register public macros, cons all
|
||||||
@@ -204,7 +204,7 @@
|
|||||||
;; we'll need them for TODO: closures support
|
;; we'll need them for TODO: closures support
|
||||||
(process-fn (copy-form-source! form `(fn ,ret-type ,name ,arglist ,@body))
|
(process-fn (copy-form-source! form `(fn ,ret-type ,name ,arglist ,@body))
|
||||||
(list)))
|
(list)))
|
||||||
(else (assert #f (fmt #f "Malformed lambda " form)))))
|
(else (sex-error form "malformed lambda" form))))
|
||||||
|
|
||||||
;;; Structs
|
;;; Structs
|
||||||
|
|
||||||
|
|||||||
@@ -74,7 +74,7 @@
|
|||||||
(cons (copy-form-source! form (take (cdr form) 4)) acc))
|
(cons (copy-form-source! form (take (cdr form) 4)) acc))
|
||||||
((define defmacro import include struct typedef union var)
|
((define defmacro import include struct typedef union var)
|
||||||
(cons (copy-form-source! form (cdr form)) acc))
|
(cons (copy-form-source! form (cdr form)) acc))
|
||||||
(else (error "Pub what? " (cadr form)))))
|
(else (sex-error form "pub must be followed by a definition" form))))
|
||||||
(else acc)))
|
(else acc)))
|
||||||
|
|
||||||
(define (load-persistent-module-paths)
|
(define (load-persistent-module-paths)
|
||||||
|
|||||||
@@ -6,7 +6,8 @@
|
|||||||
;;; the existing suite, because both need an operand shape that no
|
;;; the existing suite, because both need an operand shape that no
|
||||||
;;; earlier test program happened to use.
|
;;; earlier test program happened to use.
|
||||||
|
|
||||||
(import (chicken port)
|
(import (chicken condition)
|
||||||
|
(chicken port)
|
||||||
(chicken string)
|
(chicken string)
|
||||||
srfi-13
|
srfi-13
|
||||||
fmt-c-writer
|
fmt-c-writer
|
||||||
@@ -28,6 +29,22 @@
|
|||||||
(define (emits? source fragment)
|
(define (emits? source fragment)
|
||||||
(and (string-contains (sex->c source) fragment) #t))
|
(and (string-contains (sex->c source) fragment) #t))
|
||||||
|
|
||||||
|
(define (error-message source)
|
||||||
|
"Compile SOURCE and return the error text as the user sees it --
|
||||||
|
message plus arguments, the way CHICKEN prints it -- or #f if SOURCE
|
||||||
|
compiles."
|
||||||
|
(handle-exceptions e
|
||||||
|
(with-output-to-string
|
||||||
|
(lambda ()
|
||||||
|
(display ((condition-property-accessor 'exn 'message) e))
|
||||||
|
(for-each (lambda (a) (display " ") (write a))
|
||||||
|
((condition-property-accessor 'exn 'arguments) e))))
|
||||||
|
(begin (sex->c source) #f)))
|
||||||
|
|
||||||
|
(define (reports? source fragment)
|
||||||
|
(let ((m (error-message source)))
|
||||||
|
(and m (string-contains m fragment) #t)))
|
||||||
|
|
||||||
(define (in-fn body)
|
(define (in-fn body)
|
||||||
(string-append "(fn f ((a int) (b int)) void " body ")"))
|
(string-append "(fn f ((a int) (b int)) void " body ")"))
|
||||||
|
|
||||||
@@ -153,4 +170,26 @@
|
|||||||
(test-assert "c-or is still accepted"
|
(test-assert "c-or is still accepted"
|
||||||
(emits? (in-fn "(var x int (c-or a b))") "a || b"))
|
(emits? (in-fn "(var x int (c-or a b))") "a || b"))
|
||||||
(test-assert "c-bit-or is still accepted"
|
(test-assert "c-bit-or is still accepted"
|
||||||
(emits? (in-fn "(var x int (c-bit-or a b))") "a | b"))))
|
(emits? (in-fn "(var x int (c-bit-or a b))") "a | b")))
|
||||||
|
;; Diagnostics name also the place. Every form carries a (file
|
||||||
|
;; . line), so an error can cite it
|
||||||
|
(test-group "errors cite the source location"
|
||||||
|
(test-assert "unknown toplevel form"
|
||||||
|
(reports? "(include stdio.h)\n(wat 1 2)" "codegen.sex:2: unknown top level form"))
|
||||||
|
(test-assert "the offending form is shown too"
|
||||||
|
(reports? "(include stdio.h)\n(wat 1 2)" "(wat 1 2)"))
|
||||||
|
(test-assert "pub with nothing to define"
|
||||||
|
(reports? "(pub 1)" "codegen.sex:1:")))
|
||||||
|
|
||||||
|
;; A nested pointer chain used to silently lose a level:
|
||||||
|
;; (var p (* (* char))) emitted `char *p'
|
||||||
|
(test-group "malformed types are rejected"
|
||||||
|
(test-assert "nested pointer chain"
|
||||||
|
(reports? (in-fn "(var p (* (* char)))") "pointer chains are written flat"))
|
||||||
|
(test-assert "and names the line"
|
||||||
|
(reports? "(pub fn f () void\n (var p (* (* char))))" "codegen.sex:2:"))
|
||||||
|
;; A sublist that only groups has no `*' in it and must still work.
|
||||||
|
(test-assert "grouping sublist still accepted"
|
||||||
|
(emits? (in-fn "(var s (* (const struct suc)) 0)") "const struct suc * s"))
|
||||||
|
(test-assert "flat chain still accepted"
|
||||||
|
(emits? (in-fn "(var q (* * const char) 0)") "const char * * q"))))
|
||||||
|
|||||||
@@ -12,6 +12,8 @@
|
|||||||
form-line
|
form-line
|
||||||
copy-form-source!
|
copy-form-source!
|
||||||
stamp-form-source!
|
stamp-form-source!
|
||||||
|
form-location
|
||||||
|
sex-error
|
||||||
with-directory
|
with-directory
|
||||||
)
|
)
|
||||||
"../../utils.scm")
|
"../../utils.scm")
|
||||||
|
|||||||
@@ -12,6 +12,8 @@
|
|||||||
form-line
|
form-line
|
||||||
copy-form-source!
|
copy-form-source!
|
||||||
stamp-form-source!
|
stamp-form-source!
|
||||||
|
form-location
|
||||||
|
sex-error
|
||||||
with-directory
|
with-directory
|
||||||
)
|
)
|
||||||
"utils.scm")
|
"utils.scm")
|
||||||
|
|||||||
15
utils.scm
15
utils.scm
@@ -105,6 +105,21 @@ wrap a form-building expression."
|
|||||||
(hash-table-set! +form-sources+ to src)))
|
(hash-table-set! +form-sources+ to src)))
|
||||||
to)
|
to)
|
||||||
|
|
||||||
|
;;; Diagnostics
|
||||||
|
;;;
|
||||||
|
;;; Every form carries a location now, so an error can say where the
|
||||||
|
;;; wrong code was written
|
||||||
|
(define (form-location form)
|
||||||
|
"\"file:line: \" for FORM, or \"\" when it has none"
|
||||||
|
(let ((src (form-source form)))
|
||||||
|
(if src
|
||||||
|
(string-append (car src) ":" (number->string (cdr src)) ": ")
|
||||||
|
"")))
|
||||||
|
|
||||||
|
(define (sex-error form message . args)
|
||||||
|
"Signal an error about FORM, prefixed with where it was written."
|
||||||
|
(apply error (string-append (form-location form) message) args))
|
||||||
|
|
||||||
(define (stamp-form-source! form src)
|
(define (stamp-form-source! form src)
|
||||||
"Give FORM and every subform that has none the location SRC. Used for
|
"Give FORM and every subform that has none the location SRC. Used for
|
||||||
macro expansions, which inherit the location of the call site the way a
|
macro expansions, which inherit the location of the call site the way a
|
||||||
|
|||||||
Reference in New Issue
Block a user