1use crate::ast::{Atom, LogicOperator, MathOperator, Precedence};
2use crate::parse::{LexContext, ParseError, Token};
3use crate::play::{PlayContext, PlayResult, RunContext};
4use crate::Value;
5use std::path::PathBuf;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum ShortCircuit {
9 And,
10 Or,
11}
12
13#[derive(Debug, Clone, PartialEq)]
14pub enum Expression {
15 Atom(Atom),
16 Math(MathOperator, Box<Self>, Box<Self>),
17 Logic(LogicOperator, Box<Self>, Box<Self>),
18 Assignment(String, Option<MathOperator>, Box<Self>),
19 ShortCircuitAssignment(String, ShortCircuit, Box<Self>),
20 ShortCircuit(ShortCircuit, Box<Self>, Box<Self>),
21}
22
23impl Expression {
24 pub fn parse_toplevel(lctx: &mut LexContext) -> Result<Self, ParseError> {
25 Ok(Self::parse(lctx, true, Precedence::default())?
26 .expect("Todo: error for no valid expression"))
27 }
28
29 pub fn parse(
30 lctx: &mut LexContext,
31 comma_is_and: bool,
32 prec: Precedence,
33 ) -> Result<Option<Self>, ParseError> {
34 let Some(begin) = Atom::parse(lctx)? else {
35 return Ok(None);
36 };
37 let mut lhs = Self::Atom(begin);
38
39 while let Some(token) = lctx.next()? {
40 let token_prec = match Precedence::of(&token, comma_is_and) {
41 Some(p) if p <= prec => p,
42 _ => {
43 lctx.push_token(token);
44 break;
45 }
46 };
47
48 let rhs =
49 Self::parse(lctx, comma_is_and, token_prec)?.ok_or(ParseError::MissingRhsToOp)?;
50
51 if token == Token::Assign {
52 let Self::Atom(Atom::Variable(var)) = lhs else {
53 return Err(ParseError::AssignToNonVariable);
54 };
55
56 lhs = Self::Assignment(var, None, rhs.into());
57 continue;
58 };
59
60 if let Some((math, assign)) = MathOperator::from_token(&token) {
61 if assign {
62 let Self::Atom(Atom::Variable(var)) = lhs else {
63 return Err(ParseError::AssignToNonVariable);
64 };
65
66 lhs = Self::Assignment(var, None, rhs.into());
67 } else {
68 lhs = Self::Math(math, lhs.into(), rhs.into());
69 }
70 continue;
71 }
72
73 if let Some(logic) = LogicOperator::from_token(&token) {
74 lhs = Self::Logic(logic, lhs.into(), rhs.into());
75 continue;
76 }
77
78 if token == Token::And || token == Token::Comma && comma_is_and {
79 lhs = Self::ShortCircuit(ShortCircuit::And, lhs.into(), rhs.into());
80 continue;
81 }
82 if token == Token::Or {
83 lhs = Self::ShortCircuit(ShortCircuit::Or, lhs.into(), rhs.into());
84 continue;
85 }
86
87 lctx.push_token(token);
88 break;
89 }
90
91 Ok(Some(lhs))
92 }
93
94 pub fn parse_until1(lctx: &mut LexContext, until: Token) -> Result<Self, ParseError> {
95 todo!();
96 }
97
98 pub fn begin_position(&self) -> Vec<PathBuf> {
100 match self {
101 Self::Atom(Atom::Value(Value::Path(path))) => vec![path.to_path_buf()],
102 Self::Atom(Atom::Value(Value::PathGlob(pathglob))) => vec![pathglob.begin_position()],
103 Self::ShortCircuit(ShortCircuit::And, lhs, rhs) => {
104 let mut beginnings = lhs.begin_position();
105
106 'out: for new in rhs.begin_position() {
107 for idx in 0..beginnings.len() {
108 if beginnings[idx] == new {
110 continue 'out;
111 }
112
113 if beginnings[idx].starts_with(&new) {
115 continue 'out;
116 }
117
118 if new.starts_with(&beginnings[idx]) {
120 beginnings[idx] = new;
121 continue 'out;
122 }
123
124 for ancestor in beginnings[idx].ancestors() {
126 if new.starts_with(&ancestor) {
127 beginnings[idx] = ancestor.into();
128 continue 'out;
129 }
130 }
131
132 panic!("when does this happen?: {new:?} {old:?}", old = beginnings[idx]);
133 }
134 }
135 beginnings
136 }
137
138 Self::ShortCircuit(ShortCircuit::Or, lhs, rhs) => {
139 let mut beginnings = lhs.begin_position();
140
141 'out: for new in rhs.begin_position() {
142 for current in beginnings.iter_mut() {
143 if current.starts_with(&new) {
145 continue 'out;
146 }
147
148 if new.starts_with(¤t) {
150 *current = new;
151 continue 'out;
152 }
153 }
154 beginnings.push(new)
156 }
157 beginnings
158 }
159 _ => vec![],
160 }
161 }
162}
163
164impl Expression {
175 pub fn run(&self, ctx: &mut PlayContext, rctx: RunContext) -> PlayResult<Value> {
176 match self {
177 Self::Atom(atom) => atom.run(ctx, rctx),
178 Self::Math(op, lhs, rhs) => {
179 op.run(&lhs.run(ctx, RunContext::Any)?, &rhs.run(ctx, RunContext::Any)?)
180 }
181 Self::Logic(op, lhs, rhs) => op
182 .run(&lhs.run(ctx, RunContext::Any)?, &rhs.run(ctx, RunContext::Any)?)
183 .map(Value::from),
184 Self::Assignment(name, op, rhs) => {
185 let value = if let Some(op) = op {
186 let old = ctx.lookup_var(name)?;
187 op.run(&old, &rhs.run(ctx, RunContext::Any)?)?
188 } else {
189 rhs.run(ctx, RunContext::Any)?
190 };
191
192 ctx.assign_var(name, value.clone());
193 Ok(value)
194 }
195 Self::ShortCircuitAssignment(sc, lhs, rhs) => {
196 todo!()
197 }
207
208 Self::ShortCircuit(sc, lhs, rhs) => {
209 let lhs = lhs.run(ctx, RunContext::Logical)?;
210 if lhs.is_truthy() == (*sc == ShortCircuit::And) {
211 rhs.run(ctx, RunContext::Logical)
212 } else {
213 Ok(lhs)
214 }
215 } }
224 }
225
226 }