Module ExprPath
From Stdlib Require Import Lists.List.From Verilog Require Import Expr.
From Verilog Require Export Path.
From Verilog Require Import Utils.
Import ListNotations.
Import Expr.
Import Path.
Import Utils.
# ExprPath Module
This module formalizes the notion of *paths* within SystemVerilog expressions.
A path encodes the position of a subexpression inside a larger expression tree.
The module defines:
- a predicate `IsPath` relating expressions to valid paths,
- a function `sub_expr` for extracting subexpressions by path,
- and several lemmas connecting these concepts.
Module ExprPath. `IsPath e p` expresses that `p` is a valid path within the expression `e`.
Each constructor identifies the subexpression reached by following an index
sequence:
- `0`, `1`, or `2` select specific subfields of binary, unary, or ternary
operators;
- for concatenations, the path uses list indices via `nth_error`;
- the empty path `[]` always denotes the expression itself.
Inductive IsPath : Expr -> path -> Prop :=| P_Empty : forall e,
IsPath e []
| P_LhsBinOp : forall lhs rhs p,
IsPath lhs p -> IsPath (EBinOp lhs rhs) (0 :: p)
| P_RhsBinOp : forall lhs rhs p,
IsPath rhs p -> IsPath (EBinOp lhs rhs) (1 :: p)
| P_UnOpArg : forall arg p,
IsPath arg p -> IsPath (EUnOp arg) (0 :: p)
| P_LhsCompOp : forall lhs rhs p,
IsPath lhs p -> IsPath (EComp lhs rhs) (0 :: p)
| P_RhsCompOp : forall lhs rhs p,
IsPath rhs p -> IsPath (EComp lhs rhs) (1 :: p)
| P_LhsLogic : forall lhs rhs p,
IsPath lhs p -> IsPath (ELogic lhs rhs) (0 :: p)
| P_RhsLogic : forall lhs rhs p,
IsPath rhs p -> IsPath (ELogic lhs rhs) (1 :: p)
| P_RedArg : forall arg p,
IsPath arg p -> IsPath (EReduction arg) (0 :: p)
| P_LhsShift : forall lhs rhs p,
IsPath lhs p -> IsPath (EShift lhs rhs) (0 :: p)
| P_RhsShift : forall lhs rhs p,
IsPath rhs p -> IsPath (EShift lhs rhs) (1 :: p)
| P_AssignArg : forall op arg p,
IsPath arg p -> IsPath (EAssign op arg) (0 :: p)
| P_CondCond : forall cond tb fb p,
IsPath cond p -> IsPath (ECond cond tb fb) (0 :: p)
| P_CondTrue : forall cond tb fb p,
IsPath tb p -> IsPath (ECond cond tb fb) (1 :: p)
| P_CondFalse : forall cond tb fb p,
IsPath fb p -> IsPath (ECond cond tb fb) (2 :: p)
| P_ConcatArgs : forall n args e p,
nth_error args n = Some e -> IsPath e p -> IsPath (EConcat args) (n :: p)
| P_ReplArg : forall i arg p,
IsPath arg p -> IsPath (ERepl i arg) (0 :: p)
.
`sub_expr e p` navigates the expression `e` according to path `p`
and returns the corresponding subexpression (if it exists).
The function returns `None` when `p` does not correspond to any valid
substructure.
Fixpoint sub_expr (e: Expr) (p: path) :=match e, p with
| e, [] => Some e
| EBinOp lhs _, 0 :: p => sub_expr lhs p
| EBinOp _ rhs, 1 :: p => sub_expr rhs p
| EUnOp arg, 0 :: p => sub_expr arg p
| EComp lhs _, 0 :: p => sub_expr lhs p
| EComp _ rhs, 1 :: p => sub_expr rhs p
| ELogic lhs _, 0 :: p => sub_expr lhs p
| ELogic _ rhs, 1 :: p => sub_expr rhs p
| EReduction arg, 0 :: p => sub_expr arg p
| EShift lhs _, 0 :: p => sub_expr lhs p
| EShift _ rhs, 1 :: p => sub_expr rhs p
| EAssign _ arg, 0 :: p => sub_expr arg p
| ECond cond _ _, 0 :: p => sub_expr cond p
| ECond _ tb _, 1 :: p => sub_expr tb p
| ECond _ _ fb, 2 :: p => sub_expr fb p
| EConcat args, i :: p =>
match nth_error args i with
| Some e => sub_expr e p
| None => None
end
| ERepl _ arg, 0 :: p => sub_expr arg p
| _, _ :: _ => None
end
.
The shorthand `e @[ p ]` denotes `sub_expr e p`.
Notation "e @[ p ]" := (sub_expr e p) (at level 1). `IsPath_is_sub_expr`: if `p` is a valid path within `e`, then following `p`
via `sub_expr` yields some subexpression.
Lemma IsPath_is_sub_expr: forall e p, IsPath e p -> exists e0, e @[p] = Some e0.Proof.
intros. induction H; try (destruct IHIsPath as [x H1]; exists x; assumption).
- exists e; destruct e; reflexivity.
- destruct IHIsPath as [x H1]; exists x. simpl. rewrite H. assumption.
Qed.
- exists e; destruct e; reflexivity.
- destruct IHIsPath as [x H1]; exists x. simpl. rewrite H. assumption.
Qed.
`sub_expr_valid`: if `sub_expr e p = Some f`, then `p` is a valid path of `e`
leading to `f`.
Lemma sub_expr_valid: forall e p f, e @[p] = Some f -> IsPath e p.Proof.
induction e using Expr_ind; intros;
try (destruct p as [|[|[|[]]]]; simpl in *;
(constructor; firstorder) || congruence).
- destruct p.
+ constructor.
+ destruct (nth_error args n) eqn:H1; simpl in H0; rewrite H1 in H0.
* apply (P_ConcatArgs _ _ _ _ H1 (H _ _ H1 _ _ H0)).
* congruence.
Qed.
try (destruct p as [|[|[|[]]]]; simpl in *;
(constructor; firstorder) || congruence).
- destruct p.
+ constructor.
+ destruct (nth_error args n) eqn:H1; simpl in H0; rewrite H1 in H0.
* apply (P_ConcatArgs _ _ _ _ H1 (H _ _ H1 _ _ H0)).
* congruence.
Qed.
`IsPath_sub_expr_iff`: a path is valid for an expression if and only if
the corresponding subexpression lookup succeeds.
Lemma IsPath_sub_expr_iff: forall e p, IsPath e p <-> exists e0, e @[p] = Some e0.Proof.
`sub_expr_nil`: the empty path always corresponds to the expression itself.
Lemma sub_expr_nil: forall e, e @[[]] = Some e.Proof.
intros. destruct e; reflexivity.
Qed.
Qed.
`sub_expr_chunk`: subexpression lookups compose under path concatenation.
Following `p ++ q` is equivalent to first locating the subexpression at `p`,
then following `q` within it.
Lemma sub_expr_chunk : forall p q e g,(exists f, e @[p] = Some f /\ f @[q] = Some g) <-> e @[p ++ q] = Some g.
Proof.
induction p; split; intros.
- destruct H as [f [H1 H2]]. rewrite sub_expr_nil in H1. inv H1. assumption.
- exists e. split. apply sub_expr_nil. assumption.
- destruct H as [f [H1 H2]]. destruct e; simpl in *;
try congruence; try (destruct (nth_error _ a)); destruct a as [|[|[]]];
congruence || (apply IHp; eexists; intuition; eassumption).
- destruct e; simpl in *;
try congruence; try (destruct (nth_error _ a));
destruct a as [|[|[]]]; congruence || apply IHp; assumption.
Qed.
- destruct H as [f [H1 H2]]. rewrite sub_expr_nil in H1. inv H1. assumption.
- exists e. split. apply sub_expr_nil. assumption.
- destruct H as [f [H1 H2]]. destruct e; simpl in *;
try congruence; try (destruct (nth_error _ a)); destruct a as [|[|[]]];
congruence || (apply IHp; eexists; intuition; eassumption).
- destruct e; simpl in *;
try congruence; try (destruct (nth_error _ a));
destruct a as [|[|[]]]; congruence || apply IHp; assumption.
Qed.
`IsPath_chunk`: decomposes the validity of a concatenated path `p ++ c`
into a prefix-subexpression relation and a continuation path within that
subexpression.
Lemma IsPath_chunk : forall e p c,IsPath e (p ++ c) <-> (exists f, e @[p] = Some f /\ IsPath f c).
Proof.
split; intros.
- destruct (IsPath_is_sub_expr _ _ H) as [g H1]. apply sub_expr_chunk in H1.
destruct H1 as [f [H2 H3]]. exists f. split. assumption. apply IsPath_sub_expr_iff.
exists g. assumption.
- destruct H as [f [H1 H2]]. apply IsPath_sub_expr_iff.
apply IsPath_sub_expr_iff in H2. destruct H2 as [g H2]. exists g.
apply sub_expr_chunk. exists f. intuition.
Qed.
- destruct (IsPath_is_sub_expr _ _ H) as [g H1]. apply sub_expr_chunk in H1.
destruct H1 as [f [H2 H3]]. exists f. split. assumption. apply IsPath_sub_expr_iff.
exists g. assumption.
- destruct H as [f [H1 H2]]. apply IsPath_sub_expr_iff.
apply IsPath_sub_expr_iff in H2. destruct H2 as [g H2]. exists g.
apply sub_expr_chunk. exists f. intuition.
Qed.
`IsPath_dec`: the predicate `IsPath e p` is decidable - every path is either
a valid traversal of `e` or not.
Theorem IsPath_dec : forall e p, {IsPath e p} + {~(IsPath e p)}.Proof.
intros. destruct (e @[p]) eqn:Hep.
- left. apply (sub_expr_valid _ _ _ Hep).
- right. unfold not. intros. rewrite IsPath_sub_expr_iff in H. destruct H.
congruence.
Qed.
- left. apply (sub_expr_valid _ _ _ Hep).
- right. unfold not. intros. rewrite IsPath_sub_expr_iff in H. destruct H.
congruence.
Qed.