1use std::collections::{HashMap, VecDeque};
17
18use logicaffeine_compile::ast::stmt::{BinaryOpKind, Block, Expr, Literal, SelectBranch, Stmt};
19use logicaffeine_compile::Interner;
20use logicaffeine_verify::{BitVecOp, VerifyExpr};
21
22type FuncTable<'a> = HashMap<String, (Vec<String>, Block<'a>)>;
25
26struct SplitMix64 {
31 state: u64,
32}
33
34impl SplitMix64 {
35 fn new(seed: u64) -> Self {
36 SplitMix64 { state: seed }
37 }
38
39 fn next_u64(&mut self) -> u64 {
40 self.state = self.state.wrapping_add(0x9E37_79B9_7F4A_7C15);
41 let mut z = self.state;
42 z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
43 z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
44 z ^ (z >> 31)
45 }
46
47 fn below(&mut self, n: usize) -> usize {
50 if n <= 1 {
51 return 0;
52 }
53 (self.next_u64() % n as u64) as usize
54 }
55}
56
57pub const INT_WIDTH: u32 = 64;
59
60#[derive(Debug, Clone)]
62pub enum SymValue {
63 Int(VerifyExpr),
65 Bool(VerifyExpr),
67 Chan(usize),
71}
72
73#[derive(Debug, Clone)]
76pub struct SymSummary {
77 pub outputs: Vec<SymValue>,
79 pub errored: VerifyExpr,
82}
83
84#[derive(Debug, Clone)]
87pub struct Unsupported(pub String);
88
89fn unsupported<T>(reason: impl Into<String>) -> Result<T, Unsupported> {
90 Err(Unsupported(reason.into()))
91}
92
93struct State {
95 env: HashMap<String, SymValue>,
96 outputs: Vec<SymValue>,
97 errored: VerifyExpr,
98 channels: HashMap<usize, VecDeque<SymValue>>,
100 chan_counter: usize,
102 rng: Option<SplitMix64>,
105}
106
107impl State {
108 fn new() -> Self {
109 State {
110 env: HashMap::new(),
111 outputs: Vec::new(),
112 errored: VerifyExpr::bool(false),
113 channels: HashMap::new(),
114 chan_counter: 0,
115 rng: None,
116 }
117 }
118}
119
120pub fn execute(stmts: &[Stmt], interner: &Interner) -> Result<SymSummary, Unsupported> {
122 let funcs = collect_funcs(stmts, interner);
123 let mut state = State::new();
124 exec_block(&mut state, stmts, interner, &funcs)?;
125 Ok(SymSummary {
126 outputs: state.outputs,
127 errored: state.errored,
128 })
129}
130
131pub fn execute_seeded(stmts: &[Stmt], interner: &Interner, seed: u64) -> Result<SymSummary, Unsupported> {
136 let funcs = collect_funcs(stmts, interner);
137 let mut state = State::new();
138 state.rng = Some(SplitMix64::new(seed));
139 exec_block(&mut state, stmts, interner, &funcs)?;
140 Ok(SymSummary {
141 outputs: state.outputs,
142 errored: state.errored,
143 })
144}
145
146fn collect_funcs<'a>(stmts: &'a [Stmt<'a>], interner: &Interner) -> FuncTable<'a> {
148 let mut table = FuncTable::new();
149 for stmt in stmts {
150 if let Stmt::FunctionDef { name, params, body, is_native: false, .. } = stmt {
151 let param_names = params.iter().map(|(p, _)| interner.resolve(*p).to_string()).collect();
152 table.insert(interner.resolve(*name).to_string(), (param_names, *body));
153 }
154 }
155 table
156}
157
158fn exec_block(
159 state: &mut State,
160 stmts: &[Stmt],
161 interner: &Interner,
162 funcs: &FuncTable,
163) -> Result<(), Unsupported> {
164 for stmt in stmts {
165 exec_stmt(state, stmt, interner, funcs)?;
166 }
167 Ok(())
168}
169
170fn resolve_chan(state: &mut State, expr: &Expr, interner: &Interner) -> Result<usize, Unsupported> {
172 match eval(state, expr, interner)? {
173 SymValue::Chan(id) => Ok(id),
174 _ => unsupported("pipe operand is not a channel"),
175 }
176}
177
178fn exec_stmt(
179 state: &mut State,
180 stmt: &Stmt,
181 interner: &Interner,
182 funcs: &FuncTable,
183) -> Result<(), Unsupported> {
184 match stmt {
185 Stmt::FunctionDef { .. } => Ok(()),
187 Stmt::CreatePipe { var, .. } => {
189 let id = state.chan_counter;
190 state.chan_counter += 1;
191 state.channels.insert(id, VecDeque::new());
192 state.env.insert(interner.resolve(*var).to_string(), SymValue::Chan(id));
193 Ok(())
194 }
195 Stmt::SendPipe { value, pipe } => {
196 let v = eval(state, value, interner)?;
197 let id = resolve_chan(state, pipe, interner)?;
198 state.channels.get_mut(&id).expect("channel id is allocated").push_back(v);
199 Ok(())
200 }
201 Stmt::ReceivePipe { var, pipe } => {
202 let id = resolve_chan(state, pipe, interner)?;
203 let v = match state.channels.get_mut(&id).and_then(|q| q.pop_front()) {
204 Some(v) => v,
205 None => return unsupported("receive on an empty channel (not statically resolvable)"),
208 };
209 state.env.insert(interner.resolve(*var).to_string(), v);
210 Ok(())
211 }
212 Stmt::LaunchTask { function, args } => {
216 let fname = interner.resolve(*function).to_string();
217 let (params, body) = match funcs.get(&fname) {
218 Some(f) => f.clone(),
219 None => return unsupported(format!("launch of unknown task '{fname}'")),
220 };
221 if params.len() != args.len() {
222 return unsupported(format!("task '{fname}' arity mismatch"));
223 }
224 let arg_vals = args
225 .iter()
226 .map(|a| eval(state, a, interner))
227 .collect::<Result<Vec<_>, _>>()?;
228 let saved = std::mem::take(&mut state.env);
229 for (p, v) in params.iter().zip(arg_vals) {
230 state.env.insert(p.clone(), v);
231 }
232 let r = exec_block(state, body, interner, funcs);
233 state.env = saved;
234 r
235 }
236 Stmt::Concurrent { tasks } | Stmt::Parallel { tasks } => {
237 exec_block(state, tasks, interner, funcs)
239 }
240 Stmt::Select { branches } => {
246 if state.rng.is_none() {
247 return unsupported("Select requires seeded mode (nondeterministic)");
248 }
249 let mut ready: Vec<usize> = Vec::new();
251 for (i, b) in branches.iter().enumerate() {
252 if let SelectBranch::Receive { pipe, .. } = b {
253 let id = resolve_chan(state, pipe, interner)?;
254 if state.channels.get(&id).map(|q| !q.is_empty()).unwrap_or(false) {
255 ready.push(i);
256 }
257 }
258 }
259 let winner = if !ready.is_empty() {
260 let k = state.rng.as_mut().unwrap().below(ready.len());
261 ready[k]
262 } else {
263 match branches.iter().position(|b| matches!(b, SelectBranch::Timeout { .. })) {
265 Some(i) => i,
266 None => return unsupported("Select with no ready arm and no timeout would block"),
267 }
268 };
269 match &branches[winner] {
270 SelectBranch::Receive { var, pipe, body } => {
271 let id = resolve_chan(state, pipe, interner)?;
272 let v = state
273 .channels
274 .get_mut(&id)
275 .and_then(|q| q.pop_front())
276 .ok_or_else(|| Unsupported("Select winner channel empty".into()))?;
277 state.env.insert(interner.resolve(*var).to_string(), v);
278 exec_block(state, body, interner, funcs)
279 }
280 SelectBranch::Timeout { body, .. } => exec_block(state, body, interner, funcs),
281 }
282 }
283 Stmt::Let { var, value, .. } => {
284 let v = eval(state, value, interner)?;
285 state.env.insert(interner.resolve(*var).to_string(), v);
286 Ok(())
287 }
288 Stmt::Set { target, value } => {
289 let name = interner.resolve(*target).to_string();
290 if !state.env.contains_key(&name) {
291 return unsupported(format!("Set to variable '{name}' not in scope"));
292 }
293 let v = eval(state, value, interner)?;
294 state.env.insert(name, v);
295 Ok(())
296 }
297 Stmt::Show { object, recipient } => {
298 if let Expr::Identifier(sym) = recipient {
301 if interner.resolve(*sym) == "show" {
302 let v = eval(state, object, interner)?;
303 state.outputs.push(v);
304 return Ok(());
305 }
306 }
307 unsupported("Show to a non-console recipient")
308 }
309 other => unsupported(format!("statement {}", stmt_kind(other))),
310 }
311}
312
313fn eval(state: &mut State, expr: &Expr, interner: &Interner) -> Result<SymValue, Unsupported> {
314 match expr {
315 Expr::Literal(Literal::Number(n)) => {
316 Ok(SymValue::Int(VerifyExpr::bv_const(INT_WIDTH, *n as u64)))
317 }
318 Expr::Literal(Literal::Boolean(b)) => Ok(SymValue::Bool(VerifyExpr::bool(*b))),
319 Expr::Literal(_) => unsupported("non-Int/Bool literal"),
320 Expr::Identifier(sym) => {
321 let name = interner.resolve(*sym);
322 state
323 .env
324 .get(name)
325 .cloned()
326 .ok_or_else(|| Unsupported(format!("reference to unbound variable '{name}'")))
327 }
328 Expr::BinaryOp { op, left, right } => {
329 let l = eval(state, left, interner)?;
330 let r = eval(state, right, interner)?;
331 match op {
332 BinaryOpKind::Divide | BinaryOpKind::Modulo => {
337 let (a, b) = match (l, r) {
338 (SymValue::Int(a), SymValue::Int(b)) => (a, b),
339 _ => return unsupported("division on non-Int operands"),
340 };
341 let div_by_zero = VerifyExpr::bv_binary(
342 BitVecOp::Eq,
343 b.clone(),
344 VerifyExpr::bv_const(INT_WIDTH, 0),
345 );
346 state.errored = VerifyExpr::or(state.errored.clone(), div_by_zero);
347 let bvop = if matches!(op, BinaryOpKind::Divide) {
348 BitVecOp::SDiv
349 } else {
350 BitVecOp::SRem
351 };
352 Ok(SymValue::Int(VerifyExpr::bv_binary(bvop, a, b)))
353 }
354 _ => apply_binop(*op, l, r),
355 }
356 }
357 Expr::Not { operand } => match eval(state, operand, interner)? {
358 SymValue::Bool(e) => Ok(SymValue::Bool(VerifyExpr::not(e))),
359 SymValue::Int(e) => Ok(SymValue::Int(VerifyExpr::bv_binary(
361 BitVecOp::Xor,
362 e,
363 VerifyExpr::bv_const(INT_WIDTH, u64::MAX),
364 ))),
365 SymValue::Chan(_) => unsupported("`not` on a channel"),
366 },
367 other => unsupported(format!("expression {}", expr_kind(other))),
368 }
369}
370
371fn apply_binop(op: BinaryOpKind, l: SymValue, r: SymValue) -> Result<SymValue, Unsupported> {
372 use BinaryOpKind::*;
373 use SymValue::{Bool, Int};
374 match (op, l, r) {
375 (Add, Int(a), Int(b)) => Ok(Int(VerifyExpr::bv_binary(BitVecOp::Add, a, b))),
377 (Subtract, Int(a), Int(b)) => Ok(Int(VerifyExpr::bv_binary(BitVecOp::Sub, a, b))),
378 (Multiply, Int(a), Int(b)) => Ok(Int(VerifyExpr::bv_binary(BitVecOp::Mul, a, b))),
379
380 (Lt, Int(a), Int(b)) => Ok(Bool(VerifyExpr::bv_binary(BitVecOp::SLt, a, b))),
382 (Gt, Int(a), Int(b)) => Ok(Bool(VerifyExpr::bv_binary(BitVecOp::SLt, b, a))),
383 (LtEq, Int(a), Int(b)) => Ok(Bool(VerifyExpr::bv_binary(BitVecOp::SLe, a, b))),
384 (GtEq, Int(a), Int(b)) => Ok(Bool(VerifyExpr::bv_binary(BitVecOp::SLe, b, a))),
385
386 (Eq, Int(a), Int(b)) => Ok(Bool(VerifyExpr::bv_binary(BitVecOp::Eq, a, b))),
388 (Eq, Bool(a), Bool(b)) => Ok(Bool(VerifyExpr::iff(a, b))),
389 (NotEq, Int(a), Int(b)) => Ok(Bool(VerifyExpr::not(VerifyExpr::bv_binary(
390 BitVecOp::Eq,
391 a,
392 b,
393 )))),
394 (NotEq, Bool(a), Bool(b)) => Ok(Bool(VerifyExpr::not(VerifyExpr::iff(a, b)))),
395
396 (And, Bool(a), Bool(b)) => Ok(Bool(VerifyExpr::and(a, b))),
398 (And, Int(a), Int(b)) => Ok(Int(VerifyExpr::bv_binary(BitVecOp::And, a, b))),
399 (Or, Bool(a), Bool(b)) => Ok(Bool(VerifyExpr::or(a, b))),
400 (Or, Int(a), Int(b)) => Ok(Int(VerifyExpr::bv_binary(BitVecOp::Or, a, b))),
401
402 (op, _, _) => unsupported(format!("operator {op:?} on these operand types")),
403 }
404}
405
406fn stmt_kind(s: &Stmt) -> &'static str {
407 match s {
408 Stmt::If { .. } => "If",
409 Stmt::While { .. } => "While",
410 Stmt::Repeat { .. } => "Repeat",
411 Stmt::Return { .. } => "Return",
412 Stmt::Inspect { .. } => "Inspect",
413 Stmt::FunctionDef { .. } => "FunctionDef",
414 Stmt::Call { .. } => "Call",
415 _ => "<other>",
416 }
417}
418
419fn expr_kind(e: &Expr) -> &'static str {
420 match e {
421 Expr::Call { .. } => "Call",
422 Expr::Index { .. } => "Index",
423 Expr::FieldAccess { .. } => "FieldAccess",
424 Expr::List(_) => "List",
425 Expr::InterpolatedString(_) => "InterpolatedString",
426 _ => "<other>",
427 }
428}