From 5ccf0e353b25b9646227c150a965784ee6708f0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Pottier?= Date: Wed, 18 Nov 2015 23:20:06 +0100 Subject: [PATCH] A production can now be marked both %inline and %prec, provided it appears in tail position in its caller and its caller does not itself have a %prec annotation. --- bench/bad/no-prec-and-inline.expected | 2 - bench/bad/no-prec-and-inline.mly | 7 --- bench/bad/prec-inline-non-tail.expected | 5 ++ bench/bad/prec-inline-non-tail.mly | 37 ++++++++++++ bench/bad/prec-inline-prec.expected | 6 ++ bench/bad/prec-inline-prec.mly | 37 ++++++++++++ bench/good/dario.exp | 0 bench/good/dario.mly | 11 ++++ bench/good/dario.opp.exp | 36 +++++++++++ bench/good/prec_inline.exp | 2 + bench/good/prec_inline.mly | 40 +++++++++++++ bench/good/prec_inline.opp.exp | 80 +++++++++++++++++++++++++ src/nonTerminalDefinitionInlining.ml | 46 +++++++++++++- src/partialGrammar.ml | 5 -- 14 files changed, 297 insertions(+), 17 deletions(-) delete mode 100644 bench/bad/no-prec-and-inline.expected delete mode 100644 bench/bad/no-prec-and-inline.mly create mode 100644 bench/bad/prec-inline-non-tail.expected create mode 100644 bench/bad/prec-inline-non-tail.mly create mode 100644 bench/bad/prec-inline-prec.expected create mode 100644 bench/bad/prec-inline-prec.mly create mode 100644 bench/good/dario.exp create mode 100644 bench/good/dario.mly create mode 100644 bench/good/dario.opp.exp create mode 100644 bench/good/prec_inline.exp create mode 100644 bench/good/prec_inline.mly create mode 100644 bench/good/prec_inline.opp.exp diff --git a/bench/bad/no-prec-and-inline.expected b/bench/bad/no-prec-and-inline.expected deleted file mode 100644 index a4ebf3d0..00000000 --- a/bench/bad/no-prec-and-inline.expected +++ /dev/null @@ -1,2 +0,0 @@ -File "no-prec-and-inline.mly", line 7, characters 17-20: -Error: use of %prec is forbidden in an %inlined nonterminal definition. diff --git a/bench/bad/no-prec-and-inline.mly b/bench/bad/no-prec-and-inline.mly deleted file mode 100644 index 03c59064..00000000 --- a/bench/bad/no-prec-and-inline.mly +++ /dev/null @@ -1,7 +0,0 @@ -%start a -%left foo -%% - -a: {} - -%inline b: %prec foo {} diff --git a/bench/bad/prec-inline-non-tail.expected b/bench/bad/prec-inline-non-tail.expected new file mode 100644 index 00000000..cddeb336 --- /dev/null +++ b/bench/bad/prec-inline-non-tail.expected @@ -0,0 +1,5 @@ +File "prec-inline-non-tail.mly", line 35, characters 23-29: +File "prec-inline-non-tail.mly", line 19, characters 2-18: +Error: this production carries a %prec annotation, +and the nonterminal symbol raw_expr is marked %inline. +For this reason, raw_expr can be used only in tail position. diff --git a/bench/bad/prec-inline-non-tail.mly b/bench/bad/prec-inline-non-tail.mly new file mode 100644 index 00000000..5fcc8a78 --- /dev/null +++ b/bench/bad/prec-inline-non-tail.mly @@ -0,0 +1,37 @@ +%token INT +%token PLUS MINUS TIMES DIV +%token LPAREN RPAREN +%token EOL + +%left PLUS MINUS /* lowest precedence */ +%left TIMES DIV /* medium precedence */ +%nonassoc UMINUS /* highest precedence */ + +%start main + +%% + +main: +| e = expr EOL + { e } + +expr: + e = raw_expr INT + { e } + +%inline raw_expr: +| i = INT + { i } +| LPAREN e = expr RPAREN + { e } +| e1 = expr PLUS e2 = expr + { e1 + e2 } +| e1 = expr MINUS e2 = expr + { e1 - e2 } +| e1 = expr TIMES e2 = expr + { e1 * e2 } +| e1 = expr DIV e2 = expr + { e1 / e2 } +| MINUS e = expr %prec UMINUS + { - e } + diff --git a/bench/bad/prec-inline-prec.expected b/bench/bad/prec-inline-prec.expected new file mode 100644 index 00000000..be7602b6 --- /dev/null +++ b/bench/bad/prec-inline-prec.expected @@ -0,0 +1,6 @@ +File "prec-inline-prec.mly", line 35, characters 23-29: +File "prec-inline-prec.mly", line 19, characters 21-27: +Error: this production carries a %prec annotation, +and the nonterminal symbol raw_expr is marked %inline. +For this reason, raw_expr cannot be used in a production +which itself carries a %prec annotation. diff --git a/bench/bad/prec-inline-prec.mly b/bench/bad/prec-inline-prec.mly new file mode 100644 index 00000000..afccf174 --- /dev/null +++ b/bench/bad/prec-inline-prec.mly @@ -0,0 +1,37 @@ +%token INT +%token PLUS MINUS TIMES DIV +%token LPAREN RPAREN +%token EOL + +%left PLUS MINUS /* lowest precedence */ +%left TIMES DIV /* medium precedence */ +%nonassoc UMINUS /* highest precedence */ + +%start main + +%% + +main: +| e = expr EOL + { e } + +expr: + e = raw_expr %prec UMINUS + { e } + +%inline raw_expr: +| i = INT + { i } +| LPAREN e = expr RPAREN + { e } +| e1 = expr PLUS e2 = expr + { e1 + e2 } +| e1 = expr MINUS e2 = expr + { e1 - e2 } +| e1 = expr TIMES e2 = expr + { e1 * e2 } +| e1 = expr DIV e2 = expr + { e1 / e2 } +| MINUS e = expr %prec UMINUS + { - e } + diff --git a/bench/good/dario.exp b/bench/good/dario.exp new file mode 100644 index 00000000..e69de29b diff --git a/bench/good/dario.mly b/bench/good/dario.mly new file mode 100644 index 00000000..2d339646 --- /dev/null +++ b/bench/good/dario.mly @@ -0,0 +1,11 @@ +// Example inspired by Dario Teixeira's question. +%token A B +%start main +%nonassoc block_is_finished +%nonassoc A +%% +main: block* B {} +block: items {} +items: item {} %prec block_is_finished + | item items {} +item: A {} diff --git a/bench/good/dario.opp.exp b/bench/good/dario.opp.exp new file mode 100644 index 00000000..b49fcd2d --- /dev/null +++ b/bench/good/dario.opp.exp @@ -0,0 +1,36 @@ +%start main +%token B +%token A +%nonassoc block_is_finished +%nonassoc A +%type main +%% + +main: +| _1 = list_block_ _2 = B + { ()} + +block: +| _1 = items + { ()} + +items: +| _1 = item %prec block_is_finished + { ()} +| _1 = item _2 = items + { ()} + +item: +| _1 = A + { ()} + +list_block_: +| + { ( [] )} +| x = block xs = list_block_ + { ( x :: xs )} + +%% + + + diff --git a/bench/good/prec_inline.exp b/bench/good/prec_inline.exp new file mode 100644 index 00000000..506434c8 --- /dev/null +++ b/bench/good/prec_inline.exp @@ -0,0 +1,2 @@ +Warning: you are using the standard library and/or the %inline keyword. We +recommend switching on --infer in order to avoid obscure type error messages. diff --git a/bench/good/prec_inline.mly b/bench/good/prec_inline.mly new file mode 100644 index 00000000..5475f2ff --- /dev/null +++ b/bench/good/prec_inline.mly @@ -0,0 +1,40 @@ +%token INT +%token PLUS MINUS TIMES DIV +%token LPAREN RPAREN +%token EOL + +%left PLUS MINUS /* lowest precedence */ +%left TIMES DIV /* medium precedence */ +%nonassoc UMINUS /* highest precedence */ + +%start main + +%% + +main: +| e = expr EOL + { e } + +(* [raw_expr] is inlined into [expr], and some of its productions + carry %prec annotations. This is OK under the new rules of + 2015/11/18. *) +expr: + e = raw_expr + { e } + +%inline raw_expr: +| i = INT + { i } +| LPAREN e = expr RPAREN + { e } +| e1 = expr PLUS e2 = expr + { e1 + e2 } +| e1 = expr MINUS e2 = expr + { e1 - e2 } +| e1 = expr TIMES e2 = expr + { e1 * e2 } +| e1 = expr DIV e2 = expr + { e1 / e2 } +| MINUS e = expr %prec UMINUS + { - e } + diff --git a/bench/good/prec_inline.opp.exp b/bench/good/prec_inline.opp.exp new file mode 100644 index 00000000..badcb9d4 --- /dev/null +++ b/bench/good/prec_inline.opp.exp @@ -0,0 +1,80 @@ +Warning: you are using the standard library and/or the %inline keyword. We +recommend switching on --infer in order to avoid obscure type error messages. +%start main +%token RPAREN +%token LPAREN +%token INT +%token EOL +%token PLUS +%token MINUS +%token TIMES +%token DIV +%left PLUS MINUS +%left TIMES DIV +%nonassoc UMINUS +%type main +%% + +main: +| e = expr _2 = EOL + { ( e )} + +expr: +| i0 = INT + {let e = + let i = i0 in + ( i ) +in + ( e )} +| _10 = LPAREN e0 = expr _30 = RPAREN + {let e = + let _3 = _30 in + let e = e0 in + let _1 = _10 in + ( e ) +in + ( e )} +| e10 = expr _20 = PLUS e20 = expr + {let e = + let e2 = e20 in + let _2 = _20 in + let e1 = e10 in + ( e1 + e2 ) +in + ( e )} +| e10 = expr _20 = MINUS e20 = expr + {let e = + let e2 = e20 in + let _2 = _20 in + let e1 = e10 in + ( e1 - e2 ) +in + ( e )} +| e10 = expr _20 = TIMES e20 = expr + {let e = + let e2 = e20 in + let _2 = _20 in + let e1 = e10 in + ( e1 * e2 ) +in + ( e )} +| e10 = expr _20 = DIV e20 = expr + {let e = + let e2 = e20 in + let _2 = _20 in + let e1 = e10 in + ( e1 / e2 ) +in + ( e )} +| _10 = MINUS e0 = expr %prec UMINUS + {let e = + let e = e0 in + let _1 = _10 in + ( - e ) +in + ( e )} + +%% + + + diff --git a/src/nonTerminalDefinitionInlining.ml b/src/nonTerminalDefinitionInlining.ml index a8dd5186..1e032a56 100644 --- a/src/nonTerminalDefinitionInlining.ml +++ b/src/nonTerminalDefinitionInlining.ml @@ -148,11 +148,37 @@ let inline grammar = and expand_branch (b : branch) : branch ListMonad.m = try (* [c] is the identifier under which the callee is known. *) - let prefix, p, _nt, c, suffix = find_inline_producer b in + let prefix, p, nt, c, suffix = find_inline_producer b in use_inline := true; (* Inline a branch of [nt] at position [prefix] ... [suffix] in the branch [b]. *) let inline_branch pb = + + (* 2015/11/18. The interaction of %prec and %inline is not documented. + It used to be the case that we would disallow marking a production + both %inline and %prec. Now, we allow it, but we check that (1) it + is inlined at the last position of the host production and (2) the + host production does not already have a %prec annotation. *) + pb.branch_prec_annotation |> Option.iter (fun callee_prec -> + (* The callee has a %prec annotation. *) + (* Check condition 1. *) + if List.length suffix > 0 then + Error.error [ Positions.position callee_prec; b.branch_position ] + "this production carries a %%prec annotation,\n\ + and the nonterminal symbol %s is marked %%inline.\n\ + For this reason, %s can be used only in tail position." + nt nt; + (* Check condition 2. *) + b.branch_prec_annotation |> Option.iter (fun caller_prec -> + Error.error [ Positions.position callee_prec; Positions.position caller_prec ] + "this production carries a %%prec annotation,\n\ + and the nonterminal symbol %s is marked %%inline.\n\ + For this reason, %s cannot be used in a production\n\ + which itself carries a %%prec annotation." + nt nt + ) + ); + (* Rename the producers of this branch is they conflict with the name of the host's producers. *) let phi, inlined_producers = rename_if_necessary b pb.producers in @@ -232,9 +258,23 @@ let inline grammar = Action.rename (rename_sw_inner beforeendp) phi pb.action in + (* 2015/11/18. If the callee has a %prec annotation (which implies + the caller does not have one, and the callee appears in tail + position in the caller) then the annotation is inherited. This + seems reasonable, but remains undocumented. *) + let branch_prec_annotation = + match pb.branch_prec_annotation with + | (Some _) as annotation -> + assert (b.branch_prec_annotation = None); + annotation + | None -> + b.branch_prec_annotation + in + { b with - producers = producers; - action = Action.compose c action' outer_action + producers; + action = Action.compose c action' outer_action; + branch_prec_annotation; } in List.map inline_branch p.branches >>= expand_branch diff --git a/src/partialGrammar.ml b/src/partialGrammar.ml index 056599eb..2b137348 100644 --- a/src/partialGrammar.ml +++ b/src/partialGrammar.ml @@ -680,11 +680,6 @@ let check_parameterized_grammar_is_well_defined grammar = check_identifier_reference grammar prule terminal.value terminal.position; - (* It is forbidden to use the %prec directive with %inline. *) - if prule.pr_inline_flag then - Error.errorp terminal - "use of %%prec is forbidden in an %%inlined nonterminal definition."; - (* Furthermore, the symbol following %prec must be a valid token identifier. *) if not (StringMap.mem terminal.value grammar.p_tokens) then -- GitLab