~jonsterling/forester-devel

DRAFT: Improve parser/lexer v1 SUPERSEDED

Kento Okura: 1
 DRAFT: Improve parser/lexer

 13 files changed, 1251 insertions(+), 216 deletions(-)
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~jonsterling/forester-devel/patches/55546/mbox | git am -3
Learn more about email & git

[PATCH] DRAFT: Improve parser/lexer Export this patch

This patch includes various improvements to the parser/lexer.
Rough edges abound. Will require revising after applying the logseq
patch...

More of my thoughts are in various comments in the code.

I can confirm that golden-forest builds successfully.

In a nutshell:

- feature: Add Grammar.messages file for declarative error messages

- feature? Add Comment to parse tree. Doesn't get parsed yet, haven't
  mucked around with the lexer and parser.

- feature: better error reporting during the lexing phase, and a fatal
  bug was fixed: failure to close the verbatim block is now handled
  gracefully, and a helpful message is provided.

- FEATURE! The reported range upon encountering an unmatched delimiter
  now includes everthing including that delimiter.
---
 lib/compiler/Code.ml          |    1 +
 lib/compiler/Expand.ml        |    2 +
 lib/compiler/Grammar.messages | 1010 +++++++++++++++++++++++++++++++++
 lib/compiler/Grammar.mly      |    1 +
 lib/compiler/Lexer.mll        |   60 +-
 lib/compiler/Parse.ml         |  320 ++++++-----
 lib/compiler/Parse.mli        |    5 -
 lib/compiler/dune             |    6 +
 lib/frontend/Forester.ml      |   18 +-
 lib/frontend/Import_graph.ml  |    2 +-
 test/dune                     |    4 +-
 test/parse.expected           |   13 -
 test/parse.ml                 |   25 -
 13 files changed, 1251 insertions(+), 216 deletions(-)
 create mode 100644 lib/compiler/Grammar.messages
 delete mode 100644 lib/compiler/Parse.mli
 delete mode 100644 test/parse.expected
 delete mode 100644 test/parse.ml

diff --git a/lib/compiler/Code.ml b/lib/compiler/Code.ml
index af3e6e0..5c18de5 100644
--- a/lib/compiler/Code.ml
+++ b/lib/compiler/Code.ml
@@ -37,6 +37,7 @@ type node =
  | Decl_xmlns of string * string
  | Alloc of Trie.path
  | Namespace of Trie.path * t
  | Comment of string
[@@deriving show, repr]

and t = node Range.located list
diff --git a/lib/compiler/Expand.ml b/lib/compiler/Expand.ml
index 5f9caca..2cb145d 100644
--- a/lib/compiler/Expand.ml
+++ b/lib/compiler/Expand.ml
@@ -172,6 +172,8 @@ let rec expand : Code.t -> Syn.t = function
    let symbol = Symbol.named path in
    Sc.include_singleton path @@ Term [Range.locate_opt loc (Syn.Sym symbol)];
    expand rest
  | { value = Comment _; loc } :: rest ->
    expand rest

and expand_method (key, body) =
  key, expand body
diff --git a/lib/compiler/Grammar.messages b/lib/compiler/Grammar.messages
new file mode 100644
index 0000000..f9ba540
--- /dev/null
+++ b/lib/compiler/Grammar.messages
@@ -0,0 +1,1010 @@
main: TEXT
##
## Ends in an error in state: 0.
##
## main' -> . main [ # ]
##
## The known suffix of the stack is as follows:
##
##

<YOUR 11 ERROR MESSAGE HERE>

main: SUBTREE XML_ELT_IDENT
##
## Ends in an error in state: 4.
##
## head_node -> SUBTREE . option(squares(wstext)) LBRACE ws_list(locate(head_node)) RBRACE [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## SUBTREE
##

<YOUR 23 ERROR MESSAGE HERE>

main: SUBTREE LSQUARE XML_ELT_IDENT
##
## Ends in an error in state: 5.
##
## option(squares(wstext)) -> LSQUARE . wstext RSQUARE [ LBRACE ]
##
## The known suffix of the stack is as follows:
## LSQUARE
##

<YOUR 35 ERROR MESSAGE HERE>

main: SUBTREE LSQUARE TEXT RBRACE
##
## Ends in an error in state: 8.
##
## option(squares(wstext)) -> LSQUARE wstext . RSQUARE [ LBRACE ]
##
## The known suffix of the stack is as follows:
## LSQUARE wstext
##
## WARNING: This example involves spurious reductions.
## This implies that, although the LR(1) items shown above provide an
## accurate view of the past (what has been recognized so far), they
## may provide an INCOMPLETE view of the future (what was expected next).
## In state 10, spurious reduction of production list(ws_or_text) ->
## In state 11, spurious reduction of production list(ws_or_text) -> ws_or_text list(ws_or_text)
## In state 12, spurious reduction of production wstext -> list(ws_or_text)
##

<YOUR 55 ERROR MESSAGE HERE>

main: DECL_XMLNS LBRACE TEXT XML_ELT_IDENT
##
## Ends in an error in state: 10.
##
## list(ws_or_text) -> ws_or_text . list(ws_or_text) [ RSQUARE RBRACE ]
##
## The known suffix of the stack is as follows:
## ws_or_text
##

<YOUR 67 ERROR MESSAGE HERE>

main: SUBTREE LSQUARE RSQUARE XML_ELT_IDENT
##
## Ends in an error in state: 13.
##
## head_node -> SUBTREE option(squares(wstext)) . LBRACE ws_list(locate(head_node)) RBRACE [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## SUBTREE option(squares(wstext))
##

<YOUR 79 ERROR MESSAGE HERE>

main: SUBTREE LBRACE TEXT
##
## Ends in an error in state: 14.
##
## head_node -> SUBTREE option(squares(wstext)) LBRACE . ws_list(locate(head_node)) RBRACE [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## SUBTREE option(squares(wstext)) LBRACE
##

<YOUR 91 ERROR MESSAGE HERE>

main: SCOPE XML_ELT_IDENT
##
## Ends in an error in state: 15.
##
## head_node -> SCOPE . arg [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## SCOPE
##

<YOUR 103 ERROR MESSAGE HERE>

main: SCOPE LBRACE RSQUARE
##
## Ends in an error in state: 17.
##
## arg -> LBRACE . list(locate(textual_node)) RBRACE [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## LBRACE
##

<YOUR 115 ERROR MESSAGE HERE>

main: PUT XML_ELT_IDENT
##
## Ends in an error in state: 20.
##
## head_node -> PUT . ident arg [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## PUT
##

<YOUR 127 ERROR MESSAGE HERE>

main: PUT IDENT XML_ELT_IDENT
##
## Ends in an error in state: 22.
##
## head_node -> PUT ident . arg [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## PUT ident
##

<YOUR 139 ERROR MESSAGE HERE>

main: PATCH XML_ELT_IDENT
##
## Ends in an error in state: 24.
##
## head_node -> PATCH . LBRACE ws_list(locate(head_node)) RBRACE option(squares(bvar)) LBRACE ws_list(method_decl) RBRACE [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## PATCH
##

<YOUR 151 ERROR MESSAGE HERE>

