Finished narrowing the gap between Ligodity AST and Pascaligo AST.
This commit is contained in:
parent
0796567aee
commit
011ae44b54
@ -201,22 +201,24 @@ and field_pattern = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
and expr =
|
and expr =
|
||||||
ELetIn of let_in reg
|
ECase of expr case reg
|
||||||
| EFun of fun_expr
|
|
||||||
| ECond of conditional
|
|
||||||
| ETuple of (expr, comma) Utils.nsepseq reg
|
|
||||||
| EMatch of match_expr reg
|
|
||||||
| ESeq of sequence
|
|
||||||
| ERecord of record_expr
|
|
||||||
| ELogic of logic_expr
|
| ELogic of logic_expr
|
||||||
| EArith of arith_expr
|
| EArith of arith_expr
|
||||||
| EString of string_expr
|
| EString of string_expr
|
||||||
| ECall of (expr * expr) reg
|
|
||||||
| Path of path reg
|
|
||||||
| EUnit of the_unit reg
|
|
||||||
| EPar of expr par reg
|
|
||||||
| EList of list_expr
|
| EList of list_expr
|
||||||
| EConstr of constr
|
| EConstr of constr
|
||||||
|
| ERecord of record_expr
|
||||||
|
| EProj of projection reg
|
||||||
|
| EVar of variable
|
||||||
|
| ECall of (expr * expr) reg
|
||||||
|
| EUnit of the_unit reg
|
||||||
|
| ETuple of (expr, comma) Utils.nsepseq reg
|
||||||
|
| EPar of expr par reg
|
||||||
|
|
||||||
|
| ELetIn of let_in reg
|
||||||
|
| EFun of fun_expr
|
||||||
|
| ECond of conditional reg
|
||||||
|
| ESeq of sequence
|
||||||
|
|
||||||
and 'a injection = {
|
and 'a injection = {
|
||||||
opening : opening;
|
opening : opening;
|
||||||
@ -227,6 +229,7 @@ and 'a injection = {
|
|||||||
|
|
||||||
and opening =
|
and opening =
|
||||||
Begin of kwd_begin
|
Begin of kwd_begin
|
||||||
|
| With of kwd_with
|
||||||
| LBrace of lbrace
|
| LBrace of lbrace
|
||||||
| LBracket of lbracket
|
| LBracket of lbracket
|
||||||
|
|
||||||
@ -285,13 +288,14 @@ and comp_expr =
|
|||||||
| Equal of equal bin_op reg
|
| Equal of equal bin_op reg
|
||||||
| Neq of neq bin_op reg
|
| Neq of neq bin_op reg
|
||||||
|
|
||||||
and path = {
|
and projection = {
|
||||||
module_proj : (constr * dot) option;
|
struct_name : variable;
|
||||||
value_proj : (selection, dot) Utils.nsepseq
|
selector : dot;
|
||||||
|
field_path : (selection, dot) Utils.nsepseq
|
||||||
}
|
}
|
||||||
|
|
||||||
and selection =
|
and selection =
|
||||||
Name of variable
|
FieldName of variable
|
||||||
| Component of (string * Z.t) reg par reg
|
| Component of (string * Z.t) reg par reg
|
||||||
|
|
||||||
and record_expr = field_assignment reg injection reg
|
and record_expr = field_assignment reg injection reg
|
||||||
@ -304,18 +308,33 @@ and field_assignment = {
|
|||||||
|
|
||||||
and sequence = expr injection reg
|
and sequence = expr injection reg
|
||||||
|
|
||||||
and match_expr = kwd_match * expr * kwd_with * cases
|
and 'a case = {
|
||||||
|
kwd_match : kwd_match;
|
||||||
|
expr : expr;
|
||||||
|
opening : opening;
|
||||||
|
lead_vbar : vbar option;
|
||||||
|
cases : ('a case_clause reg, vbar) Utils.nsepseq reg;
|
||||||
|
closing : closing
|
||||||
|
}
|
||||||
|
|
||||||
and cases =
|
and 'a case_clause = {
|
||||||
vbar option * (pattern * arrow * expr, vbar) Utils.nsepseq
|
pattern : pattern;
|
||||||
|
arrow : arrow;
|
||||||
|
rhs : 'a
|
||||||
|
}
|
||||||
|
|
||||||
and let_in = kwd_let * let_bindings * kwd_in * expr
|
and let_in = kwd_let * let_bindings * kwd_in * expr
|
||||||
|
|
||||||
and fun_expr = (kwd_fun * variable * arrow * expr) reg
|
and fun_expr = (kwd_fun * variable * arrow * expr) reg
|
||||||
|
|
||||||
and conditional =
|
and conditional = {
|
||||||
IfThen of (kwd_if * expr * kwd_then * expr) reg
|
kwd_if : kwd_if;
|
||||||
| IfThenElse of (kwd_if * expr * kwd_then * expr * kwd_else * expr) reg
|
test : expr;
|
||||||
|
kwd_then : kwd_then;
|
||||||
|
ifso : expr;
|
||||||
|
kwd_else : kwd_else;
|
||||||
|
ifnot : expr
|
||||||
|
}
|
||||||
|
|
||||||
(* Projecting regions of the input source code *)
|
(* Projecting regions of the input source code *)
|
||||||
|
|
||||||
@ -364,9 +383,8 @@ let region_of_expr = function
|
|||||||
| EString e -> region_of_string_expr e
|
| EString e -> region_of_string_expr e
|
||||||
| EList e -> region_of_list_expr e
|
| EList e -> region_of_list_expr e
|
||||||
| ELetIn {region;_} | EFun {region;_}
|
| ELetIn {region;_} | EFun {region;_}
|
||||||
| ECond IfThen {region;_} | ECond IfThenElse {region; _}
|
| ECond {region;_} | ETuple {region;_} | ECase {region;_}
|
||||||
| ETuple {region;_} | EMatch {region;_}
|
| ECall {region;_} | EVar {region; _} | EProj {region; _}
|
||||||
| ECall {region;_} | Path {region;_}
|
|
||||||
| EUnit {region;_} | EPar {region;_}
|
| EUnit {region;_} | EPar {region;_}
|
||||||
| ESeq {region; _} | ERecord {region; _}
|
| ESeq {region; _} | ERecord {region; _}
|
||||||
| EConstr {region; _} -> region
|
| EConstr {region; _} -> region
|
||||||
@ -384,11 +402,8 @@ let norm_fun region kwd_fun pattern eq expr =
|
|||||||
PVar v -> kwd_fun, v, eq, expr
|
PVar v -> kwd_fun, v, eq, expr
|
||||||
| _ -> let value = Utils.gen_sym () in
|
| _ -> let value = Utils.gen_sym () in
|
||||||
let fresh = Region.{region=Region.ghost; value} in
|
let fresh = Region.{region=Region.ghost; value} in
|
||||||
let proj = Name fresh, [] in
|
|
||||||
let path = {module_proj=None; value_proj=proj} in
|
|
||||||
let path = Region.{region=Region.ghost; value=path} in
|
|
||||||
let bindings = {pattern; eq;
|
let bindings = {pattern; eq;
|
||||||
lhs_type=None; let_rhs = Path path}, [] in
|
lhs_type=None; let_rhs = EVar fresh}, [] in
|
||||||
let let_in = ghost_let, bindings, ghost_in, expr in
|
let let_in = ghost_let, bindings, ghost_in, expr in
|
||||||
let expr = ELetIn {value=let_in; region=Region.ghost}
|
let expr = ELetIn {value=let_in; region=Region.ghost}
|
||||||
in kwd_fun, fresh, ghost_arrow, expr
|
in kwd_fun, fresh, ghost_arrow, expr
|
||||||
@ -452,7 +467,6 @@ let print_sepseq sep print = function
|
|||||||
| Some seq -> print_nsepseq sep print seq
|
| Some seq -> print_nsepseq sep print seq
|
||||||
|
|
||||||
let print_csv print = print_nsepseq "," print
|
let print_csv print = print_nsepseq "," print
|
||||||
let print_bsv print = print_nsepseq "|" print
|
|
||||||
|
|
||||||
let print_token (reg: Region.t) conc =
|
let print_token (reg: Region.t) conc =
|
||||||
Printf.printf "%s: %s\n" (reg#compact `Byte) conc
|
Printf.printf "%s: %s\n" (reg#compact `Byte) conc
|
||||||
@ -512,18 +526,14 @@ and print_type_par {value={lpar;inside=t;rpar}; _} =
|
|||||||
print_type_expr t;
|
print_type_expr t;
|
||||||
print_token rpar ")"
|
print_token rpar ")"
|
||||||
|
|
||||||
and print_path Region.{value; _} =
|
and print_projection Region.{value; _} =
|
||||||
let {module_proj; value_proj} = value in
|
let {struct_name; selector; field_path} = value in
|
||||||
let () =
|
print_uident struct_name;
|
||||||
match module_proj with
|
print_token selector ".";
|
||||||
None -> ()
|
print_nsepseq "." print_selection field_path
|
||||||
| Some (name, dot) ->
|
|
||||||
print_uident name;
|
|
||||||
print_token dot "."
|
|
||||||
in print_nsepseq "." print_selection value_proj
|
|
||||||
|
|
||||||
and print_selection = function
|
and print_selection = function
|
||||||
Name id -> print_var id
|
FieldName id -> print_var id
|
||||||
| Component {value; _} ->
|
| Component {value; _} ->
|
||||||
let {lpar; inside; rpar} = value in
|
let {lpar; inside; rpar} = value in
|
||||||
let Region.{value=lexeme,z; region} = inside in
|
let Region.{value=lexeme,z; region} = inside in
|
||||||
@ -563,6 +573,7 @@ and print_injection :
|
|||||||
|
|
||||||
and print_opening = function
|
and print_opening = function
|
||||||
Begin region -> print_token region "begin"
|
Begin region -> print_token region "begin"
|
||||||
|
| With region -> print_token region "with"
|
||||||
| LBrace region -> print_token region "{"
|
| LBrace region -> print_token region "{"
|
||||||
| LBracket region -> print_token region "["
|
| LBracket region -> print_token region "["
|
||||||
|
|
||||||
@ -651,7 +662,7 @@ and print_expr undo = function
|
|||||||
ELetIn {value;_} -> print_let_in undo value
|
ELetIn {value;_} -> print_let_in undo value
|
||||||
| ECond cond -> print_conditional undo cond
|
| ECond cond -> print_conditional undo cond
|
||||||
| ETuple {value;_} -> print_csv (print_expr undo) value
|
| ETuple {value;_} -> print_csv (print_expr undo) value
|
||||||
| EMatch {value;_} -> print_match_expr undo value
|
| ECase {value;_} -> print_match_expr undo value
|
||||||
| EFun {value=(kwd_fun,_,_,_) as f; _} as e ->
|
| EFun {value=(kwd_fun,_,_,_) as f; _} as e ->
|
||||||
if undo then
|
if undo then
|
||||||
let patterns, arrow, expr = unparse' e in
|
let patterns, arrow, expr = unparse' e in
|
||||||
@ -666,7 +677,8 @@ and print_expr undo = function
|
|||||||
| EString e -> print_string_expr undo e
|
| EString e -> print_string_expr undo e
|
||||||
|
|
||||||
| ECall {value=e1,e2; _} -> print_expr undo e1; print_expr undo e2
|
| ECall {value=e1,e2; _} -> print_expr undo e1; print_expr undo e2
|
||||||
| Path p -> print_path p
|
| EVar v -> print_var v
|
||||||
|
| EProj p -> print_projection p
|
||||||
| EUnit {value=lpar,rpar; _} ->
|
| EUnit {value=lpar,rpar; _} ->
|
||||||
print_token lpar "("; print_token rpar ")"
|
print_token lpar "("; print_token rpar ")"
|
||||||
| EPar {value={lpar;inside=e;rpar}; _} ->
|
| EPar {value={lpar;inside=e;rpar}; _} ->
|
||||||
@ -749,17 +761,28 @@ and print_field_assignment undo {value; _} =
|
|||||||
|
|
||||||
and print_sequence undo seq = print_injection (print_expr undo) seq
|
and print_sequence undo seq = print_injection (print_expr undo) seq
|
||||||
|
|
||||||
and print_match_expr undo (kwd_match, expr, kwd_with, (_,cases)) =
|
and print_match_expr undo expr =
|
||||||
|
let {kwd_match; expr; opening;
|
||||||
|
lead_vbar; cases; closing} = expr in
|
||||||
print_token kwd_match "match";
|
print_token kwd_match "match";
|
||||||
print_expr undo expr;
|
print_expr undo expr;
|
||||||
print_token kwd_with "with";
|
print_opening opening;
|
||||||
print_bsv (print_case undo) cases;
|
print_token_opt lead_vbar "|";
|
||||||
print_token Region.ghost "end"
|
print_cases undo cases;
|
||||||
|
print_closing closing
|
||||||
|
|
||||||
and print_case undo (pattern, arrow, expr) =
|
and print_token_opt = function
|
||||||
|
None -> fun _ -> ()
|
||||||
|
| Some region -> print_token region
|
||||||
|
|
||||||
|
and print_cases undo {value; _} =
|
||||||
|
print_nsepseq "|" (print_case_clause undo) value
|
||||||
|
|
||||||
|
and print_case_clause undo {value; _} =
|
||||||
|
let {pattern; arrow; rhs} = value in
|
||||||
print_pattern pattern;
|
print_pattern pattern;
|
||||||
print_token arrow "->";
|
print_token arrow "->";
|
||||||
print_expr undo expr
|
print_expr undo rhs
|
||||||
|
|
||||||
and print_let_in undo (kwd_let, let_bindings, kwd_in, expr) =
|
and print_let_in undo (kwd_let, let_bindings, kwd_in, expr) =
|
||||||
print_token kwd_let "let";
|
print_token kwd_let "let";
|
||||||
@ -773,20 +796,14 @@ and print_fun_expr undo (kwd_fun, rvar, arrow, expr) =
|
|||||||
print_token arrow "->";
|
print_token arrow "->";
|
||||||
print_expr undo expr
|
print_expr undo expr
|
||||||
|
|
||||||
and print_conditional undo = function
|
and print_conditional undo {value; _} =
|
||||||
IfThenElse Region.{value=kwd_if, e1, kwd_then, e2, kwd_else, e3; _} ->
|
let open Region in
|
||||||
print_token Region.ghost "(";
|
let {kwd_if; test; kwd_then; ifso; kwd_else; ifnot} = value
|
||||||
|
in print_token ghost "(";
|
||||||
print_token kwd_if "if";
|
print_token kwd_if "if";
|
||||||
print_expr undo e1;
|
print_expr undo test;
|
||||||
print_token kwd_then "then";
|
print_token kwd_then "then";
|
||||||
print_expr undo e2;
|
print_expr undo ifso;
|
||||||
print_token kwd_else "else";
|
print_token kwd_else "else";
|
||||||
print_expr undo e3;
|
print_expr undo ifnot;
|
||||||
print_token Region.ghost ")"
|
print_token ghost ")"
|
||||||
| IfThen Region.{value=kwd_if, e1, kwd_then, e2; _} ->
|
|
||||||
print_token Region.ghost "(";
|
|
||||||
print_token kwd_if "if";
|
|
||||||
print_expr undo e1;
|
|
||||||
print_token kwd_then "then";
|
|
||||||
print_expr undo e2;
|
|
||||||
print_token Region.ghost ")"
|
|
||||||
|
@ -177,23 +177,6 @@ and field_decl = {
|
|||||||
|
|
||||||
and type_tuple = (type_expr, comma) Utils.nsepseq par
|
and type_tuple = (type_expr, comma) Utils.nsepseq par
|
||||||
|
|
||||||
and 'a injection = {
|
|
||||||
opening : opening;
|
|
||||||
elements : ('a, semi) Utils.sepseq;
|
|
||||||
terminator : semi option;
|
|
||||||
closing : closing
|
|
||||||
}
|
|
||||||
|
|
||||||
and opening =
|
|
||||||
Begin of kwd_begin
|
|
||||||
| LBrace of lbrace
|
|
||||||
| LBracket of lbracket
|
|
||||||
|
|
||||||
and closing =
|
|
||||||
End of kwd_end
|
|
||||||
| RBrace of rbrace
|
|
||||||
| RBracket of rbracket
|
|
||||||
|
|
||||||
and pattern =
|
and pattern =
|
||||||
PTuple of (pattern, comma) Utils.nsepseq reg (* p1, p2, ... *)
|
PTuple of (pattern, comma) Utils.nsepseq reg (* p1, p2, ... *)
|
||||||
| PList of list_pattern
|
| PList of list_pattern
|
||||||
@ -228,24 +211,42 @@ and field_pattern = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
and expr =
|
and expr =
|
||||||
ELetIn of let_in reg (* let p1 = e1 and p2 = e2 and ... in e *)
|
ECase of expr case reg (* p1 -> e1 | p2 -> e2 | ... *)
|
||||||
| EFun of fun_expr (* fun x -> e *)
|
|
||||||
| ECond of conditional (* if e1 then e2 else e3 *)
|
|
||||||
| ETuple of (expr, comma) Utils.nsepseq reg (* e1, e2, ... *)
|
|
||||||
| EMatch of match_expr reg (* p1 -> e1 | p2 -> e2 | ... *)
|
|
||||||
| ESeq of sequence (* begin e1; e2; ... ; en end *)
|
|
||||||
| ERecord of record_expr (* {f1=e1; ... } *)
|
|
||||||
|
|
||||||
| ELogic of logic_expr
|
| ELogic of logic_expr
|
||||||
| EArith of arith_expr
|
| EArith of arith_expr
|
||||||
| EString of string_expr
|
| EString of string_expr
|
||||||
| ECall of (expr * expr) reg (* f e *)
|
|
||||||
|
|
||||||
| Path of path reg (* x x.y.z *)
|
|
||||||
| EUnit of the_unit reg (* () *)
|
|
||||||
| EPar of expr par reg (* (e) *)
|
|
||||||
| EList of list_expr
|
| EList of list_expr
|
||||||
| EConstr of constr
|
| EConstr of constr
|
||||||
|
| ERecord of record_expr (* {f1=e1; ... } *)
|
||||||
|
| EProj of projection reg (* x.y.z M.x.y *)
|
||||||
|
| EVar of variable (* x *)
|
||||||
|
| ECall of (expr * expr) reg (* f e *)
|
||||||
|
| EUnit of the_unit reg (* () *)
|
||||||
|
| ETuple of (expr, comma) Utils.nsepseq reg (* e1, e2, ... *)
|
||||||
|
| EPar of expr par reg (* (e) *)
|
||||||
|
|
||||||
|
| ELetIn of let_in reg (* let p1 = e1 and p2 = e2 and ... in e *)
|
||||||
|
| EFun of fun_expr (* fun x -> e *)
|
||||||
|
| ECond of conditional reg (* if e1 then e2 else e3 *)
|
||||||
|
| ESeq of sequence (* begin e1; e2; ... ; en end *)
|
||||||
|
|
||||||
|
and 'a injection = {
|
||||||
|
opening : opening;
|
||||||
|
elements : ('a, semi) Utils.sepseq;
|
||||||
|
terminator : semi option;
|
||||||
|
closing : closing
|
||||||
|
}
|
||||||
|
|
||||||
|
and opening =
|
||||||
|
Begin of kwd_begin
|
||||||
|
| With of kwd_with
|
||||||
|
| LBrace of lbrace
|
||||||
|
| LBracket of lbracket
|
||||||
|
|
||||||
|
and closing =
|
||||||
|
End of kwd_end
|
||||||
|
| RBrace of rbrace
|
||||||
|
| RBracket of rbracket
|
||||||
|
|
||||||
and list_expr =
|
and list_expr =
|
||||||
Cons of cat bin_op reg (* e1 :: e3 *)
|
Cons of cat bin_op reg (* e1 :: e3 *)
|
||||||
@ -296,22 +297,15 @@ and comp_expr =
|
|||||||
| Geq of geq bin_op reg
|
| Geq of geq bin_op reg
|
||||||
| Equal of equal bin_op reg
|
| Equal of equal bin_op reg
|
||||||
| Neq of neq bin_op reg
|
| Neq of neq bin_op reg
|
||||||
(*
|
|
||||||
| Lt of (expr * lt * expr) reg
|
|
||||||
| LEq of (expr * le * expr) reg
|
|
||||||
| Gt of (expr * gt * expr) reg
|
|
||||||
| GEq of (expr * ge * expr) reg
|
|
||||||
| NEq of (expr * ne * expr) reg
|
|
||||||
| Eq of (expr * eq * expr) reg
|
|
||||||
*)
|
|
||||||
|
|
||||||
and path = {
|
and projection = {
|
||||||
module_proj : (constr * dot) option;
|
struct_name : variable;
|
||||||
value_proj : (selection, dot) Utils.nsepseq
|
selector : dot;
|
||||||
|
field_path : (selection, dot) Utils.nsepseq
|
||||||
}
|
}
|
||||||
|
|
||||||
and selection =
|
and selection =
|
||||||
Name of variable
|
FieldName of variable
|
||||||
| Component of (string * Z.t) reg par reg
|
| Component of (string * Z.t) reg par reg
|
||||||
|
|
||||||
and record_expr = field_assignment reg injection reg
|
and record_expr = field_assignment reg injection reg
|
||||||
@ -324,18 +318,33 @@ and field_assignment = {
|
|||||||
|
|
||||||
and sequence = expr injection reg
|
and sequence = expr injection reg
|
||||||
|
|
||||||
and match_expr = kwd_match * expr * kwd_with * cases
|
and 'a case = {
|
||||||
|
kwd_match : kwd_match;
|
||||||
|
expr : expr;
|
||||||
|
opening : opening;
|
||||||
|
lead_vbar : vbar option;
|
||||||
|
cases : ('a case_clause reg, vbar) Utils.nsepseq reg;
|
||||||
|
closing : closing
|
||||||
|
}
|
||||||
|
|
||||||
and cases =
|
and 'a case_clause = {
|
||||||
vbar option * (pattern * arrow * expr, vbar) Utils.nsepseq
|
pattern : pattern;
|
||||||
|
arrow : arrow;
|
||||||
|
rhs : 'a
|
||||||
|
}
|
||||||
|
|
||||||
and let_in = kwd_let * let_bindings * kwd_in * expr
|
and let_in = kwd_let * let_bindings * kwd_in * expr
|
||||||
|
|
||||||
and fun_expr = (kwd_fun * variable * arrow * expr) reg
|
and fun_expr = (kwd_fun * variable * arrow * expr) reg
|
||||||
|
|
||||||
and conditional =
|
and conditional = {
|
||||||
IfThen of (kwd_if * expr * kwd_then * expr) reg
|
kwd_if : kwd_if;
|
||||||
| IfThenElse of (kwd_if * expr * kwd_then * expr * kwd_else * expr) reg
|
test : expr;
|
||||||
|
kwd_then : kwd_then;
|
||||||
|
ifso : expr;
|
||||||
|
kwd_else : kwd_else;
|
||||||
|
ifnot : expr
|
||||||
|
}
|
||||||
|
|
||||||
(* Normalising nodes of the AST so the interpreter is more uniform and
|
(* Normalising nodes of the AST so the interpreter is more uniform and
|
||||||
no source regions are lost in order to enable all manner of
|
no source regions are lost in order to enable all manner of
|
||||||
|
@ -46,6 +46,21 @@ ident : reg(Ident) { $1 }
|
|||||||
constr : reg(Constr) { $1 }
|
constr : reg(Constr) { $1 }
|
||||||
string : reg(Str) { $1 }
|
string : reg(Str) { $1 }
|
||||||
eof : oreg(EOF) { $1 }
|
eof : oreg(EOF) { $1 }
|
||||||
|
vbar : sym(VBAR) { $1 }
|
||||||
|
lpar : sym(LPAR) { $1 }
|
||||||
|
rpar : sym(RPAR) { $1 }
|
||||||
|
lbracket : sym(LBRACKET) { $1 }
|
||||||
|
rbracket : sym(RBRACKET) { $1 }
|
||||||
|
lbrace : sym(LBRACE) { $1 }
|
||||||
|
rbrace : sym(RBRACE) { $1 }
|
||||||
|
comma : sym(COMMA) { $1 }
|
||||||
|
semi : sym(SEMI) { $1 }
|
||||||
|
colon : sym(COLON) { $1 }
|
||||||
|
eq : sym(EQ) { $1 }
|
||||||
|
dot : sym(DOT) { $1 }
|
||||||
|
arrow : sym(ARROW) { $1 }
|
||||||
|
wild : sym(WILD) { $1 }
|
||||||
|
cons : sym(CONS) { $1 }
|
||||||
|
|
||||||
(* The rule [sep_or_term(item,sep)] ("separated or terminated list")
|
(* The rule [sep_or_term(item,sep)] ("separated or terminated list")
|
||||||
parses a non-empty list of items separated by [sep], and optionally
|
parses a non-empty list of items separated by [sep], and optionally
|
||||||
@ -66,9 +81,9 @@ sep_or_term_list(item,sep):
|
|||||||
|
|
||||||
(* Compound constructs *)
|
(* Compound constructs *)
|
||||||
|
|
||||||
par(X): sym(LPAR) X sym(RPAR) { {lpar=$1; inside=$2; rpar=$3} }
|
par(X): lpar X rpar { {lpar=$1; inside=$2; rpar=$3} }
|
||||||
|
|
||||||
brackets(X): sym(LBRACKET) X sym(RBRACKET) {
|
brackets(X): lbracket X rbracket {
|
||||||
{lbracket=$1; inside=$2; rbracket=$3} }
|
{lbracket=$1; inside=$2; rbracket=$3} }
|
||||||
|
|
||||||
(* Sequences
|
(* Sequences
|
||||||
@ -120,12 +135,12 @@ struct_name : Ident { $1 }
|
|||||||
(* Non-empty comma-separated values (at least two values) *)
|
(* Non-empty comma-separated values (at least two values) *)
|
||||||
|
|
||||||
tuple(item):
|
tuple(item):
|
||||||
item sym(COMMA) nsepseq(item,sym(COMMA)) { let h,t = $3 in $1,($2,h)::t }
|
item comma nsepseq(item,comma) { let h,t = $3 in $1,($2,h)::t }
|
||||||
|
|
||||||
(* Possibly empty semicolon-separated values between brackets *)
|
(* Possibly empty semicolon-separated values between brackets *)
|
||||||
|
|
||||||
list_of(item):
|
list_of(item):
|
||||||
sym(LBRACKET) sepseq(item,sym(SEMI)) sym(RBRACKET) {
|
lbracket sepseq(item,semi) rbracket {
|
||||||
{opening = LBracket $1;
|
{opening = LBracket $1;
|
||||||
elements = $2;
|
elements = $2;
|
||||||
terminator = None;
|
terminator = None;
|
||||||
@ -144,7 +159,7 @@ declaration:
|
|||||||
(* Type declarations *)
|
(* Type declarations *)
|
||||||
|
|
||||||
type_decl:
|
type_decl:
|
||||||
kwd(Type) type_name sym(EQ) type_expr {
|
kwd(Type) type_name eq type_expr {
|
||||||
{kwd_type=$1; name=$2; eq=$3; type_expr=$4} }
|
{kwd_type=$1; name=$2; eq=$3; type_expr=$4} }
|
||||||
|
|
||||||
type_expr:
|
type_expr:
|
||||||
@ -160,25 +175,11 @@ fun_type:
|
|||||||
| reg(arrow_type) { TFun $1 }
|
| reg(arrow_type) { TFun $1 }
|
||||||
|
|
||||||
arrow_type:
|
arrow_type:
|
||||||
core_type sym(ARROW) fun_type { $1,$2,$3 }
|
core_type arrow fun_type { $1,$2,$3 }
|
||||||
|
|
||||||
core_type:
|
core_type:
|
||||||
reg(path) {
|
type_projection {
|
||||||
let {module_proj; value_proj} = $1.value in
|
TAlias $1
|
||||||
let selection_to_string = function
|
|
||||||
Name ident -> ident.value
|
|
||||||
| Component {value={inside;_}; _} ->
|
|
||||||
fst inside.value in
|
|
||||||
let module_str =
|
|
||||||
match module_proj with
|
|
||||||
None -> ""
|
|
||||||
| Some (constr,_) -> constr.value ^ "." in
|
|
||||||
let value_str =
|
|
||||||
Utils.nsepseq_to_list value_proj
|
|
||||||
|> List.map selection_to_string
|
|
||||||
|> String.concat "." in
|
|
||||||
let alias = module_str ^ value_str
|
|
||||||
in TAlias {$1 with value=alias}
|
|
||||||
}
|
}
|
||||||
| reg(core_type type_constr {$1,$2}) {
|
| reg(core_type type_constr {$1,$2}) {
|
||||||
let arg, constr = $1.value in
|
let arg, constr = $1.value in
|
||||||
@ -194,6 +195,16 @@ core_type:
|
|||||||
let Region.{region; value={lpar; inside=prod; rpar}} = $1 in
|
let Region.{region; value={lpar; inside=prod; rpar}} = $1 in
|
||||||
TPar Region.{region; value={lpar; inside = TProd prod; rpar}} }
|
TPar Region.{region; value={lpar; inside = TProd prod; rpar}} }
|
||||||
|
|
||||||
|
type_projection:
|
||||||
|
type_name {
|
||||||
|
$1
|
||||||
|
}
|
||||||
|
| reg(module_name dot type_name {$1,$2,$3}) {
|
||||||
|
let open Region in
|
||||||
|
let module_name,_ , type_name = $1.value in
|
||||||
|
let value = module_name.value ^ "." ^ type_name.value
|
||||||
|
in {$1 with value} }
|
||||||
|
|
||||||
type_constr:
|
type_constr:
|
||||||
type_name { $1 }
|
type_name { $1 }
|
||||||
| kwd(Set) { Region.{value="set"; region=$1} }
|
| kwd(Set) { Region.{value="set"; region=$1} }
|
||||||
@ -204,15 +215,14 @@ type_tuple:
|
|||||||
par(tuple(type_expr)) { $1 }
|
par(tuple(type_expr)) { $1 }
|
||||||
|
|
||||||
sum_type:
|
sum_type:
|
||||||
ioption(sym(VBAR)) nsepseq(reg(variant), sym(VBAR)) { $2 }
|
ioption(vbar) nsepseq(reg(variant),vbar) { $2 }
|
||||||
|
|
||||||
variant:
|
variant:
|
||||||
constr kwd(Of) cartesian { {constr=$1; args = Some ($2,$3)} }
|
constr kwd(Of) cartesian { {constr=$1; args = Some ($2,$3)} }
|
||||||
| constr { {constr=$1; args=None} }
|
| constr { {constr=$1; args = None} }
|
||||||
|
|
||||||
record_type:
|
record_type:
|
||||||
sym(LBRACE) sep_or_term_list(reg(field_decl),sym(SEMI))
|
lbrace sep_or_term_list(reg(field_decl),semi) rbrace {
|
||||||
sym(RBRACE) {
|
|
||||||
let elements, terminator = $2 in {
|
let elements, terminator = $2 in {
|
||||||
opening = LBrace $1;
|
opening = LBrace $1;
|
||||||
elements = Some elements;
|
elements = Some elements;
|
||||||
@ -220,7 +230,7 @@ record_type:
|
|||||||
closing = RBrace $3} }
|
closing = RBrace $3} }
|
||||||
|
|
||||||
field_decl:
|
field_decl:
|
||||||
field_name sym(COLON) type_expr {
|
field_name colon type_expr {
|
||||||
{field_name=$1; colon=$2; field_type=$3} }
|
{field_name=$1; colon=$2; field_type=$3} }
|
||||||
|
|
||||||
(* Non-recursive definitions *)
|
(* Non-recursive definitions *)
|
||||||
@ -229,15 +239,15 @@ let_bindings:
|
|||||||
nsepseq(let_binding, kwd(And)) { $1 }
|
nsepseq(let_binding, kwd(And)) { $1 }
|
||||||
|
|
||||||
let_binding:
|
let_binding:
|
||||||
ident nseq(sub_irrefutable) option(type_annotation) sym(EQ) expr {
|
ident nseq(sub_irrefutable) type_annotation? eq expr {
|
||||||
let let_rhs = EFun (norm $2 $4 $5) in
|
let let_rhs = EFun (norm $2 $4 $5) in
|
||||||
{pattern = PVar $1; lhs_type=$3; eq = Region.ghost; let_rhs}
|
{pattern = PVar $1; lhs_type=$3; eq = Region.ghost; let_rhs}
|
||||||
}
|
}
|
||||||
| irrefutable option(type_annotation) sym(EQ) expr {
|
| irrefutable type_annotation? eq expr {
|
||||||
{pattern=$1; lhs_type=$2; eq=$3; let_rhs=$4} }
|
{pattern=$1; lhs_type=$2; eq=$3; let_rhs=$4} }
|
||||||
|
|
||||||
type_annotation:
|
type_annotation:
|
||||||
sym(COLON) type_expr { $1,$2 }
|
colon type_expr { $1,$2 }
|
||||||
|
|
||||||
(* Patterns *)
|
(* Patterns *)
|
||||||
|
|
||||||
@ -247,7 +257,7 @@ irrefutable:
|
|||||||
|
|
||||||
sub_irrefutable:
|
sub_irrefutable:
|
||||||
ident { PVar $1 }
|
ident { PVar $1 }
|
||||||
| sym(WILD) { PWild $1 }
|
| wild { PWild $1 }
|
||||||
| unit { PUnit $1 }
|
| unit { PUnit $1 }
|
||||||
| reg(par(closed_irrefutable)) { PPar $1 }
|
| reg(par(closed_irrefutable)) { PPar $1 }
|
||||||
|
|
||||||
@ -258,11 +268,10 @@ closed_irrefutable:
|
|||||||
| reg(typed_pattern) { PTyped $1 }
|
| reg(typed_pattern) { PTyped $1 }
|
||||||
|
|
||||||
typed_pattern:
|
typed_pattern:
|
||||||
irrefutable sym(COLON) type_expr {
|
irrefutable colon type_expr { {pattern=$1; colon=$2; type_expr=$3} }
|
||||||
{pattern=$1; colon=$2; type_expr=$3} }
|
|
||||||
|
|
||||||
pattern:
|
pattern:
|
||||||
reg(sub_pattern sym(CONS) tail {$1,$2,$3}) { PList (PCons $1) }
|
reg(sub_pattern cons tail {$1,$2,$3}) { PList (PCons $1) }
|
||||||
| reg(tuple(sub_pattern)) { PTuple $1 }
|
| reg(tuple(sub_pattern)) { PTuple $1 }
|
||||||
| core_pattern { $1 }
|
| core_pattern { $1 }
|
||||||
|
|
||||||
@ -272,7 +281,7 @@ sub_pattern:
|
|||||||
|
|
||||||
core_pattern:
|
core_pattern:
|
||||||
ident { PVar $1 }
|
ident { PVar $1 }
|
||||||
| sym(WILD) { PWild $1 }
|
| wild { PWild $1 }
|
||||||
| unit { PUnit $1 }
|
| unit { PUnit $1 }
|
||||||
| reg(Int) { PInt $1 }
|
| reg(Int) { PInt $1 }
|
||||||
| kwd(True) { PTrue $1 }
|
| kwd(True) { PTrue $1 }
|
||||||
@ -284,9 +293,7 @@ core_pattern:
|
|||||||
| reg(record_pattern) { PRecord $1 }
|
| reg(record_pattern) { PRecord $1 }
|
||||||
|
|
||||||
record_pattern:
|
record_pattern:
|
||||||
sym(LBRACE)
|
lbrace sep_or_term_list(reg(field_pattern),semi) rbrace {
|
||||||
sep_or_term_list(reg(field_pattern),sym(SEMI))
|
|
||||||
sym(RBRACE) {
|
|
||||||
let elements, terminator = $2 in
|
let elements, terminator = $2 in
|
||||||
{opening = LBrace $1;
|
{opening = LBrace $1;
|
||||||
elements = Some elements;
|
elements = Some elements;
|
||||||
@ -294,7 +301,7 @@ record_pattern:
|
|||||||
closing = RBrace $3} }
|
closing = RBrace $3} }
|
||||||
|
|
||||||
field_pattern:
|
field_pattern:
|
||||||
field_name sym(EQ) sub_pattern {
|
field_name eq sub_pattern {
|
||||||
{field_name=$1; eq=$2; pattern=$3} }
|
{field_name=$1; eq=$2; pattern=$3} }
|
||||||
|
|
||||||
constr_pattern:
|
constr_pattern:
|
||||||
@ -305,17 +312,17 @@ ptuple:
|
|||||||
reg(tuple(tail)) { PTuple $1 }
|
reg(tuple(tail)) { PTuple $1 }
|
||||||
|
|
||||||
unit:
|
unit:
|
||||||
reg(sym(LPAR) sym(RPAR) {$1,$2}) { $1 }
|
reg(lpar rpar {$1,$2}) { $1 }
|
||||||
|
|
||||||
tail:
|
tail:
|
||||||
reg(sub_pattern sym(CONS) tail {$1,$2,$3}) { PList (PCons $1) }
|
reg(sub_pattern cons tail {$1,$2,$3}) { PList (PCons $1) }
|
||||||
| sub_pattern { $1 }
|
| sub_pattern { $1 }
|
||||||
|
|
||||||
(* Expressions *)
|
(* Expressions *)
|
||||||
|
|
||||||
expr:
|
expr:
|
||||||
base_cond__open(expr) { $1 }
|
base_cond__open(expr) { $1 }
|
||||||
| match_expr(base_cond) { EMatch $1 }
|
| reg(match_expr(base_cond)) { ECase $1 }
|
||||||
|
|
||||||
base_cond__open(x):
|
base_cond__open(x):
|
||||||
base_expr(x)
|
base_expr(x)
|
||||||
@ -331,57 +338,63 @@ base_expr(right_expr):
|
|||||||
| reg(tuple(disj_expr_level)) { ETuple $1 }
|
| reg(tuple(disj_expr_level)) { ETuple $1 }
|
||||||
|
|
||||||
conditional(right_expr):
|
conditional(right_expr):
|
||||||
if_then_else(right_expr)
|
reg(if_then_else(right_expr))
|
||||||
| if_then(right_expr) { ECond $1 }
|
| reg(if_then(right_expr)) { ECond $1 }
|
||||||
|
|
||||||
if_then(right_expr):
|
if_then(right_expr):
|
||||||
reg(kwd(If) expr kwd(Then) right_expr {$1,$2,$3,$4}) { IfThen $1 }
|
kwd(If) expr kwd(Then) right_expr {
|
||||||
|
let open Region in
|
||||||
|
let the_unit = ghost, ghost in
|
||||||
|
let ifnot = EUnit {region=ghost; value=the_unit} in
|
||||||
|
{kwd_if=$1; test=$2; kwd_then=$3; ifso=$4;
|
||||||
|
kwd_else=Region.ghost; ifnot} }
|
||||||
|
|
||||||
if_then_else(right_expr):
|
if_then_else(right_expr):
|
||||||
reg(kwd(If) expr kwd(Then) closed_if kwd(Else) right_expr {
|
kwd(If) expr kwd(Then) closed_if kwd(Else) right_expr {
|
||||||
$1,$2,$3,$4,$5,$6 }) { IfThenElse $1 }
|
{kwd_if=$1; test=$2; kwd_then=$3; ifso=$4;
|
||||||
|
kwd_else=$5; ifnot = $6} }
|
||||||
|
|
||||||
base_if_then_else__open(x):
|
base_if_then_else__open(x):
|
||||||
base_expr(x) { $1 }
|
base_expr(x) { $1 }
|
||||||
| if_then_else(x) { ECond $1 }
|
| reg(if_then_else(x)) { ECond $1 }
|
||||||
|
|
||||||
base_if_then_else:
|
base_if_then_else:
|
||||||
base_if_then_else__open(base_if_then_else) { $1 }
|
base_if_then_else__open(base_if_then_else) { $1 }
|
||||||
|
|
||||||
closed_if:
|
closed_if:
|
||||||
base_if_then_else__open(closed_if) { $1 }
|
base_if_then_else__open(closed_if) { $1 }
|
||||||
| match_expr(base_if_then_else) { EMatch $1 }
|
| reg(match_expr(base_if_then_else)) { ECase $1 }
|
||||||
|
|
||||||
match_expr(right_expr):
|
match_expr(right_expr):
|
||||||
reg(kwd(Match) expr kwd(With)
|
kwd(Match) expr kwd(With) vbar? reg(cases(right_expr)) {
|
||||||
option(sym(VBAR)) cases(right_expr) {
|
let cases = Utils.nsepseq_rev $5.value in
|
||||||
$1,$2,$3, ($4, Utils.nsepseq_rev $5) })
|
{kwd_match = $1; expr = $2; opening = With $3;
|
||||||
| reg(match_nat(right_expr)) { $1 }
|
lead_vbar = $4; cases = {$5 with value=cases};
|
||||||
|
closing = End Region.ghost}
|
||||||
match_nat(right_expr):
|
}
|
||||||
kwd(MatchNat) expr kwd(With)
|
| kwd(MatchNat) expr kwd(With) vbar? reg(cases(right_expr)) {
|
||||||
option(sym(VBAR)) cases(right_expr) {
|
|
||||||
let open Region in
|
let open Region in
|
||||||
let cast_name = Name {region=ghost; value="assert_pos"} in
|
let cases = Utils.nsepseq_rev $5.value in
|
||||||
let cast_path = {module_proj=None; value_proj=cast_name,[]} in
|
let cast = EVar {region=ghost; value="assert_pos"} in
|
||||||
let cast_fun = Path {region=ghost; value=cast_path} in
|
let cast = ECall {region=ghost; value=cast,$2} in
|
||||||
let cast = ECall {region=ghost; value=cast_fun,$2}
|
{kwd_match = $1; expr = cast; opening = With $3;
|
||||||
in $1, cast, $3, ($4, Utils.nsepseq_rev $5) }
|
lead_vbar = $4; cases = {$5 with value=cases};
|
||||||
|
closing = End Region.ghost} }
|
||||||
|
|
||||||
cases(right_expr):
|
cases(right_expr):
|
||||||
case(right_expr) { $1, [] }
|
reg(case_clause(right_expr)) { $1, [] }
|
||||||
| cases(base_cond) sym(VBAR) case(right_expr) {
|
| cases(base_cond) vbar reg(case_clause(right_expr)) {
|
||||||
let h,t = $1 in $3, ($2,h)::t }
|
let h,t = $1 in $3, ($2,h)::t }
|
||||||
|
|
||||||
case(right_expr):
|
case_clause(right_expr):
|
||||||
pattern sym(ARROW) right_expr { $1,$2,$3 }
|
pattern arrow right_expr { {pattern=$1; arrow=$2; rhs=$3} }
|
||||||
|
|
||||||
let_expr(right_expr):
|
let_expr(right_expr):
|
||||||
reg(kwd(Let) let_bindings kwd(In) right_expr {$1,$2,$3,$4}) {
|
reg(kwd(Let) let_bindings kwd(In) right_expr {$1,$2,$3,$4}) {
|
||||||
ELetIn $1 }
|
ELetIn $1 }
|
||||||
|
|
||||||
fun_expr(right_expr):
|
fun_expr(right_expr):
|
||||||
reg(kwd(Fun) nseq(irrefutable) sym(ARROW) right_expr {$1,$2,$3,$4}) {
|
reg(kwd(Fun) nseq(irrefutable) arrow right_expr {$1,$2,$3,$4}) {
|
||||||
let Region.{region; value = kwd_fun, patterns, arrow, expr} = $1
|
let Region.{region; value = kwd_fun, patterns, arrow, expr} = $1
|
||||||
in EFun (norm ~reg:(region, kwd_fun) patterns arrow expr) }
|
in EFun (norm ~reg:(region, kwd_fun) patterns arrow expr) }
|
||||||
|
|
||||||
@ -428,7 +441,7 @@ ge_expr:
|
|||||||
bin_op(comp_expr_level, sym(GE), cat_expr_level) { $1 }
|
bin_op(comp_expr_level, sym(GE), cat_expr_level) { $1 }
|
||||||
|
|
||||||
eq_expr:
|
eq_expr:
|
||||||
bin_op(comp_expr_level, sym(EQ), cat_expr_level) { $1 }
|
bin_op(comp_expr_level, eq, cat_expr_level) { $1 }
|
||||||
|
|
||||||
ne_expr:
|
ne_expr:
|
||||||
bin_op(comp_expr_level, sym(NE), cat_expr_level) { $1 }
|
bin_op(comp_expr_level, sym(NE), cat_expr_level) { $1 }
|
||||||
@ -449,7 +462,7 @@ cons_expr_level:
|
|||||||
| add_expr_level { $1 }
|
| add_expr_level { $1 }
|
||||||
|
|
||||||
cons_expr:
|
cons_expr:
|
||||||
bin_op(add_expr_level, sym(CONS), cons_expr_level) { $1 }
|
bin_op(add_expr_level, cons, cons_expr_level) { $1 }
|
||||||
|
|
||||||
add_expr_level:
|
add_expr_level:
|
||||||
reg(plus_expr) { EArith (Add $1) }
|
reg(plus_expr) { EArith (Add $1) }
|
||||||
@ -499,37 +512,39 @@ core_expr:
|
|||||||
reg(Int) { EArith (Int $1) }
|
reg(Int) { EArith (Int $1) }
|
||||||
| reg(Mtz) { EArith (Mtz $1) }
|
| reg(Mtz) { EArith (Mtz $1) }
|
||||||
| reg(Nat) { EArith (Nat $1) }
|
| reg(Nat) { EArith (Nat $1) }
|
||||||
| reg(path) { Path $1 }
|
| ident | reg(module_field) { EVar $1 }
|
||||||
|
| reg(projection) { EProj $1 }
|
||||||
| string { EString (String $1) }
|
| string { EString (String $1) }
|
||||||
| unit { EUnit $1 }
|
| unit { EUnit $1 }
|
||||||
| kwd(False) { ELogic (BoolExpr (False $1)) }
|
| kwd(False) { ELogic (BoolExpr (False $1)) }
|
||||||
| kwd(True) { ELogic (BoolExpr ( True $1)) }
|
| kwd(True) { ELogic (BoolExpr (True $1)) }
|
||||||
| reg(list_of(expr)) { EList (List $1) }
|
| reg(list_of(expr)) { EList (List $1) }
|
||||||
| reg(par(expr)) { EPar $1 }
|
| reg(par(expr)) { EPar $1 }
|
||||||
| constr { EConstr $1 }
|
| constr { EConstr $1 }
|
||||||
| reg(sequence) { ESeq $1 }
|
| reg(sequence) { ESeq $1 }
|
||||||
| reg(record_expr) { ERecord $1 }
|
| reg(record_expr) { ERecord $1 }
|
||||||
|
|
||||||
path:
|
module_field:
|
||||||
reg(struct_name) sym(DOT) nsepseq(selection,sym(DOT)) {
|
module_name dot field_name { $1.value ^ "." ^ $3.value }
|
||||||
let head, tail = $3 in
|
|
||||||
let seq = Name $1, ($2,head)::tail
|
projection:
|
||||||
in {module_proj=None; value_proj=seq}
|
reg(struct_name) dot nsepseq(selection,dot) {
|
||||||
|
{struct_name = $1; selector = $2; field_path = $3}
|
||||||
}
|
}
|
||||||
| module_name sym(DOT) nsepseq(selection,sym(DOT)) {
|
| reg(module_name dot field_name {$1,$3})
|
||||||
{module_proj = Some ($1,$2); value_proj=$3}
|
dot nsepseq(selection,dot) {
|
||||||
}
|
let open Region in
|
||||||
| ident {
|
let module_name, field_name = $1.value in
|
||||||
{module_proj = None; value_proj = Name $1, []} }
|
let value = module_name.value ^ "." ^ field_name.value in
|
||||||
|
let struct_name = {$1 with value} in
|
||||||
|
{struct_name; selector = $2; field_path = $3} }
|
||||||
|
|
||||||
selection:
|
selection:
|
||||||
ident { Name $1 }
|
field_name { FieldName $1 }
|
||||||
| reg(par(reg(Int))) { Component $1 }
|
| reg(par(reg(Int))) { Component $1 }
|
||||||
|
|
||||||
record_expr:
|
record_expr:
|
||||||
sym(LBRACE)
|
lbrace sep_or_term_list(reg(field_assignment),semi) rbrace {
|
||||||
sep_or_term_list(reg(field_assignment),sym(SEMI))
|
|
||||||
sym(RBRACE) {
|
|
||||||
let elements, terminator = $2 in
|
let elements, terminator = $2 in
|
||||||
{opening = LBrace $1;
|
{opening = LBrace $1;
|
||||||
elements = Some elements;
|
elements = Some elements;
|
||||||
@ -537,11 +552,11 @@ record_expr:
|
|||||||
closing = RBrace $3} }
|
closing = RBrace $3} }
|
||||||
|
|
||||||
field_assignment:
|
field_assignment:
|
||||||
field_name sym(EQ) expr {
|
field_name eq expr {
|
||||||
{field_name=$1; assignment=$2; field_expr=$3} }
|
{field_name=$1; assignment=$2; field_expr=$3} }
|
||||||
|
|
||||||
sequence:
|
sequence:
|
||||||
kwd(Begin) sep_or_term_list(expr,sym(SEMI)) kwd(End) {
|
kwd(Begin) sep_or_term_list(expr,semi) kwd(End) {
|
||||||
let elements, terminator = $2 in
|
let elements, terminator = $2 in
|
||||||
{opening = Begin $1;
|
{opening = Begin $1;
|
||||||
elements = Some elements;
|
elements = Some elements;
|
||||||
|
Loading…
Reference in New Issue
Block a user