1use anyhow::{Context, Result, anyhow};
22use mangle_ast as ast;
23use mangle_common::Value;
24use mangle_parse::Parser;
25use std::collections::HashMap;
26use std::io::{BufRead, BufReader, Read, Write};
27
28pub struct SimpleRowData {
30 pub tables: HashMap<String, Vec<Vec<Value>>>,
31}
32
33pub fn read_from_bytes(data: &[u8]) -> Result<SimpleRowData> {
35 let reader = BufReader::new(data);
36 read_simple_row(reader)
37}
38
39pub fn read_from_reader<R: Read>(reader: R) -> Result<SimpleRowData> {
41 let reader = BufReader::new(reader);
42 read_simple_row(reader)
43}
44
45struct PredInfo {
46 name: String,
47 arity: usize,
48 num_facts: usize,
49}
50
51fn read_simple_row<R: BufRead>(mut reader: R) -> Result<SimpleRowData> {
52 let mut line = String::new();
53
54 reader.read_line(&mut line)?;
56 let num_preds: usize = line.trim().parse().context("parsing num_preds")?;
57 line.clear();
58
59 let mut preds = Vec::with_capacity(num_preds);
61 for _ in 0..num_preds {
62 reader.read_line(&mut line)?;
63 let parts: Vec<&str> = line.split_whitespace().collect();
64 if parts.len() != 3 {
65 return Err(anyhow!("Invalid predicate header: {line}"));
66 }
67 let name = parts[0].to_string();
68 let arity: usize = parts[1].parse().context("parsing arity")?;
69 let num_facts: usize = parts[2].parse().context("parsing num_facts")?;
70 preds.push(PredInfo {
71 name,
72 arity,
73 num_facts,
74 });
75 line.clear();
76 }
77
78 let mut tables = HashMap::new();
79
80 let arena = ast::Arena::new_with_global_interner();
82 for pred in &preds {
83 let mut facts = Vec::with_capacity(pred.num_facts);
84
85 if pred.arity == 0 {
86 for _ in 0..pred.num_facts {
88 line.clear();
89 reader.read_line(&mut line)?;
90 facts.push(vec![]);
92 }
93 tables.insert(pred.name.clone(), facts);
94 continue;
95 }
96
97 for _ in 0..pred.num_facts {
98 line.clear();
99 if reader.read_line(&mut line)? == 0 {
100 return Err(anyhow!("Unexpected EOF reading facts for {}", pred.name));
101 }
102 let text = line.trim();
103 if text.is_empty() {
104 continue;
105 }
106
107 let mut parser = Parser::new(&arena, text.as_bytes(), "simplerow");
109 parser.next_token().map_err(|e| anyhow!(e))?;
110 let clause = parser.parse_clause()?;
111 let atom = &clause.head;
112
113 let mut tuple = Vec::with_capacity(pred.arity);
114 for arg in atom.args {
115 tuple.push(term_to_value(arg));
116 }
117 facts.push(tuple);
118 }
119 tables.insert(pred.name.clone(), facts);
120 }
121
122 Ok(SimpleRowData { tables })
123}
124
125fn term_to_value(term: &ast::BaseTerm) -> Value {
126 match term {
127 ast::BaseTerm::Const(ast::Const::Number(n)) => Value::Number(*n),
128 ast::BaseTerm::Const(ast::Const::String(s)) => Value::String(s.to_string()),
129 ast::BaseTerm::Const(ast::Const::Name(n)) => {
130 Value::String(format!("{n:?}"))
132 }
133 _ => Value::String(format!("{term:?}")),
134 }
135}
136
137pub fn write_simple_row<W: Write>(
139 writer: &mut W,
140 tables: &[(String, Vec<Vec<Value>>)],
141) -> Result<()> {
142 writeln!(writer, "{}", tables.len())?;
144
145 for (name, facts) in tables {
147 let arity = facts.first().map_or(0, |f| f.len());
148 writeln!(writer, "{} {} {}", name, arity, facts.len())?;
149 }
150
151 for (name, facts) in tables {
153 for tuple in facts {
154 write!(writer, "{name}(")?;
155 for (i, val) in tuple.iter().enumerate() {
156 if i > 0 {
157 write!(writer, ", ")?;
158 }
159 write!(writer, "{val}")?;
160 }
161 writeln!(writer, ").")?;
162 }
163 }
164
165 Ok(())
166}
167
168#[cfg(test)]
169mod tests {
170 use super::*;
171
172 #[test]
173 fn test_round_trip() -> Result<()> {
174 let tables = vec![
175 (
176 "edge".to_string(),
177 vec![
178 vec![Value::Number(1), Value::Number(2)],
179 vec![Value::Number(2), Value::Number(3)],
180 ],
181 ),
182 (
183 "user".to_string(),
184 vec![vec![Value::String("Alice".to_string()), Value::Number(30)]],
185 ),
186 ];
187
188 let mut buf = Vec::new();
189 write_simple_row(&mut buf, &tables)?;
190
191 let data = read_from_bytes(&buf)?;
192
193 assert_eq!(data.tables["edge"].len(), 2);
194 assert_eq!(
195 data.tables["edge"][0],
196 vec![Value::Number(1), Value::Number(2)]
197 );
198 assert_eq!(
199 data.tables["edge"][1],
200 vec![Value::Number(2), Value::Number(3)]
201 );
202
203 assert_eq!(data.tables["user"].len(), 1);
204 assert_eq!(
205 data.tables["user"][0],
206 vec![Value::String("Alice".to_string()), Value::Number(30)]
207 );
208
209 Ok(())
210 }
211
212 #[test]
213 fn test_write_format() -> Result<()> {
214 let tables = vec![(
215 "p".to_string(),
216 vec![vec![Value::Number(42), Value::String("hello".to_string())]],
217 )];
218
219 let mut buf = Vec::new();
220 write_simple_row(&mut buf, &tables)?;
221 let output = String::from_utf8(buf)?;
222
223 assert!(output.contains("1\n")); assert!(output.contains("p 2 1\n")); assert!(output.contains("p(42, \"hello\").\n"));
226
227 Ok(())
228 }
229}