main: PATCH LBRACE TEXT
##
## Ends in an error in state: 25.
##
## head_node -> PATCH LBRACE . ws_list(locate(head_node)) RBRACE option(squares(bvar)) LBRACE ws_list(method_decl) RBRACE [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## PATCH LBRACE
##

<YOUR 163 ERROR MESSAGE HERE>

main: OPEN XML_ELT_IDENT
##
## Ends in an error in state: 26.
##
## head_node -> OPEN . ident [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## OPEN
##

<YOUR 175 ERROR MESSAGE HERE>

main: OBJECT XML_ELT_IDENT
##
## Ends in an error in state: 28.
##
## head_node -> OBJECT . option(squares(bvar)) LBRACE ws_list(method_decl) RBRACE [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## OBJECT
##

<YOUR 187 ERROR MESSAGE HERE>

main: OBJECT LSQUARE XML_ELT_IDENT
##
## Ends in an error in state: 29.
##
## option(squares(bvar)) -> LSQUARE . bvar RSQUARE [ LBRACE ]
##
## The known suffix of the stack is as follows:
## LSQUARE
##

<YOUR 199 ERROR MESSAGE HERE>

main: OBJECT LSQUARE TEXT XML_ELT_IDENT
##
## Ends in an error in state: 31.
##
## option(squares(bvar)) -> LSQUARE bvar . RSQUARE [ LBRACE ]
##
## The known suffix of the stack is as follows:
## LSQUARE bvar
##

<YOUR 211 ERROR MESSAGE HERE>

main: OBJECT LSQUARE TEXT RSQUARE XML_ELT_IDENT
##
## Ends in an error in state: 33.
##
## head_node -> OBJECT option(squares(bvar)) . LBRACE ws_list(method_decl) RBRACE [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## OBJECT option(squares(bvar))
##

<YOUR 223 ERROR MESSAGE HERE>

main: OBJECT LBRACE XML_ELT_IDENT
##
## Ends in an error in state: 34.
##
## head_node -> OBJECT option(squares(bvar)) LBRACE . ws_list(method_decl) RBRACE [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## OBJECT option(squares(bvar)) LBRACE
##

<YOUR 235 ERROR MESSAGE HERE>

main: OBJECT LBRACE LSQUARE XML_ELT_IDENT
##
## Ends in an error in state: 36.
##
## method_decl -> LSQUARE . TEXT RSQUARE list(WHITESPACE) arg [ WHITESPACE RBRACE LSQUARE ]
##
## The known suffix of the stack is as follows:
## LSQUARE
##

<YOUR 247 ERROR MESSAGE HERE>

main: OBJECT LBRACE LSQUARE TEXT XML_ELT_IDENT
##
## Ends in an error in state: 37.
##
## method_decl -> LSQUARE TEXT . RSQUARE list(WHITESPACE) arg [ WHITESPACE RBRACE LSQUARE ]
##
## The known suffix of the stack is as follows:
## LSQUARE TEXT
##

<YOUR 259 ERROR MESSAGE HERE>

main: OBJECT LBRACE LSQUARE TEXT RSQUARE XML_ELT_IDENT
##
## Ends in an error in state: 38.
##
## method_decl -> LSQUARE TEXT RSQUARE . list(WHITESPACE) arg [ WHITESPACE RBRACE LSQUARE ]
##
## The known suffix of the stack is as follows:
## LSQUARE TEXT RSQUARE
##

<YOUR 271 ERROR MESSAGE HERE>

main: OBJECT LBRACE LSQUARE TEXT RSQUARE WHITESPACE XML_ELT_IDENT
##
## Ends in an error in state: 39.
##
## list(WHITESPACE) -> WHITESPACE . list(WHITESPACE) [ VERBATIM LBRACE ]
##
## The known suffix of the stack is as follows:
## WHITESPACE
##

<YOUR 283 ERROR MESSAGE HERE>

main: OBJECT LBRACE WHITESPACE XML_ELT_IDENT
##
## Ends in an error in state: 43.
##
## list(ws_or(method_decl)) -> ws_or(method_decl) . list(ws_or(method_decl)) [ RBRACE ]
##
## The known suffix of the stack is as follows:
## ws_or(method_decl)
##

<YOUR 295 ERROR MESSAGE HERE>

main: NAMESPACE XML_ELT_IDENT
##
## Ends in an error in state: 49.
##
## head_node -> NAMESPACE . ident LBRACE ws_list(locate(head_node)) RBRACE [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## NAMESPACE
##

<YOUR 307 ERROR MESSAGE HERE>

main: NAMESPACE IDENT XML_ELT_IDENT
##
## Ends in an error in state: 50.
##
## head_node -> NAMESPACE ident . LBRACE ws_list(locate(head_node)) RBRACE [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## NAMESPACE ident
##

<YOUR 319 ERROR MESSAGE HERE>

main: NAMESPACE IDENT LBRACE TEXT
##
## Ends in an error in state: 51.
##
## head_node -> NAMESPACE ident LBRACE . ws_list(locate(head_node)) RBRACE [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## NAMESPACE ident LBRACE
##

<YOUR 331 ERROR MESSAGE HERE>

main: LSQUARE RPAREN
##
## Ends in an error in state: 52.
##
## head_node -> LSQUARE . list(locate(textual_node)) RSQUARE [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## LSQUARE
##

<YOUR 343 ERROR MESSAGE HERE>

main: LPAREN RSQUARE
##
## Ends in an error in state: 53.
##
## head_node -> LPAREN . list(locate(textual_node)) RPAREN [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## LPAREN
##

<YOUR 355 ERROR MESSAGE HERE>

main: LET XML_ELT_IDENT
##
## Ends in an error in state: 54.
##
## head_node -> LET . ident list(squares(bvar_with_strictness)) arg [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## LET
##

<YOUR 367 ERROR MESSAGE HERE>

main: LET IDENT XML_ELT_IDENT
##
## Ends in an error in state: 55.
##
## head_node -> LET ident . list(squares(bvar_with_strictness)) arg [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## LET ident
##

<YOUR 379 ERROR MESSAGE HERE>

main: FUN LSQUARE XML_ELT_IDENT
##
## Ends in an error in state: 56.
##
## list(squares(bvar_with_strictness)) -> LSQUARE . bvar_with_strictness RSQUARE list(squares(bvar_with_strictness)) [ VERBATIM LBRACE ]
##
## The known suffix of the stack is as follows:
## LSQUARE
##

<YOUR 391 ERROR MESSAGE HERE>

main: FUN LSQUARE TEXT XML_ELT_IDENT
##
## Ends in an error in state: 58.
##
## list(squares(bvar_with_strictness)) -> LSQUARE bvar_with_strictness . RSQUARE list(squares(bvar_with_strictness)) [ VERBATIM LBRACE ]
##
## The known suffix of the stack is as follows:
## LSQUARE bvar_with_strictness
##

<YOUR 403 ERROR MESSAGE HERE>

main: FUN LSQUARE TEXT RSQUARE XML_ELT_IDENT
##
## Ends in an error in state: 59.
##
## list(squares(bvar_with_strictness)) -> LSQUARE bvar_with_strictness RSQUARE . list(squares(bvar_with_strictness)) [ VERBATIM LBRACE ]
##
## The known suffix of the stack is as follows:
## LSQUARE bvar_with_strictness RSQUARE
##

<YOUR 415 ERROR MESSAGE HERE>

