diff --git a/bench/bad/no-prec-and-inline.expected b/bench/bad/no-prec-and-inline.expected deleted file mode 100644 index a4ebf3d002750228e670b814ad1bc243cfa4b120..0000000000000000000000000000000000000000 --- 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 03c59064f6c092a57794d7ad6b706d87f835c58d..0000000000000000000000000000000000000000 --- 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 0000000000000000000000000000000000000000..cddeb3362a9123666417e4701f481294f2d7e68c --- /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 0000000000000000000000000000000000000000..5fcc8a783796698286e09cfafe76dae599afa0b0 --- /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 0000000000000000000000000000000000000000..be7602b6adb88d0d5d13d3cff16211312581adcc --- /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 0000000000000000000000000000000000000000..afccf174baf2c61b355a00f2d7b0d0353b1bfc95 --- /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 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/bench/good/dario.mly b/bench/good/dario.mly new file mode 100644 index 0000000000000000000000000000000000000000..2d3396463b75719450fde793b2425dfc8197c62f --- /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 0000000000000000000000000000000000000000..b49fcd2d50c9537c008c95bf621fb05e901a75cb --- /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 0000000000000000000000000000000000000000..506434c8e3e063ba9169a96a1e1504b7c2515778 --- /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 0000000000000000000000000000000000000000..5475f2ff761a4f48142ede76ab725dc6e719e30e --- /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 0000000000000000000000000000000000000000..badcb9d4790b4439a98a8a521ceda89457fe101f --- /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 a8dd5186d57bb6c0b2bdd0fac29ec802a5b59d7c..1e032a56e173a720c91d1a5d88437aeda3ff2acb 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 056599eb98557cdae1927c16d38eb0d5d6ca9dad..2b13734852b48e76b0c34c190dc499dd9eea22ec 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