1use super::{
2 BinOp, FnCall, Ident, Loc, LocInfo, Num, Spanned, Str, UnaryOp, str,
3};
4
5#[derive(Clone, PartialEq, derive_more::From, derive_more::Debug)]
6pub enum Expr<'i> {
7 #[debug("{_0:?}")]
8 Ident(Ident<'i>),
9 #[debug("{_0:?}")]
10 Str(Str<'i>),
11 #[debug("{_0:?}")]
12 FnCall(FnCall<'i>),
13 #[debug("{_0:?}")]
14 Num(Num),
15 #[debug("{_1:?} {} {_2:?}", _to_op(*_0))]
16 BinOp(BinOp, Box<Self>, Box<Self>),
17 #[debug("{} {_2:?}", _to_unary_op(*_0))]
18 UnaryOp(UnaryOp, Loc, Box<Self>),
19}
20
21#[inline]
22const fn _to_op(op: BinOp) -> &'static str {
23 match op {
24 BinOp::Add => "+",
25 BinOp::Sub => "-",
26 BinOp::Mul => "*",
27 BinOp::Div => "/",
28 BinOp::Eq => "=",
29 BinOp::Ge => "≥",
30 BinOp::Gt => ">",
31 BinOp::Le => "≤",
32 BinOp::Lt => "<",
33 BinOp::And => "and",
34 BinOp::Or => "or",
35 }
36}
37
38#[inline]
39const fn _to_unary_op(op: UnaryOp) -> &'static str {
40 match op {
41 UnaryOp::Not => "not",
42 }
43}
44
45impl Spanned for Expr<'_> {
46 fn loc(&self) -> LocInfo {
47 match self {
48 Expr::Ident(id) => id.loc,
49 Expr::Str(s) => s.loc,
50 Expr::FnCall(fncall) => fncall.loc,
51 Expr::Num(int) => int.loc,
52 Expr::BinOp(_, l, r) => (l.loc().0, r.loc().1),
53 Expr::UnaryOp(_, start, e) => (*start, e.loc().1),
54 }
55 }
56}
57
58impl Expr<'_> {
59 pub(crate) fn binop(op: BinOp, l: Self, r: Self) -> Self {
60 Self::BinOp(op, Box::new(l), Box::new(r))
61 }
62
63 pub(crate) fn unary_op(op: UnaryOp, start: Loc, e: Self) -> Self {
64 Self::UnaryOp(op, start, Box::new(e))
65 }
66}
67
68#[cfg(test)]
69#[cfg_attr(coverage_nightly, coverage(off))]
70mod tests {
71 use super::*;
72 use crate::*;
73
74 fn assert_expr(src: &str, expected: &str) {
75 assert_eq!(format!("{:?}", parse_expr(src).unwrap()), expected);
76 }
77
78 #[test]
79 fn test_binop() {
80 assert_expr("1 + 1", "1 + 1");
81 assert_expr("1 - 1", "1 - 1");
82 assert_expr("1 * 1", "1 * 1");
83 assert_expr("1 / 1", "1 / 1");
84
85 assert_expr("1 = 1", "1 = 1");
86 assert_expr("1 > 1", "1 > 1");
87 assert_expr("1 >= 1", "1 ≥ 1");
88 assert_expr("1 < 1", "1 < 1");
89 assert_expr("1 <= 1", "1 ≤ 1");
90
91 assert_expr("a and b", "a and b");
92 assert_expr("a or b", "a or b");
93 assert_expr("a AND b", "a and b");
94 assert_expr("a OR b", "a or b");
95 assert_expr("a or b and c", "a or b and c");
96 assert_expr("a = 1 and b = 2", "a = 1 and b = 2");
97 }
98
99 #[test]
100 fn test_unary_op() {
101 assert_expr("not a", "not a");
102 assert_expr("NOT a", "not a");
103 assert_expr("not not a", "not not a");
104 assert_expr("not a and b", "not a and b");
105 assert_expr("a and not b", "a and not b");
106 assert_expr("not a = 1", "not a = 1");
107 }
108
109 #[test]
110 fn test_expr() {
111 assert_expr("id", "id");
112 assert_expr("'str'", "\"str\"");
113 assert_expr("sin(x)", "sin(x)");
114 }
115
116 fn assert_loc(src: &str, start: Loc, end: Loc) {
117 assert_eq!(parse_expr(src).unwrap().loc(), (start, end));
118 }
119
120 #[test]
121 fn test_loc() {
122 assert_loc("a", 0, 1);
123 assert_loc("'a'", 0, 3);
124 assert_loc("sin(x)", 0, 6);
125 assert_loc("10", 0, 2);
126 assert_loc("1 >= 3", 0, 6);
127 assert_loc("not a", 0, 5);
128 }
129
130 #[test]
131 fn test_debug() {
132 fn assert_dbg(s: &str, expected: &str) {
133 assert_eq!(format!("{:?}", parse_expr(s).unwrap()), expected);
134 }
135
136 assert_dbg("ident", "ident");
137
138 assert_dbg("'string'", "\"string\"");
139
140 assert_dbg("fncall(v1, v2)", "fncall(v1,v2)");
141
142 assert_dbg("100", "100");
143 assert_dbg("3.14", "3.14");
144
145 assert_dbg("1 = 1", "1 = 1");
146 assert_dbg("1 >= 1", "1 ≥ 1");
147 }
148}