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