(values nil value)))))))
(export 'test-parser)
-(defmacro test-parser ((scanner &key) parser input)
+(defmacro test-parser ((scanner &key backtrace) parser input)
"Convenient macro for testing parsers at the REPL.
This is a macro so that the parser can use the fancy syntax. The name
SCANNER is bound to a `sod-token-scanner' reading tokens from the INPUT
- string. Then the PARSER is invoked and three values are returned: a
- `successp' flag indicating whether the parser succeeded; the result,
- output or error indicator, of the parser; and a list consisting of the
- lookahead token type and value, and a string containing the untokenized
- remaining input."
+ string. Then the PARSER is invoked and three values are returned: the
+ result of the parse, or `nil' if the main parse failed; a list containing
+ the number of errors and warnings (respectively) reported during the
+ parse; and a list consisting of the lookahead token type and value, and a
+ string containing the untokenized remaining input.
+
+ If BACKTRACE is nil (the default) then leave errors to the calling
+ environment to sort out (e.g., by entering the Lisp debugger); otherwise,
+ catch and report them as they happen so that you can test error recovery
+ strategies."
(once-only (input)
- (with-gensyms (char-scanner value winp consumedp where)
- `(let* ((,char-scanner (make-string-scanner ,input))
- (,scanner (make-instance 'sod-token-scanner
- :char-scanner ,char-scanner
- :filename "<test-input>")))
+ (with-gensyms (char-scanner value winp body consumedp where nerror nwarn)
+ `(let ((,char-scanner nil) (,scanner nil))
(with-parser-context (token-scanner-context :scanner ,scanner)
- (multiple-value-bind (,value ,winp ,consumedp) (parse ,parser)
- (declare (ignore ,consumedp))
+ (multiple-value-bind (,value ,nerror ,nwarn)
+ (flet ((,body ()
+ (setf ,char-scanner (make-string-scanner ,input)
+ ,scanner (make-instance
+ 'sod-token-scanner
+ :char-scanner ,char-scanner))
+ (multiple-value-bind (,value ,winp ,consumedp)
+ (parse ,parser)
+ (declare (ignore ,consumedp))
+ (cond (,winp ,value)
+ (t (syntax-error ,scanner ,value)
+ nil)))))
+ (if ,backtrace (,body)
+ (count-and-report-errors ()
+ (with-default-error-location (,scanner)
+ (,body)))))
(let ((,where (scanner-capture-place ,char-scanner)))
- (values ,winp ,value
- (list (token-type ,scanner) (token-value ,scanner)
- (subseq ,input ,where))))))))))
+ (values ,value
+ (list ,nerror ,nwarn)
+ (and ,scanner (list (token-type ,scanner)
+ (token-value ,scanner)
+ (subseq ,input ,where)))))))))))
;;;--------------------------------------------------------------------------
;;; Calisthenics.