main: LBRACE RSQUARE
##
## Ends in an error in state: 63.
##
## head_node -> LBRACE . list(locate(textual_node)) RBRACE [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## LBRACE
##

<YOUR 427 ERROR MESSAGE HERE>

main: IMPORT XML_ELT_IDENT
##
## Ends in an error in state: 64.
##
## head_node -> IMPORT . LBRACE wstext RBRACE [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## IMPORT
##

<YOUR 439 ERROR MESSAGE HERE>

main: IMPORT LBRACE XML_ELT_IDENT
##
## Ends in an error in state: 65.
##
## head_node -> IMPORT LBRACE . wstext RBRACE [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## IMPORT LBRACE
##

<YOUR 451 ERROR MESSAGE HERE>

main: IMPORT LBRACE TEXT RSQUARE
##
## Ends in an error in state: 66.
##
## head_node -> IMPORT LBRACE wstext . RBRACE [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## IMPORT LBRACE wstext
##
## WARNING: This example involves spurious reductions.
## This implies that, although the LR(1) items shown above provide an
## accurate view of the past (what has been recognized so far), they
## may provide an INCOMPLETE view of the future (what was expected next).
## In state 10, spurious reduction of production list(ws_or_text) ->
## In state 11, spurious reduction of production list(ws_or_text) -> ws_or_text list(ws_or_text)
## In state 12, spurious reduction of production wstext -> list(ws_or_text)
##

<YOUR 471 ERROR MESSAGE HERE>

main: HASH_LBRACE RSQUARE
##
## Ends in an error in state: 68.
##
## head_node -> HASH_LBRACE . list(locate(textual_node)) RBRACE [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## HASH_LBRACE
##

<YOUR 483 ERROR MESSAGE HERE>

main: HASH_HASH_LBRACE RSQUARE
##
## Ends in an error in state: 70.
##
## head_node -> HASH_HASH_LBRACE . list(locate(textual_node)) RBRACE [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## HASH_HASH_LBRACE
##

<YOUR 495 ERROR MESSAGE HERE>

main: GET XML_ELT_IDENT
##
## Ends in an error in state: 71.
##
## head_node -> GET . ident [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## GET
##

<YOUR 507 ERROR MESSAGE HERE>

main: FUN XML_ELT_IDENT
##
## Ends in an error in state: 73.
##
## head_node -> FUN . list(squares(bvar_with_strictness)) arg [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## FUN
##

<YOUR 519 ERROR MESSAGE HERE>

main: EXPORT XML_ELT_IDENT
##
## Ends in an error in state: 76.
##
## head_node -> EXPORT . LBRACE wstext RBRACE [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## EXPORT
##

<YOUR 531 ERROR MESSAGE HERE>

main: EXPORT LBRACE XML_ELT_IDENT
##
## Ends in an error in state: 77.
##
## head_node -> EXPORT LBRACE . wstext RBRACE [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## EXPORT LBRACE
##

<YOUR 543 ERROR MESSAGE HERE>

main: EXPORT LBRACE TEXT RSQUARE
##
## Ends in an error in state: 78.
##
## head_node -> EXPORT LBRACE wstext . RBRACE [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## EXPORT LBRACE wstext
##
## WARNING: This example involves spurious reductions.
## This implies that, although the LR(1) items shown above provide an
## accurate view of the past (what has been recognized so far), they
## may provide an INCOMPLETE view of the future (what was expected next).
## In state 10, spurious reduction of production list(ws_or_text) ->
## In state 11, spurious reduction of production list(ws_or_text) -> ws_or_text list(ws_or_text)
## In state 12, spurious reduction of production wstext -> list(ws_or_text)
##

<YOUR 563 ERROR MESSAGE HERE>

main: DEFAULT XML_ELT_IDENT
##
## Ends in an error in state: 80.
##
## head_node -> DEFAULT . ident arg [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## DEFAULT
##

<YOUR 575 ERROR MESSAGE HERE>

main: DEFAULT IDENT XML_ELT_IDENT
##
## Ends in an error in state: 81.
##
## head_node -> DEFAULT ident . arg [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## DEFAULT ident
##

<YOUR 587 ERROR MESSAGE HERE>

main: DEF XML_ELT_IDENT
##
## Ends in an error in state: 83.
##
## head_node -> DEF . ident list(squares(bvar_with_strictness)) arg [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## DEF
##

<YOUR 599 ERROR MESSAGE HERE>

main: DEF IDENT XML_ELT_IDENT
##
## Ends in an error in state: 84.
##
## head_node -> DEF ident . list(squares(bvar_with_strictness)) arg [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## DEF ident
##

<YOUR 611 ERROR MESSAGE HERE>

main: DECL_XMLNS XML_ELT_IDENT
##
## Ends in an error in state: 87.
##
## head_node -> DECL_XMLNS . LBRACE wstext RBRACE [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## DECL_XMLNS
##

<YOUR 623 ERROR MESSAGE HERE>

main: DECL_XMLNS LBRACE XML_ELT_IDENT
##
## Ends in an error in state: 88.
##
## head_node -> DECL_XMLNS LBRACE . wstext RBRACE [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## DECL_XMLNS LBRACE
##

<YOUR 635 ERROR MESSAGE HERE>

main: DECL_XMLNS LBRACE TEXT RSQUARE
##
## Ends in an error in state: 89.
##
## head_node -> DECL_XMLNS LBRACE wstext . RBRACE [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## DECL_XMLNS LBRACE wstext
##
## WARNING: This example involves spurious reductions.
## This implies that, although the LR(1) items shown above provide an
## accurate view of the past (what has been recognized so far), they
## may provide an INCOMPLETE view of the future (what was expected next).
## In state 10, spurious reduction of production list(ws_or_text) ->
## In state 11, spurious reduction of production list(ws_or_text) -> ws_or_text list(ws_or_text)
## In state 12, spurious reduction of production wstext -> list(ws_or_text)
##

<YOUR 655 ERROR MESSAGE HERE>

main: CALL XML_ELT_IDENT
##
## Ends in an error in state: 91.
##
## head_node -> CALL . LBRACE ws_list(locate(head_node)) RBRACE LBRACE wstext RBRACE [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## CALL
##

<YOUR 667 ERROR MESSAGE HERE>

main: CALL LBRACE TEXT
##
## Ends in an error in state: 92.
##
## head_node -> CALL LBRACE . ws_list(locate(head_node)) RBRACE LBRACE wstext RBRACE [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## CALL LBRACE
##

<YOUR 679 ERROR MESSAGE HERE>

main: ALLOC XML_ELT_IDENT
##
## Ends in an error in state: 93.
##
## head_node -> ALLOC . ident [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## ALLOC
##

<YOUR 691 ERROR MESSAGE HERE>

main: IDENT TEXT
##
## Ends in an error in state: 95.
##
## list(ws_or(locate(head_node))) -> ws_or(locate(head_node)) . list(ws_or(locate(head_node))) [ RBRACE EOF ]
##
## The known suffix of the stack is as follows:
## ws_or(locate(head_node))
##

<YOUR 703 ERROR MESSAGE HERE>

