1pub fn escape_string(string: &str) -> String {
9 string.replace(r#"""#, r#""""#)
10}
11
12use std::fmt::Display;
13
14use predicates::{is_printable, is_white_space};
15
16use super::{
17 scripts::CommandType,
18 sexprs::{Sexpr, SpecialConstant},
19 terms::Term,
20};
21
22#[derive(Debug, Clone)]
24pub struct InvalidStringError(String);
25
26#[derive(Clone, Debug)]
27pub struct StringConstant(String);
28
29impl StringConstant {
30 pub fn new(string: String) -> Result<Self, InvalidStringError> {
31 if string.chars().all(|c| is_printable(c) || is_white_space(c)) {
32 Ok(Self(string))
33 } else {
34 Err(InvalidStringError(string))
35 }
36 }
37
38 pub fn new_str(string: &str) -> Result<Self, InvalidStringError> {
39 Self::new(string.to_string())
40 }
41
42 pub fn new_expect(string: String, expect_msg: &str) -> Self {
43 Self::new(string).expect(expect_msg)
44 }
45
46 pub fn new_str_expect(string: &str, expect_msg: &str) -> Self {
47 Self::new_expect(string.to_string(), expect_msg)
48 }
49
50 pub fn string_constant(&self) -> &str {
51 &self.0
52 }
53}
54impl From<StringConstant> for SpecialConstant {
55 fn from(value: StringConstant) -> Self {
56 SpecialConstant::String(value)
57 }
58}
59
60impl From<StringConstant> for Sexpr {
61 fn from(value: StringConstant) -> Self {
62 Sexpr::SpecialConstant(value.into())
63 }
64}
65
66impl From<i64> for Sexpr {
67 fn from(value: i64) -> Self {
68 Sexpr::SpecialConstant(value.into())
69 }
70}
71
72impl From<i64> for Term {
73 fn from(value: i64) -> Self {
74 Term::SpecialConstant(value.into())
75 }
76}
77
78impl Display for StringConstant {
79 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
80 write!(f, r#""{}""#, escape_string(&self.0))
81 }
82}
83
84pub enum Reserved {
85 Binary,
86 Decimal,
87 Hexadecimal,
88 Numeral,
89 String,
90 Underscore,
91 Bang,
92 As,
93 Let,
94 Exists,
95 Forall,
96 Match,
97 Par,
98 Command(CommandType),
99}
100
101impl Display for Reserved {
102 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103 match self {
104 Reserved::Binary => write!(f, "BINARY"),
105 Reserved::Decimal => write!(f, "DECIMAL"),
106 Reserved::Hexadecimal => write!(f, "HEXADECIMAL"),
107 Reserved::Numeral => write!(f, "NUMERAL"),
108 Reserved::String => write!(f, "STRING"),
109 Reserved::Underscore => write!(f, "_"),
110 Reserved::Bang => write!(f, "!"),
111 Reserved::As => write!(f, "as"),
112 Reserved::Let => write!(f, "let"),
113 Reserved::Exists => write!(f, "exists"),
114 Reserved::Forall => write!(f, "forall"),
115 Reserved::Match => write!(f, "match"),
116 Reserved::Par => write!(f, "par"),
117 Reserved::Command(cmd) => match cmd {
118 CommandType::Assert => write!(f, "assert"),
119 CommandType::CheckSat => write!(f, "check-sat"),
120 CommandType::CheckSatAssuming => write!(f, "check-sat-assuming"),
121 CommandType::DeclareConst => write!(f, "declare-const"),
122 CommandType::DeclareDatatype => write!(f, "declare-datatype"),
123 CommandType::DeclareDatatypes => write!(f, "declare-datatypes"),
124 CommandType::DeclareFun => write!(f, "declare-fun"),
125 CommandType::DeclareSort => write!(f, "declare-sort"),
126 CommandType::DefineFun => write!(f, "define-fun"),
127 CommandType::DefineFunRec => write!(f, "define-fun-rec"),
128 CommandType::DefineFunsRec => write!(f, "define-funs-rec"),
129 CommandType::DefineSort => write!(f, "define_sort"),
130 CommandType::Echo => write!(f, "echo"),
131 CommandType::Exit => write!(f, "exit"),
132 CommandType::GetAssertions => write!(f, "get-assertions"),
133 CommandType::GetAssignment => write!(f, "get-assignment"),
134 CommandType::GetModel => write!(f, "get-model"),
135 CommandType::GetOption => write!(f, "get-option"),
136 CommandType::GetProof => write!(f, "get-proof"),
137 CommandType::GetUnsatAssumptions => write!(f, "get-unsat-assumptions"),
138 CommandType::GetUnsatCore => write!(f, "get-unsat-core"),
139 CommandType::GetValue => write!(f, "get-value"),
140 CommandType::Pop => write!(f, "pop"),
141 CommandType::Push => write!(f, "push"),
142 CommandType::Reset => write!(f, "reset"),
143 CommandType::ResetAssertions => write!(f, "reset-assertions"),
144 CommandType::SetLogic => write!(f, "set-logic"),
145 },
146 }
147 }
148}
149
150impl From<Reserved> for Sexpr {
151 fn from(value: Reserved) -> Self {
152 Sexpr::Reserved(value)
153 }
154}
155
156#[derive(Clone, Debug, PartialEq, Eq)]
157pub struct Symbol {
158 symbol: String,
159 is_quoted: bool,
160}
161
162impl From<Symbol> for Sexpr {
163 fn from(value: Symbol) -> Self {
164 Sexpr::Symbol(value)
165 }
166}
167
168impl Display for Symbol {
169 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
170 if self.is_quoted() {
171 write!(f, "|")?;
172 write!(f, "{}", self.symbol)?;
173 write!(f, "|")
174 } else {
175 write!(f, "{}", self.symbol)
176 }
177 }
178}
179
180#[derive(Clone, Debug, PartialEq, Eq)]
181pub struct InvalidSymbolError(String);
182
183impl Symbol {
184 pub fn new(symbol: String) -> Result<Symbol, InvalidSymbolError> {
185 if symbol.contains('|') || symbol.contains('\\') {
186 Err(InvalidSymbolError(symbol))
187 } else {
188 let is_quoted = !predicates::is_simple_symbol(&symbol);
189
190 Ok(Symbol { symbol, is_quoted })
191 }
192 }
193
194 pub fn symbol(&self) -> &str {
195 &self.symbol
196 }
197 pub fn is_quoted(&self) -> bool {
198 self.is_quoted
199 }
200}
201
202#[derive(Clone, Debug)]
203pub struct Keyword(String);
204
205impl Keyword {
206 pub fn keyword(&self) -> &str {
207 &self.0
208 }
209}
210
211impl From<Keyword> for Sexpr {
212 fn from(value: Keyword) -> Self {
213 Sexpr::Keyword(value)
214 }
215}
216
217impl std::fmt::Display for Keyword {
218 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
219 write!(f, ":{}", self.0)
220 }
221}
222
223pub mod predicates {
224 const RESERVED: [&str; 13] = [
225 "!",
226 "BINARY",
227 "DECIMAL",
228 "HEXADECIMAL",
229 "NUMERAL",
230 "STRING",
231 "_",
232 "as",
233 "exists",
234 "forall",
235 "let",
236 "match",
237 "par",
238 ];
239
240 pub fn is_white_space(c: char) -> bool {
241 match c {
242 '\t' | '\r' | '\n' | ' ' => true,
243 _ => false,
244 }
245 }
246
247 pub fn is_printable(c: char) -> bool {
248 match c {
249 '\0'..='\x1f' | '\x7f' => false,
250 _ => true,
251 }
252 }
253
254 pub fn is_digit(c: char) -> bool {
255 match c {
256 '0'..='9' => true,
257 _ => false,
258 }
259 }
260
261 pub fn is_letter(c: char) -> bool {
262 match c {
263 'A'..='Z' | 'a'..='z' => true,
264 _ => false,
265 }
266 }
267
268 pub fn is_numeral(s: &str) -> bool {
269 if s == "0" {
270 return true;
271 }
272
273 for (i, c) in s.chars().enumerate() {
274 match (i, c) {
275 (_, c) if !is_digit(c) => return false,
277
278 (0, '0') => return false,
280
281 _ => {}
283 }
284 }
285
286 true
287 }
288
289 pub fn is_simple_symbol(s: &str) -> bool {
290 let legal_nonletters = "~!@$%^&*_-+=<>.?/";
297
298 for (i, c) in s.chars().enumerate() {
299 match (i, c) {
300 (0, c) if is_digit(c) => return false,
302
303 (_, c) if is_letter(c) || is_digit(c) || legal_nonletters.contains(c) => {}
305
306 _ => return false,
308 }
309 }
310
311 !s.is_empty() && !RESERVED.contains(&s)
312 }
313}