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