main: CALL LBRACE IDENT EOF
##
## Ends in an error in state: 99.
##
## head_node -> CALL LBRACE ws_list(locate(head_node)) . RBRACE LBRACE wstext RBRACE [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## CALL LBRACE ws_list(locate(head_node))
##
## WARNING: This example involves spurious reductions.
## This implies that, although the LR(1) items shown above provide an
## accurate view of the past (what has been recognized so far), they
## may provide an INCOMPLETE view of the future (what was expected next).
## In state 95, spurious reduction of production list(ws_or(locate(head_node))) ->
## In state 96, spurious reduction of production list(ws_or(locate(head_node))) -> ws_or(locate(head_node)) list(ws_or(locate(head_node)))
## In state 104, spurious reduction of production ws_list(locate(head_node)) -> list(ws_or(locate(head_node)))
##

<YOUR 723 ERROR MESSAGE HERE>

main: CALL LBRACE RBRACE XML_ELT_IDENT
##
## Ends in an error in state: 100.
##
## head_node -> CALL LBRACE ws_list(locate(head_node)) RBRACE . LBRACE wstext RBRACE [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## CALL LBRACE ws_list(locate(head_node)) RBRACE
##

<YOUR 735 ERROR MESSAGE HERE>

main: CALL LBRACE RBRACE LBRACE XML_ELT_IDENT
##
## Ends in an error in state: 101.
##
## head_node -> CALL LBRACE ws_list(locate(head_node)) RBRACE LBRACE . wstext RBRACE [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## CALL LBRACE ws_list(locate(head_node)) RBRACE LBRACE
##

<YOUR 747 ERROR MESSAGE HERE>

main: CALL LBRACE RBRACE LBRACE TEXT RSQUARE
##
## Ends in an error in state: 102.
##
## head_node -> CALL LBRACE ws_list(locate(head_node)) RBRACE LBRACE wstext . RBRACE [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## CALL LBRACE ws_list(locate(head_node)) RBRACE LBRACE wstext
##
## WARNING: This example involves spurious reductions.
## This implies that, although the LR(1) items shown above provide an
## accurate view of the past (what has been recognized so far), they
## may provide an INCOMPLETE view of the future (what was expected next).
## In state 10, spurious reduction of production list(ws_or_text) ->
## In state 11, spurious reduction of production list(ws_or_text) -> ws_or_text list(ws_or_text)
## In state 12, spurious reduction of production wstext -> list(ws_or_text)
##

<YOUR 767 ERROR MESSAGE HERE>

main: HASH_HASH_LBRACE IDENT EOF
##
## Ends in an error in state: 105.
##
## list(locate(textual_node)) -> textual_node . list(locate(textual_node)) [ RSQUARE RPAREN RBRACE ]
##
## The known suffix of the stack is as follows:
## textual_node
##

Did you forget to a closing brace?

main: HASH_HASH_LBRACE IDENT RSQUARE
##
## Ends in an error in state: 108.
##
## head_node -> HASH_HASH_LBRACE list(locate(textual_node)) . RBRACE [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## HASH_HASH_LBRACE list(locate(textual_node))
##
## WARNING: This example involves spurious reductions.
## This implies that, although the LR(1) items shown above provide an
## accurate view of the past (what has been recognized so far), they
## may provide an INCOMPLETE view of the future (what was expected next).
## In state 105, spurious reduction of production list(locate(textual_node)) ->
## In state 106, spurious reduction of production list(locate(textual_node)) -> textual_node list(locate(textual_node))
##

<YOUR 798 ERROR MESSAGE HERE>

main: HASH_LBRACE IDENT RSQUARE
##
## Ends in an error in state: 110.
##
## head_node -> HASH_LBRACE list(locate(textual_node)) . RBRACE [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## HASH_LBRACE list(locate(textual_node))
##
## WARNING: This example involves spurious reductions.
## This implies that, although the LR(1) items shown above provide an
## accurate view of the past (what has been recognized so far), they
## may provide an INCOMPLETE view of the future (what was expected next).
## In state 105, spurious reduction of production list(locate(textual_node)) ->
## In state 106, spurious reduction of production list(locate(textual_node)) -> textual_node list(locate(textual_node))
##

<YOUR 817 ERROR MESSAGE HERE>

main: LBRACE IDENT RSQUARE
##
## Ends in an error in state: 112.
##
## head_node -> LBRACE list(locate(textual_node)) . RBRACE [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## LBRACE list(locate(textual_node))
##
## WARNING: This example involves spurious reductions.
## This implies that, although the LR(1) items shown above provide an
## accurate view of the past (what has been recognized so far), they
## may provide an INCOMPLETE view of the future (what was expected next).
## In state 105, spurious reduction of production list(locate(textual_node)) ->
## In state 106, spurious reduction of production list(locate(textual_node)) -> textual_node list(locate(textual_node))
##

<YOUR 836 ERROR MESSAGE HERE>

main: LPAREN IDENT RSQUARE
##
## Ends in an error in state: 114.
##
## head_node -> LPAREN list(locate(textual_node)) . RPAREN [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## LPAREN list(locate(textual_node))
##
## WARNING: This example involves spurious reductions.
## This implies that, although the LR(1) items shown above provide an
## accurate view of the past (what has been recognized so far), they
## may provide an INCOMPLETE view of the future (what was expected next).
## In state 105, spurious reduction of production list(locate(textual_node)) ->
## In state 106, spurious reduction of production list(locate(textual_node)) -> textual_node list(locate(textual_node))
##

<YOUR 855 ERROR MESSAGE HERE>

main: LSQUARE IDENT RPAREN
##
## Ends in an error in state: 116.
##
## head_node -> LSQUARE list(locate(textual_node)) . RSQUARE [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## LSQUARE list(locate(textual_node))
##
## WARNING: This example involves spurious reductions.
## This implies that, although the LR(1) items shown above provide an
## accurate view of the past (what has been recognized so far), they
## may provide an INCOMPLETE view of the future (what was expected next).
## In state 105, spurious reduction of production list(locate(textual_node)) ->
## In state 106, spurious reduction of production list(locate(textual_node)) -> textual_node list(locate(textual_node))
##

<YOUR 874 ERROR MESSAGE HERE>

main: NAMESPACE IDENT LBRACE IDENT EOF
##
## Ends in an error in state: 118.
##
## head_node -> NAMESPACE ident LBRACE ws_list(locate(head_node)) . RBRACE [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## NAMESPACE ident LBRACE ws_list(locate(head_node))
##
## WARNING: This example involves spurious reductions.
## This implies that, although the LR(1) items shown above provide an
## accurate view of the past (what has been recognized so far), they
## may provide an INCOMPLETE view of the future (what was expected next).
## In state 95, spurious reduction of production list(ws_or(locate(head_node))) ->
## In state 96, spurious reduction of production list(ws_or(locate(head_node))) -> ws_or(locate(head_node)) list(ws_or(locate(head_node)))
## In state 104, spurious reduction of production ws_list(locate(head_node)) -> list(ws_or(locate(head_node)))
##

<YOUR 894 ERROR MESSAGE HERE>

