blob: f768a128dad4752005150f1b78dde0819940486a [file] [log] [blame] [edit]
%{
open Util
open Source
open El.Ast
open Xl
(* Errors *)
let error at msg = Error.error at "syntax" msg
(* Position handling *)
let position_to_pos position =
{ file = position.Lexing.pos_fname;
line = position.Lexing.pos_lnum;
column = position.Lexing.pos_cnum - position.Lexing.pos_bol
}
let positions_to_region position1 position2 =
{ left = position_to_pos position1;
right = position_to_pos position2
}
let at (l, r) = positions_to_region l r
let ($) it pos = it $ at pos
let ($$) it pos = it $$ at pos % Atom.info ""
(* Conversions *)
let as_seq_typ typ =
match typ.it with
| SeqT (_::_::_ as typs) -> typs
| _ -> [typ]
let as_seq_exp exp =
match exp.it with
| SeqE (_::_::_ as exps) -> exps
| _ -> [exp]
let as_seq_sym sym =
match sym.it with
| SeqG (_::_::_ as syms) -> syms
| _ -> [Elem sym]
let as_alt_sym sym =
match sym.it with
| AltG (_::_::_ as syms) -> syms
| _ -> [Elem sym]
(* Identifiers *)
let check_varid_bind id =
if id.it = (El.Convert.strip_var_suffix id).it then id else
error id.at "invalid identifer suffix in binding position"
(* Classifications *)
let is_post_exp e =
match e.it with
| VarE _ | AtomE _
| BoolE _ | NumE _
| EpsE
| ParenE _ | TupE _ | BrackE _
| ListE _ | IdxE _ | SliceE _ | ExtE _
| StrE _ | DotE _
| IterE _ | CvtE _ | CallE _
| HoleE _ -> true
| _ -> false
let is_atom t =
match t.it with
| AtomT _ -> true
| _ -> false
let is_typfield t =
match t.it with
| SeqT [t1; _] -> is_atom t1
| VarT _ | BoolT | NumT _ | TextT | TupT _ | SeqT _
| AtomT _ | InfixT _ | BrackT _
| ParenT _ | IterT _ -> false
| StrT _ | CaseT _ | ConT _ | RangeT _ -> assert false
let rec is_typcase t =
match t.it with
| AtomT _ | InfixT _ | BrackT _ -> true
| SeqT (t'::_) -> is_typcase t'
| VarT _ | BoolT | NumT _ | TextT | TupT _ | SeqT _
| ParenT _ | IterT _ -> false
| StrT _ | CaseT _ | ConT _ | RangeT _ -> assert false
let rec is_typcon t =
match t.it with
| AtomT _ | InfixT _ | BrackT _ | SeqT _ -> true
| VarT _ | BoolT | NumT _ | TextT | TupT _ | ParenT _ -> false
| IterT (t1, _) -> is_typcon t1
| StrT _ | CaseT _ | ConT _ | RangeT _ -> assert false
(* Helpers for grammar productions *)
let rec alt_sym = function
| [] -> assert false
| Nl::alts -> alt_sym alts
| (Elem g)::alts when List.for_all ((=) Nl) alts -> g
| alts ->
let open Source in
AltG alts $ over_region (El.Convert.map_filter_nl_list Source.at alts)
let long_prod (g, e, prems) =
let open Source in
let ats = g.at :: e.at :: El.Convert.map_filter_nl_list Source.at prems in
Elem (SynthP (g, e, prems) $ over_region ats)
let equiv_prod (g1, g2, prems) =
let open Source in
let ats = g1.at :: g2.at :: El.Convert.map_filter_nl_list Source.at prems in
Elem (EquivP (g1, g2, prems) $ over_region ats)
let short_prod (g, prems) =
let open Source in
let var () = VarE ("<implicit-prod-result>" $ g.at, []) $ g.at in
let ats = g.at :: El.Convert.map_filter_nl_list Source.at prems in
Elem (SynthP (AttrG (var (), g) $ g.at, var (), prems) $ over_region ats)
let rec long_alt_prod (els, e, prems) = long_alt_prod' (List.rev els, e, prems)
and long_alt_prod' = function
| ([], _, _) -> assert false
| (Nl::elsr, e, []) -> long_alt_prod' (elsr, e, []) @ [Nl]
| (Nl::elsr, e, prems) -> long_alt_prod' (elsr, e, prems)
| ((Elem g)::elsr, e, prems) when List.for_all ((=) Nl) elsr ->
[long_prod (g, e, prems)]
| (elsr, e, prems) ->
let open Source in
let ats = El.Convert.map_filter_nl_list Source.at elsr in
[long_prod (AltG (List.rev elsr) $ over_region ats, e, prems)]
let rec long_equiv_prod (alts, g2, prems) = long_equiv_prod' (List.rev alts, g2, prems)
and long_equiv_prod' = function
| ([], _, _) -> assert false
| (Nl::elsr, g2, []) -> long_equiv_prod' (elsr, g2, []) @ [Nl]
| (Nl::elsr, g2, prems) -> long_equiv_prod' (elsr, g2, prems)
| ((Elem g1)::elsr, g2, prems) when List.for_all ((=) Nl) elsr ->
[equiv_prod (g1, g2, prems)]
| (elsr, g2, prems) ->
let open Source in
let ats = El.Convert.map_filter_nl_list Source.at elsr in
[equiv_prod (AltG (List.rev elsr) $ over_region ats, g2, prems)]
let rec short_alt_prod (els, prems) = short_alt_prod' (List.rev els, prems)
and short_alt_prod' = function
| ([], []) -> []
| ([], _) -> assert false
| (Nl::elsr, []) -> short_alt_prod' (elsr, []) @ [Nl]
| (Nl::elsr, prems) -> short_alt_prod' (elsr, prems)
| ((Elem g)::elsr, prems) ->
List.rev_map (function Nl -> Nl | Elem g -> short_prod (g, [])) elsr
@ [short_prod (g, prems)]
%}
%token LPAREN RPAREN LBRACK RBRACK LBRACE RBRACE
%token COLON SEMICOLON COMMA DOT DOTDOT DOTDOTDOT BAR BARBAR DASH COLONSUB
%token BIGAND BIGOR BIGADD BIGMUL BIGCAT
%token COMMA_NL NL_BAR NL_NL NL_NL_NL
%token EQ NE LT GT LE GE APPROX EQUIV ASSIGN SUB SUP EQCAT EQSUB EQUIVSUB APPROXSUB
%token NOT AND OR
%token QUEST PLUS MINUS STAR SLASH BACKSLASH UP CAT PLUSMINUS MINUSPLUS
%token ARROW ARROW2 ARROWSUB ARROW2SUB DARROW2 SQARROW SQARROWSUB SQARROWSTAR SQARROWSTARSUB
%token MEM NOTMEM PREC SUCC TURNSTILE TILESTURN TURNSTILESUB TILESTURNSUB
%token DOLLAR TICK
%token BOT TOP
%token HOLE MULTIHOLE NOTHING FUSE FUSEFUSE LATEX
%token<int> HOLEN
%token BOOL NAT INT RAT REAL TEXT
%token SYNTAX GRAMMAR RELATION RULE VAR DEF
%token IF OTHERWISE HINT_LPAREN
%token EPS INFINITY
%token<bool> BOOLLIT
%token<Z.t> NATLIT HEXLIT CHARLIT
%token<string> TEXTLIT
%token<string> UPID LOID DOTID UPID_LPAREN LOID_LPAREN
%token EOF
%right ARROW2 DARROW2 ARROW2SUB
%left OR
%left AND
%nonassoc TURNSTILE TURNSTILESUB
%nonassoc TILESTURN TILESTURNSUB
%right SQARROW SQARROWSUB SQARROWSTAR SQARROWSTARSUB PREC SUCC BIGAND BIGOR BIGADD BIGMUL BIGCAT
%left COLON SUB SUP ASSIGN EQUIV APPROX COLONSUB EQUIVSUB APPROXSUB
%left COMMA COMMA_NL
%right EQ NE LT GT LE GE MEM NOTMEM EQSUB
%right ARROW ARROWSUB
%left SEMICOLON
%left DOT DOTDOT DOTDOTDOT
%left PLUS MINUS CAT
%left STAR SLASH BACKSLASH
%start script typ_eof exp_eof sym_eof check_atom
%type<El.Ast.script> script
%type<El.Ast.typ> typ_eof
%type<El.Ast.exp> exp_eof
%type<El.Ast.sym> sym_eof
%type<bool> check_atom
%%
(* Lists *)
%inline bar(X) : (* X is dummy to enforce polymorphism *)
| BAR { [] }
| NL_BAR { [Nl] }
%inline comma(X) : (* X is dummy to enforce polymorphism *)
| COMMA { [] }
| COMMA_NL { [Nl] }
comma_list(X) :
| (* empty *) { [] }
| X { $1::[] }
| X comma(X) comma_list(X) { $1::$3 }
comma_nl_list(X) :
| (* empty *) { [] }
| X { (Elem $1)::[] }
| X comma(X) comma_nl_list(X) { (Elem $1)::$2 @ $3 }
nl_bar_list(X) : nl_bar_list1(X, X) { $1 }
nl_bar_list1(X, Y) :
| X { Elem $1 :: [] }
| X bar(X) nl_bar_list(Y) { (Elem $1)::$2 @ $3 }
nl_dash_list(X) :
| (* empty *) { [] }
| nl_dash_list1(X) { $1 }
nl_dash_list1(X) :
| DASH DASH nl_dash_list(X) { Nl::$3 }
| DASH X nl_dash_list(X) { (Elem $2)::$3 }
%inline bar_dots :
| DOTDOTDOT {}
| bar(bar_dots) DOTDOTDOT {}
dots_bar_list(X) :
| dots_bar_list1(X) { let x, y = $1 in (NoDots, x, y) }
| bar(X) dots_bar_list1(X) { let x, y = $2 in (NoDots, x, y) }
| bar_dots bar(X) dots_bar_list1(X) { let x, y = $3 in (Dots, $2 @ x, y) }
dots_bar_list1(X) :
| (* empty *) { [], NoDots }
| DOTDOTDOT { [], Dots }
| X { (Elem $1)::[], NoDots }
| X bar(X) dots_bar_list1(X) { let x, y = $3 in (Elem $1)::$2 @ x, y }
dots_comma_list(X) :
| dots_comma_list1(X) { let x, y = $1 in (NoDots, x, y) }
| DOTDOTDOT comma(X) dots_comma_list1(X) { let x, y = $3 in (Dots, $2 @ x, y) }
dots_comma_list1(X) :
| (* empty *) { [], NoDots }
| DOTDOTDOT { [], Dots }
| X { (Elem $1)::[], NoDots }
| X comma(X) dots_comma_list1(X) { let x, y = $3 in (Elem $1)::$2 @ x, y }
(* Identifiers *)
id : UPID { $1 } | LOID { $1 }
id_lparen : UPID_LPAREN { $1 } | LOID_LPAREN { $1 }
atomid_ : UPID { $1 }
varid : LOID { $1 $ $sloc }
defid : id { $1 $ $sloc } | IF { "if" $ $sloc }
relid : id { $1 $ $sloc }
gramid : id { $1 $ $sloc }
hintid : id { $1 }
fieldid : atomid_ { Atom.Atom $1 $$ $sloc } | atom_escape { $1 $$ $sloc }
dotid : DOTID { Atom.Atom $1 $$ $sloc }
atomid_lparen : UPID_LPAREN { $1 }
varid_lparen : LOID_LPAREN { $1 $ $sloc }
defid_lparen : id_lparen { $1 $ $sloc }
gramid_lparen : id_lparen { $1 $ $sloc }
ruleid : ruleid_ { $1 }
ruleid_ :
| id { $1 }
| NATLIT { Z.to_string $1 }
| BOOLLIT { Bool.to_string $1 }
| INFINITY { "infinity" }
| EPS { "eps" }
| IF { "if" }
| VAR { "var" }
| DEF { "def" }
| RULE { "rule" }
| RELATION { "relation" }
| SYNTAX { "syntax" }
| GRAMMAR { "grammar" }
| ruleid_ DOTID { $1 ^ "." ^ $2 }
atomid : atomid_ { $1 } | atomid DOTID { $1 ^ "." ^ $2 }
atom :
| atom_ { $1 $$ $sloc }
atom_ :
| atomid { Atom.Atom $1 }
| atom_escape { $1 }
atom_escape :
| TICK EQ { Atom.Equal }
| TICK NE { Atom.NotEqual }
| TICK LT { Atom.Less }
| TICK GT { Atom.Greater }
| TICK LE { Atom.LessEqual }
| TICK GE { Atom.GreaterEqual }
| TICK MEM { Atom.Mem }
| TICK NOTMEM { Atom.NotMem }
| TICK QUEST { Atom.Quest }
| TICK PLUS { Atom.Plus }
| TICK STAR { Atom.Star }
| TICK BAR { Atom.Bar }
| TICK CAT { Atom.Cat }
| TICK COMMA { Atom.Comma }
| TICK ARROW2 { Atom.Arrow2 }
| TICK infixop_ { $2 }
| TICK relop_ { $2 }
| BOT { Atom.Bot }
| TOP { Atom.Top }
| INFINITY { Atom.Infinity }
varid_bind_with_suffix :
| varid { $1 }
| atomid_ { Id.make_var $1; $1 $ $sloc }
varid_bind :
| varid_bind_with_suffix { check_varid_bind $1 }
varid_bind_lparen :
| varid_lparen { check_varid_bind $1 }
| atomid_lparen { Id.make_var $1; check_varid_bind ($1 $ $sloc) }
enter_scope :
| (* empty *) { Id.enter_scope () }
exit_scope :
| (* empty *) { Id.exit_scope () }
check_atom :
| UPID EOF { Id.is_var (El.Convert.strip_var_suffix ($1 $ $sloc)).it }
(* Operators *)
%inline unop :
| NOT { `NotOp }
| PLUS { `PlusOp }
| MINUS { `MinusOp }
| PLUSMINUS { `PlusMinusOp }
| MINUSPLUS { `MinusPlusOp }
%inline binop :
| PLUS { `AddOp }
| MINUS { `SubOp }
| STAR { `MulOp }
| SLASH { `DivOp }
| BACKSLASH { `ModOp }
%inline cmpop :
| EQ { `EqOp }
| NE { `NeOp }
| LT { `LtOp }
| GT { `GtOp }
| LE { `LeOp }
| GE { `GeOp }
%inline boolop :
| AND { `AndOp }
| OR { `OrOp }
| ARROW2 { `ImplOp }
| DARROW2 { `EquivOp }
%inline infixop :
| infixop_ { $1 $$ $sloc }
%inline infixop_ :
| DOT { Atom.Dot }
| DOTDOT { Atom.Dot2 }
| DOTDOTDOT { Atom.Dot3 }
| SEMICOLON { Atom.Semicolon }
| BACKSLASH { Atom.Backslash }
| ARROW { Atom.Arrow }
| ARROWSUB { Atom.ArrowSub }
| ARROW2SUB { Atom.Arrow2Sub }
| BIGAND { Atom.BigAnd }
| BIGOR { Atom.BigOr }
| BIGADD { Atom.BigAdd }
| BIGMUL { Atom.BigMul }
| BIGCAT { Atom.BigCat }
%inline relop :
| relop_ { $1 $$ $sloc }
%inline relop_ :
| EQSUB { Atom.EqualSub }
| COLON { Atom.Colon }
| COLONSUB { Atom.ColonSub }
| SUB { Atom.Sub }
| SUP { Atom.Sup }
| ASSIGN { Atom.Assign }
| EQUIV { Atom.Equiv }
| EQUIVSUB { Atom.EquivSub }
| APPROX { Atom.Approx }
| APPROXSUB { Atom.ApproxSub }
| SQARROW { Atom.SqArrow }
| SQARROWSUB { Atom.SqArrowSub }
| SQARROWSTAR { Atom.SqArrowStar }
| SQARROWSTARSUB { Atom.SqArrowStarSub }
| PREC { Atom.Prec }
| SUCC { Atom.Succ }
| TILESTURN { Atom.Tilesturn }
| TILESTURNSUB { Atom.TilesturnSub }
| TURNSTILE { Atom.Turnstile }
| TURNSTILESUB { Atom.TurnstileSub }
(* Iteration *)
iter :
| QUEST { Opt }
| PLUS { List1 }
| STAR { List }
| UP arith_prim
{ match $2.it with
| ParenE {it = CmpE ({it = VarE (id, []); _}, `LtOp, e); _} ->
ListN (e, Some id)
| _ -> ListN ($2, None)
}
(* Types *)
numtyp :
| NAT { `NatT }
| INT { `IntT }
| RAT { `RatT }
| REAL { `RealT }
(*typ_prim : typ_prim_ { $1 $ $sloc }*)
typ_prim_ :
| varid { VarT ($1, []) }
| varid_lparen comma_list(arg) RPAREN { VarT ($1, $2) }
| BOOL { BoolT }
| numtyp { NumT $1 }
| TEXT { TextT }
typ_post : typ_post_ { $1 $ $sloc }
typ_post_ :
| typ_prim_ { $1 }
| LPAREN comma_list(typ) RPAREN
{ match $2 with
| [] -> ParenT (SeqT [] $ $sloc)
| [t] -> ParenT t
| ts -> TupT ts }
| typ_post iter { IterT ($1, $2) }
typ : typ_post { $1 }
deftyp : deftyp_ { $1 $ $sloc }
deftyp_ :
| LBRACE dots_comma_list(fieldtyp) RBRACE
{ let dots1, tfs, dots2 = $2 in
match dots1, El.Convert.filter_nl tfs, dots2 with
| NoDots, [(t, prems, hints)], NoDots when not (is_typfield t) ->
if prems <> [] then
error t.at "misplaced premise"
else if hints <> [] then
error (List.hd hints).hintid.at "misplaced hint"
else
t.it
| _ ->
let y1, y2, _ =
List.fold_right
(fun elem (y1, y2, at) ->
(* at is the position of leftmost id element so far *)
match elem with
| Nl -> if at = None then y1, Nl::y2, at else Nl::y1, y2, at
| Elem (t, prems, hints) ->
match t.it with
| SeqT [{it = AtomT atom; _}; t2] when at = None ->
y1, (Elem (atom, (t2, prems), hints))::y2, None
| AtomT _ | InfixT _ | BrackT _ | SeqT _ ->
error t.at "malformed field type"
| _ when prems = [] && hints = [] ->
(Elem t)::y1, y2, Some t.at
| _ ->
let at = Option.value at ~default: t.at in
error at "misplaced type"
) tfs ([], [], None)
in StrT (dots1, y1, y2, dots2) }
| dots_bar_list(casetyp)
{ let dots1, tcs, dots2 = $1 in
match dots1, El.Convert.filter_nl tcs, dots2 with
| NoDots, [(t, prems, hints)], NoDots when not (is_typcase t) ->
if is_typcon t || prems <> [] then
ConT ((t, prems), hints)
else if hints = [] then
t.it
else
error (List.hd hints).hintid.at "misplaced hint"
| _ ->
let y1, y2, _ =
List.fold_right
(fun elem (y1, y2, at) ->
(* at is the position of leftmost id element so far *)
match elem with
| Nl -> if at = None then y1, Nl::y2, at else Nl::y1, y2, at
| Elem (t, prems, hints) ->
match t.it with
| AtomT atom
| InfixT (_, atom, _)
| BrackT (atom, _, _)
| SeqT ({it = AtomT atom; _}::_)
| SeqT ({it = InfixT (_, atom, _); _}::_)
| SeqT ({it = BrackT (atom, _, _); _}::_) when at = None ->
y1, (Elem (atom, (t, prems), hints))::y2, None
| _ when prems = [] && hints = [] ->
(Elem t)::y1, y2, Some t.at
| _ ->
let at = Option.value at ~default: t.at in
error at "misplaced type"
) tcs ([], [], None)
in CaseT (dots1, y1, y2, dots2) }
| nl_bar_list1(enumtyp(enum1), enumtyp(arith)) { RangeT $1 }
(*nottyp_prim : nottyp_prim_ { $1 $ $sloc }*)
nottyp_prim_ :
| typ_prim_ { $1 }
| atom { AtomT $1 }
| atomid_lparen nottyp RPAREN
{ SeqT [
AtomT (Atom.Atom $1 $$ $loc($1)) $ $loc($1);
ParenT $2 $ $loc($2)
] }
| TICK LPAREN nottyp RPAREN
{ BrackT (Atom.LParen $$ $loc($2), $3, Atom.RParen $$ $loc($4)) }
| TICK LBRACK nottyp RBRACK
{ BrackT (Atom.LBrack $$ $loc($2), $3, Atom.RBrack $$ $loc($4)) }
| TICK LBRACE nottyp RBRACE
{ BrackT (Atom.LBrace $$ $loc($2), $3, Atom.RBrace $$ $loc($4)) }
| LPAREN comma_list(typ) RPAREN
{ match $2 with
| [] -> ParenT (SeqT [] $ $sloc)
| [t] -> ParenT t
| ts -> TupT ts }
nottyp_post : nottyp_post_ { $1 $ $sloc }
nottyp_post_ :
| nottyp_prim_ { $1 }
| nottyp_post iter { IterT ($1, $2) }
nottyp_seq : nottyp_seq_ { $1 $ $sloc }
nottyp_seq_ :
| nottyp_post_ { $1 }
| nottyp_post nottyp_seq { SeqT ($1 :: as_seq_typ $2) }
nottyp_un : nottyp_un_ { $1 $ $sloc }
nottyp_un_ :
| nottyp_seq_ { $1 }
| infixop nottyp_un { InfixT (SeqT [] $ $loc($1), $1, $2) }
nottyp_bin : nottyp_bin_ { $1 $ $sloc }
nottyp_bin_ :
| nottyp_un_ { $1 }
| nottyp_bin infixop nottyp_bin { InfixT ($1, $2, $3) }
nottyp_rel : nottyp_rel_ { $1 $ $sloc }
nottyp_rel_ :
| nottyp_bin_ { $1 }
| relop nottyp_rel { InfixT (SeqT [] $ $loc($1), $1, $2) }
| nottyp_rel relop nottyp_rel { InfixT ($1, $2, $3) }
nottyp : nottyp_rel { $1 }
fieldtyp :
| nottyp hint* prem_bin_list { $1, $3, $2 }
casetyp :
| nottyp hint* prem_list { $1, $3, $2 }
%inline enumtyp(X) :
| X { ($1, None) }
| X BAR DOTDOTDOT BAR arith { ($1, Some $5) }
%inline enum1 :
| exp_lit { $1 }
| PLUS arith_un { UnE (`PlusOp, $2) $ $sloc }
| MINUS arith_un { UnE (`MinusOp, $2) $ $sloc }
| DOLLAR LPAREN arith RPAREN { $3 }
| DOLLAR numtyp DOLLAR LPAREN arith RPAREN { CvtE ($5, $2) $ $sloc }
(* Expressions *)
exp_lit : exp_lit_ { $1 $ $sloc }
exp_lit_ :
| BOOLLIT { BoolE $1 }
| NATLIT { NumE (`DecOp, `Nat $1) }
| HEXLIT { NumE (`HexOp, `Nat $1) }
| CHARLIT { NumE (`CharOp, `Nat $1) }
| TICK NATLIT { NumE (`AtomOp, `Nat $2) }
| TEXTLIT { TextE $1 }
exp_var_ :
| varid { VarE ($1, []) }
| varid_lparen comma_list(arg) RPAREN { VarE ($1, $2) }
| BOOL { VarE ("bool" $ $sloc, []) }
| NAT { VarE ("nat" $ $sloc, []) }
| INT { VarE ("int" $ $sloc, []) }
| RAT { VarE ("rat" $ $sloc, []) }
| REAL { VarE ("real" $ $sloc, []) }
| TEXT { VarE ("text" $ $sloc, []) }
exp_call : exp_call_ { $1 $ $sloc }
exp_call_ :
| DOLLAR defid { CallE ($2, []) }
| DOLLAR defid_lparen comma_list(arg) RPAREN { CallE ($2, $3) }
exp_hole_ :
| HOLEN { HoleE (`Num $1) }
| HOLE { HoleE `Next }
| MULTIHOLE { HoleE `Rest }
| NOTHING { HoleE `None }
| LATEX LPAREN list(TEXTLIT) RPAREN { LatexE (String.concat " " $3) }
exp_prim : exp_prim_ { $1 $ $sloc }
exp_prim_ :
| exp_lit_ { $1 }
| exp_var_ { $1 }
| exp_call_ { $1 }
| exp_hole_ { $1 }
| EPS { EpsE }
| LBRACE comma_nl_list(fieldexp) RBRACE { StrE $2 }
| LPAREN comma_list(exp_bin) RPAREN
{ match $2 with
| [] -> ParenE (SeqE [] $ $sloc)
| [e] -> ParenE e
| es -> TupE es }
| TICK LPAREN exp RPAREN
{ BrackE (Atom.LParen $$ $loc($2), $3, Atom.RParen $$ $loc($4)) }
| TICK LBRACK exp RBRACK
{ BrackE (Atom.LBrack $$ $loc($2), $3, Atom.RBrack $$ $loc($4)) }
| TICK LBRACE exp RBRACE
{ BrackE (Atom.LBrace $$ $loc($2), $3, Atom.RBrace $$ $loc($4)) }
| DOLLAR LPAREN arith RPAREN { $3.it }
| DOLLAR numtyp DOLLAR LPAREN arith RPAREN { CvtE ($5, $2) }
| FUSEFUSE exp_prim { UnparenE $2 }
exp_post : exp_post_ { $1 $ $sloc }
exp_post_ :
| exp_prim_ { $1 }
| exp_atom LBRACK arith RBRACK { IdxE ($1, $3) }
| exp_atom LBRACK arith COLON arith RBRACK { SliceE ($1, $3, $5) }
| exp_atom LBRACK path EQ exp RBRACK { UpdE ($1, $3, $5) }
| exp_atom LBRACK path EQCAT exp RBRACK { ExtE ($1, $3, $5) }
| exp_atom iter { IterE ($1, $2) }
| exp_post dotid { DotE ($1, $2) }
exp_atom : exp_atom_ { $1 $ $sloc }
exp_atom_ :
| exp_post_ { $1 }
| atom { AtomE $1 }
| atomid_lparen exp RPAREN
{ SeqE [
AtomE (Atom.Atom $1 $$ $loc($1)) $ $loc($1);
ParenE $2 $ $loc($2)
] }
exp_list : exp_list_ { $1 $ $sloc }
exp_list_ :
| LBRACK RBRACK { ListE [] }
| LBRACK exp_seq RBRACK { ListE (as_seq_exp $2) }
| exp_list iter { IterE ($1, $2) }
exp_seq : exp_seq_ { $1 $ $sloc }
exp_seq_ :
| exp_atom_ { $1 }
| exp_list_ { $1 }
| exp_seq exp_atom { SeqE (as_seq_exp $1 @ [$2]) }
| exp_seq FUSE exp_atom { FuseE ($1, $3) }
exp_un : exp_un_ { $1 $ $sloc }
exp_un_ :
| exp_seq_ { $1 }
| bar(exp) exp bar(exp) { LenE $2 }
| BARBAR gramid BARBAR { SizeE $2 }
| unop exp_un { UnE ($1, $2) }
| infixop exp_un { InfixE (SeqE [] $ $loc($1), $1, $2) }
exp_bin : exp_bin_ { $1 $ $sloc }
exp_bin_ :
| exp_un_ { $1 }
| exp_bin infixop exp_bin { InfixE ($1, $2, $3) }
| exp_bin cmpop exp_bin { CmpE ($1, $2, $3) }
| exp_bin boolop exp_bin { BinE ($1, $2, $3) }
| exp_bin CAT exp_bin { CatE ($1, $3) }
| exp_bin MEM exp_bin { MemE ($1, $3) }
| exp_bin NOTMEM exp_bin { UnE (`NotOp, MemE ($1, $3) $ $sloc) }
exp_rel : exp_rel_ { $1 $ $sloc }
exp_rel_ :
| exp_bin_ { $1 }
| comma(exp) exp_rel { CommaE (SeqE [] $ $loc($1), $2) }
| relop exp_rel { InfixE (SeqE [] $ $loc($1), $1, $2) }
| exp_rel comma(exp) exp_rel { CommaE ($1, $3) }
| exp_rel relop exp_rel { InfixE ($1, $2, $3) }
exp : exp_rel { $1 }
fieldexp :
| fieldid exp_atom+
{ ($1, match $2 with [e] -> e | es -> SeqE es $ $loc($2)) }
arith_prim : arith_prim_ { $1 $ $sloc }
arith_prim_ :
| exp_lit_ { $1 }
| exp_var_ { $1 }
| exp_call_ { $1 }
| exp_hole_ { $1 }
| LPAREN arith RPAREN { ParenE $2 }
| LPAREN arith_bin STAR RPAREN
{ (* HACK: to allow "(s*)" as arithmetic expression. *)
if not (is_post_exp $2) then
error (at $loc($3)) "misplaced token";
IterE ($2, List) }
| LPAREN arith_bin PLUS RPAREN
{ (* HACK: to allow "(s+)" as arithmetic expression. *)
if not (is_post_exp $2) then
error (at $loc($3)) "misplaced token";
IterE ($2, List1) }
| LPAREN arith_bin QUEST RPAREN
{ (* HACK: to allow "(s?)" as arithmetic expression. *)
if not (is_post_exp $2) then
error (at $loc($3)) "misplaced token";
IterE ($2, Opt) }
| DOLLAR LPAREN exp RPAREN { $3.it }
| DOLLAR numtyp DOLLAR LPAREN arith RPAREN { CvtE ($5, $2) }
arith_post : arith_post_ { $1 $ $sloc }
arith_post_ :
| arith_prim_ { $1 }
| arith_atom UP arith_prim { BinE ($1, `PowOp, $3) }
| arith_atom LBRACK arith RBRACK { IdxE ($1, $3) }
| arith_post dotid { DotE ($1, $2) }
arith_atom : arith_atom_ { $1 $ $sloc }
arith_atom_ :
| arith_post_ { $1 }
| atom { AtomE $1 }
arith_un : arith_un_ { $1 $ $sloc }
arith_un_ :
| arith_atom_ { $1 }
| bar(exp) exp bar(exp) { LenE $2 }
| BARBAR gramid BARBAR { SizeE $2 }
| unop arith_un { UnE ($1, $2) }
arith_bin : arith_bin_ { $1 $ $sloc }
arith_bin_ :
| arith_un_ { $1 }
| arith_bin binop arith_bin { BinE ($1, $2, $3) }
| arith_bin cmpop arith_bin { CmpE ($1, $2, $3) }
| arith_bin boolop arith_bin { BinE ($1, $2, $3) }
| arith_bin CAT arith_bin { CatE ($1, $3) }
| arith_bin MEM arith_bin { MemE ($1, $3) }
| arith_bin NOTMEM arith_bin { UnE (`NotOp, MemE ($1, $3) $ $sloc) }
arith : arith_bin { $1 }
path : path_ { $1 $ $sloc }
path_ :
| (* empty *) { RootP }
| path LBRACK arith RBRACK { IdxP ($1, $3) }
| path LBRACK arith COLON arith RBRACK { SliceP ($1, $3, $5) }
| path dotid { DotP ($1, $2) }
(* Premises *)
prem_list :
| enter_scope nl_dash_list(prem) exit_scope { $2 }
prem_list1 :
| enter_scope nl_dash_list1(prem) exit_scope { $2 }
prem_bin_list :
| enter_scope nl_dash_list(prem_bin) exit_scope { $2 }
(*prem_post : prem_post_ { $1 $ $sloc }*)
prem_post_ :
| OTHERWISE { ElsePr }
| LPAREN prem RPAREN iter*
{ let rec iters prem = function
| [] -> prem
| iter::iters' -> iters (IterPr (prem, iter) $ $sloc) iters'
in (iters $2 $4).it }
prem_bin : prem_bin_ { $1 $ $sloc }
prem_bin_ :
| prem_post_ { $1 }
| relid COLON exp_bin { RulePr ($1, $3) }
| IF exp_bin
{ let rec iters e =
match e.it with
| IterE (e1, iter) -> IterPr (Source.(iters e1 $ e1.at), iter)
| _ -> IfPr e
in iters $2 }
prem : prem_ { $1 $ $sloc }
prem_ :
| prem_post_ { $1 }
| relid COLON exp { RulePr ($1, $3) }
| VAR varid_bind_with_suffix COLON typ { VarPr ($2, $4) }
| IF exp
{ let rec iters e =
match e.it with
| IterE (e1, iter) -> IterPr (Source.(iters e1 $ e1.at), iter)
| _ -> IfPr e
in iters $2 }
(* Grammars *)
(*sym_prim : sym_prim_ { $1 $ $sloc }*)
sym_prim_ :
| gramid { VarG ($1, []) }
| gramid_lparen comma_list(arg) RPAREN { VarG ($1, $2) }
| NATLIT { NumG (`DecOp, $1) }
| HEXLIT { NumG (`HexOp, $1) }
| CHARLIT { NumG (`CharOp, $1) }
| TEXTLIT { TextG $1 }
| TICK NATLIT { NumG (`AtomOp, $2) }
| EPS { EpsG }
| LPAREN comma_list(sym) RPAREN
{ match $2 with
| [] -> ParenG (SeqG [] $ $sloc)
| [g] -> ParenG g
| gs -> TupG gs }
| DOLLAR LPAREN arith RPAREN { ArithG $3 }
| DOLLAR numtyp DOLLAR LPAREN arith RPAREN { ArithG (CvtE ($5, $2) $ $sloc) }
sym_post : sym_post_ { $1 $ $sloc }
sym_post_ :
| sym_prim_ { $1 }
| sym_post iter { IterG ($1, $2) }
sym_attr : sym_attr_ { $1 $ $sloc }
sym_attr_ :
| sym_post_ { $1 }
| sym_post COLON sym_post { AttrG (El.Convert.exp_of_sym $1, $3) }
sym_seq : sym_seq_ { $1 $ $sloc }
sym_seq_ :
| sym_attr_ { $1 }
| sym_seq sym_attr { SeqG (as_seq_sym $1 @ [Elem $2]) }
(*
| sym_seq NL_NL sym_attr { SeqG (as_seq_sym $1 @ [Nl; Elem $3]) }
| sym_seq NL_NL_NL sym_attr { SeqG (as_seq_sym $1 @ [Nl; Elem $3]) }
*)
sym_alt : sym_alt_ { $1 $ $sloc }
sym_alt_ :
| sym_seq_ { $1 }
| sym_alt bar(sym) sym_seq { AltG (as_alt_sym $1 @ $2 @ [Elem $3]) }
| sym_alt bar(sym) DOTDOTDOT bar(sym) sym_seq { RangeG ($1, $5) }
sym : sym_alt { $1 }
(*
prod : prod_ { $1 $ $sloc }
prod_ :
| sym ARROW2 exp prem_list { SynthP ($1, $3, $4) }
| sym ARROW2 exp bar(sym) DOTDOTDOT bar(sym) sym ARROW2 exp
{ RangeP ($1, $3, $7, $9) }
| sym EQUIV sym prem_list { EquivP ($1, $3, $4) }
gram :
| dots_bar_list(prod) { $1 $ $sloc }
prod_short : prod_short_ { $1 $ $sloc }
prod_short_ :
| sym_seq prem_list
{ let var () = VarE ("res" $ $loc($1), []) $ $loc($1) in
(AttrG (var (), $1) $ $loc($1), var (), $2)
}
gram_short :
| dots_bar_list(prod_short) { $1 $ $sloc }
*)
gram : gram_ { $1 $ $sloc }
gram_ : (* bar_dots * prod nl_list * bar_dots *)
(* Inline and transform dots_bar_list to avoid conflicts *)
| gram_long_or_short { let x, y = $1 [] in (NoDots, x, y) }
| bar(sym) gram_long_or_short { let x, y = $2 [] in (NoDots, x, y) }
| bar_dots bar(gram) gram_long_or_short { let x, y = $3 [] in (Dots, $2 @ x, y) }
gram_long_or_short : (* prod nl_list * bar_dots *)
| gram_empty { fun alts -> short_alt_prod (alts, []), $1 }
| gram_long1 { $1 }
| gram_short1 { $1 }
| sym_seq { fun alts -> short_alt_prod (alts @ [Elem $1], []), NoDots }
| sym_seq bar(sym) short_range_cont_or_gram_long_or_short { fun alts -> $3 alts $1 $2 }
gram_empty : (* dots *)
| (* empty *) { NoDots }
| DOTDOTDOT { Dots }
gram_long1 : (* sym nl_list -> prod nl_list * dots *)
| sym_seq ARROW2 exp
{ fun alts -> long_alt_prod (alts @ [Elem $1], $3, []), NoDots }
| sym_seq ARROW2 exp prem_list1 gram_cont(gram_long)
{ fun alts -> let x, y = $5 in
long_alt_prod (alts @ [Elem $1], $3, $4) @ x, y }
| sym_seq ARROW2 exp bar(gram) long_range_cont_or_gram_long
{ fun alts -> $5 (alt_sym (alts @ [Elem $1])) $3 $4 }
| sym_seq EQUIV sym_seq prem_list gram_cont(gram_long)
{ fun alts -> let x, y = $5 in
long_equiv_prod (alts @ [Elem $1], $3, $4) @ x, y }
gram_long : (* prod nl_list * dots *)
| gram_empty { [], $1 }
| sym_alt ARROW2 exp { [long_prod ($1, $3, [])], NoDots }
| sym_alt ARROW2 exp prem_list1 gram_cont(gram_long)
{ let x, y = $5 in long_prod ($1, $3, $4) :: x, y }
| sym_alt ARROW2 exp bar(gram) long_range_cont_or_gram_long { $5 $1 $3 $4 }
| sym_alt EQUIV sym_seq prem_list gram_cont(gram_long)
{ let x, y = $5 in equiv_prod ($1, $3, $4) :: x, y }
gram_short1 : (* sym nl_list -> prod nl_list * dots *)
| prod_short1 gram_cont(gram_short)
{ fun alts -> let x, y = $2 in ($1 alts) @ x, y }
gram_short : (* prod nl_list * dots *)
| gram_empty { [], $1 }
| prod_short gram_cont(gram_short) { let x, y = $2 in $1::x, y }
%inline gram_cont(gram (* prod nl_list * dots *)) : (* prod nl_list * dots *)
| (* empty *) { [], NoDots }
| bar(gram) gram { let x, y = $2 in $1 @ x, y }
(*
prod_long1 : (* sym nl_list -> prod nl_list *)
| sym_seq ARROW2 exp prem_list
{ fun alts -> long_alt_prod (alts @ [Elem $1], $3, $4) }
| sym_seq EQUIV sym prem_list
{ fun alts -> [long_equiv_prod (alts @ [Elem $1], $3, $4)] }
prod_long : (* prem nl_list -> prod nl_elem *)
| sym_alt ARROW2 exp { fun prems -> long_prod ($1, $3, prems) }
| sym_alt EQUIV sym { fun prems -> long_equiv_prod ($1, $3, prems) }
*)
prod_short1 : (* sym nl_list -> prod nl_list *)
| sym_seq prem_list1 { fun alts -> short_alt_prod (alts @ [Elem $1], $2) }
prod_short : (* prod nl_elem *)
| sym_seq prem_list { short_prod ($1, $2) }
long_range_cont_or_gram_long : (* sym -> exp -> prod nl_list -> prod nl_list * dots *)
| long_range_cont gram_cont(gram_long)
{ fun g1 e1 nl -> let x, y = $2 in $1 g1 e1 :: nl @ x, y }
| gram_long
{ fun g1 e1 nl -> let x, y = $1 in
Elem Source.(SynthP (g1, e1, []) $ over_region [g1.at; e1.at]) :: nl @ x, y }
long_range_cont : (* sym -> exp -> prod nl_elem *)
| DOTDOTDOT bar(gram) sym_seq ARROW2 exp
{ fun g1 e1 ->
Elem Source.(RangeP (g1, e1, $3, $5) $ over_region [g1.at; $5.at]) }
short_range_cont_or_gram_long_or_short : (* sym nl_list -> sym -> sym nl_list -> prod nl_list * dots *)
| short_range_cont ARROW2 exp prem_list gram_cont(gram_long)
{ fun alts g1 nl ->
let nl' = List.map (function Nl -> Nl | Elem _ -> assert false) nl in
let x, y = $5 in long_alt_prod (alts @ [Elem ($1 g1)], $3, $4) @ nl' @ x, y }
| short_range_cont prem_list1 gram_cont(gram_short)
{ fun alts g1 nl ->
let nl' = List.map (function Nl -> Nl | Elem _ -> assert false) nl in
let x, y = $3 in short_alt_prod (alts @ [Elem ($1 g1)], $2) @ nl' @ x, y }
| short_range_cont
{ fun alts g1 nl ->
let nl' = List.map (function Nl -> Nl | Elem _ -> assert false) nl in
short_alt_prod (alts @ [Elem ($1 g1)], []) @ nl', NoDots }
| short_range_cont bar(sym) gram_long_or_short
{ fun alts g1 nl -> $3 (alts @ [Elem ($1 g1)] @ nl @ $2) }
| gram_long_or_short
{ fun alts g1 nl -> $1 (alts @ [Elem g1] @ nl) }
short_range_cont : (* sym -> sym *)
| DOTDOTDOT bar(sym) sym_seq
{ fun g1 -> Source.(RangeG (g1, $3) $ over_region [g1.at; $3.at]) }
(* Definitions *)
arg : arg_ { ref $1 $ $sloc }
arg_ :
| exp_bin { ExpA $1 }
| SYNTAX typ { TypA $2 }
| SYNTAX atomid_ { Id.make_var $2; TypA (VarT ($2 $ $loc($2), []) $ $loc($2)) }
| GRAMMAR sym { GramA $2 }
| DEF DOLLAR defid { DefA $3 }
(* HACK for representing def parameters as args *)
| DEF exp_call COLON typ { ExpA (TypE ($2, $4) $ $sloc) }
param : param_ { $1 $ $sloc }
param_ :
| varid_bind_with_suffix COLON typ { ExpP ($1, $3) }
| typ
{ let id =
try El.Convert.varid_of_typ $1 with Error.Error _ -> "_" $ $sloc
in ExpP (id, $1) }
| SYNTAX varid_bind { TypP $2 }
| GRAMMAR gramid COLON typ { GramP ($2, [], $4) }
| GRAMMAR gramid_lparen enter_scope comma_list(param) RPAREN COLON typ exit_scope
{ GramP ($2, $4, $7) }
| DEF DOLLAR defid COLON typ
{ DefP ($3, [], $5) }
| DEF DOLLAR defid_lparen enter_scope comma_list(param) RPAREN COLON typ exit_scope
{ DefP ($3, $5, $8) }
def :
| def_ NL_NL* { $1 $ $loc($1) }
def_ :
| SYNTAX varid_bind_lparen enter_scope comma_list(arg) RPAREN ruleid_list hint* exit_scope
{ FamD ($2, List.map El.Convert.param_of_arg $4, $7) }
| SYNTAX varid_bind ruleid_list hint* EQ deftyp
{ let id = if $3 = "" then "" else String.sub $3 1 (String.length $3 - 1) in
TypD ($2, id $ $loc($3), [], $6, $4) }
| SYNTAX varid_bind_lparen enter_scope comma_list(arg) RPAREN ruleid_list hint* EQ deftyp exit_scope
{ let id = if $6 = "" then "" else String.sub $6 1 (String.length $6 - 1) in
TypD ($2, id $ $loc($6), $4, $9, $7) }
| GRAMMAR varid_bind ruleid_list COLON typ hint* EQ gram
{ let id = if $3 = "" then "" else String.sub $3 1 (String.length $3 - 1) in
GramD ($2, id $ $loc($3), [], $5, $8, $6) }
| GRAMMAR varid_bind ruleid_list hint* EQ gram
{ let id = if $3 = "" then "" else String.sub $3 1 (String.length $3 - 1) in
GramD ($2, id $ $loc($3), [], TupT [] $ $loc($1), $6, $4) }
| GRAMMAR varid_bind_lparen enter_scope comma_list(param) RPAREN ruleid_list COLON typ hint* EQ gram exit_scope
{ let id = if $6 = "" then "" else String.sub $6 1 (String.length $6 - 1) in
GramD ($2, id $ $loc($6), $4, $8, $11, $9) }
| GRAMMAR varid_bind_lparen enter_scope comma_list(param) RPAREN ruleid_list hint* EQ gram exit_scope
{ let id = if $6 = "" then "" else String.sub $6 1 (String.length $6 - 1) in
GramD ($2, id $ $loc($6), $4, TupT [] $ $loc($1), $9, $7) }
| RELATION relid COLON nottyp hint*
{ RelD ($2, $4, $5) }
| RULE relid ruleid_list COLON exp prem_list
{ let id = if $3 = "" then "" else String.sub $3 1 (String.length $3 - 1) in
RuleD ($2, id $ $loc($3), $5, $6) }
| VAR varid_bind COLON typ hint*
{ VarD ($2, $4, $5) }
| DEF DOLLAR defid COLON typ hint*
{ DecD ($3, [], $5, $6) }
| DEF DOLLAR defid_lparen enter_scope comma_list(arg) RPAREN COLON typ hint* exit_scope
{ DecD ($3, List.map El.Convert.param_of_arg $5, $8, $9) }
| DEF DOLLAR defid EQ exp prem_list
{ DefD ($3, [], $5, $6) }
| DEF DOLLAR defid_lparen enter_scope comma_list(arg) RPAREN EQ exp prem_list exit_scope
{ DefD ($3, $5, $8, $9) }
| NL_NL_NL
{ SepD }
| SYNTAX varid_bind ruleid_list hint*
{ let id = if $3 = "" then "" else String.sub $3 1 (String.length $3 - 1) in
HintD (TypH ($2, id $ $loc($3), $4) $ $sloc) }
| SYNTAX varid_bind ruleid_list atom hint*
{ HintD (AtomH ($2, $4, $5) $ $sloc) }
| SYNTAX varid_bind ruleid_list TICK LPAREN hint*
{ HintD (AtomH ($2, Atom.LParen $$ $loc($5), $6) $ $sloc) }
| SYNTAX varid_bind ruleid_list TICK LBRACK hint*
{ HintD (AtomH ($2, Atom.LBrack $$ $loc($5), $6) $ $sloc) }
| SYNTAX varid_bind ruleid_list TICK LBRACE hint*
{ HintD (AtomH ($2, Atom.LBrace $$ $loc($5), $6) $ $sloc) }
| GRAMMAR varid_bind ruleid_list hint*
{ let id = if $3 = "" then "" else String.sub $3 1 (String.length $3 - 1) in
HintD (GramH ($2, id $ $loc($3), $4) $ $sloc) }
| RELATION relid hint*
{ HintD (RelH ($2, $3) $ $sloc) }
| VAR varid_bind hint*
{ HintD (VarH ($2, $3) $ $sloc) }
| DEF DOLLAR defid hint*
{ HintD (DecH ($3, $4) $ $sloc) }
ruleid_list :
| (* empty *) { "" }
| SLASH ruleid ruleid_list { "/" ^ $2 ^ $3 }
| MINUS ruleid ruleid_list { "-" ^ $2 ^ $3 }
hint :
| HINT_LPAREN hintid exp RPAREN
{ {hintid = $2 $ $loc($2); hintexp = $3} }
| HINT_LPAREN hintid RPAREN
{ {hintid = $2 $ $loc($2); hintexp = SeqE [] $ $loc($2)} }
(* Scripts *)
script :
| NL_NL* def* EOF { $2 }
typ_eof :
| typ EOF { $1 }
exp_eof :
| exp EOF { $1 }
sym_eof :
| sym EOF { $1 }
%%