Commit 1c71fc4a authored by POTTIER Francois's avatar POTTIER Francois

New command line switch `--no-dollars`.

parent bf5e8658
# Changes
## 2018/10/19
## 2018/10/24
* New flag `--no-dollars`, which disallows the use of `$i` in semantic actions.
* When generating OCaml code, include all record fields in record patterns,
even when bound to a wildcard pattern. Thus, avoid triggering OCaml's
......
......@@ -143,6 +143,7 @@
\newcommand{\ologautomaton}{\oo{log-automaton}}
\newcommand{\ologcode}{\oo{log-code}}
\newcommand{\ologgrammar}{\oo{log-grammar}}
\newcommand{\onodollars}{\oo{no-dollars}}
\newcommand{\onoinline}{\oo{no-inline}}
\newcommand{\onostdlib}{\oo{no-stdlib}}
\newcommand{\oocamlc}{\oo{ocamlc}}
......
......@@ -227,6 +227,9 @@ causes some information about the grammar to be logged to the standard error
channel. When \nt{level} is 2, the \emph{nullable}, \emph{FIRST}, and
\emph{FOLLOW} tables are displayed.
\docswitch{\onodollars} This switch disallows the use of positional keywords
of the form \kw{\$i}.
\docswitch{\onoinline} This switch causes all \dinline keywords in the
grammar specification to be ignored. This is especially useful in order
to understand whether these keywords help solve any conflicts.
......@@ -612,7 +615,9 @@ production via the semantic value identifiers bound by the production.
For compatibility with \ocamlyacc, semantic actions can also refer to
unnamed semantic values via positional keywords of the form
\kw{\$1}, \kw{\$2}, etc.\ This style is discouraged. Furthermore, as
\kw{\$1}, \kw{\$2}, etc.\ This style is discouraged.
(It is in fact forbidden if \onodollars is turned on.)
Furthermore, as
a positional keyword of the form \kw{\$i} is internally rewritten as
\nt{\_i}, the user should not use identifiers of the form \nt{\_i}.
......
......@@ -37,7 +37,7 @@ open Positions
%token <Stretch.t> HEADER
%token <Stretch.ocamltype> OCAMLTYPE
%token <Stretch.t Lazy.t> PERCENTPERCENT
%token <Syntax.identifier option array -> Action.t> ACTION
%token <Settings.dollars -> Syntax.identifier option array -> Action.t> ACTION
%token <Syntax.attribute> ATTRIBUTE GRAMMARATTRIBUTE
%token PERCENTATTRIBUTE
......@@ -246,8 +246,8 @@ production_group:
let pr_producers = ParserAux.normalize_producers producers in
(* Distribute the semantic action. Also, check that every [$i]
is within bounds. *)
let action : Syntax.identifier option array -> Action.t = action in
let pr_action = action (ParserAux.producer_names producers) in
let names = ParserAux.producer_names producers in
let pr_action = action Settings.dollars names in
{
pr_producers;
pr_action;
......
......@@ -62,9 +62,10 @@ type monster = {
pos: Positions.t;
(* This method is passed an array of (optional) names for the producers,
that is, the elements of the production's right-hand side. It may
perform some checks and is allowed to fail. *)
check: string option array -> unit;
that is, the elements of the production's right-hand side. It is also
passed a flag which tells whether [$i] syntax is allowed or disallowed.
It may perform some checks and is allowed to fail. *)
check: check;
(* This method transforms the keyword (in place) into a conventional
OCaml identifier. This is done by replacing '$', '(', and ')' with
......@@ -77,12 +78,21 @@ type monster = {
}
and check =
Settings.dollars -> string option array -> unit
(* No check. *)
let none : check =
fun _ _ -> ()
(* ------------------------------------------------------------------------ *)
(* The [$syntaxerror] monster. *)
let syntaxerror pos : monster =
let check _ = ()
let check =
none
and transform ofs1 content =
(* [$syntaxerror] is replaced with
[(raise _eRR)]. Same length. *)
......@@ -102,18 +112,25 @@ let syntaxerror pos : monster =
a mistake. (Plus, this simplies our life, as we rewrite [$i] to [_i],
and we would have to rewrite it to a different identifier otherwise.) *)
let check_dollar pos i producers =
let check_dollar pos i : check = fun dollars producers ->
(* If [i] is out of range, say so. *)
if not (0 <= i - 1 && i - 1 < Array.length producers) then
Error.error [pos] "$%d refers to a nonexistent symbol." i
else
producers.(i - 1) |> Option.iter (fun x ->
Error.error [pos] "please do not say: $%d. Instead, say: %s." i x
)
Error.error [pos] "$%d refers to a nonexistent symbol." i;
(* If [$i] could be referred to via a name, say so. *)
producers.(i - 1) |> Option.iter (fun x ->
Error.error [pos] "please do not say: $%d. Instead, say: %s." i x
);
(* If [$i] syntax is disallowed, say so. *)
match dollars with
| Settings.DollarsDisallowed ->
Error.error [pos] "please do not use $%d. Instead, name this value." i
| Settings.DollarsAllowed ->
()
(* We check that every reference to a producer [x] in a position keyword,
such as [$startpos(x)], exists. *)
let check_producer pos x producers =
let check_producer pos x : check = fun _ producers ->
if not (List.mem (Some x) (Array.to_list producers)) then
Error.error [pos] "%s refers to a nonexistent symbol." x
......@@ -122,7 +139,7 @@ let check_producer pos x producers =
(* The [$i] monster. *)
let dollar pos i : monster =
let check = check_dollar pos i
let check : check = check_dollar pos i
and transform ofs1 content =
(* [$i] is replaced with [_i]. Thus, it is no longer a keyword. *)
let pos = start_of_position pos in
......@@ -142,7 +159,6 @@ let position pos
(flavor : string)
(i : string option) (x : string option)
=
let none _ = () in
let check_no_parameter () =
if i <> None || x <> None then
Error.error [pos] "$%s%s does not take a parameter." where flavor
......@@ -451,12 +467,13 @@ rule main = parse
let stretchpos = lexeme_end_p lexbuf in
let closingpos, monsters = action false openingpos [] lexbuf in
ACTION (
fun (producers : string option array) ->
List.iter (fun monster -> monster.check producers) monsters;
fun dollars producers ->
List.iter (fun monster -> monster.check dollars producers) monsters;
let stretch = mk_stretch stretchpos closingpos true monsters in
Action.from_stretch stretch
)
) }
)
}
| ('%'? as percent) "[@" (attributechar+ as id) whitespace*
{ let openingpos = lexeme_start_p lexbuf in
let stretchpos = lexeme_end_p lexbuf in
......
......@@ -290,6 +290,13 @@ let cmly =
let coq_lib_path =
ref (Some "MenhirLib")
type dollars =
| DollarsDisallowed
| DollarsAllowed
let dollars =
ref DollarsAllowed
(* When new command line options are added, please update both the manual
in [doc/manual.tex] and the man page in [doc/menhir.1]. *)
......@@ -328,6 +335,7 @@ let options = Arg.align [
"--log-code", Arg.Set_int logC, "<level> Log information about the generated code";
"--log-grammar", Arg.Set_int logG, "<level> Log information about the grammar";
"--no-code-inlining", Arg.Clear code_inlining, " (undocumented)";
"--no-dollars", Arg.Unit (fun () -> dollars := DollarsDisallowed), " Disallow $i in semantic actions";
"--no-inline", Arg.Clear inline, " Ignore the %inline keyword";
"--no-pager", Arg.Unit (fun () -> if !construction_mode = ModePager then construction_mode := ModeInclusionOnly), " (undocumented)";
"--no-prefix", Arg.Set noprefix, " (undocumented)";
......@@ -590,6 +598,9 @@ let cmly =
let coq_lib_path =
!coq_lib_path
let dollars =
!dollars
let infer =
!infer
......
......@@ -237,3 +237,11 @@ val cmly: bool
known. Its default value is [Some "MenhirLib"]. *)
val coq_lib_path: string option
(* This flag tells whether [$i] notation in semantic actions is allowed. *)
type dollars =
| DollarsDisallowed
| DollarsAllowed
val dollars: dollars
......@@ -32,7 +32,7 @@ open Positions
%token <Stretch.t> HEADER
%token <Stretch.ocamltype> OCAMLTYPE
%token <Stretch.t Lazy.t> PERCENTPERCENT
%token <Syntax.identifier option array -> Syntax.action> ACTION
%token <Settings.dollars -> Syntax.identifier option array -> Action.t> ACTION
%token <Syntax.attribute> ATTRIBUTE GRAMMARATTRIBUTE
%token PERCENTATTRIBUTE
%start grammar
......@@ -307,7 +307,8 @@ production_group:
let pr_producers = ParserAux.normalize_producers producers in
(* Distribute the semantic action. Also, check that every [$i]
is within bounds. *)
let pr_action = action (ParserAux.producer_names producers) in
let names = ParserAux.producer_names producers in
let pr_action = action Settings.dollars names in
{
pr_producers;
pr_action;
......
File "calc-no-dollars.mly", line 24, characters 11-13:
Error: please do not use $3. Instead, name this value.
%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:
| i = INT
{ i }
| LPAREN e = expr RPAREN
{ e }
| expr PLUS expr
{ $1 + $3 }
(* We use dollars and --no-dollars is enabled; this should fail. *)
| 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 }
......@@ -953,7 +953,7 @@ raw_reasonable_expression:
| TAKE e1 = expression FROM e2 = reasonable_expression
{ ETake (e1, e2) }
| GIVE e1 = expression TO e2 = reasonable_expression
{ EGive (e1, e2) }
{ EGive (e1, e2) }
| taking = TAKING e1 = expression FROM e2 = tight_expression BEGIN e = expression fin = END
{
taking; fin; (* avoid ocaml warnings about unused variables *)
......@@ -1065,8 +1065,8 @@ definition:
{ PConstraint (p, t), e }
| p = normal_pattern e = anonymous_function
{ p, e }
| p = normal_pattern COLON t = normal_type EQUAL BUILTIN b = LIDENT
{ PConstraint (p, t), ELocated (EBuiltin b, ($startpos($5), $endpos($5))) }
| p = normal_pattern COLON t = normal_type EQUAL bb = BUILTIN b = LIDENT
{ PConstraint (p, t), ELocated (EBuiltin b, ($startpos(bb), $endpos(bb))) }
(* The syntax of anonymous function appears as part of definitions (above)
and also, when preceded with the keyword [fun], as a stand-alone
......@@ -1098,9 +1098,9 @@ anonymous_function:
(* This is a toplevel item; it appears in implementations only. *)
definition_group:
| VAL flag_defs = definitions
| v = VAL flag_defs = definitions
{ let flag, defs = flag_defs in
let loc = ($startpos($1), $endpos) in
let loc = ($startpos(v), $endpos) in
ValueDefinitions (loc, flag, defs) }
(* TEMPORARY why do we have a single location for an entire group
of definitions? *)
......@@ -1211,4 +1211,3 @@ range:
tuple types and multi-argument function types. So, I give up and use a
dedicated symbol, STAR, for conjunction. Somewhat analogously, yet another
symbol, BAR, is now used for the conjunction of a type and a permission. *)
......@@ -2997,7 +2997,7 @@ in
( p )
in
( p, e )}
| x00 = raw_normal_pattern _2 = COLON x01 = raw_normal_type _4 = EQUAL _5 = BUILTIN b = LIDENT
| x00 = raw_normal_pattern _2 = COLON x01 = raw_normal_type _4 = EQUAL bb = BUILTIN b = LIDENT
{let t =
let _endpos_x0_ = _endpos_x01_ in
let _startpos_x0_ = _startpos_x01_ in
......@@ -3024,7 +3024,7 @@ let p =
in
( p )
in
( PConstraint (p, t), ELocated (EBuiltin b, (_startpos__5_, _endpos__5_)) )}
( PConstraint (p, t), ELocated (EBuiltin b, (_startpos_bb_, _endpos_bb_)) )}
anonymous_function:
type_parameters = loption_type_parameters_ cs = list_terminated_mode_constraint_DBLARROW__ x00 = raw_parenthetic_type _4 = COLON x01 = raw_normal_type _6 = EQUAL x000 = raw_tuple_or_raw_fragile_expression_
......@@ -3079,7 +3079,7 @@ let _startpos_formal_ = _startpos_x00_ in
EFun (type_parameters, formal, result, body) )}
definition_group:
_1 = VAL flag0 = rec_flag xs00 = loption_separated_nonempty_list_AND_definition__
v = VAL flag0 = rec_flag xs00 = loption_separated_nonempty_list_AND_definition__
{let flag_defs =
let xs0 = xs00 in
let flag = flag0 in
......@@ -3092,7 +3092,7 @@ in
let _endpos_flag_defs_ = _endpos_xs00_ in
let _endpos = _endpos_flag_defs_ in
( let flag, defs = flag_defs in
let loc = (_startpos__1_, _endpos) in
let loc = (_startpos_v_, _endpos) in
ValueDefinitions (loc, flag, defs) )}
value_declaration:
......
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