main: PATCH LBRACE IDENT EOF
##
## Ends in an error in state: 120.
##
## head_node -> PATCH LBRACE ws_list(locate(head_node)) . RBRACE option(squares(bvar)) LBRACE ws_list(method_decl) RBRACE [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## PATCH LBRACE ws_list(locate(head_node))
##
## WARNING: This example involves spurious reductions.
## This implies that, although the LR(1) items shown above provide an
## accurate view of the past (what has been recognized so far), they
## may provide an INCOMPLETE view of the future (what was expected next).
## In state 95, spurious reduction of production list(ws_or(locate(head_node))) ->
## In state 96, spurious reduction of production list(ws_or(locate(head_node))) -> ws_or(locate(head_node)) list(ws_or(locate(head_node)))
## In state 104, spurious reduction of production ws_list(locate(head_node)) -> list(ws_or(locate(head_node)))
##

<YOUR 914 ERROR MESSAGE HERE>

main: PATCH LBRACE RBRACE XML_ELT_IDENT
##
## Ends in an error in state: 121.
##
## head_node -> PATCH LBRACE ws_list(locate(head_node)) RBRACE . option(squares(bvar)) LBRACE ws_list(method_decl) RBRACE [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## PATCH LBRACE ws_list(locate(head_node)) RBRACE
##

<YOUR 926 ERROR MESSAGE HERE>

main: PATCH LBRACE RBRACE LSQUARE TEXT RSQUARE XML_ELT_IDENT
##
## Ends in an error in state: 122.
##
## head_node -> PATCH LBRACE ws_list(locate(head_node)) RBRACE option(squares(bvar)) . LBRACE ws_list(method_decl) RBRACE [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## PATCH LBRACE ws_list(locate(head_node)) RBRACE option(squares(bvar))
##

<YOUR 938 ERROR MESSAGE HERE>

main: PATCH LBRACE RBRACE LBRACE XML_ELT_IDENT
##
## Ends in an error in state: 123.
##
## head_node -> PATCH LBRACE ws_list(locate(head_node)) RBRACE option(squares(bvar)) LBRACE . ws_list(method_decl) RBRACE [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## PATCH LBRACE ws_list(locate(head_node)) RBRACE option(squares(bvar)) LBRACE
##

<YOUR 950 ERROR MESSAGE HERE>

main: SCOPE LBRACE IDENT RSQUARE
##
## Ends in an error in state: 126.
##
## arg -> LBRACE list(locate(textual_node)) . RBRACE [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## LBRACE list(locate(textual_node))
##
## WARNING: This example involves spurious reductions.
## This implies that, although the LR(1) items shown above provide an
## accurate view of the past (what has been recognized so far), they
## may provide an INCOMPLETE view of the future (what was expected next).
## In state 105, spurious reduction of production list(locate(textual_node)) ->
## In state 106, spurious reduction of production list(locate(textual_node)) -> textual_node list(locate(textual_node))
##

<YOUR 969 ERROR MESSAGE HERE>

main: SUBTREE LBRACE IDENT EOF
##
## Ends in an error in state: 129.
##
## head_node -> SUBTREE option(squares(wstext)) LBRACE ws_list(locate(head_node)) . RBRACE [ XML_ELT_IDENT WHITESPACE VERBATIM TEXT SUBTREE SCOPE RSQUARE RPAREN RBRACE PUT PATCH OPEN OBJECT NAMESPACE LSQUARE LPAREN LET LBRACE IMPORT IDENT HASH_LBRACE HASH_IDENT HASH_HASH_LBRACE GET FUN EXPORT EOF DEFAULT DEF DECL_XMLNS CALL ALLOC ]
##
## The known suffix of the stack is as follows:
## SUBTREE option(squares(wstext)) LBRACE ws_list(locate(head_node))
##
## WARNING: This example involves spurious reductions.
## This implies that, although the LR(1) items shown above provide an
## accurate view of the past (what has been recognized so far), they
## may provide an INCOMPLETE view of the future (what was expected next).
## In state 95, spurious reduction of production list(ws_or(locate(head_node))) ->
## In state 96, spurious reduction of production list(ws_or(locate(head_node))) -> ws_or(locate(head_node)) list(ws_or(locate(head_node)))
## In state 104, spurious reduction of production ws_list(locate(head_node)) -> list(ws_or(locate(head_node)))
##

<YOUR 989 ERROR MESSAGE HERE>

main: IDENT RBRACE
##
## Ends in an error in state: 131.
##
## main -> ws_list(locate(head_node)) . EOF [ # ]
##
## The known suffix of the stack is as follows:
## ws_list(locate(head_node))
##
## WARNING: This example involves spurious reductions.
## This implies that, although the LR(1) items shown above provide an
## accurate view of the past (what has been recognized so far), they
## may provide an INCOMPLETE view of the future (what was expected next).
## In state 95, spurious reduction of production list(ws_or(locate(head_node))) ->
## In state 96, spurious reduction of production list(ws_or(locate(head_node))) -> ws_or(locate(head_node)) list(ws_or(locate(head_node)))
## In state 104, spurious reduction of production ws_list(locate(head_node)) -> list(ws_or(locate(head_node)))
##

This brace is unmatched.

diff --git a/lib/compiler/Grammar.mly b/lib/compiler/Grammar.mly
index 612f9b2..512686f 100644
--- a/lib/compiler/Grammar.mly
+++ b/lib/compiler/Grammar.mly
@@ -6,6 +6,7 @@
%token <string> XML_ELT_IDENT
%token <string> DECL_XMLNS
%token <string> TEXT VERBATIM
%token <string> COMMENT
%token <string> WHITESPACE
%token <string> IDENT
%token <string> HASH_IDENT
diff --git a/lib/compiler/Lexer.mll b/lib/compiler/Lexer.mll
index 1818243..451894f 100644
--- a/lib/compiler/Lexer.mll
+++ b/lib/compiler/Lexer.mll
@@ -1,10 +1,14 @@
{
  open Forester_prelude
  let drop_sigil c str = 1 |> List.nth @@ String.split_on_char c str
  let raise_err lexbuf =
  exception Lex_err of (Lexing.lexbuf * string option)

  let raise_err lexbuf ?msg =
    let loc = Asai.Range.of_lexbuf lexbuf in
    Forester_core.Reporter.fatalf ~loc Forester_core.Reporter.Message.Parse_error "unrecognized token `%s`" @@
    String.escaped @@ Lexing.lexeme lexbuf
    Forester_core.Reporter.fatalf
      ~loc
      Forester_core.Reporter.Message.Parse_error
      "unrecognized token `%s`.@ %a" (String.escaped @@ Lexing.lexeme lexbuf) Format.(pp_print_option pp_print_string) msg
}

let digit = ['0'-'9']
@@ -24,6 +28,7 @@ let verbatim_herald_sep = '|'

rule token = parse
  | "\\%" { Grammar.TEXT "%" }
  (* I added comments to the syntax tree, but how to proceed here? *)
  | "%" { comment lexbuf }
  | "##{" { Grammar.HASH_HASH_LBRACE }
  | "#{" { Grammar.HASH_LBRACE }
@@ -79,7 +84,7 @@ rule token = parse
  | wschar+ { Grammar.WHITESPACE (Lexing.lexeme lexbuf) }
  | newline { Lexing.new_line lexbuf; Grammar.WHITESPACE (Lexing.lexeme lexbuf) }
  | eof { Grammar.EOF }
  | _ { raise_err lexbuf }
  | _ { raise_err lexbuf ?msg:None}

and comment = parse
  | newline_followed_by_ws { Lexing.new_line lexbuf; token lexbuf }
@@ -88,13 +93,13 @@ and comment = parse

