litex/execute/by_stmt/
closed_range_by_stmt.rs1use 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
141fn 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}