1use crate::parser;
2use netidx::{subscriber::Value, utils};
3use regex::Regex;
4use serde::{
5 de::{self, Visitor},
6 Deserialize, Deserializer, Serialize, Serializer,
7};
8use std::{
9 cmp::{Ordering, PartialEq, PartialOrd},
10 fmt::{self, Write},
11 result,
12 str::FromStr,
13};
14
15lazy_static! {
16 pub static ref VNAME: Regex = Regex::new("^[a-z][a-z0-9_]*$").unwrap();
17}
18
19atomic_id!(ExprId);
20
21#[derive(Debug, Clone, PartialOrd, PartialEq)]
22pub enum ExprKind {
23 Constant(Value),
24 Apply { args: Vec<Expr>, function: String },
25}
26
27impl ExprKind {
28 pub fn to_expr(self) -> Expr {
29 Expr { id: ExprId::new(), kind: self }
30 }
31
32 pub fn to_string_pretty(&self, col_limit: usize) -> String {
33 let mut buf = String::new();
34 self.pretty_print(0, col_limit, &mut buf).unwrap();
35 buf
36 }
37
38 fn pretty_print(&self, indent: usize, limit: usize, buf: &mut String) -> fmt::Result {
39 fn push_indent(indent: usize, buf: &mut String) {
40 buf.extend((0..indent).into_iter().map(|_| ' '));
41 }
42 match self {
43 ExprKind::Constant(_) => {
44 push_indent(indent, buf);
45 write!(buf, "{}", self)
46 }
47 ExprKind::Apply { function, args } => {
48 let mut tmp = String::new();
49 push_indent(indent, &mut tmp);
50 write!(tmp, "{}", self)?;
51 if tmp.len() < limit {
52 buf.push_str(&*tmp);
53 Ok(())
54 } else {
55 if function == "string_concat" {
56 buf.push_str(&*tmp);
57 Ok(())
58 } else if function == "get" && args.len() == 1 && args[0].is_fn() {
59 buf.push_str(&*tmp);
60 Ok(())
61 } else if (function == "set" || function == "let")
62 && args.len() == 2
63 && args[0].is_fn()
64 {
65 match &args[0].kind {
66 ExprKind::Constant(Value::String(c)) => {
67 push_indent(indent, buf);
68 let local = if function == "let" { "let " } else { "" };
69 writeln!(buf, "{}{} <-", local, c)?;
70 args[1].kind.pretty_print(indent + 2, limit, buf)
71 }
72 _ => unreachable!(),
73 }
74 } else if function == "do" {
75 push_indent(indent, buf);
76 writeln!(buf, "{}", "{")?;
77 for i in 0..args.len() {
78 args[i].kind.pretty_print(indent + 2, limit, buf)?;
79 if i < args.len() - 1 {
80 writeln!(buf, ";")?
81 } else {
82 writeln!(buf, "")?
83 }
84 }
85 push_indent(indent, buf);
86 writeln!(buf, "{}", "}")
87 } else if function == "array" {
88 push_indent(indent, buf);
89 writeln!(buf, "{}", "[")?;
90 for i in 0..args.len() {
91 args[i].kind.pretty_print(indent + 2, limit, buf)?;
92 if i < args.len() - 1 {
93 writeln!(buf, ",")?
94 } else {
95 writeln!(buf, "")?
96 }
97 }
98 push_indent(indent, buf);
99 writeln!(buf, "{}", "]")
100 } else {
101 push_indent(indent, buf);
102 writeln!(buf, "{}(", function)?;
103 for i in 0..args.len() {
104 args[i].kind.pretty_print(indent + 2, limit, buf)?;
105 if i < args.len() - 1 {
106 writeln!(buf, ", ")?;
107 } else {
108 writeln!(buf, "")?;
109 }
110 }
111 push_indent(indent, buf);
112 writeln!(buf, ")")
113 }
114 }
115 }
116 }
117 }
118}
119
120impl fmt::Display for ExprKind {
121 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
122 match self {
123 ExprKind::Constant(v) => v.fmt_ext(f, &parser::BSCRIPT_ESC, true),
124 ExprKind::Apply { args, function } => {
125 if function == "string_concat" && args.len() > 0 {
126 write!(f, "\"")?;
128 for s in args {
129 match &s.kind {
130 ExprKind::Constant(Value::String(s)) if s.len() > 0 => {
131 write!(
132 f,
133 "{}",
134 utils::escape(&*s, '\\', &parser::BSCRIPT_ESC)
135 )?;
136 }
137 s => {
138 write!(f, "[{}]", s)?;
139 }
140 }
141 }
142 write!(f, "\"")
143 } else if function == "get" && args.len() == 1 && args[0].is_fn() {
144 match &args[0].kind {
146 ExprKind::Constant(Value::String(c)) => write!(f, "{}", c),
147 _ => unreachable!(),
148 }
149 } else if (function == "set" || function == "let")
150 && args.len() == 2
151 && args[0].is_fn()
152 {
153 match &args[0].kind {
155 ExprKind::Constant(Value::String(c)) => {
156 let local = if function == "let" { "let " } else { "" };
157 write!(f, "{}{} <- {}", local, c, &args[1])
158 }
159 _ => unreachable!(),
160 }
161 } else if function == "do" {
162 write!(f, "{}", '{')?;
164 for i in 0..args.len() {
165 if i < args.len() - 1 {
166 write!(f, "{};", &args[i])?
167 } else {
168 write!(f, "{}", &args[i])?
169 }
170 }
171 write!(f, "{}", '}')
172 } else if function == "array" {
173 write!(f, "{}", '[')?;
174 for i in 0..args.len() {
175 if i < args.len() - 1 {
176 write!(f, "{},", &args[i])?
177 } else {
178 write!(f, "{}", &args[i])?
179 }
180 }
181 write!(f, "{}", ']')
182 } else {
183 write!(f, "{}(", function)?;
185 for i in 0..args.len() {
186 write!(f, "{}", &args[i])?;
187 if i < args.len() - 1 {
188 write!(f, ", ")?;
189 }
190 }
191 write!(f, ")")
192 }
193 }
194 }
195 }
196}
197
198#[derive(Debug, Clone)]
199pub struct Expr {
200 pub id: ExprId,
201 pub kind: ExprKind,
202}
203
204impl PartialOrd for Expr {
205 fn partial_cmp(&self, rhs: &Expr) -> Option<Ordering> {
206 self.kind.partial_cmp(&rhs.kind)
207 }
208}
209
210impl PartialEq for Expr {
211 fn eq(&self, rhs: &Expr) -> bool {
212 self.kind.eq(&rhs.kind)
213 }
214}
215
216impl Eq for Expr {}
217
218impl Serialize for Expr {
219 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
220 where
221 S: Serializer,
222 {
223 serializer.serialize_str(&self.to_string())
224 }
225}
226
227impl Default for Expr {
228 fn default() -> Self {
229 ExprKind::Constant(Value::Null).to_expr()
230 }
231}
232
233#[derive(Clone, Copy)]
234struct ExprVisitor;
235
236impl<'de> Visitor<'de> for ExprVisitor {
237 type Value = Expr;
238
239 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
240 write!(formatter, "expected expression")
241 }
242
243 fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
244 where
245 E: de::Error,
246 {
247 Expr::from_str(s).map_err(de::Error::custom)
248 }
249
250 fn visit_borrowed_str<E>(self, s: &'de str) -> Result<Self::Value, E>
251 where
252 E: de::Error,
253 {
254 Expr::from_str(s).map_err(de::Error::custom)
255 }
256
257 fn visit_string<E>(self, s: String) -> Result<Self::Value, E>
258 where
259 E: de::Error,
260 {
261 Expr::from_str(&s).map_err(de::Error::custom)
262 }
263}
264
265impl<'de> Deserialize<'de> for Expr {
266 fn deserialize<D>(de: D) -> Result<Self, D::Error>
267 where
268 D: Deserializer<'de>,
269 {
270 de.deserialize_str(ExprVisitor)
271 }
272}
273
274impl Expr {
275 pub fn new(kind: ExprKind) -> Self {
276 Expr { id: ExprId::new(), kind }
277 }
278
279 pub fn is_fn(&self) -> bool {
280 match &self.kind {
281 ExprKind::Constant(Value::String(c)) => VNAME.is_match(&*c),
282 ExprKind::Constant(_) | ExprKind::Apply { .. } => false,
283 }
284 }
285
286 pub fn to_string_pretty(&self, col_limit: usize) -> String {
287 self.kind.to_string_pretty(col_limit)
288 }
289}
290
291impl fmt::Display for Expr {
292 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
293 write!(f, "{}", self.kind)
294 }
295}
296
297impl FromStr for Expr {
298 type Err = anyhow::Error;
299
300 fn from_str(s: &str) -> result::Result<Self, Self::Err> {
301 parser::parse_expr(s)
302 }
303}
304
305#[cfg(test)]
306mod tests {
307 use super::*;
308 use bytes::Bytes;
309 use chrono::prelude::*;
310 use netidx_core::chars::Chars;
311 use proptest::{collection, prelude::*};
312 use std::time::Duration;
313
314 fn datetime() -> impl Strategy<Value = DateTime<Utc>> {
315 (
316 DateTime::<Utc>::MIN_UTC.timestamp()..DateTime::<Utc>::MAX_UTC.timestamp(),
317 0..1_000_000_000u32,
318 )
319 .prop_map(|(s, ns)| Utc.timestamp_opt(s, ns).unwrap())
320 }
321
322 fn duration() -> impl Strategy<Value = Duration> {
323 (any::<u64>(), 0..1_000_000_000u32).prop_map(|(s, ns)| Duration::new(s, ns))
324 }
325
326 fn bytes() -> impl Strategy<Value = Bytes> {
327 any::<Vec<u8>>().prop_map(Bytes::from)
328 }
329
330 fn chars() -> impl Strategy<Value = Chars> {
331 any::<String>().prop_map(Chars::from)
332 }
333
334 fn value() -> impl Strategy<Value = Value> {
335 prop_oneof![
336 any::<u32>().prop_map(Value::U32),
337 any::<u32>().prop_map(Value::V32),
338 any::<i32>().prop_map(Value::I32),
339 any::<i32>().prop_map(Value::Z32),
340 any::<u64>().prop_map(Value::U64),
341 any::<u64>().prop_map(Value::V64),
342 any::<i64>().prop_map(Value::I64),
343 any::<i64>().prop_map(Value::Z64),
344 any::<f32>().prop_map(Value::F32),
345 any::<f64>().prop_map(Value::F64),
346 datetime().prop_map(Value::DateTime),
347 duration().prop_map(Value::Duration),
348 chars().prop_map(Value::String),
349 bytes().prop_map(Value::Bytes),
350 Just(Value::True),
351 Just(Value::False),
352 Just(Value::Null),
353 Just(Value::Ok),
354 chars().prop_map(Value::Error),
355 ]
356 }
357
358 prop_compose! {
359 fn random_fname()(s in "[a-z][a-z0-9_]*".prop_filter("Filter reserved words", |s| {
360 s != "ok"
361 && s != "true"
362 && s != "false"
363 && s != "null"
364 && s != "load"
365 && s != "store"
366 && s != "load_var"
367 && s != "store_var"
368 })) -> String {
369 s
370 }
371 }
372
373 fn valid_fname() -> impl Strategy<Value = String> {
374 prop_oneof![
375 Just(String::from("any")),
376 Just(String::from("array")),
377 Just(String::from("all")),
378 Just(String::from("sum")),
379 Just(String::from("product")),
380 Just(String::from("divide")),
381 Just(String::from("mean")),
382 Just(String::from("min")),
383 Just(String::from("max")),
384 Just(String::from("and")),
385 Just(String::from("or")),
386 Just(String::from("not")),
387 Just(String::from("cmp")),
388 Just(String::from("if")),
389 Just(String::from("filter")),
390 Just(String::from("cast")),
391 Just(String::from("isa")),
392 Just(String::from("eval")),
393 Just(String::from("count")),
394 Just(String::from("sample")),
395 Just(String::from("string_join")),
396 Just(String::from("string_concat")),
397 Just(String::from("navigate")),
398 Just(String::from("confirm")),
399 Just(String::from("load")),
400 Just(String::from("get")),
401 Just(String::from("store")),
402 Just(String::from("set")),
403 Just(String::from("let")),
404 ]
405 }
406
407 fn fname() -> impl Strategy<Value = String> {
408 prop_oneof![random_fname(), valid_fname(),]
409 }
410
411 fn expr() -> impl Strategy<Value = Expr> {
412 let leaf = value().prop_map(|v| ExprKind::Constant(v).to_expr());
413 leaf.prop_recursive(100, 1000000, 10, |inner| {
414 prop_oneof![(collection::vec(inner, (0, 10)), fname()).prop_map(|(s, f)| {
415 ExprKind::Apply { function: f, args: s }.to_expr()
416 })]
417 })
418 }
419
420 fn acc_strings(args: &Vec<Expr>) -> Vec<Expr> {
421 let mut v: Vec<Expr> = Vec::new();
422 for s in args {
423 let s = s.clone();
424 match s.kind {
425 ExprKind::Constant(Value::String(ref c1)) => match v.last_mut() {
426 None => v.push(s),
427 Some(e0) => match &mut e0.kind {
428 ExprKind::Constant(Value::String(c0))
429 if c1.len() > 0 && c0.len() > 0 =>
430 {
431 let mut st = String::new();
432 st.push_str(&*c0);
433 st.push_str(&*c1);
434 *c0 = Chars::from(st);
435 }
436 _ => v.push(s),
437 },
438 },
439 _ => v.push(s),
440 }
441 }
442 v
443 }
444
445 fn check(s0: &Expr, s1: &Expr) -> bool {
446 match (&s0.kind, &s1.kind) {
447 (ExprKind::Constant(v0), ExprKind::Constant(v1)) => match (v0, v1) {
448 (Value::Duration(d0), Value::Duration(d1)) => {
449 let f0 = d0.as_secs_f64();
450 let f1 = d1.as_secs_f64();
451 f0 == f1 || (f0 != 0. && f1 != 0. && ((f0 - f1).abs() / f0) < 1e-8)
452 }
453 (Value::F32(v0), Value::F32(v1)) => v0 == v1 || (v0 - v1).abs() < 1e-7,
454 (Value::F64(v0), Value::F64(v1)) => v0 == v1 || (v0 - v1).abs() < 1e-8,
455 (v0, v1) => dbg!(dbg!(v0) == dbg!(v1)),
456 },
457 (
458 ExprKind::Apply { args: srs0, function: fn0 },
459 ExprKind::Constant(Value::String(c1)),
460 ) if fn0 == "string_concat" => match &acc_strings(srs0)[..] {
461 [Expr { kind: ExprKind::Constant(Value::String(c0)), .. }] => c0 == c1,
462 _ => false,
463 },
464 (
465 ExprKind::Apply { args: srs0, function: fn0 },
466 ExprKind::Apply { args: srs1, function: fn1 },
467 ) if fn0 == fn1 && fn0.as_str() == "string_concat" => {
468 let srs0 = acc_strings(srs0);
469 srs0.iter().zip(srs1.iter()).fold(true, |r, (s0, s1)| r && check(s0, s1))
470 }
471 (
472 ExprKind::Apply { args: srs0, function: f0 },
473 ExprKind::Apply { args: srs1, function: f1 },
474 ) if f0 == f1 && srs0.len() == srs1.len() => {
475 srs0.iter().zip(srs1.iter()).fold(true, |r, (s0, s1)| r && check(s0, s1))
476 }
477 (_, _) => false,
478 }
479 }
480
481 proptest! {
482 #[test]
483 fn expr_round_trip(s in expr()) {
484 assert!(check(dbg!(&s), &dbg!(dbg!(s.to_string()).parse::<Expr>().unwrap())))
485 }
486
487 #[test]
488 fn expr_pp_round_trip(s in expr()) {
489 assert!(check(dbg!(&s), &dbg!(dbg!(s.to_string_pretty(80)).parse::<Expr>().unwrap())))
490 }
491 }
492}