1use btor2tools_sys::{
24 btor2parser_delete, btor2parser_error, btor2parser_iter_init, btor2parser_iter_next,
25 btor2parser_new, btor2parser_read_lines, fclose, fopen, Btor2Line as CBtor2Line,
26 Btor2LineIterator as CBtor2LineIterator, Btor2Parser as CBtor2Parser,
27 Btor2SortTag as CBtor2SortTag, Btor2Tag as CBtor2Tag,
28};
29use std::{
30 convert::From,
31 ffi::{CStr, CString},
32 fmt,
33 marker::PhantomData,
34 os::raw::c_char,
35 path::Path,
36 slice,
37};
38use thiserror::Error;
39
40pub struct Btor2Parser {
41 internal: *mut CBtor2Parser,
42}
43
44impl Btor2Parser {
45 pub fn new() -> Self {
46 Self {
47 internal: unsafe { btor2parser_new() },
48 }
49 }
50
51 pub fn read_lines<P>(&mut self, file: P) -> Result<Btor2LineIterator, Btor2ParserError>
54 where
55 P: AsRef<Path>,
56 {
57 unsafe {
58 let file_path = if let Some(p) = file.as_ref().to_str() {
59 p
60 } else {
61 return Err(Btor2ParserError::InvalidPathEncoding(String::from(
62 "Path is not UTF-8 encoded",
63 )));
64 };
65
66 let c_file_path = CString::new(file_path).map_err(|_| {
67 Btor2ParserError::InvalidPathEncoding(String::from(
68 "Path contains a illegal 0 byte",
69 ))
70 })?;
71
72 let c_file_mode = CString::new("r").unwrap();
73
74 let file = fopen(c_file_path.as_ptr(), c_file_mode.as_ptr());
75
76 if file.is_null() {
77 Err(Btor2ParserError::CouldNotOpenFile(file_path.to_owned()))
78 } else {
79 let result = btor2parser_read_lines(self.internal, file);
80
81 fclose(file);
82
83 if result == 0 {
84 let c_msg = CStr::from_ptr(btor2parser_error(self.internal));
85
86 Err(Btor2ParserError::SyntaxError(
87 c_msg
88 .to_str()
89 .expect("Btor2tools do not use valid UTF-8 strings")
90 .to_owned(),
91 ))
92 } else {
93 Ok(Btor2LineIterator::new(self))
94 }
95 }
96 }
97 }
98}
99
100impl Drop for Btor2Parser {
101 fn drop(&mut self) {
102 unsafe { btor2parser_delete(self.internal) }
103 }
104}
105
106#[derive(Error, Debug)]
107pub enum Btor2ParserError {
108 #[error("Could not open file: {0}")]
109 CouldNotOpenFile(String),
110
111 #[error("BTOR2 syntax error in {0}")]
112 SyntaxError(String),
113
114 #[error("File path violates encoding rules: {0}")]
115 InvalidPathEncoding(String),
116}
117
118#[derive(Copy, Clone)]
119pub struct Btor2LineIterator<'parser> {
120 parser: PhantomData<&'parser Btor2Parser>,
121 internal: CBtor2LineIterator,
122}
123
124impl<'parser> Btor2LineIterator<'parser> {
125 fn new(parser: &'parser Btor2Parser) -> Self {
126 Self {
127 parser: PhantomData,
128 internal: unsafe { btor2parser_iter_init(parser.internal) },
129 }
130 }
131}
132
133impl<'parser> Iterator for Btor2LineIterator<'parser> {
134 type Item = Btor2Line<'parser>;
135
136 fn next(&mut self) -> Option<Self::Item> {
137 unsafe {
138 let c_line = btor2parser_iter_next(&mut self.internal);
139
140 if c_line.is_null() {
141 None
142 } else {
143 Some(Btor2Line::new(c_line))
144 }
145 }
146 }
147}
148
149#[derive(Clone)]
150pub struct Btor2Line<'parser> {
151 parser: PhantomData<&'parser Btor2Parser>,
152 internal: *const CBtor2Line,
153}
154
155impl<'parser> Btor2Line<'parser> {
156 fn new(internal: *mut CBtor2Line) -> Self {
157 Self {
158 parser: PhantomData,
159 internal,
160 }
161 }
162
163 pub fn id(&self) -> i64 {
165 unsafe { (*self.internal).id }
166 }
167
168 pub fn lineno(&self) -> i64 {
170 unsafe { (*self.internal).lineno }
171 }
172
173 pub fn name(&self) -> &CStr {
175 unsafe { CStr::from_ptr((*self.internal).name) }
176 }
177
178 pub fn tag(&self) -> Btor2Tag {
180 unsafe { Btor2Tag::from((*self.internal).tag) }
181 }
182
183 pub fn sort(&self) -> Btor2Sort {
184 Btor2Sort {
185 line: PhantomData,
186 internal: self.internal,
187 }
188 }
189
190 pub fn init(&self) -> i64 {
192 unsafe { (*self.internal).init }
193 }
194
195 pub fn next(&self) -> i64 {
197 unsafe { (*self.internal).next }
198 }
199
200 pub fn constant(&self) -> Option<&CStr> {
202 wrap_nullable_c_string(unsafe { (*self.internal).constant })
203 }
204
205 pub fn symbol(&self) -> Option<&CStr> {
207 wrap_nullable_c_string(unsafe { (*self.internal).symbol })
208 }
209
210 pub fn args(&self) -> &[i64] {
212 unsafe { slice::from_raw_parts((*self.internal).args, (*self.internal).nargs as usize) }
213 }
214}
215
216impl<'parser> fmt::Debug for Btor2Line<'parser> {
217 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
218 f.debug_struct("Btor2Line")
219 .field("id", &self.id())
220 .field("lineno", &self.lineno())
221 .field("name", &self.name())
222 .field("tag", &self.tag())
223 .field("sort", &self.sort())
224 .field("init", &self.init())
225 .field("next", &self.next())
226 .field("constant", &self.constant())
227 .field("symbol", &self.symbol())
228 .field("args", &self.args())
229 .finish()
230 }
231}
232
233#[derive(Copy, Clone)]
234pub struct Btor2Sort<'line, 'parser> {
235 line: PhantomData<&'line Btor2Line<'parser>>,
236 internal: *const CBtor2Line,
237}
238
239impl<'line, 'parser> Btor2Sort<'line, 'parser> {
240 pub fn id(&self) -> i64 {
241 unsafe { (*self.internal).sort.id }
242 }
243
244 pub fn tag(&self) -> Btor2SortTag {
245 unsafe { Btor2SortTag::from((*self.internal).sort.tag) }
246 }
247
248 pub fn name(&self) -> Option<&CStr> {
249 wrap_nullable_c_string(unsafe { (*self.internal).sort.name })
250 }
251
252 pub fn content(&self) -> Btor2SortContent {
253 unsafe {
254 match self.tag() {
255 Btor2SortTag::Array => Btor2SortContent::Array {
256 index: (*self.internal).sort.__bindgen_anon_1.array.index,
257 element: (*self.internal).sort.__bindgen_anon_1.array.element,
258 },
259 Btor2SortTag::Bitvec => Btor2SortContent::Bitvec {
260 width: (*self.internal).sort.__bindgen_anon_1.bitvec.width,
261 },
262 }
263 }
264 }
265}
266
267impl<'line, 'parser> fmt::Debug for Btor2Sort<'line, 'parser> {
268 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
269 f.debug_struct("Btor2Sort")
270 .field("id", &self.id())
271 .field("tag", &self.tag())
272 .field("name", &self.name())
273 .field("content", &self.content())
274 .finish()
275 }
276}
277
278#[derive(Debug, Copy, Clone)]
279pub enum Btor2SortContent {
280 Array { index: i64, element: i64 },
281 Bitvec { width: u32 },
282}
283
284#[repr(C)]
291#[derive(Debug, Copy, Clone)]
292pub enum Btor2Tag {
293 Add,
294 And,
295 Bad,
296 Concat,
297 Const,
298 Constraint,
299 Constd,
300 Consth,
301 Dec,
302 Eq,
303 Fair,
304 Iff,
305 Implies,
306 Inc,
307 Init,
308 Input,
309 Ite,
310 Justice,
311 Mul,
312 Nand,
313 Neq,
314 Neg,
315 Next,
316 Nor,
317 Not,
318 One,
319 Ones,
320 Or,
321 Output,
322 Read,
323 Redand,
324 Redor,
325 Redxor,
326 Rol,
327 Ror,
328 Saddo,
329 Sdiv,
330 Sdivo,
331 Sext,
332 Sgt,
333 Sgte,
334 Slice,
335 Sll,
336 Slt,
337 Slte,
338 Sort,
339 Smod,
340 Smulo,
341 Sra,
342 Srem,
343 Srl,
344 Ssubo,
345 State,
346 Sub,
347 Uaddo,
348 Udiv,
349 Uext,
350 Ugt,
351 Ugte,
352 Ult,
353 Ulte,
354 Umulo,
355 Urem,
356 Usubo,
357 Write,
358 Xnor,
359 Xor,
360 Zero,
361}
362
363impl From<CBtor2Tag> for Btor2Tag {
364 fn from(raw: CBtor2Tag) -> Btor2Tag {
365 unsafe { core::mem::transmute(raw) }
366 }
367}
368
369impl Into<CBtor2Tag> for Btor2Tag {
370 fn into(self) -> CBtor2Tag {
371 unsafe { core::mem::transmute(self) }
372 }
373}
374
375#[repr(C)]
376#[derive(Debug, Copy, Clone)]
377pub enum Btor2SortTag {
378 Array,
379 Bitvec,
380}
381
382impl From<CBtor2SortTag> for Btor2SortTag {
383 fn from(raw: CBtor2SortTag) -> Btor2SortTag {
384 unsafe { std::mem::transmute(raw) }
385 }
386}
387
388impl Into<CBtor2SortTag> for Btor2SortTag {
389 fn into(self) -> CBtor2SortTag {
390 unsafe { std::mem::transmute(self) }
391 }
392}
393
394fn wrap_nullable_c_string<'a>(str: *const c_char) -> Option<&'a CStr> {
395 unsafe {
396 if str.is_null() {
397 None
398 } else {
399 Some(CStr::from_ptr(str))
400 }
401 }
402}