sapio_miniscript/
expression.rs1use std::str::FromStr;
19
20use errstr;
21use Error;
22
23use MAX_RECURSION_DEPTH;
24
25#[derive(Debug)]
26pub struct Tree<'a> {
28 pub name: &'a str,
30 pub args: Vec<Tree<'a>>,
32}
33
34pub trait FromTree: Sized {
36 fn from_tree(top: &Tree) -> Result<Self, Error>;
38}
39
40enum Found {
41 Nothing,
42 LBracket(usize), Comma(usize),
44 RBracket(usize), }
46
47fn next_expr(sl: &str, delim: char) -> Found {
48 let mut found = Found::Nothing;
49 if delim == '(' {
50 for (n, ch) in sl.char_indices() {
51 match ch {
52 '(' => {
53 found = Found::LBracket(n);
54 break;
55 }
56 ',' => {
57 found = Found::Comma(n);
58 break;
59 }
60 ')' => {
61 found = Found::RBracket(n);
62 break;
63 }
64 _ => {}
65 }
66 }
67 } else if delim == '{' {
68 let mut new_count = 0;
69 for (n, ch) in sl.char_indices() {
70 match ch {
71 '{' => {
72 found = Found::LBracket(n);
73 break;
74 }
75 '(' => {
76 new_count += 1;
77 }
78 ',' => {
79 if new_count == 0 {
80 found = Found::Comma(n);
81 break;
82 }
83 }
84 ')' => {
85 new_count -= 1;
86 }
87 '}' => {
88 found = Found::RBracket(n);
89 break;
90 }
91 _ => {}
92 }
93 }
94 } else {
95 unreachable!("{}", "Internal: delimiters in parsing must be '(' or '{'");
96 }
97 found
98}
99
100fn closing_delim(delim: char) -> char {
102 match delim {
103 '(' => ')',
104 '{' => '}',
105 _ => unreachable!("Unknown delimiter"),
106 }
107}
108
109impl<'a> Tree<'a> {
110 pub fn from_slice(sl: &'a str) -> Result<(Tree<'a>, &'a str), Error> {
112 Self::from_slice_delim(sl, 0u32, '(')
114 }
115
116 pub(crate) fn from_slice_delim(
117 mut sl: &'a str,
118 depth: u32,
119 delim: char,
120 ) -> Result<(Tree<'a>, &'a str), Error> {
121 if depth >= MAX_RECURSION_DEPTH {
122 return Err(Error::MaxRecursiveDepthExceeded);
123 }
124
125 match next_expr(sl, delim) {
126 Found::Nothing => Ok((
128 Tree {
129 name: &sl[..],
130 args: vec![],
131 },
132 "",
133 )),
134 Found::Comma(n) | Found::RBracket(n) => Ok((
136 Tree {
137 name: &sl[..n],
138 args: vec![],
139 },
140 &sl[n..],
141 )),
142 Found::LBracket(n) => {
144 let mut ret = Tree {
145 name: &sl[..n],
146 args: vec![],
147 };
148
149 sl = &sl[n + 1..];
150 loop {
151 let (arg, new_sl) = Tree::from_slice_delim(sl, depth + 1, delim)?;
152 ret.args.push(arg);
153
154 if new_sl.is_empty() {
155 return Err(Error::ExpectedChar(closing_delim(delim)));
156 }
157
158 sl = &new_sl[1..];
159 match new_sl.as_bytes()[0] {
160 b',' => {}
161 last_byte => {
162 if last_byte == closing_delim(delim) as u8 {
163 break;
164 } else {
165 return Err(Error::ExpectedChar(closing_delim(delim)));
166 }
167 }
168 }
169 }
170 Ok((ret, sl))
171 }
172 }
173 }
174
175 pub fn from_str(s: &'a str) -> Result<Tree<'a>, Error> {
177 for ch in s.bytes() {
180 if !ch.is_ascii() {
181 return Err(Error::Unprintable(ch));
182 }
183 }
184
185 let (top, rem) = Tree::from_slice(s)?;
186 if rem.is_empty() {
187 Ok(top)
188 } else {
189 Err(errstr(rem))
190 }
191 }
192}
193
194pub fn parse_num(s: &str) -> Result<u32, Error> {
196 if s.len() > 1 {
197 let ch = s.chars().next().unwrap();
198 if ch < '1' || ch > '9' {
199 return Err(Error::Unexpected(
200 "Number must start with a digit 1-9".to_string(),
201 ));
202 }
203 }
204 u32::from_str(s).map_err(|_| errstr(s))
205}
206
207pub fn terminal<T, F, Err>(term: &Tree, convert: F) -> Result<T, Error>
209where
210 F: FnOnce(&str) -> Result<T, Err>,
211 Err: ToString,
212{
213 if term.args.is_empty() {
214 convert(term.name).map_err(|e| Error::Unexpected(e.to_string()))
215 } else {
216 Err(errstr(term.name))
217 }
218}
219
220pub fn unary<L, T, F>(term: &Tree, convert: F) -> Result<T, Error>
222where
223 L: FromTree,
224 F: FnOnce(L) -> T,
225{
226 if term.args.len() == 1 {
227 let left = FromTree::from_tree(&term.args[0])?;
228 Ok(convert(left))
229 } else {
230 Err(errstr(term.name))
231 }
232}
233
234pub fn binary<L, R, T, F>(term: &Tree, convert: F) -> Result<T, Error>
236where
237 L: FromTree,
238 R: FromTree,
239 F: FnOnce(L, R) -> T,
240{
241 if term.args.len() == 2 {
242 let left = FromTree::from_tree(&term.args[0])?;
243 let right = FromTree::from_tree(&term.args[1])?;
244 Ok(convert(left, right))
245 } else {
246 Err(errstr(term.name))
247 }
248}
249
250#[cfg(test)]
251mod tests {
252
253 use super::parse_num;
254
255 #[test]
256 fn test_parse_num() {
257 assert!(parse_num("0").is_ok());
258 assert!(parse_num("00").is_err());
259 assert!(parse_num("0000").is_err());
260 assert!(parse_num("06").is_err());
261 assert!(parse_num("+6").is_err());
262 assert!(parse_num("-6").is_err());
263 }
264
265 }