Commit 5ccf0e35 authored by POTTIER Francois's avatar POTTIER Francois

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.
parent cfc8a200
File "no-prec-and-inline.mly", line 7, characters 17-20:
Error: use of %prec is forbidden in an %inlined nonterminal definition.
%start<int> a
%left foo
%%
a: {}
%inline b: %prec foo {}
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.
%token <int> 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 <int> 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 }
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.
%token <int> 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 <int> 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 }
// Example inspired by Dario Teixeira's question.
%token A B
%start<unit> main
%nonassoc block_is_finished
%nonassoc A
%%
main: block* B {}
block: items {}
items: item {} %prec block_is_finished
| item items {}
item: A {}
%start main
%token B
%token A
%nonassoc block_is_finished
%nonassoc A
%type <unit> 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 )}
%%
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.
%token <int> 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 <int> 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 }
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> INT
%token EOL
%token PLUS
%token MINUS
%token TIMES
%token DIV
%left PLUS MINUS
%left TIMES DIV
%nonassoc UMINUS
%type <int> 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 )}
%%
......@@ -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
......
......@@ -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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment