blob: 149f1ae9b6f589f6464045590c4e565d30256721 [file] [log] [blame] [edit]
(**
Encode lhs of a reduction rule into premises.
ex)
v3* v2^n v1 INSTR ~> eps
-->
v3* v2^n v1 INSTR ~> eps
-- if v1, _s1 = POP(1), 0
-- if v2^n, _s2 = POP(n), _s1
-- if v3* , _s3 = POP(-1), _s2
**)
open Util
open Source
open Free
open Il
open Ast
(* open Print *)
(* Helpers *)
let error at msg = Error.error at "prose translation" msg
let mk_id x =
x $ no_region
let mk_varT xt =
VarT (mk_id xt, []) $ no_region
let mk_varE xe xt =
VarE (mk_id xe) $$ no_region % (mk_varT xt)
let is_case e =
match e.it with
| CaseE _ -> true
| _ -> false
let case_of_case e =
match e.it with
| CaseE (mixop, _) -> mixop
| _ -> error e.at "cannot get case of case expression"
let args_of_case e =
match e.it with
| CaseE (_, { it = TupE exps; _ }) -> exps
| CaseE (_, exp) -> [ exp ]
| _ -> error e.at "cannot get arguments of case expression"
let is_context e =
is_case e && List.mem (Il2al_util.case_head (case_of_case e)) Al.Al_util.context_names
let rec stack_to_list e =
match e.it with
| CatE (e1, e2) -> stack_to_list e1 @ stack_to_list e2
| ListE es -> es
(* List.map (fun e -> { e with it = ListE [e] }) es *)
| _ -> [ e ]
let rec drop_until' acc f xs =
match xs with
| [] -> acc, []
| hd :: tl -> if f hd then acc, xs else drop_until' (hd :: acc) f tl
let drop_until = drop_until' []
let free_ids e =
(free_exp false e)
.varid
|> Set.elements
let dim e =
let t = (NumT `NatT $ no_region) in
match e.it with
| IterE (_, (ListN (e_n, _), _)) -> e_n
| IterE _ -> NumE (`Nat Z.minus_one) $$ e.at % t
| ListE es -> NumE (`Nat (List.length es |> Z.of_int)) $$ e.at % t
| _ -> NumE (`Nat Z.one) $$ e.at % t
let arg e =
ExpA e $ e.at
let input_vars = [
"input";
"stack0";
"ctxt";
"state";
"unused";
]
(* Encode stack *)
let encode_inner_stack context_opt stack =
let dropped, es = stack_to_list stack |> List.rev |> drop_until is_case in
let unused_prems =
if List.length dropped = 0 then
[]
else
let unused = TupE dropped $$ no_region % (mk_varT "unusedT") in
[LetPr (unused, mk_varE "unused" "unusedT", free_ids unused) $ no_region]
in
match es with
| [] ->
(* ASSUMPTION: The target instruction was actually the outer context (i.e. LABEL_) *)
(
match context_opt with
| None -> assert false
| Some e ->
Some (LetPr (e, mk_varE "input" "inputT", free_ids e) $ e.at), unused_prems
)
| _ ->
(* ASSUMPTION: The top of the stack should be now the target instruction *)
let winstr, operands = Lib.List.split_hd es in
let prem = LetPr (winstr, mk_varE "input" "inputT", free_ids winstr) $ winstr.at in
let prems = List.mapi (fun i e ->
let s0 = ("stack" ^ string_of_int i) in
let s1 = ("stack" ^ string_of_int (i+1)) in
let t = mk_varT "stackT" in
let stack1 = mk_varE s1 "stackT" in
let lhs = TupE [e; stack1] $$ no_region % t in
let n = dim e in
let stack0 = mk_varE s0 "stackT" in
let rhs = CallE (mk_id "pop", [arg n; arg stack0]) $$ no_region % t in
IfPr (CmpE (`EqOp, `BoolT, lhs, rhs) $$ e.at % (BoolT $ no_region)) $ e.at
) operands in
None, prem :: prems @ unused_prems
let encode_stack stack =
match stack.it with
| ListE [e] when is_context e ->
let mixop = case_of_case e in
let args = args_of_case e in
(* ASSUMPTION: the inner stack of the ctxt instruction is always the last arg *)
let args', inner_stack = Lib.List.split_last args in
let mixop' = Il2al_util.split_last_case mixop in
let e1 = { e with it = CaseE (mixop', TupE args' $$ no_region % (mk_varT "")) } in
let e2 = (mk_varE "ctxt" "contextT") in
let pr = LetPr (e1, e2, free_ids e1) $ e2.at in
let pr_opt, prs = encode_inner_stack (Some e) inner_stack in
(
match pr_opt with
| None -> pr
| Some pr -> pr
) :: prs
| _ ->
encode_inner_stack None stack |> snd
(* Encode lhs *)
let encode_lhs lhs =
match lhs.it with
| CaseE (Xl.Mixop.(Infix (Arg (), {it = Semicolon; _}, Arg ())), {it = TupE [z; stack]; _}) ->
let prem = LetPr (z, mk_varE "state" "stateT", free_ids z) $ z.at in
prem :: encode_stack stack
| _ ->
let stack = lhs in
encode_stack stack
(* Encode rule *)
let encode_rule r =
match r.it with
| RuleD(id, binds, mixop, args, prems) ->
match (mixop, args.it) with
(* lhs ~> rhs *)
| (Xl.Mixop.(Infix (Arg (), {it = SqArrow; _}, Arg ())), TupE ([lhs; _rhs])) ->
let name = String.split_on_char '-' id.it |> List.hd in
if List.mem name ["pure"; "read"; "trap"; "ctxt"] then (* Administrative rules *)
r
else
let new_prems = encode_lhs lhs in
RuleD(id, binds, mixop, args, new_prems @ prems) $ r.at
| _ -> r
(* Encode defs *)
let rec encode_def d =
match d.it with
| RelD (id, ps, mixop, t, rules) ->
let rules' = List.map encode_rule rules in
RelD (id, ps, mixop, t, rules') $ d.at
| RecD ds -> RecD (List.map encode_def ds) $ d.at
| DecD _ | TypD _ | GramD _ | HintD _ -> d
(* Main entry *)
let transform (defs : script) =
List.map encode_def defs