smtlib_lowlevel/backend/
mod.rs1use std::{
25 io::{BufRead, BufReader, Write},
26 process::{Child, ChildStdin, ChildStdout},
27};
28
29use logos::Lexer;
30
31pub mod cvc5_binary;
32pub mod z3_binary;
33#[cfg(feature = "z3-static")]
34pub mod z3_static;
35
36use crate::parse::Token;
37
38pub trait Backend {
43 fn exec(&mut self, cmd: crate::Command) -> Result<String, crate::Error>;
44}
45
46struct BinaryBackend {
47 #[allow(unused)]
48 child: Child,
49 stdin: ChildStdin,
50 stdout: BufReader<ChildStdout>,
51 buf: String,
52}
53
54impl BinaryBackend {
55 pub(crate) fn new(
56 program: impl AsRef<std::ffi::OsStr>,
57 init: impl FnOnce(&mut std::process::Command),
58 ) -> Result<Self, std::io::Error> {
59 use std::process::{Command, Stdio};
60
61 let mut cmd = Command::new(program);
62 init(&mut cmd);
63 let mut child = cmd.stdin(Stdio::piped()).stdout(Stdio::piped()).spawn()?;
64 let stdin = child.stdin.take().unwrap();
65 let stdout = BufReader::new(child.stdout.take().unwrap());
66
67 Ok(BinaryBackend {
68 child,
69 stdin,
70 stdout,
71 buf: String::new(),
72 })
73 }
74 pub(crate) fn exec(&mut self, cmd: crate::Command) -> Result<&str, crate::Error> {
75 writeln!(self.stdin, "{cmd}")?;
77 self.stdin.flush()?;
78
79 self.buf.clear();
80 loop {
81 let n = self.stdout.read_line(&mut self.buf)?;
82 if n == 0 {
83 continue;
84 }
85 if Lexer::new(self.buf.as_str())
86 .map(|tok| tok.unwrap_or(Token::Error))
87 .fold(0i32, |acc, tok| match tok {
88 Token::LParen => acc + 1,
89 Token::RParen => acc - 1,
90 _ => acc,
91 })
92 != 0
93 {
94 continue;
95 }
96 return Ok(&self.buf);
97 }
98 }
99}
100
101#[cfg(feature = "tokio")]
102pub mod tokio {
103 use std::future::Future;
104
105 use logos::Lexer;
106 use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
107
108 use crate::parse::Token;
109
110 pub trait TokioBackend {
116 fn exec(
117 &mut self,
118 cmd: crate::Command,
119 ) -> impl Future<Output = Result<String, crate::Error>>;
120 }
121
122 pub(crate) struct TokioBinaryBackend {
123 #[allow(unused)]
124 child: tokio::process::Child,
125 stdin: tokio::process::ChildStdin,
126 stdout: BufReader<tokio::process::ChildStdout>,
127 buf: String,
128 }
129
130 #[cfg(feature = "tokio")]
131 impl TokioBinaryBackend {
132 pub(crate) async fn new(
133 program: impl AsRef<std::ffi::OsStr>,
134 init: impl FnOnce(&mut tokio::process::Command),
135 ) -> Result<Self, std::io::Error> {
136 use std::process::Stdio;
137
138 use tokio::process::Command;
139
140 let mut cmd = Command::new(program);
141 init(&mut cmd);
142 let mut child = cmd
143 .kill_on_drop(true)
144 .stdin(Stdio::piped())
145 .stdout(Stdio::piped())
146 .spawn()?;
147 let stdin = child.stdin.take().unwrap();
148 let stdout = BufReader::new(child.stdout.take().unwrap());
149
150 Ok(TokioBinaryBackend {
151 child,
152 stdin,
153 stdout,
154 buf: String::new(),
155 })
156 }
157 pub(crate) async fn exec(&mut self, cmd: crate::Command<'_>) -> Result<&str, crate::Error> {
158 self.stdin.write_all(cmd.to_string().as_bytes()).await?;
160 self.stdin.write_all(b"\n").await?;
161 self.stdin.flush().await?;
162
163 self.buf.clear();
164 loop {
165 let n = self.stdout.read_line(&mut self.buf).await?;
166 if n == 0 {
167 continue;
168 }
169 if Lexer::new(self.buf.as_str())
170 .map(|tok| tok.unwrap_or(Token::Error))
171 .fold(0i32, |acc, tok| match tok {
172 Token::LParen => acc + 1,
173 Token::RParen => acc - 1,
174 _ => acc,
175 })
176 != 0
177 {
178 continue;
179 }
180 return Ok(&self.buf);
181 }
182 }
183 }
184}