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