1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Serialize, Deserialize)]
5pub struct CanonicalCnf {
6 pub version: String,
7 pub vars: Vec<VarDef>,
8 #[serde(default)]
9 pub imports: Vec<ImportDef>,
10 #[serde(default)]
11 pub functions: Vec<FunctionDef>,
12 pub clauses: Vec<ClauseDef>,
13 pub metadata: Metadata,
14}
15
16#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct VarDef {
18 pub name: String,
19 #[serde(rename = "type")]
20 pub var_type: String, #[serde(skip_serializing_if = "Option::is_none")]
22 pub dim1: Option<usize>,
23 #[serde(skip_serializing_if = "Option::is_none")]
24 pub dim2: Option<usize>,
25}
26
27#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct ImportDef {
29 pub path: String,
30 pub hash: String,
31 pub content: String,
32}
33
34#[derive(Debug, Clone, Serialize, Deserialize)]
35pub struct FunctionDef {
36 pub name: String,
37 pub signature: String,
38 #[serde(default)]
39 pub auto_generated: bool,
40}
41
42#[derive(Debug, Clone, Serialize, Deserialize)]
43pub struct ClauseDef {
44 pub expr: String,
45 pub id: u64,
46}
47
48#[derive(Debug, Clone, Serialize, Deserialize)]
49pub struct Metadata {
50 pub style: String,
51 pub source_hash: String,
52}
53
54impl CanonicalCnf {
55 pub fn from_reader<R: std::io::Read>(reader: R) -> serde_json::Result<Self> {
56 serde_json::from_reader(reader)
57 }
58
59 pub fn to_writer<W: std::io::Write>(&self, writer: W) -> serde_json::Result<()> {
60 serde_json::to_writer_pretty(writer, self)
61 }
62}
63
64use crate::advanced_cnf::{AdvancedCnf, VariableDef, Clause, ClauseType, AbiConstraint};
65use crate::parser::{Parser, Expr};
66use satellite_base::types::VarType;
67use std::collections::HashMap;
68
69impl TryFrom<CanonicalCnf> for AdvancedCnf {
70 type Error = String;
71
72 fn try_from(canonical: CanonicalCnf) -> Result<Self, Self::Error> {
73 let mut acnf = AdvancedCnf::new();
74 let mut name_to_id = HashMap::new();
75 let mut next_id = 1;
76
77 for var in canonical.vars {
79 let id = next_id;
80 name_to_id.insert(var.name.clone(), id);
81
82 let vtype = match var.var_type.as_str() {
83 "bool" => VarType::Bool,
84 "batch" => VarType::Batch {
85 dim: var.dim1.ok_or("Batch missing dim1")?
86 },
87 "vec" => VarType::Vec {
88 inner_dim: var.dim1.ok_or("Vec missing dim1")?,
89 outer_dim: var.dim2.ok_or("Vec missing dim2")?,
90 },
91 _ => return Err(format!("Unknown var type: {}", var.var_type)),
92 };
93
94 acnf.variables.push(VariableDef {
95 id,
96 var_type: vtype.clone(),
97 name: Some(var.name),
98 });
99
100 next_id += vtype.bool_count() as u64;
101 }
102
103 for clause_def in canonical.clauses {
105 let mut parser = Parser::new(&clause_def.expr);
106 let expr = parser.parse().map_err(|e| format!("Parse error in clause {}: {}", clause_def.id, e))?;
107
108 lower_expr(expr, &name_to_id, &mut acnf)?;
110 }
111
112 Ok(acnf)
113 }
114}
115
116fn lower_expr(expr: Expr, map: &HashMap<String, u64>, acnf: &mut AdvancedCnf) -> Result<(), String> {
117 match expr {
118 Expr::BinaryOp { op, left, right } => {
119 if op == "AND" || op == "and" || op == "&&" {
120 lower_expr(*left, map, acnf)?;
122 lower_expr(*right, map, acnf)?;
123 } else if op == "OR" || op == "or" || op == "||" {
124 let mut literals = Vec::new();
128 collect_disjunction(*left, map, &mut literals)?;
129 collect_disjunction(*right, map, &mut literals)?;
130 acnf.clauses.push(Clause {
131 literals,
132 clause_type: ClauseType::Original,
133 lbd: None
134 });
135 } else if op == "eq" {
136 let inputs = collect_vars_deep(&Expr::BinaryOp{op: op.clone(), left: left.clone(), right: right.clone()}, map);
140 acnf.abi_constraints.push(AbiConstraint {
141 name: "eq".to_string(), inputs,
143 code_hash: "interactive_eq".to_string(), cached: false,
145 });
146 } else {
147 return Err(format!("Unsupported binary op at top level: {}", op));
148 }
149 },
150 Expr::UnaryOp { .. } | Expr::Var(_) | Expr::Lit(_) => {
151 let mut literals = Vec::new();
153 collect_disjunction(expr, map, &mut literals)?;
154 acnf.clauses.push(Clause {
155 literals,
156 clause_type: ClauseType::Original,
157 lbd: None
158 });
159 }
160 _ => return Err("Unsupported top level expression".to_string())
161 }
162 Ok(())
163}
164
165fn collect_disjunction(expr: Expr, map: &HashMap<String, u64>, literals: &mut Vec<i64>) -> Result<(), String> {
166 match expr {
167 Expr::BinaryOp { op, left, right } => {
168 if op == "OR" || op == "or" || op == "||" {
169 collect_disjunction(*left, map, literals)?;
170 collect_disjunction(*right, map, literals)?;
171 } else {
172 return Err("Nested non-OR in disjunction not supported in MVP".to_string()); }
174 },
175 Expr::Var(name) => {
176 if let Some(&id) = map.get(&name) {
177 literals.push(id as i64);
181 } else {
182 if let Some(&id) = map.get(name.trim_start_matches('%')) {
184 literals.push(id as i64);
185 } else {
186 return Err(format!("Unknown variable: {}", name));
187 }
188 }
189 },
190 Expr::UnaryOp { op, expr } => {
191 if op == "NOT" || op == "not" || op == "!" || op == "-" {
192 if let Expr::Var(name) = *expr {
193 let id = if let Some(&id) = map.get(&name) { id }
194 else if let Some(&id) = map.get(name.trim_start_matches('%')) { id }
195 else { return Err(format!("Unknown variable: {}", name)); };
196 literals.push(-(id as i64));
197 } else {
198 return Err("Only direct negation of vars supported in MVP".to_string());
199 }
200 } else {
201 return Err(format!("Unknown unary op: {}", op));
202 }
203 }
204 _ => return Err("Unsupported element in disjunction".to_string())
205 }
206 Ok(())
207}
208
209fn collect_vars_deep(expr: &Expr, map: &HashMap<String, u64>) -> Vec<u64> {
210 let mut vars = Vec::new();
211 match expr {
212 Expr::Var(name) => {
213 if let Some(&id) = map.get(name) { vars.push(id); }
214 else if let Some(&id) = map.get(name.trim_start_matches('%')) { vars.push(id); }
215 },
216 Expr::BinaryOp { left, right, .. } => {
217 vars.extend(collect_vars_deep(left, map));
218 vars.extend(collect_vars_deep(right, map));
219 },
220 Expr::UnaryOp { expr, .. } => {
221 vars.extend(collect_vars_deep(expr, map));
222 },
223 Expr::Call { args, .. } => {
224 for arg in args {
225 vars.extend(collect_vars_deep(arg, map));
226 }
227 }
228 _ => {}
229 }
230 vars
231}