Skip to main content

litex/execute/by_stmt/
closed_range_by_stmt.rs

1use crate::common::helper::is_number_string_literally_integer_without_dot;
2use crate::prelude::*;
3
4impl Runtime {
5    pub fn exec_by_enumerate_closed_range_stmt(
6        &mut self,
7        stmt: &ByEnumerateClosedRangeStmt,
8    ) -> Result<StmtResult, RuntimeError> {
9        let set_obj: Obj = stmt.closed_range.clone().into();
10        let element = stmt.element.clone();
11        let in_fact = InFact::new(element, set_obj, stmt.line_file.clone());
12        let in_atomic: AtomicFact = in_fact.clone().into();
13        let verify_state = VerifyState::new(0, false);
14        let membership = self.verify_atomic_fact(&in_atomic, &verify_state)?;
15        if membership.is_unknown() {
16            return Err(short_exec_error(
17                stmt.clone().into(),
18                format!(
19                    "by enumerate closed_range: membership `{}` is not known",
20                    in_fact
21                ),
22                None,
23                vec![],
24            ));
25        }
26
27        let z_set: Obj = StandardSet::Z.into();
28        let lf = stmt.line_file.clone();
29        for (side, endpoint) in [
30            ("left", stmt.closed_range.start.as_ref().clone()),
31            ("right", stmt.closed_range.end.as_ref().clone()),
32        ] {
33            let in_z: AtomicFact = InFact::new(endpoint, z_set.clone(), lf.clone()).into();
34            let in_z_ok = self.verify_atomic_fact(&in_z, &verify_state)?;
35            if in_z_ok.is_unknown() {
36                return Err(short_exec_error(
37                    stmt.clone().into(),
38                    format!(
39                        "by enumerate closed_range: range {} endpoint must be known in Z (`{}`)",
40                        side, in_z
41                    ),
42                    None,
43                    vec![],
44                ));
45            }
46        }
47
48        let branches = match or_branches_integer_closed_range_equalities(
49            stmt.element.clone(),
50            &stmt.closed_range,
51            &stmt.line_file,
52        ) {
53            Ok(b) => {
54                if b.is_empty() {
55                    return Err(short_exec_error(
56                        stmt.clone().into(),
57                        "by enumerate closed_range: integer range is empty (end < start)"
58                            .to_string(),
59                        None,
60                        vec![],
61                    ));
62                }
63                b
64            }
65            Err(literal_err) => match or_branches_closed_range_start_plus_offset_equalities(
66                stmt.element.clone(),
67                &stmt.closed_range,
68                &stmt.line_file,
69            ) {
70                Ok(b) => b,
71                Err(offset_err) => {
72                    return Err(short_exec_error(
73                        stmt.clone().into(),
74                        format!("{} ({})", offset_err, literal_err),
75                        None,
76                        vec![],
77                    ));
78                }
79            },
80        };
81        let disjunction = OrFact::new(branches, stmt.line_file.clone());
82        let disjunction_fact: Fact = disjunction.into();
83        let infer_after_store = self
84            .verify_well_defined_and_store_and_infer_with_default_verify_state(
85                disjunction_fact.clone(),
86            )
87            .map_err(|e| exec_stmt_error_with_stmt_and_cause(stmt.clone().into(), e))?;
88
89        let mut infer_result = InferResult::new();
90        infer_result.new_fact(&disjunction_fact);
91        infer_result.new_infer_result_inside(infer_after_store);
92
93        Ok(NonFactualStmtSuccess::new(stmt.clone().into(), infer_result, vec![]).into())
94    }
95}
96
97fn closed_range_endpoint_integer_string(obj: &Obj) -> Result<String, String> {
98    let Obj::Number(n) = obj else {
99        return Err(
100            "by enumerate closed_range: range endpoints must be integer literals".to_string(),
101        );
102    };
103    let s = n.normalized_value.clone();
104    if !is_number_string_literally_integer_without_dot(s.clone()) {
105        return Err(
106            "by enumerate closed_range: range endpoints must be integers (no decimal point)"
107                .to_string(),
108        );
109    }
110    Ok(s)
111}
112
113fn or_branches_integer_closed_range_equalities(
114    element: Obj,
115    closed: &ClosedRange,
116    line_file: &LineFile,
117) -> Result<Vec<AndChainAtomicFact>, String> {
118    let start_s = closed_range_endpoint_integer_string(closed.start.as_ref())?;
119    let end_s = closed_range_endpoint_integer_string(closed.end.as_ref())?;
120    let start_i: i128 = start_s
121        .parse()
122        .map_err(|_| format!("by enumerate closed_range: invalid integer `{}`", start_s))?;
123    let end_i: i128 = end_s
124        .parse()
125        .map_err(|_| format!("by enumerate closed_range: invalid integer `{}`", end_s))?;
126
127    let mut branches: Vec<AndChainAtomicFact> = Vec::new();
128    let mut v = start_i;
129    while v <= end_i {
130        let eq = EqualFact::new(
131            element.clone(),
132            Number::new(v.to_string()).into(),
133            line_file.clone(),
134        );
135        branches.push(AndChainAtomicFact::AtomicFact(eq.into()));
136        v += 1;
137    }
138    Ok(branches)
139}
140
141/// `closed_range(start, start + N)` with integer literal `N >= 0`; `start` may be any obj.
142fn or_branches_closed_range_start_plus_offset_equalities(
143    element: Obj,
144    closed: &ClosedRange,
145    line_file: &LineFile,
146) -> Result<Vec<AndChainAtomicFact>, String> {
147    let start = closed.start.as_ref();
148    let end = closed.end.as_ref();
149    let Obj::Add(add) = end else {
150        return Err(
151            "by enumerate closed_range: when start is not an integer literal, end must be start + N"
152                .to_string(),
153        );
154    };
155    if add.left.as_ref().to_string() != start.to_string() {
156        return Err(
157            "by enumerate closed_range: end must be start + N (left addend equals range start)"
158                .to_string(),
159        );
160    }
161    let Obj::Number(n) = add.right.as_ref() else {
162        return Err(
163            "by enumerate closed_range: N in start + N must be an integer literal".to_string(),
164        );
165    };
166    let s = n.normalized_value.clone();
167    if !is_number_string_literally_integer_without_dot(s.clone()) {
168        return Err(
169            "by enumerate closed_range: N in start + N must be an integer (no decimal point)"
170                .to_string(),
171        );
172    }
173    let k: i128 = s
174        .parse()
175        .map_err(|_| format!("by enumerate closed_range: invalid integer offset `{}`", s))?;
176    if k < 0 {
177        return Err(
178            "by enumerate closed_range: offset N in start + N must be non-negative".to_string(),
179        );
180    }
181
182    let mut branches: Vec<AndChainAtomicFact> = Vec::new();
183    let mut i = 0_i128;
184    while i <= k {
185        let rhs = if i == 0 {
186            start.clone()
187        } else {
188            Add::new(start.clone(), Number::new(i.to_string()).into()).into()
189        };
190        let eq = EqualFact::new(element.clone(), rhs, line_file.clone());
191        branches.push(AndChainAtomicFact::AtomicFact(eq.into()));
192        i += 1;
193    }
194    Ok(branches)
195}