Skip to main content

shape_ast/ast/
expressions.rs

1//! Core expression types for Shape AST
2
3use serde::{Deserialize, Serialize};
4
5use super::data_refs::{DataDateTimeRef, DataIndex, DataRef};
6use super::literals::{Duration, Literal};
7use super::operators::{BinaryOp, RangeKind, UnaryOp};
8use super::span::{Span, Spanned};
9use super::time::{DateTimeExpr, TimeReference, Timeframe};
10use super::types::TypeAnnotation;
11
12/// Entry in an object literal - either a field or a spread
13#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
14pub enum ObjectEntry {
15    /// Regular field: key: value or key: Type = value
16    Field {
17        key: String,
18        value: Expr,
19        type_annotation: Option<TypeAnnotation>,
20    },
21    /// Spread: ...expr
22    Spread(Expr),
23}
24
25#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
26pub enum EnumConstructorPayload {
27    Unit,
28    Tuple(Vec<Expr>),
29    Struct(Vec<(String, Expr)>),
30}
31
32#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
33pub enum Expr {
34    /// Literal values
35    Literal(Literal, Span),
36    /// Variable/identifier reference
37    Identifier(String, Span),
38    /// Data reference: data[0], data[-1], data[1:5]
39    DataRef(DataRef, Span),
40    /// DateTime-based data reference: data[@"2024-01-15"]
41    DataDateTimeRef(DataDateTimeRef, Span),
42    /// Relative access from a reference: ref[0], ref[-1]
43    DataRelativeAccess {
44        reference: Box<Expr>,
45        index: DataIndex,
46        span: Span,
47    },
48    /// Property access: expr.property or expr?.property
49    PropertyAccess {
50        object: Box<Expr>,
51        property: String,
52        optional: bool,
53        span: Span,
54    },
55    /// Index access: expr[index] or expr[start:end]
56    IndexAccess {
57        object: Box<Expr>,
58        index: Box<Expr>,
59        end_index: Option<Box<Expr>>,
60        span: Span,
61    },
62    /// Binary operations
63    BinaryOp {
64        left: Box<Expr>,
65        op: BinaryOp,
66        right: Box<Expr>,
67        span: Span,
68    },
69    /// Fuzzy comparison with explicit tolerance: a ~= b within 0.02
70    FuzzyComparison {
71        left: Box<Expr>,
72        op: super::operators::FuzzyOp,
73        right: Box<Expr>,
74        tolerance: super::operators::FuzzyTolerance,
75        span: Span,
76    },
77    /// Unary operations
78    UnaryOp {
79        op: UnaryOp,
80        operand: Box<Expr>,
81        span: Span,
82    },
83    /// Function calls: sma(20), rsi(14), momentum(period: 10, threshold: 0.01)
84    FunctionCall {
85        name: String,
86        args: Vec<Expr>,
87        named_args: Vec<(String, Expr)>,
88        span: Span,
89    },
90    /// Enum constructor: Enum::Variant, Enum::Variant(...), Enum::Variant { ... }
91    EnumConstructor {
92        enum_name: String,
93        variant: String,
94        payload: EnumConstructorPayload,
95        span: Span,
96    },
97    /// Time reference: @today, @"2024-01-15"
98    TimeRef(TimeReference, Span),
99    /// DateTime expression: @"2024-01-15", @market_open, etc.
100    DateTime(DateTimeExpr, Span),
101    /// Pattern reference in expressions
102    PatternRef(String, Span),
103    /// Conditional expression: if cond then expr else expr
104    Conditional {
105        condition: Box<Expr>,
106        then_expr: Box<Expr>,
107        else_expr: Option<Box<Expr>>,
108        span: Span,
109    },
110    /// Object literal: { field1: expr1, field2: expr2, ...spread }
111    Object(Vec<ObjectEntry>, Span),
112    /// Array literal: [1, 2, 3]
113    Array(Vec<Expr>, Span),
114    /// List comprehension: [expr for var in iter if cond]
115    ListComprehension(Box<super::expr_helpers::ListComprehension>, Span),
116    /// Block expression: { let x = 10; x + 5 }
117    Block(super::expr_helpers::BlockExpr, Span),
118    /// Type assertion: expr as Type or expr as Type { param: value }
119    TypeAssertion {
120        expr: Box<Expr>,
121        type_annotation: TypeAnnotation,
122        /// Meta parameter overrides: as Percent { decimals: 4 }
123        meta_param_overrides: Option<std::collections::HashMap<String, Expr>>,
124        span: Span,
125    },
126    /// Instance check: expr instanceof Type
127    InstanceOf {
128        expr: Box<Expr>,
129        type_annotation: TypeAnnotation,
130        span: Span,
131    },
132    /// Function expression: function(x, y) { return x + y }
133    FunctionExpr {
134        params: Vec<super::functions::FunctionParameter>,
135        return_type: Option<TypeAnnotation>,
136        body: Vec<super::statements::Statement>,
137        span: Span,
138    },
139    /// Duration literal: 30d, 1h, 15m
140    Duration(Duration, Span),
141    /// Spread expression: ...expr (used in arrays and objects)
142    Spread(Box<Expr>, Span),
143
144    // ===== Expression-based Control Flow =====
145    /// If expression: if condition { expr } else { expr }
146    If(Box<super::expr_helpers::IfExpr>, Span),
147
148    /// While expression: while condition { expr }
149    While(Box<super::expr_helpers::WhileExpr>, Span),
150
151    /// For expression: for x in iter { expr }
152    For(Box<super::expr_helpers::ForExpr>, Span),
153
154    /// Loop expression: loop { expr }
155    Loop(Box<super::expr_helpers::LoopExpr>, Span),
156
157    /// Let binding expression: let x = value; expr
158    Let(Box<super::expr_helpers::LetExpr>, Span),
159
160    /// Assignment expression: x = value (returns value)
161    Assign(Box<super::expr_helpers::AssignExpr>, Span),
162
163    /// Break with optional value
164    Break(Option<Box<Expr>>, Span),
165
166    /// Continue
167    Continue(Span),
168
169    /// Return with optional value
170    Return(Option<Box<Expr>>, Span),
171
172    /// Method call: expr.method(args) or expr.method(name: value)
173    MethodCall {
174        receiver: Box<Expr>,
175        method: String,
176        args: Vec<Expr>,
177        named_args: Vec<(String, Expr)>,
178        span: Span,
179    },
180
181    /// Match expression
182    Match(Box<super::expr_helpers::MatchExpr>, Span),
183
184    /// Unit value (void)
185    Unit(Span),
186
187    /// Range expression with Rust-style syntax
188    Range {
189        start: Option<Box<Expr>>,
190        end: Option<Box<Expr>>,
191        kind: RangeKind,
192        span: Span,
193    },
194
195    /// Timeframe context expression: on(1h) { expr }
196    TimeframeContext {
197        timeframe: Timeframe,
198        expr: Box<Expr>,
199        span: Span,
200    },
201
202    /// Try operator for fallible propagation (Result/Option): expr?
203    TryOperator(Box<Expr>, Span),
204
205    /// Named implementation selector: `expr using ImplName`
206    UsingImpl {
207        expr: Box<Expr>,
208        impl_name: String,
209        span: Span,
210    },
211
212    /// Simulation call with inline parameters
213    SimulationCall {
214        name: String,
215        params: Vec<(String, Expr)>,
216        span: Span,
217    },
218
219    /// Window function expression: expr OVER (partition by ... order by ...)
220    WindowExpr(Box<super::windows::WindowExpr>, Span),
221
222    /// LINQ-style from query expression
223    /// Syntax: from var in source [clauses...] select expr
224    FromQuery(Box<super::expr_helpers::FromQueryExpr>, Span),
225
226    /// Struct literal: TypeName { field: value, ... }
227    StructLiteral {
228        type_name: String,
229        fields: Vec<(String, Expr)>,
230        span: Span,
231    },
232
233    /// Await expression: await expr
234    Await(Box<Expr>, Span),
235
236    /// Join expression: await join all|race|any|settle { branch, ... }
237    Join(Box<super::expr_helpers::JoinExpr>, Span),
238
239    /// Annotated expression: @annotation expr
240    /// Used for expression-level annotations like `@timeout(5s) fetch()` or `@timed computation()`
241    /// Multiple annotations nest left-to-right: `@retry(3) @timeout(5s) fetch()` becomes
242    /// `Annotated { @retry(3), target: Annotated { @timeout(5s), target: fetch() } }`
243    Annotated {
244        annotation: super::functions::Annotation,
245        target: Box<Expr>,
246        span: Span,
247    },
248
249    /// Async let expression: `async let name = expr`
250    /// Spawns a task and binds a future handle to a local variable.
251    AsyncLet(Box<super::expr_helpers::AsyncLetExpr>, Span),
252
253    /// Async scope expression: `async scope { ... }`
254    /// Cancellation boundary — on scope exit, all pending tasks are cancelled in reverse order.
255    AsyncScope(Box<Expr>, Span),
256
257    /// Compile-time block expression: `comptime { stmts }`
258    /// Evaluated at compile time via the mini-VM. The result replaces this node with a literal.
259    Comptime(Vec<super::statements::Statement>, Span),
260
261    /// Compile-time for loop: `comptime for field in target.fields { ... }`
262    /// Unrolled at compile time. Each iteration is compiled with the loop variable
263    /// bound to the concrete field descriptor. Used inside comptime annotation handlers.
264    ComptimeFor(Box<super::expr_helpers::ComptimeForExpr>, Span),
265
266    /// Reference expression: `&expr` or `&mut expr`.
267    /// Creates a shared or exclusive reference to a local variable.
268    Reference {
269        expr: Box<Expr>,
270        /// True for `&mut expr` (exclusive/mutable reference).
271        is_mutable: bool,
272        span: Span,
273    },
274
275    /// Table row literal: `[a, b, c], [d, e, f]`
276    /// Used with `let t: Table<T> = [row1], [row2], ...` syntax.
277    /// Each inner Vec<Expr> is one row's positional field values.
278    TableRows(Vec<Vec<Expr>>, Span),
279}
280
281impl Expr {
282    /// Convert expression to a JSON value (for literals used in metadata)
283    pub fn to_json_value(&self) -> serde_json::Value {
284        match self {
285            Expr::Literal(lit, _) => lit.to_json_value(),
286            Expr::Array(elements, _) => {
287                serde_json::Value::Array(elements.iter().map(|e| e.to_json_value()).collect())
288            }
289            Expr::Object(entries, _) => {
290                let mut map = serde_json::Map::new();
291                for entry in entries {
292                    if let ObjectEntry::Field { key, value, .. } = entry {
293                        map.insert(key.clone(), value.to_json_value());
294                    }
295                }
296                serde_json::Value::Object(map)
297            }
298            _ => serde_json::Value::Null, // Fallback for non-literals
299        }
300    }
301}
302
303impl Spanned for Expr {
304    fn span(&self) -> Span {
305        match self {
306            Expr::Literal(_, span) => *span,
307            Expr::Identifier(_, span) => *span,
308            Expr::DataRef(_, span) => *span,
309            Expr::DataDateTimeRef(_, span) => *span,
310            Expr::DataRelativeAccess { span, .. } => *span,
311            Expr::PropertyAccess { span, .. } => *span,
312            Expr::IndexAccess { span, .. } => *span,
313            Expr::BinaryOp { span, .. } => *span,
314            Expr::FuzzyComparison { span, .. } => *span,
315            Expr::UnaryOp { span, .. } => *span,
316            Expr::FunctionCall { span, .. } => *span,
317            Expr::EnumConstructor { span, .. } => *span,
318            Expr::TimeRef(_, span) => *span,
319            Expr::DateTime(_, span) => *span,
320            Expr::PatternRef(_, span) => *span,
321            Expr::Conditional { span, .. } => *span,
322            Expr::Object(_, span) => *span,
323            Expr::Array(_, span) => *span,
324            Expr::ListComprehension(_, span) => *span,
325            Expr::Block(_, span) => *span,
326            Expr::TypeAssertion { span, .. } => *span,
327            Expr::InstanceOf { span, .. } => *span,
328            Expr::FunctionExpr { span, .. } => *span,
329            Expr::Duration(_, span) => *span,
330            Expr::Spread(_, span) => *span,
331            Expr::If(_, span) => *span,
332            Expr::While(_, span) => *span,
333            Expr::For(_, span) => *span,
334            Expr::Loop(_, span) => *span,
335            Expr::Let(_, span) => *span,
336            Expr::Assign(_, span) => *span,
337            Expr::Break(_, span) => *span,
338            Expr::Continue(span) => *span,
339            Expr::Return(_, span) => *span,
340            Expr::MethodCall { span, .. } => *span,
341            Expr::Match(_, span) => *span,
342            Expr::Unit(span) => *span,
343            Expr::Range { span, .. } => *span,
344            Expr::TimeframeContext { span, .. } => *span,
345            Expr::TryOperator(_, span) => *span,
346            Expr::UsingImpl { span, .. } => *span,
347            Expr::SimulationCall { span, .. } => *span,
348            Expr::WindowExpr(_, span) => *span,
349            Expr::FromQuery(_, span) => *span,
350            Expr::StructLiteral { span, .. } => *span,
351            Expr::Await(_, span) => *span,
352            Expr::Join(_, span) => *span,
353            Expr::Annotated { span, .. } => *span,
354            Expr::AsyncLet(_, span) => *span,
355            Expr::AsyncScope(_, span) => *span,
356            Expr::Comptime(_, span) => *span,
357            Expr::ComptimeFor(_, span) => *span,
358            Expr::Reference { span, .. } => *span,
359            Expr::TableRows(_, span) => *span,
360        }
361    }
362}