varpulis_core/ast.rs
1//! Abstract Syntax Tree for VPL
2
3use crate::span::Spanned;
4use crate::types::Type;
5use serde::{Deserialize, Serialize};
6
7/// A complete VPL program
8#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
9pub struct Program {
10 /// The top-level statements that make up the program.
11 pub statements: Vec<Spanned<Stmt>>,
12}
13
14/// Top-level statement
15#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
16pub enum Stmt {
17 /// Connector declaration: `connector MyMqtt = mqtt (host: "localhost", port: 1883)`
18 ConnectorDecl {
19 /// Connector name.
20 name: String,
21 /// Connector protocol type (e.g., `"mqtt"`, `"kafka"`).
22 connector_type: String,
23 /// Connection parameters.
24 params: Vec<ConnectorParam>,
25 },
26 /// Stream declaration: `stream X = Y` or `stream X = Y.where(...)`
27 StreamDecl {
28 /// Stream name.
29 name: String,
30 /// Optional type annotation for the stream.
31 type_annotation: Option<Type>,
32 /// Event source for this stream.
33 source: StreamSource,
34 /// Chained stream operations.
35 ops: Vec<StreamOp>,
36 /// Per-operation source spans (parallel to `ops`). Empty when spans unavailable.
37 #[serde(default)]
38 op_spans: Vec<crate::span::Span>,
39 },
40 /// Event declaration: `event X: ...`
41 EventDecl {
42 /// Event type name.
43 name: String,
44 /// Parent event type if this event extends another.
45 extends: Option<String>,
46 /// Fields declared on this event type.
47 fields: Vec<Field>,
48 },
49 /// Type alias: `type X = Y`
50 TypeDecl {
51 /// Alias name.
52 name: String,
53 /// The aliased type.
54 ty: Type,
55 },
56 /// Variable declaration: `let x = ...` or `var x = ...`
57 VarDecl {
58 /// Whether the variable can be reassigned.
59 mutable: bool,
60 /// Variable name.
61 name: String,
62 /// Optional type annotation.
63 ty: Option<Type>,
64 /// Initial value expression.
65 value: Expr,
66 },
67 /// Constant declaration: `const X = ...`
68 ConstDecl {
69 /// Constant name.
70 name: String,
71 /// Optional type annotation.
72 ty: Option<Type>,
73 /// Constant value expression.
74 value: Expr,
75 },
76 /// Function declaration: `fn x(...) -> T: ...`
77 FnDecl {
78 /// Function name.
79 name: String,
80 /// Function parameters.
81 params: Vec<Param>,
82 /// Optional return type.
83 ret: Option<Type>,
84 /// Function body statements.
85 body: Vec<Spanned<Self>>,
86 },
87 /// Configuration block with name (e.g., `config mqtt { ... }`)
88 ///
89 /// **Deprecated:** Use `connector` declarations instead.
90 /// ```vpl
91 /// connector MyMqtt = mqtt (
92 /// broker: "localhost",
93 /// port: 1883,
94 /// topic: "events/#"
95 /// )
96 /// ```
97 Config {
98 /// Configuration block name.
99 name: String,
100 /// Configuration key-value items.
101 items: Vec<ConfigItem>,
102 },
103 /// Import statement
104 Import {
105 /// Module path to import.
106 path: String,
107 /// Optional import alias.
108 alias: Option<String>,
109 },
110 /// Expression statement
111 Expr(Expr),
112 /// If statement
113 If {
114 /// Condition expression.
115 cond: Expr,
116 /// Statements to execute when the condition is true.
117 then_branch: Vec<Spanned<Self>>,
118 /// Optional else-if branches (condition, body pairs).
119 elif_branches: Vec<(Expr, Vec<Spanned<Self>>)>,
120 /// Optional else branch.
121 else_branch: Option<Vec<Spanned<Self>>>,
122 },
123 /// For loop
124 For {
125 /// Loop variable name.
126 var: String,
127 /// Iterable expression.
128 iter: Expr,
129 /// Loop body statements.
130 body: Vec<Spanned<Self>>,
131 },
132 /// While loop
133 While {
134 /// Loop condition.
135 cond: Expr,
136 /// Loop body statements.
137 body: Vec<Spanned<Self>>,
138 },
139 /// Return statement
140 Return(Option<Expr>),
141 /// Break statement
142 Break,
143 /// Continue statement
144 Continue,
145 /// Emit event statement: `emit EventType(field1: expr1, field2: expr2)`
146 Emit {
147 /// Event type to emit.
148 event_type: String,
149 /// Named field values for the emitted event.
150 fields: Vec<NamedArg>,
151 },
152 /// Variable assignment: `name := value`
153 Assignment {
154 /// Variable name to assign.
155 name: String,
156 /// New value expression.
157 value: Expr,
158 },
159 /// SASE+ Pattern declaration: `pattern Name = SEQ(A, B+) within 1h partition by user_id`
160 PatternDecl {
161 /// Pattern name.
162 name: String,
163 /// SASE+ pattern expression.
164 expr: SasePatternExpr,
165 /// Optional time window constraint.
166 within: Option<Expr>,
167 /// Optional partition key expression.
168 partition_by: Option<Expr>,
169 },
170 /// Context declaration: `context name (cores: [0, 1])`
171 ContextDecl {
172 /// Context name.
173 name: String,
174 /// Optional CPU core affinity list.
175 cores: Option<Vec<usize>>,
176 },
177}
178
179/// Connector parameter: `host: "localhost"` or `port: 1883`
180#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
181pub struct ConnectorParam {
182 /// Parameter name.
183 pub name: String,
184 /// Parameter value.
185 pub value: ConfigValue,
186}
187
188/// SASE+ Pattern Expression for complex event processing
189#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
190pub enum SasePatternExpr {
191 /// Sequence: SEQ(A, B, C)
192 Seq(Vec<SasePatternItem>),
193 /// Conjunction: A AND B
194 And(Box<Self>, Box<Self>),
195 /// Disjunction: A OR B
196 Or(Box<Self>, Box<Self>),
197 /// Negation: NOT A
198 Not(Box<Self>),
199 /// Single event type reference
200 Event(String),
201 /// Grouped expression
202 Group(Box<Self>),
203}
204
205/// Item in a SASE+ sequence with optional Kleene operator
206#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
207pub struct SasePatternItem {
208 /// Event type name
209 pub event_type: String,
210 /// Optional alias for the event
211 pub alias: Option<String>,
212 /// Kleene operator: None, Plus (+), Star (*), Optional (?)
213 pub kleene: Option<KleeneOp>,
214 /// Optional filter condition
215 pub filter: Option<Expr>,
216}
217
218/// Kleene operators for SASE+ patterns
219#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
220pub enum KleeneOp {
221 /// One or more (+)
222 Plus,
223 /// Zero or more (*)
224 Star,
225 /// Zero or one (?)
226 Optional,
227}
228
229/// Stream source
230#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
231pub enum StreamSource {
232 /// Reference to existing stream with optional alias
233 Ident(String),
234 /// Event type with optional alias: `EventType as alias`
235 IdentWithAlias {
236 /// Event type or stream name.
237 name: String,
238 /// Alias for the source.
239 alias: String,
240 },
241 /// Event type with all quantifier and optional alias: `all EventType as alias`
242 AllWithAlias {
243 /// Event type name.
244 name: String,
245 /// Optional alias for the source.
246 alias: Option<String>,
247 },
248 /// From connector: `EventType.from(Connector, topic: "...", qos: 1)`
249 FromConnector {
250 /// Event type to receive from the connector.
251 event_type: String,
252 /// Connector name to read from.
253 connector_name: String,
254 /// Additional connector parameters.
255 params: Vec<ConnectorParam>,
256 },
257 /// Merge multiple streams
258 Merge(Vec<InlineStreamDecl>),
259 /// Join multiple streams
260 Join(Vec<JoinClause>),
261 /// Sequence construct: `sequence(step1: Event1, step2: Event2 where cond, ...)`
262 Sequence(SequenceDecl),
263 /// Periodic timer source: `timer(5s)` or `timer(5s, initial_delay: 1s)`
264 Timer(TimerDecl),
265}
266
267/// Timer declaration for periodic event generation
268#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
269pub struct TimerDecl {
270 /// Interval between timer fires (duration expression)
271 pub interval: Expr,
272 /// Optional initial delay before first fire
273 pub initial_delay: Option<Box<Expr>>,
274}
275
276/// Sequence declaration for temporal event correlation
277#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
278pub struct SequenceDecl {
279 /// Whether to match all first events or just one
280 pub match_all: bool,
281 /// Global timeout for the sequence
282 pub timeout: Option<Box<Expr>>,
283 /// Named steps in the sequence
284 pub steps: Vec<SequenceStepDecl>,
285}
286
287/// A single step in a sequence declaration
288#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
289pub struct SequenceStepDecl {
290 /// Alias for the captured event
291 pub alias: String,
292 /// Event type to match
293 pub event_type: String,
294 /// Optional filter condition
295 pub filter: Option<Expr>,
296 /// Optional timeout for this step
297 pub timeout: Option<Box<Expr>>,
298}
299
300/// Inline stream declaration used in merge/join
301#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
302pub struct InlineStreamDecl {
303 /// Name alias for this inline stream.
304 pub name: String,
305 /// Source event type or stream name.
306 pub source: String,
307 /// Optional filter condition for this source.
308 pub filter: Option<Expr>,
309}
310
311/// Join type for stream correlation
312#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
313pub enum JoinType {
314 /// Inner join (default) — emit only when all sources match
315 #[default]
316 Inner,
317 /// Left join — emit when left source has an event, fill nulls for missing right
318 Left,
319 /// Right join — emit when right source has an event, fill nulls for missing left
320 Right,
321 /// Full outer join — emit for either side, fill nulls for missing sources
322 Full,
323}
324
325/// Join clause
326#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
327pub struct JoinClause {
328 /// Name alias for this join source.
329 pub name: String,
330 /// Source event type or stream name.
331 pub source: String,
332 /// Optional join condition expression.
333 pub on: Option<Expr>,
334 /// Type of join (inner, left, right, full).
335 #[serde(default)]
336 pub join_type: JoinType,
337}
338
339/// Stream operation (method call on stream)
340#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
341pub enum StreamOp {
342 /// Filter: `.where(cond)`
343 Where(Expr),
344 /// Projection: `.select(fields)`
345 Select(Vec<SelectItem>),
346 /// Window: `.window(duration, ...)`
347 Window(WindowArgs),
348 /// Aggregation: `.aggregate(...)`
349 Aggregate(Vec<AggItem>),
350 /// Having: `.having(cond)` - filter after aggregation
351 Having(Expr),
352 /// Partitioning: `.partition_by(key)`
353 PartitionBy(Expr),
354 /// Ordering: `.order_by(...)`
355 OrderBy(Vec<OrderItem>),
356 /// Limit: `.limit(n)`
357 Limit(Expr),
358 /// Distinct: `.distinct(expr?)`
359 Distinct(Option<Expr>),
360 /// Map: `.map(fn)`
361 Map(Expr),
362 /// Filter with lambda: `.filter(fn)`
363 Filter(Expr),
364 /// Tap for observability: `.tap(...)`
365 Tap(Vec<NamedArg>),
366 /// Print to console: `.print(...)` or `.print("message", expr)`
367 Print(Vec<Expr>),
368 /// Log with level: `.log(level: "info", message: "...", data: expr)`
369 Log(Vec<NamedArg>),
370 /// Emit output: `.emit(fields...)` or `.emit as Type (fields...)`
371 Emit {
372 /// Optional type cast: `.emit as Alert (...)`
373 output_type: Option<String>,
374 /// Field mappings
375 fields: Vec<NamedArg>,
376 /// Optional target context for cross-context emit: `.emit(context: ctx_name, ...)`
377 target_context: Option<String>,
378 },
379 /// Send to connector: `.to(Connector, topic: "...", method: "POST")`
380 To {
381 /// Target connector name.
382 connector_name: String,
383 /// Sink parameters (topic, method, etc.).
384 params: Vec<ConnectorParam>,
385 },
386 /// Send to destination (legacy): `.to(target)`
387 ToExpr(Expr),
388 /// Pattern matching: `.pattern(...)`
389 Pattern(PatternDef),
390 /// Concurrent processing: `.concurrent(...)`
391 Concurrent(Vec<NamedArg>),
392 /// Process with function: `.process(fn)`
393 Process(Expr),
394 /// Error handler: `.on_error(fn)`
395 OnError(Expr),
396 /// Collect results: `.collect()`
397 Collect,
398 /// Join condition: `.on(expr)`
399 On(Expr),
400 /// Followed-by sequence: `-> EventType where condition as alias`
401 FollowedBy(FollowedByClause),
402 /// Timeout constraint: `.within(duration)`
403 Within(Expr),
404 /// Negation: `.not(EventType where condition)`
405 Not(FollowedByClause),
406 /// Fork into multiple parallel paths: `.fork(path1: ..., path2: ...)`
407 Fork(Vec<ForkPath>),
408 /// Wait for any path to complete: `.any()` or `.any(n)` for at least n
409 Any(Option<usize>),
410 /// Wait for all paths to complete: `.all()`
411 All,
412 /// Wait for first path to complete: `.first()`
413 First,
414 /// Assign stream to a context: `.context(name)`
415 Context(String),
416 /// Watermark configuration: `.watermark(out_of_order: 10s)`
417 Watermark(Vec<NamedArg>),
418 /// Allowed lateness for late data: `.allowed_lateness(30s)`
419 AllowedLateness(Expr),
420 /// Trend aggregation over Kleene patterns (Hamlet engine):
421 /// `.trend_aggregate(count: count_trends(), events: count_events(rising))`
422 TrendAggregate(Vec<TrendAggItem>),
423 /// ONNX model scoring: `.score(model: "path.onnx", inputs: [...], outputs: [...])`
424 Score(ScoreSpec),
425 /// PST-based pattern forecasting: `.forecast(confidence: 0.7, horizon: 2m, warmup: 500, max_depth: 5)`
426 Forecast(ForecastSpec),
427 /// External connector enrichment: `.enrich(WeatherAPI, key: t.city, fields: [forecast, humidity], cache_ttl: 5m)`
428 Enrich(EnrichSpec),
429}
430
431/// A path in a fork construct
432#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
433pub struct ForkPath {
434 /// Name of the path
435 pub name: String,
436 /// Sequence of operations for this path
437 pub ops: Vec<StreamOp>,
438}
439
440/// Item in a trend_aggregate operation
441#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
442pub struct TrendAggItem {
443 /// Output alias for the aggregation result
444 pub alias: String,
445 /// Aggregation function name: "count_trends", "count_events", "sum_trends", etc.
446 pub func: String,
447 /// Optional argument (field reference for sum/avg/min/max, alias for count_events)
448 pub arg: Option<Expr>,
449}
450
451/// Specification for ONNX model scoring
452#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
453pub struct ScoreSpec {
454 /// Path to the ONNX model file.
455 pub model_path: String,
456 /// Input tensor names to feed from event fields.
457 pub inputs: Vec<String>,
458 /// Output tensor names to extract as event fields.
459 pub outputs: Vec<String>,
460 /// Enable GPU acceleration (default: false)
461 #[serde(default)]
462 pub gpu: bool,
463 /// Batch size for inference (default: 1, >1 enables batch mode)
464 #[serde(default = "default_batch_size")]
465 pub batch_size: usize,
466 /// GPU device ID (default: 0)
467 #[serde(default)]
468 pub device_id: i32,
469}
470
471const fn default_batch_size() -> usize {
472 1
473}
474
475/// Specification for PST-based pattern forecasting
476#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
477pub struct ForecastSpec {
478 /// Minimum probability to emit forecast (default 0.5)
479 pub confidence: Option<Expr>,
480 /// Forecast time horizon (default = within duration)
481 pub horizon: Option<Expr>,
482 /// Events before forecasting starts (default 100)
483 pub warmup: Option<Expr>,
484 /// Maximum PST context depth (default 5)
485 pub max_depth: Option<Expr>,
486 /// Enable Hawkes intensity modulation (default true)
487 pub hawkes: Option<Expr>,
488 /// Enable conformal prediction intervals (default true)
489 pub conformal: Option<Expr>,
490 /// Preset mode: "fast", "accurate", or "balanced" (default "balanced")
491 pub mode: Option<Expr>,
492}
493
494/// Specification for external connector enrichment
495#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
496pub struct EnrichSpec {
497 /// Name of the connector to use for lookups
498 pub connector_name: String,
499 /// Expression to evaluate as the lookup key
500 pub key_expr: Box<Expr>,
501 /// Fields to extract from the enrichment response
502 pub fields: Vec<String>,
503 /// Cache TTL (optional, duration expression)
504 pub cache_ttl: Option<Expr>,
505 /// Timeout for the enrichment request (optional, duration expression)
506 pub timeout: Option<Expr>,
507 /// Fallback value when lookup fails (optional)
508 pub fallback: Option<Expr>,
509}
510
511/// Followed-by clause for temporal sequences
512#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
513pub struct FollowedByClause {
514 /// Event type to match
515 pub event_type: String,
516 /// Optional filter condition
517 pub filter: Option<Expr>,
518 /// Optional alias for captured event
519 pub alias: Option<String>,
520 /// Whether to match all events (true) or just one (false)
521 pub match_all: bool,
522}
523
524/// Select item in projection
525#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
526pub enum SelectItem {
527 /// Simple field reference
528 Field(String),
529 /// Aliased expression: `alias: expr`
530 Alias(String, Expr),
531}
532
533/// Aggregation item
534#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
535pub struct AggItem {
536 /// Output field name for the aggregation result.
537 pub alias: String,
538 /// Aggregation expression (e.g., `avg(temperature)`).
539 pub expr: Expr,
540}
541
542/// Order item
543#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
544pub struct OrderItem {
545 /// Expression to sort by.
546 pub expr: Expr,
547 /// Whether to sort in descending order.
548 pub descending: bool,
549}
550
551/// Window arguments
552#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
553pub struct WindowArgs {
554 /// Window duration (tumbling window size).
555 pub duration: Expr,
556 /// Optional slide interval for sliding windows.
557 pub sliding: Option<Expr>,
558 /// Optional eviction policy expression.
559 pub policy: Option<Expr>,
560 /// Session window gap duration (syntax: `.window(session: 5m)`)
561 pub session_gap: Option<Expr>,
562}
563
564/// Pattern definition
565#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
566pub struct PatternDef {
567 /// Pattern name.
568 pub name: String,
569 /// Matcher expression for the pattern.
570 pub matcher: Expr,
571}
572
573/// Named argument: `name: value`
574#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
575pub struct NamedArg {
576 /// Argument name.
577 pub name: String,
578 /// Argument value expression.
579 pub value: Expr,
580}
581
582/// Field in event declaration
583#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
584pub struct Field {
585 /// Field name.
586 pub name: String,
587 /// Field type.
588 pub ty: Type,
589 /// Whether the field is optional.
590 pub optional: bool,
591}
592
593/// Function parameter
594#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
595pub struct Param {
596 /// Parameter name.
597 pub name: String,
598 /// Parameter type.
599 pub ty: Type,
600}
601
602/// Expression
603#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
604pub enum Expr {
605 /// Null literal.
606 Null,
607 /// Boolean literal.
608 Bool(bool),
609 /// Integer literal.
610 Int(i64),
611 /// Floating-point literal.
612 Float(f64),
613 /// String literal.
614 Str(String),
615 /// Duration literal in nanoseconds.
616 Duration(u64),
617 /// Timestamp literal in nanoseconds since epoch.
618 Timestamp(i64),
619
620 /// Array literal: `[1, 2, 3]`.
621 Array(Vec<Self>),
622 /// Map literal: `{key: value, ...}`.
623 Map(Vec<(String, Self)>),
624
625 /// Identifier reference.
626 Ident(String),
627
628 /// Binary operation: `left op right`.
629 Binary {
630 /// The binary operator.
631 op: BinOp,
632 /// Left operand.
633 left: Box<Self>,
634 /// Right operand.
635 right: Box<Self>,
636 },
637
638 /// Unary operation: `op expr`.
639 Unary {
640 /// The unary operator.
641 op: UnaryOp,
642 /// Operand expression.
643 expr: Box<Self>,
644 },
645
646 /// Member access: `expr.member`.
647 Member {
648 /// Object expression.
649 expr: Box<Self>,
650 /// Member name to access.
651 member: String,
652 },
653
654 /// Optional member access: `expr?.member`.
655 OptionalMember {
656 /// Object expression (may be null).
657 expr: Box<Self>,
658 /// Member name to access.
659 member: String,
660 },
661
662 /// Index access: `expr[index]`.
663 Index {
664 /// Collection expression.
665 expr: Box<Self>,
666 /// Index expression.
667 index: Box<Self>,
668 },
669
670 /// Slice: `expr[start:end]`.
671 Slice {
672 /// Collection expression.
673 expr: Box<Self>,
674 /// Optional start index.
675 start: Option<Box<Self>>,
676 /// Optional end index.
677 end: Option<Box<Self>>,
678 },
679
680 /// Function call: `func(args)`.
681 Call {
682 /// Function expression to call.
683 func: Box<Self>,
684 /// Arguments to the function.
685 args: Vec<Arg>,
686 },
687
688 /// Lambda: `x => expr` or `(x, y) => expr`.
689 Lambda {
690 /// Lambda parameter names.
691 params: Vec<String>,
692 /// Lambda body expression.
693 body: Box<Self>,
694 },
695
696 /// Conditional expression: `if cond then a else b`.
697 If {
698 /// Condition expression.
699 cond: Box<Self>,
700 /// Value when true.
701 then_branch: Box<Self>,
702 /// Value when false.
703 else_branch: Box<Self>,
704 },
705
706 /// Null coalescing: `expr ?? default`.
707 Coalesce {
708 /// Expression that may be null.
709 expr: Box<Self>,
710 /// Fallback value when expr is null.
711 default: Box<Self>,
712 },
713
714 /// Range: `start..end` or `start..=end`.
715 Range {
716 /// Range start.
717 start: Box<Self>,
718 /// Range end.
719 end: Box<Self>,
720 /// Whether the end is inclusive (`..=`).
721 inclusive: bool,
722 },
723
724 /// Block expression: `{ let a = 1; let b = 2; a + b }`.
725 Block {
726 /// Local bindings: (name, optional type, value, is_mutable).
727 stmts: Vec<(String, Option<Type>, Self, bool)>,
728 /// Final expression that produces the block's value.
729 result: Box<Self>,
730 },
731}
732
733/// Function argument
734#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
735pub enum Arg {
736 /// Positional argument.
737 Positional(Expr),
738 /// Named argument: `name: value`.
739 Named(String, Expr),
740}
741
742/// Binary operator
743#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
744pub enum BinOp {
745 /// Addition (`+`).
746 Add,
747 /// Subtraction (`-`).
748 Sub,
749 /// Multiplication (`*`).
750 Mul,
751 /// Division (`/`).
752 Div,
753 /// Modulo (`%`).
754 Mod,
755 /// Exponentiation (`**`).
756 Pow,
757
758 /// Equality (`==`).
759 Eq,
760 /// Inequality (`!=`).
761 NotEq,
762 /// Less than (`<`).
763 Lt,
764 /// Less than or equal (`<=`).
765 Le,
766 /// Greater than (`>`).
767 Gt,
768 /// Greater than or equal (`>=`).
769 Ge,
770 /// Membership test (`in`).
771 In,
772 /// Negated membership test (`not in`).
773 NotIn,
774 /// Type identity test (`is`).
775 Is,
776
777 /// Logical AND (`and`).
778 And,
779 /// Logical OR (`or`).
780 Or,
781 /// Logical XOR (`xor`).
782 Xor,
783
784 /// Temporal followed-by operator (`->`).
785 FollowedBy,
786
787 /// Bitwise AND (`&`).
788 BitAnd,
789 /// Bitwise OR (`|`).
790 BitOr,
791 /// Bitwise XOR (`^`).
792 BitXor,
793 /// Left shift (`<<`).
794 Shl,
795 /// Right shift (`>>`).
796 Shr,
797}
798
799impl BinOp {
800 /// Returns the operator's source-level string representation.
801 pub const fn as_str(&self) -> &'static str {
802 match self {
803 Self::Add => "+",
804 Self::Sub => "-",
805 Self::Mul => "*",
806 Self::Div => "/",
807 Self::Mod => "%",
808 Self::Pow => "**",
809 Self::Eq => "==",
810 Self::NotEq => "!=",
811 Self::Lt => "<",
812 Self::Le => "<=",
813 Self::Gt => ">",
814 Self::Ge => ">=",
815 Self::In => "in",
816 Self::NotIn => "not in",
817 Self::Is => "is",
818 Self::And => "and",
819 Self::Or => "or",
820 Self::Xor => "xor",
821 Self::FollowedBy => "->",
822 Self::BitAnd => "&",
823 Self::BitOr => "|",
824 Self::BitXor => "^",
825 Self::Shl => "<<",
826 Self::Shr => ">>",
827 }
828 }
829}
830
831/// Unary operator
832#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
833pub enum UnaryOp {
834 /// Arithmetic negation (`-`).
835 Neg,
836 /// Logical negation (`not`).
837 Not,
838 /// Bitwise complement (`~`).
839 BitNot,
840}
841
842impl UnaryOp {
843 /// Returns the operator's source-level string representation.
844 pub const fn as_str(&self) -> &'static str {
845 match self {
846 Self::Neg => "-",
847 Self::Not => "not",
848 Self::BitNot => "~",
849 }
850 }
851}
852
853/// Configuration item
854#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
855pub enum ConfigItem {
856 /// Simple key-value pair.
857 Value(String, ConfigValue),
858 /// Nested configuration block.
859 Nested(String, Vec<Self>),
860}
861
862/// Configuration value
863#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
864pub enum ConfigValue {
865 /// Boolean value.
866 Bool(bool),
867 /// Integer value.
868 Int(i64),
869 /// Floating-point value.
870 Float(f64),
871 /// String value.
872 Str(String),
873 /// Duration value in nanoseconds.
874 Duration(u64),
875 /// Identifier reference.
876 Ident(String),
877 /// Array of configuration values.
878 Array(Vec<Self>),
879 /// Map of key-value pairs.
880 Map(Vec<(String, Self)>),
881}
882
883impl ConfigValue {
884 /// Get value as string (works for Str and Ident)
885 pub fn as_string(&self) -> Option<&str> {
886 match self {
887 Self::Str(s) => Some(s),
888 Self::Ident(s) => Some(s),
889 _ => None,
890 }
891 }
892
893 /// Get value as i64
894 pub const fn as_int(&self) -> Option<i64> {
895 match self {
896 Self::Int(i) => Some(*i),
897 Self::Float(f) => Some(*f as i64),
898 _ => None,
899 }
900 }
901
902 /// Get value as f64
903 pub const fn as_float(&self) -> Option<f64> {
904 match self {
905 Self::Float(f) => Some(*f),
906 Self::Int(i) => Some(*i as f64),
907 _ => None,
908 }
909 }
910
911 /// Get value as bool
912 pub const fn as_bool(&self) -> Option<bool> {
913 match self {
914 Self::Bool(b) => Some(*b),
915 _ => None,
916 }
917 }
918}