and custom_verbatim_herald = parse
  | verbatim_herald as herald { eat_verbatim_herald_sep (custom_verbatim herald (Buffer.create 2000)) lexbuf }
  | newline { Lexing.new_line lexbuf; raise_err lexbuf }
  | _ { raise_err lexbuf }
  | newline { Lexing.new_line lexbuf; raise_err lexbuf ?msg:None }
  | _ { raise_err lexbuf ?msg:None }

and eat_verbatim_herald_sep kont = parse
  | verbatim_herald_sep { kont lexbuf }
  | newline { Lexing.new_line lexbuf; raise_err lexbuf }
  | _ { raise_err lexbuf }
  | newline { Lexing.new_line lexbuf; raise_err ~msg:"Did you forget to start the verbatim block with `|`?" lexbuf }
  | _ { raise_err ?msg:None lexbuf }

and custom_verbatim herald buffer = parse
  | newline as c
@@ -106,31 +111,34 @@ and custom_verbatim herald buffer = parse
  | _ as c
    {
      Buffer.add_char buffer c;
      let buff_len = Buffer.length buffer in
      let herald_len = String.length herald in
      let offset = buff_len - herald_len in
      if offset >= 0 && Buffer.sub buffer offset herald_len = herald then
        let text =
          String_util.trim_trailing_whitespace @@
          String_util.trim_newlines @@
          Buffer.sub buffer 0 offset
        in
        Grammar.VERBATIM text
      else
        custom_verbatim herald buffer lexbuf
      try
        let buff_len = Buffer.length buffer in
        let herald_len = String.length herald in
        let offset = buff_len - herald_len in
        if offset >= 0 && Buffer.sub buffer offset herald_len = herald then
          let text =
            String_util.trim_trailing_whitespace @@
            String_util.trim_newlines @@
            Buffer.sub buffer 0 offset
          in
          Grammar.VERBATIM text
        else
          custom_verbatim herald buffer lexbuf
      (* How can we report the location where the verbatim block was opened?*)
      with _ -> raise_err lexbuf ~msg:(Format.sprintf "Did you forget to close the verbatim block with `%s`?" herald);
    }

and xml_qname = parse
  | xml_qname as qname { qname }
  | newline { Lexing.new_line lexbuf; raise_err lexbuf }
  | _ { raise_err lexbuf }
  | newline { Lexing.new_line lexbuf; raise_err ?msg:None lexbuf }
  | _ { raise_err ?msg:None lexbuf }

and xml_base_ident = parse
  | xml_base_ident as x { x }
  | newline { Lexing.new_line lexbuf; raise_err lexbuf }
  | _ { raise_err lexbuf }
  | newline { Lexing.new_line lexbuf; raise_err ?msg:None lexbuf }
  | _ { raise_err ?msg:None lexbuf }

and rangle = parse
  | ">" { () }
  | newline { Lexing.new_line lexbuf; raise_err lexbuf }
  | _ { raise_err lexbuf }
  | newline { Lexing.new_line lexbuf; raise_err ?msg:None lexbuf }
  | _ { raise_err ?msg:None lexbuf }
diff --git a/lib/compiler/Parse.ml b/lib/compiler/Parse.ml
index e5f276c..d821cea 100644
--- a/lib/compiler/Parse.ml
+++ b/lib/compiler/Parse.ml
@@ -4,155 +4,197 @@ open Lexing

module I = Grammar.MenhirInterpreter

(* debugging helpers *)
let _string_of_token token =
  match token with
  | Grammar.LBRACE -> "LBRACE"
  | Grammar.RBRACE -> "RBRACE"
  | Grammar.LSQUARE -> "LSQUARE"
  | Grammar.RSQUARE -> "RSQUARE"
  | Grammar.LPAREN -> "LPAREN"
  | Grammar.RPAREN -> "RPAREN"
  | Grammar.HASH_LBRACE -> "HASH_LBRACE"
  | Grammar.HASH_HASH_LBRACE -> "HASH_HASH_LBRACE"
  | Grammar.WHITESPACE w -> w
  | Grammar.TEXT s -> s
  | Grammar.EOF -> "EOF"
  | Grammar.IDENT s -> Format.sprintf "IDENT(%s)" s
  | _ -> "<unimplemented>"
(*  NOTE:
    I am unsure if we should ever fail during lexing. I propose: We should
    introduce an `Error` syntax node so that the parse tree is capable of
    representing arbitrary erroneous input. We never want this during batch
    compilation, but always want this for the IDE. Depending on the context in
    which the parser is running, we can immediately fail when encountering
    such a node, or continue. This is what the `I.AboutToReduce` state is for.
  *)

let _char_of_token token =
  match token with
  | Grammar.LBRACE -> '{'
  | Grammar.RBRACE -> '}'
  | Grammar.LSQUARE -> '['
  | Grammar.RSQUARE -> ']'
  | Grammar.LPAREN -> '('
  | Grammar.RPAREN -> ')'
  | Grammar.HASH_LBRACE -> '#'
  | Grammar.HASH_HASH_LBRACE -> '#'
  | _ -> 'x'
(* NOTE: Menhir generates a .messages file with one message per possible
   error state. The incremental API associates a number to each state,
   which we use to look up the custom parse error
   *)
let get_parse_error env =
  match I.stack env with
  | lazy(Nil) -> "Invalid syntax"
  | lazy(Cons (I.Element (state, _, _, _), _)) ->
    try
      let msg = (Grammar_messages.message (I.number state)) in
      (* HACK: messages are currently placeholders that start with '<'. We want
         to hide those "in production". During development, remove this check.
         Whenever you encounter a parse error, the number in the returned
         message will tell you the line number where it is defined in the
         `Grammar.messages` file. You can then write a more helpful message.
       *)
      match msg.[0] with
      | '<' -> ""
      | _ -> msg
    with
      | Not_found -> "invalid syntax (no specific message for this eror)"

(* drive the parser to the next InputNeeded checkpoint *)
let rec resumes checkpoint =
  match checkpoint with
  | I.InputNeeded env -> I.input_needed env
  | I.Shifting _ | I.AboutToReduce _ -> resumes @@ I.resume checkpoint
  | _ -> assert false
let get_range : I.element option -> (position * position) option = fun el ->
    match el with
    | Some (I.Element (_, _, start_pos, end_pos)) ->
      Some (start_pos, end_pos)
    | None -> None

(* strategy: whenever we hit an unexpected closing delimiter, we look for a matching opening delimiter in the past
   if we find one, close all intermediate (hanging) delimiters and then continue parsing
   otherwise just continue parsing
   if we hit a premature EOF, try to close all delimiters, and if that fails return the last good parse
   (on each token, we test if the ending here would have produced a valid parse) *)
