Skip to content

Commit b064339

Browse files
committed
[spectec] Implement IterPr in IL evaluator
1 parent 4e314da commit b064339

File tree

2 files changed

+123
-14
lines changed

2 files changed

+123
-14
lines changed
0 Bytes
Binary file not shown.

spectec/src/il/eval.ml

Lines changed: 123 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -474,34 +474,143 @@ and reduce_exp_call env id args at = function
474474
reduce_exp_call env id args at clauses'
475475
| None -> reduce_exp_call env id args at clauses'
476476
| Some s ->
477-
match reduce_prems env Subst.(subst_list subst_prem s prems) with
477+
match reduce_prems env s prems with
478478
| None -> None
479479
| Some false -> reduce_exp_call env id args at clauses'
480480
| Some true -> Some (reduce_exp env (Subst.subst_exp s e))
481481

482-
and reduce_prems env = function
482+
and reduce_prems env s = function
483483
| [] -> Some true
484484
| prem::prems ->
485-
match reduce_prem env prem with
486-
| Some true -> reduce_prems env prems
487-
| other -> other
485+
match reduce_prem env (Subst.subst_prem s prem) with
486+
| `True s' -> reduce_prems env (Subst.union s s') prems
487+
| `False -> Some false
488+
| `None -> None
488489

489-
and reduce_prem env prem : bool option =
490+
and reduce_prem env prem : [`True of Subst.t | `False | `None] =
490491
match prem.it with
491-
| RulePr _ -> None
492+
| RulePr _ -> `None
492493
| IfPr e ->
493494
(match (reduce_exp env e).it with
494-
| BoolE b -> Some b
495-
| _ -> None
495+
| BoolE b -> if b then `True Subst.empty else `False
496+
| _ -> `None
496497
)
497-
| ElsePr -> Some true
498+
| ElsePr -> `True Subst.empty
498499
| LetPr (e1, e2, _ids) ->
499500
(match match_exp env Subst.empty e2 e1 with
500-
| Some _ -> Some true (* TODO(2, rossberg): need to keep substitution? *)
501-
| None -> None
502-
| exception Irred -> None
501+
| Some s -> `True s
502+
| None -> `None
503+
| exception Irred -> `None
503504
)
504-
| IterPr (_prem, _iter) -> None (* TODO(3, rossberg): reduce? *)
505+
| IterPr (prem1, iterexp) ->
506+
let iter', xes' = reduce_iterexp env iterexp in
507+
(* Distinguish between let-defined variables, which flow outwards,
508+
* and others, which are assumed to flow inwards. *)
509+
let rec is_let_bound prem (x, _) =
510+
match prem.it with
511+
| LetPr (_, _, xs) -> List.mem x.it xs
512+
| IterPr (premI, iterexpI) ->
513+
let _iter1', xes1' = reduce_iterexp env iterexpI in
514+
let xes1_out, _ = List.partition (is_let_bound premI) xes1' in
515+
List.exists (fun (_, e1) -> Free.(Set.mem x.it (free_exp e1).varid)) xes1_out
516+
| _ -> false
517+
in
518+
let xes_out, xes_in = List.partition (is_let_bound prem) xes' in
519+
let xs_out, es_out = List.split xes_out in
520+
let xs_in, es_in = List.split xes_in in
521+
if not (List.for_all is_head_normal_exp es_in) || iter' <= List1 && es_in = [] then
522+
(* We don't know the number of iterations (yet): can't do anything. *)
523+
`None
524+
else
525+
(match iter' with
526+
| Opt ->
527+
let eos_in = List.map as_opt_exp es_in in
528+
if List.for_all Option.is_none eos_in then
529+
(* Iterating over empty option: nothing to do. *)
530+
`True Subst.empty
531+
else if List.for_all Option.is_some eos_in then
532+
(* All iteration variables are non-empty: reduce body. *)
533+
let es1_in = List.map Option.get eos_in in
534+
(* s substitutes in-bound iteration variables with corresponding
535+
* values. *)
536+
let s = List.fold_left2 Subst.add_varid Subst.empty xs_in es1_in in
537+
match reduce_prem env (Subst.subst_prem s prem1) with
538+
| (`None | `False) as r -> r
539+
| `True s' ->
540+
(* Body is true: now reverse-match out-bound iteration values
541+
* against iteration sources. *)
542+
match
543+
List.fold_left (fun s_opt (xI, eI) ->
544+
let* s = s_opt in
545+
let tI = match eI.note.it with IterT (tI, _) -> tI | _ -> assert false in
546+
match_exp' env s (OptE (Some (Subst.subst_exp s' (VarE xI $$ xI.at % tI))) $> eI) eI
547+
) (Some Subst.empty) xes_out
548+
with
549+
| Some s'' -> `True s''
550+
| None -> `None
551+
else
552+
(* Inconsistent arity of iteration values: can't perform mapping.
553+
* (This is a stuck computation, i.e., undefined.) *)
554+
`None
555+
| List | List1 ->
556+
(* Unspecified iteration count: get length from (first) iteration value
557+
* and start over; es'/es_in is in hnf, so got to be a list. *)
558+
let n = List.length (as_list_exp (List.hd es_in)) in
559+
if iter' = List || n >= 1 then
560+
let en = NumE (`Nat (Z.of_int n)) $$ prem.at % (NumT `NatT $ prem.at) in
561+
reduce_prem env (IterPr (prem1, (ListN (en, None), xes')) $> prem)
562+
else
563+
(* List is empty although it is List1: inconsistency.
564+
* (This is a stuck computation, i.e., undefined.) *)
565+
`None
566+
| ListN ({it = NumE (`Nat n'); _}, xo) ->
567+
(* Iterationen values es'/es_in are in hnf, so got to be lists. *)
568+
let ess_in = List.map as_list_exp es_in in
569+
let ns = List.map List.length ess_in in
570+
let n = Z.to_int n' in
571+
if List.for_all ((=) n) ns then
572+
(* All in-bound lists have the expected length: reduce body,
573+
* once for each tuple of values from the iterated lists. *)
574+
let rs = List.init n (fun i ->
575+
let esI_in = List.map (fun es -> List.nth es i) ess_in in
576+
(* s substitutes in-bound iteration variables with corresponding
577+
* values for this respective iteration. *)
578+
let s = List.fold_left2 Subst.add_varid Subst.empty xs_in esI_in in
579+
(* Add iteration counter variable if used. *)
580+
let s' =
581+
Option.fold xo ~none:s ~some:(fun x ->
582+
let en = NumE (`Nat (Z.of_int i)) $$ x.at % (NumT `NatT $ x.at) in
583+
Subst.add_varid s x en
584+
)
585+
in
586+
reduce_prem env (Subst.subst_prem s' prem1)
587+
)
588+
in
589+
if List.mem `None rs then `None else
590+
if List.mem `False rs then `False else
591+
(* Body was true in every iteration: now reverse-match out-bound
592+
* iteration variables against iteration sources. *)
593+
let ss = List.map (function `True s -> s | _ -> assert false) rs in
594+
(* Aggregate the out-lists for each out-bound variable. *)
595+
let es_out' =
596+
List.map2 (fun xI eI ->
597+
let tI = match eI.note.it with IterT (tI, _) -> tI | _ -> assert false in
598+
let esI = List.map (fun sJ ->
599+
Subst.subst_exp sJ (VarE xI $$ xI.at % tI)
600+
) ss
601+
in ListE esI $> eI
602+
) xs_out es_out
603+
in
604+
(* Reverse-match out-bound list values against iteration sources. *)
605+
match match_list match_exp env Subst.empty es_out' es_out with
606+
| Some s' -> `True s'
607+
| None -> `None
608+
else
609+
(* Inconsistent list lengths: can't perform mapping.
610+
* (This is a stuck computation, i.e., undefined.) *)
611+
`None
612+
| ListN _ -> `None
613+
)
505614

506615

507616
(* Matching *)

0 commit comments

Comments
 (0)