let try_parse lexbuf =
  let rec fail bracketing last_token last_accept before supplier chkpt =
    match chkpt with
    | I.HandlingError _ ->
      let loc = Asai.Range.of_lexbuf lexbuf in
      Reporter.emitf ~loc Parse_error "syntax error, unexpected `%s`\n" (Lexing.lexeme lexbuf);
      begin
        match last_token with
        | Grammar.RPAREN
        | Grammar.RSQUARE
        | Grammar.RBRACE ->
          begin
            match List.find_index (fun c -> c = last_token) bracketing with
            | Some i ->
              (* try to find a small enclosing scope *)
              let consume = List.to_seq bracketing |> Seq.take (i + 1) in
              let remaining = List.to_seq bracketing |> Seq.drop i |> List.of_seq in
              let continue = Seq.fold_left (fun acc t -> resumes @@ I.offer acc (t, lexbuf.lex_curr_p, lexbuf.lex_curr_p)) before consume in
              run remaining last_token last_accept before supplier continue
            | None ->
              (* ignore this token and move on *)
              run bracketing Grammar.EOF last_accept before supplier before
          end
        | Grammar.EOF ->
          if not @@ List.is_empty bracketing then
            (* have hanging delimiters to close *)
            let continue = List.fold_left (fun acc t -> resumes @@ I.offer acc (t, lexbuf.lex_curr_p, lexbuf.lex_curr_p)) before bracketing in
            run [] last_token last_accept before supplier continue
          else
            (* can't continue, give up and use last_accept *)
            run [] last_token last_accept before supplier last_accept
        | _ ->
          (* ignore this token and move on *)
          run bracketing Grammar.EOF last_accept before supplier before
      end
    | _ -> Reporter.fatal Parse_error "unreachable parser state"
(* This is an attempt to create a function that better illustrates the
   range in which an error occurs, but it turns out this is not really
   the right design, and we probably need to use the full flexibility
   of the incremental api, like in `incr_parse`.

  and run bracketing last_token last_accept last_input_needed supplier checkpoint =
    match checkpoint with
    | I.InputNeeded _ ->
      (* last_token has been accepted, update bracketing *)
      let bracketing =
        match last_token with
        | Grammar.RPAREN
        | Grammar.RSQUARE
        | Grammar.RBRACE ->
          assert (List.hd bracketing = last_token); List.tl bracketing
        | _ -> bracketing
      in
      (* get new token *)
      let token, start, end_ = supplier () in
      let bracketing =
        match token with
        | Grammar.LPAREN -> Grammar.RPAREN :: bracketing
        | Grammar.LSQUARE -> Grammar.RSQUARE :: bracketing
        | Grammar.LBRACE
        | Grammar.HASH_LBRACE
        | Grammar.HASH_HASH_LBRACE ->
          Grammar.RBRACE :: bracketing
        | _ -> bracketing
      in
      (* check if it's possible to end parsing here, update last_accept *)
      let la =
        if I.acceptable checkpoint Grammar.EOF start then checkpoint
        else last_accept
      in
      run bracketing token la checkpoint supplier @@ I.offer checkpoint (token, start, end_)
    | I.Accepted v -> v
    | I.Rejected
    | I.HandlingError _ ->
      fail bracketing last_token last_accept last_input_needed supplier checkpoint
    | I.Shifting _
    | I.AboutToReduce _ ->
      run bracketing last_token last_accept last_input_needed supplier @@ I.resume checkpoint
  in
  let checkpoint = Grammar.Incremental.main lexbuf.lex_curr_p in
  let supplier = I.lexer_lexbuf_to_supplier Lexer.token lexbuf in
  run [] Grammar.EOF checkpoint checkpoint supplier checkpoint

let maybe_with_errors (f : unit -> 'a) : ('a, 'a * 'b list) result =
  let errors = ref Bwd.Emp in
  let result =
    let@ () =
      Reporter.map_diagnostic @@
        fun d ->
          errors := Bwd.snoc !errors d;
          d
   The motivation is to report things like
   ```
   \foo{
       ^ this brace is unmatched
   ```
   The problem here is that the opening brace and the point at which the
   parser realizes that it is not getting closed might be arbitrarily far
   apart. It might happen on EOF, or when an outer delimiter is closed,
   like this:
   ```
   {foo bar ]
   ```

   The function `loop_handle_undo` thus does not provide us with enough
   context to compute the location of the unmatched brace.
 *)

let parse lexbuf =
  I.loop_handle_undo
    Result.ok
    (
      fun last_successful_checkpt first_error_checkpt ->
        match last_successful_checkpt, first_error_checkpt with
        | I.InputNeeded succ_env, I.HandlingError err_env ->
          let success_range = get_range @@ I.top succ_env in
          let failure_range = get_range @@ I.top err_env in
          let loc =
            match success_range, failure_range with
            | (Some (s, _), Some (_, e))
            | (_, Some (s, e))
            | (Some (s, e), _) ->
              Option.some @@ Range.of_lex_range (s, e)
            | None, None -> None
          in
          let message = get_parse_error err_env in
          let diagnostic =
            Reporter.diagnostic ?loc ~severity: Error Parse_error message
          in
          Error diagnostic
        (*This should never ever happen. Report a bug at menhir!*)
        | _, _ -> assert false
    )
    (I.lexer_lexbuf_to_supplier Lexer.token lexbuf)
    (Grammar.Incremental.main lexbuf.lex_curr_p)

let closed_by c o =
  match (o, c) with
  | (Grammar.LSQUARE, Grammar.RSQUARE)
  | (Grammar.LPAREN, Grammar.RPAREN)
  | (Grammar.LBRACE, Grammar.RBRACE)
  | (Grammar.HASH_LBRACE, Grammar.RBRACE)
  | (Grammar.HASH_HASH_LBRACE, Grammar.RBRACE) ->
    true
  | _ -> false

let is_opening_delim = function
  | Grammar.LSQUARE
  | Grammar.LPAREN
  | Grammar.LBRACE
  | Grammar.HASH_LBRACE
  | Grammar.HASH_HASH_LBRACE ->
    true
  | _ -> false

let is_closing_delim = function
  | Grammar.RSQUARE
  | Grammar.RPAREN
  | Grammar.RBRACE ->
    true
  | _ -> false

let incr_parse
    : lexbuf ->
    (Code.t, Reporter.Message.t Asai.Diagnostic.t) Result.t
  = fun lexbuf ->
    let delim_stack = Stack.create () in
    let supplier = I.lexer_lexbuf_to_supplier (Lexer.token) lexbuf in
    (* The idea is simple: push opening delimiters onto the stack, pop them
       once they are closed. Upon encountering an error, the reported range
       includes the last unclosed delimiter.
     *)
    let initial_checkpoint = (Grammar.Incremental.main lexbuf.lex_curr_p) in
    let rec run
        : _ I.checkpoint ->
        (Code.t, Reporter.Message.t Asai.Diagnostic.t) Result.t
      = fun checkpoint ->
        match checkpoint with
        | I.InputNeeded _env ->
          (* In this phase we push and pop delimiters onto the stack.*)
          let token = Lexer.token lexbuf in
          let start_position = lexbuf.lex_start_p in
          let end_position = lexbuf.lex_curr_p in
          if is_opening_delim token then
            let range = Range.of_lex_range (start_position, end_position) in
            Stack.push (token, range) delim_stack; ;
          let range_of_last_opening_delim =
            if is_closing_delim token then
              match Stack.top_opt delim_stack with
              | Some (open_delim, range) ->
                if (open_delim |> closed_by token) then
                  Option.map snd @@ Stack.pop_opt delim_stack
                else None
              | None -> None
            else None
          in
          let checkpoint = I.offer checkpoint (token, start_position, end_position) in
          run checkpoint
        | I.Shifting((_, _, _): Code.t I.env * Code.t I.env * bool) ->
          let checkpoint = I.resume checkpoint ~strategy: `Simplified in
          run checkpoint
        | I.AboutToReduce (_, production) ->
          let checkpoint = I.resume checkpoint ~strategy: `Simplified in
          run checkpoint
        | I.HandlingError env ->
          let err = get_parse_error env in
          let range_of_last_unclosed =
            match Stack.top_opt delim_stack with
            | Some (token, range) ->
              Some range
            | None -> None
          in
          let loc =
            match Option.map Range.view range_of_last_unclosed with
            | Some (`Range (start_pos, end_pos)) ->
              Range.make
                (start_pos, Range.of_lex_position @@ Lexing.lexeme_start_p lexbuf)
            | Some (`End_of_file _) -> Range.of_lexbuf lexbuf
            | None -> Range.of_lexbuf lexbuf
          in
          Reporter.fatalf ~loc Parse_error "syntax error, unexpected `%s`. %s" (Lexing.lexeme lexbuf) err
        | I.Accepted code -> Ok code
        | I.Rejected ->
          assert false
    in
    f ()
  in
  match !errors with
  | Emp -> Result.ok result
  | errs -> Result.error (result, Bwd.prepend errs [])
    run initial_checkpoint

let parse_channel filename ch =
  let@ () = Reporter.tracef "when parsing file `%s`" filename in
  let lexbuf = Lexing.from_channel ch in
  lexbuf.lex_curr_p <- { lexbuf.lex_curr_p with pos_fname = filename };
  let@ () = maybe_with_errors in
  try_parse lexbuf
  try
    incr_parse lexbuf
  with
    | Grammar.Error ->
      Reporter.fatalf ~loc: (Range.of_lexbuf lexbuf) Parse_error "failed to parse"
    | exn -> raise exn

let parse_file filename =
  let ch = open_in filename in
  let@ () = Fun.protect ~finally: (fun _ -> close_in ch) in
  parse_channel filename ch

let parse_string str =
  let@ () = Reporter.tracef "when parsing string" in
  let lexbuf = Lexing.from_string str in
  let@ () = maybe_with_errors in
  try_parse lexbuf
  Fun.protect ~finally: (fun _ -> close_in ch) @@
    fun _ ->
      parse_channel filename ch
diff --git a/lib/compiler/Parse.mli b/lib/compiler/Parse.mli
deleted file mode 100644
index 1a0a374..0000000
--- a/lib/compiler/Parse.mli
@@ -1,5 +0,0 @@
open Forester_core

val parse_file : string -> (Code.t, Code.t * Reporter.Message.t Asai.Diagnostic.t list) result

val parse_string : string -> (Code.t, Code.t * Reporter.Message.t Asai.Diagnostic.t list) result
diff --git a/lib/compiler/dune b/lib/compiler/dune
index 1d38bc8..12742b5 100644
--- a/lib/compiler/dune
+++ b/lib/compiler/dune
@@ -5,6 +5,12 @@
 (explain true)
 (flags --inspection --table --dump))

; The target for saving menhir's stdout to Grammar_messages.ml
(rule
 (targets Grammar_messages.ml)
 (deps Grammar.messages Grammar.mly)
 (action  (with-stdout-to %{targets} (run menhir --compile-errors %{deps}))))

(library
 (name Forester_compiler)
 (preprocess
diff --git a/lib/frontend/Forester.ml b/lib/frontend/Forester.ml
index 002a5bf..cdb1232 100644
--- a/lib/frontend/Forester.ml
+++ b/lib/frontend/Forester.ml
@@ -118,11 +118,19 @@ let parse_trees_in_dirs ~dev ?(ignore_malformed = false) dirs =
  let addr = Filename.chop_extension basename in
  let native = EP.native_exn fp in
  let source_path = if dev then Some (Unix.realpath native) else None in
  match Parse.parse_file native with
  | Result.Ok code -> Some Code.{ source_path; addr = Some addr; code }
  | Result.Error _ -> None
  | exception exn ->
    if ignore_malformed then None else raise exn
  try
    match Parse.parse_file native with
    | Ok code -> Some Code.{ source_path; addr = Some addr; code }
    | Error diagnostic ->
      if ignore_malformed then None
      else
        begin
          Reporter.Tty.display diagnostic;
          None
        end
  with
    | exn ->
      if ignore_malformed then None else raise exn

let plant_forest_from_dirs ~env ~host ~dev ~tree_dirs ~asset_dirs : unit =
  let parsed_trees = parse_trees_in_dirs ~dev tree_dirs in
diff --git a/lib/frontend/Import_graph.ml b/lib/frontend/Import_graph.ml
index 42dee1a..cd1d42c 100644
--- a/lib/frontend/Import_graph.ml
+++ b/lib/frontend/Import_graph.ml
@@ -34,7 +34,7 @@ let build (trees : Code.tree list) =
    | Object { methods; _ } | Patch { methods; _ } ->
      let@ _, code = List.iter @~ methods in
      analyse_code roots code
    | Text _ | Hash_ident _ | Angle_ident _ | Verbatim _ | Ident _ | Open _ | Put _ | Default _ | Get _ | Decl_xmlns _ | Call _ | Alloc _ -> ()
    | Text _ | Hash_ident _ | Angle_ident _ | Verbatim _ | Ident _ | Open _ | Put _ | Default _ | Get _ | Decl_xmlns _ | Call _ | Alloc _ | Comment _ -> ()
  in
  begin
    let@ tree = List.iter @~ trees in
diff --git a/test/dune b/test/dune
index 7db3d0d..f14d778 100644
--- a/test/dune
+++ b/test/dune
@@ -1,5 +1,5 @@
(tests
 (names parse dsl)
(test
 (name dsl)
 (preprocess
  (pps ppx_deriving.show))
 (libraries
diff --git a/test/parse.expected b/test/parse.expected
deleted file mode 100644
index 5cbb180..0000000
--- a/test/parse.expected
@@ -1,13 +0,0 @@
parse_good_result:
[(Code.Ident title); (Code.Group (Base.Braces, [(Code.Text "Good")]));
  (Code.Ident taxon); (Code.Group (Base.Braces, [(Code.Text "Test")]));
  (Code.Ident author); (Code.Group (Base.Braces, [(Code.Text "Testy")]));
  (Code.Ident p);
  (Code.Group (Base.Braces,
     [(Code.Text "\n"); (Code.Text "      "); (Code.Text "This");
       (Code.Text " "); (Code.Text "should"); (Code.Text " ");
       (Code.Text "parse"); (Code.Text " "); (Code.Text "correctly.");
       (Code.Text "\n"); (Code.Text "    ")]
     ))
  ]

diff --git a/test/parse.ml b/test/parse.ml
deleted file mode 100644
index 862167c..0000000
--- a/test/parse.ml
@@ -1,25 +0,0 @@
open Forester_core
open Forester_compiler

let emit _ = () (* ignore *)

let fatal _ = exit 1

let _ =
  Reporter.run ~emit ~fatal @@
    fun () ->
      let good =
        Result.get_ok @@
          Parse.parse_string
            {|
    \title{Good}
    \taxon{Test}
    \author{Testy}

    \p{
      This should parse correctly.
    }
    |}
      in
      Format.printf "parse_good_result:\n%s\n\n" (Code.show good)

-- 
2.46.0