Skip to main content

tl_ast/
lib.rs

1// ThinkingLanguage — Abstract Syntax Tree
2// Licensed under MIT OR Apache-2.0
3//
4// Defines the tree structure produced by the parser.
5// Phase 0 subset: let bindings, functions, if/else, match/case,
6// pipe operator, basic types, print.
7
8use tl_errors::Span;
9
10/// A complete TL program is a list of statements
11#[derive(Debug, Clone)]
12pub struct Program {
13    pub statements: Vec<Stmt>,
14    /// Module-level documentation from `//!` comments at the top of the file
15    pub module_doc: Option<String>,
16}
17
18/// A statement with source location information.
19#[derive(Debug, Clone)]
20pub struct Stmt {
21    pub kind: StmtKind,
22    pub span: Span,
23    /// Documentation comment attached to this statement (from `///` comments)
24    pub doc_comment: Option<String>,
25}
26
27/// A use-import target
28#[derive(Debug, Clone)]
29pub enum UseItem {
30    /// `use data.transforms.clean_users`
31    Single(Vec<String>),
32    /// `use data.transforms.{clean_users, CleanedUser}`
33    Group(Vec<String>, Vec<String>),
34    /// `use data.transforms.*`
35    Wildcard(Vec<String>),
36    /// `use data.connectors.postgres as pg`
37    Aliased(Vec<String>, String),
38}
39
40/// A trait bound on a type parameter: `T: Comparable + Hashable`
41#[derive(Debug, Clone)]
42pub struct TraitBound {
43    pub type_param: String,
44    pub traits: Vec<String>,
45}
46
47/// A method signature within a trait definition
48#[derive(Debug, Clone)]
49pub struct TraitMethod {
50    pub name: String,
51    pub params: Vec<Param>,
52    pub return_type: Option<TypeExpr>,
53}
54
55/// Statement variants
56#[derive(Debug, Clone)]
57pub enum StmtKind {
58    /// `let x = expr` or `let mut x: type = expr`
59    Let {
60        name: String,
61        mutable: bool,
62        type_ann: Option<TypeExpr>,
63        value: Expr,
64        is_public: bool,
65    },
66
67    /// `fn name<T, U>(params) -> return_type where T: Bound { body }`
68    FnDecl {
69        name: String,
70        type_params: Vec<String>,
71        params: Vec<Param>,
72        return_type: Option<TypeExpr>,
73        bounds: Vec<TraitBound>,
74        body: Vec<Stmt>,
75        is_generator: bool,
76        is_public: bool,
77        is_async: bool,
78    },
79
80    /// Expression statement (e.g., a function call on its own line)
81    Expr(Expr),
82
83    /// `return expr`
84    Return(Option<Expr>),
85
86    /// `if cond { body } else if cond { body } else { body }`
87    If {
88        condition: Expr,
89        then_body: Vec<Stmt>,
90        else_ifs: Vec<(Expr, Vec<Stmt>)>,
91        else_body: Option<Vec<Stmt>>,
92    },
93
94    /// `while cond { body }`
95    While { condition: Expr, body: Vec<Stmt> },
96
97    /// `for name in iter { body }`
98    For {
99        name: String,
100        iter: Expr,
101        body: Vec<Stmt>,
102    },
103
104    /// `parallel for name in iter { body }`
105    ParallelFor {
106        name: String,
107        iter: Expr,
108        body: Vec<Stmt>,
109    },
110
111    /// `schema Name { field: type, ... }`
112    Schema {
113        name: String,
114        fields: Vec<SchemaField>,
115        is_public: bool,
116        /// Schema version from `@version N` doc comment annotation
117        version: Option<i64>,
118        /// Parent version this schema evolves from
119        parent_version: Option<i64>,
120    },
121
122    /// `migrate SchemaName from V1 to V2 { add_column(...), ... }`
123    Migrate {
124        schema_name: String,
125        from_version: i64,
126        to_version: i64,
127        operations: Vec<MigrateOp>,
128    },
129
130    /// `model name = train algorithm { key: value, ... }`
131    Train {
132        name: String,
133        algorithm: String,
134        config: Vec<(String, Expr)>,
135    },
136
137    /// `pipeline name { extract { ... } transform { ... } load { ... } }`
138    Pipeline {
139        name: String,
140        extract: Vec<Stmt>,
141        transform: Vec<Stmt>,
142        load: Vec<Stmt>,
143        schedule: Option<String>,
144        timeout: Option<String>,
145        retries: Option<i64>,
146        on_failure: Option<Vec<Stmt>>,
147        on_success: Option<Vec<Stmt>>,
148    },
149
150    /// `stream name { source: expr, window: spec, transform: { ... }, sink: expr }`
151    StreamDecl {
152        name: String,
153        source: Expr,
154        transform: Vec<Stmt>,
155        sink: Option<Expr>,
156        window: Option<WindowSpec>,
157        watermark: Option<String>,
158    },
159
160    /// `source name = connector TYPE { key: value, ... }`
161    SourceDecl {
162        name: String,
163        connector_type: String,
164        config: Vec<(String, Expr)>,
165    },
166
167    /// `sink name = connector TYPE { key: value, ... }`
168    SinkDecl {
169        name: String,
170        connector_type: String,
171        config: Vec<(String, Expr)>,
172    },
173
174    /// `struct Name<T, U> { field: type, ... }`
175    StructDecl {
176        name: String,
177        type_params: Vec<String>,
178        fields: Vec<SchemaField>,
179        is_public: bool,
180    },
181
182    /// `enum Name<T, E> { Variant, Variant(types), ... }`
183    EnumDecl {
184        name: String,
185        type_params: Vec<String>,
186        variants: Vec<EnumVariant>,
187        is_public: bool,
188    },
189
190    /// `impl<T> Type { fn methods... }`
191    ImplBlock {
192        type_name: String,
193        type_params: Vec<String>,
194        methods: Vec<Stmt>,
195    },
196
197    /// `try { ... } catch e { ... }`
198    TryCatch {
199        try_body: Vec<Stmt>,
200        catch_var: String,
201        catch_body: Vec<Stmt>,
202    },
203
204    /// `throw expr`
205    Throw(Expr),
206
207    /// `import "path.tl"` or `import "path.tl" as name`
208    Import { path: String, alias: Option<String> },
209
210    /// `test "name" { ... }`
211    Test { name: String, body: Vec<Stmt> },
212
213    /// `use data.transforms.clean_users` etc.
214    Use { item: UseItem, is_public: bool },
215
216    /// `mod transforms` or `pub mod transforms`
217    ModDecl { name: String, is_public: bool },
218
219    /// `trait Display<T> { fn show(self) -> string }`
220    TraitDef {
221        name: String,
222        type_params: Vec<String>,
223        methods: Vec<TraitMethod>,
224        is_public: bool,
225    },
226
227    /// `impl Display for Point { fn show(self) -> string { ... } }`
228    TraitImpl {
229        trait_name: String,
230        type_name: String,
231        type_params: Vec<String>,
232        methods: Vec<Stmt>,
233    },
234
235    /// `let { x, y } = expr` or `let [a, b] = expr`
236    LetDestructure {
237        pattern: Pattern,
238        mutable: bool,
239        value: Expr,
240        is_public: bool,
241    },
242
243    /// `type Mapper = fn(int64) -> int64`
244    TypeAlias {
245        name: String,
246        type_params: Vec<String>,
247        value: TypeExpr,
248        is_public: bool,
249    },
250
251    /// `break`
252    Break,
253
254    /// `continue`
255    Continue,
256}
257
258/// Enum variant definition
259#[derive(Debug, Clone)]
260pub struct EnumVariant {
261    pub name: String,
262    pub fields: Vec<TypeExpr>,
263}
264
265/// Window specification for stream processing
266#[derive(Debug, Clone)]
267pub enum WindowSpec {
268    /// `tumbling(duration)` — fixed-size, non-overlapping windows
269    Tumbling(String),
270    /// `sliding(window_size, slide_interval)` — overlapping windows
271    Sliding(String, String),
272    /// `session(gap_duration)` — session windows based on activity gap
273    Session(String),
274}
275
276/// A pattern for match arms and let-destructuring.
277/// Identifiers in pattern position are bindings (create new variables),
278/// not value references. Use literals, enum variants, or guards for comparison.
279#[derive(Debug, Clone)]
280pub enum Pattern {
281    /// `_` — matches anything, binds nothing
282    Wildcard,
283    /// Literal value: 1, "hi", true, none
284    Literal(Expr),
285    /// Binding: `x` — matches anything, binds to name
286    Binding(String),
287    /// Enum variant: `Color::Red(r, g, b)` or `None`
288    Enum {
289        type_name: String,
290        variant: String,
291        args: Vec<Pattern>,
292    },
293    /// Struct pattern: `Point { x, y }` or `{ x, y }`
294    Struct {
295        name: Option<String>,
296        fields: Vec<StructPatternField>,
297    },
298    /// List pattern: `[a, b, ...rest]`
299    List {
300        elements: Vec<Pattern>,
301        rest: Option<String>,
302    },
303    /// OR pattern: `A | B | C`
304    Or(Vec<Pattern>),
305}
306
307/// A field in a struct destructuring pattern.
308#[derive(Debug, Clone)]
309pub struct StructPatternField {
310    pub name: String,
311    /// None = shorthand `{ x }` means `{ x: x }`
312    pub pattern: Option<Pattern>,
313}
314
315/// A match arm: `pattern [if guard] => body`
316#[derive(Debug, Clone)]
317pub struct MatchArm {
318    pub pattern: Pattern,
319    pub guard: Option<Expr>,
320    pub body: Expr,
321}
322
323/// Schema migration operation
324#[derive(Debug, Clone)]
325pub enum MigrateOp {
326    /// `add_column(name: type, default: expr)`
327    AddColumn {
328        name: String,
329        type_ann: TypeExpr,
330        default: Option<Expr>,
331    },
332    /// `drop_column(name)`
333    DropColumn { name: String },
334    /// `rename_column(old_name, new_name)`
335    RenameColumn { from: String, to: String },
336    /// `alter_type(column, new_type)`
337    AlterType { column: String, new_type: TypeExpr },
338    /// `add_constraint(column, constraint_name)`
339    AddConstraint { column: String, constraint: String },
340    /// `drop_constraint(column, constraint_name)`
341    DropConstraint { column: String, constraint: String },
342}
343
344/// Closure body: either a single expression or a block with statements.
345#[derive(Debug, Clone)]
346pub enum ClosureBody {
347    /// `(x) => x * 2`
348    Expr(Box<Expr>),
349    /// `(x) -> int64 { let y = x * 2; y + 1 }`
350    Block {
351        stmts: Vec<Stmt>,
352        expr: Option<Box<Expr>>,
353    },
354}
355
356/// Expressions
357#[derive(Debug, Clone)]
358pub enum Expr {
359    // ── Literals ──
360    Int(i64),
361    Float(f64),
362    String(String),
363    Bool(bool),
364    None,
365    /// Decimal literal: `3.14d` — fixed-point decimal
366    Decimal(String),
367
368    /// Variable reference
369    Ident(String),
370
371    /// Binary operation: left op right
372    BinOp {
373        left: Box<Expr>,
374        op: BinOp,
375        right: Box<Expr>,
376    },
377
378    /// Unary operation: op expr
379    UnaryOp {
380        op: UnaryOp,
381        expr: Box<Expr>,
382    },
383
384    /// Function call: name(args)
385    Call {
386        function: Box<Expr>,
387        args: Vec<Expr>,
388    },
389
390    /// Named argument in a call: key: value
391    NamedArg {
392        name: String,
393        value: Box<Expr>,
394    },
395
396    /// Pipe: left |> right
397    Pipe {
398        left: Box<Expr>,
399        right: Box<Expr>,
400    },
401
402    /// Member access: expr.field
403    Member {
404        object: Box<Expr>,
405        field: String,
406    },
407
408    /// Index access: expr[index]
409    Index {
410        object: Box<Expr>,
411        index: Box<Expr>,
412    },
413
414    /// List literal: [a, b, c]
415    List(Vec<Expr>),
416
417    /// Map literal: { key: value, ... }
418    Map(Vec<(Expr, Expr)>),
419
420    /// Block expression: { stmts; expr }
421    Block {
422        stmts: Vec<Stmt>,
423        expr: Option<Box<Expr>>,
424    },
425
426    /// case { pattern => expr, ... }
427    Case {
428        arms: Vec<MatchArm>,
429    },
430
431    /// match expr { pattern => expr, ... }
432    Match {
433        subject: Box<Expr>,
434        arms: Vec<MatchArm>,
435    },
436
437    /// Closure: (params) => expr  or  (params) -> Type { stmts; expr }
438    Closure {
439        params: Vec<Param>,
440        return_type: Option<TypeExpr>,
441        body: ClosureBody,
442    },
443
444    /// Range: start..end
445    Range {
446        start: Box<Expr>,
447        end: Box<Expr>,
448    },
449
450    /// Null coalesce: expr ?? default
451    NullCoalesce {
452        expr: Box<Expr>,
453        default: Box<Expr>,
454    },
455
456    /// Assignment: name = value (for reassigning mut variables)
457    Assign {
458        target: Box<Expr>,
459        value: Box<Expr>,
460    },
461
462    /// Struct initialization: Name { field: value, ... }
463    StructInit {
464        name: String,
465        fields: Vec<(String, Expr)>,
466    },
467
468    /// Enum variant: Enum::Variant or Enum::Variant(args)
469    EnumVariant {
470        enum_name: String,
471        variant: String,
472        args: Vec<Expr>,
473    },
474
475    /// Await expression: `await expr`
476    Await(Box<Expr>),
477
478    /// Yield expression: `yield expr` or bare `yield`
479    Yield(Option<Box<Expr>>),
480
481    /// Try propagation: `expr?` — unwrap Result/Option or early return
482    Try(Box<Expr>),
483}
484
485/// Binary operators
486#[derive(Debug, Clone, PartialEq)]
487pub enum BinOp {
488    // Arithmetic
489    Add,
490    Sub,
491    Mul,
492    Div,
493    Mod,
494    Pow,
495    // Comparison
496    Eq,
497    Neq,
498    Lt,
499    Gt,
500    Lte,
501    Gte,
502    // Logical
503    And,
504    Or,
505}
506
507impl std::fmt::Display for BinOp {
508    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
509        match self {
510            BinOp::Add => write!(f, "+"),
511            BinOp::Sub => write!(f, "-"),
512            BinOp::Mul => write!(f, "*"),
513            BinOp::Div => write!(f, "/"),
514            BinOp::Mod => write!(f, "%"),
515            BinOp::Pow => write!(f, "**"),
516            BinOp::Eq => write!(f, "=="),
517            BinOp::Neq => write!(f, "!="),
518            BinOp::Lt => write!(f, "<"),
519            BinOp::Gt => write!(f, ">"),
520            BinOp::Lte => write!(f, "<="),
521            BinOp::Gte => write!(f, ">="),
522            BinOp::And => write!(f, "and"),
523            BinOp::Or => write!(f, "or"),
524        }
525    }
526}
527
528/// Unary operators
529#[derive(Debug, Clone, PartialEq)]
530pub enum UnaryOp {
531    Neg,
532    Not,
533    /// `&expr` — read-only reference
534    Ref,
535}
536
537/// Function parameter
538#[derive(Debug, Clone)]
539pub struct Param {
540    pub name: String,
541    pub type_ann: Option<TypeExpr>,
542}
543
544/// Annotation on schema/struct fields
545#[derive(Debug, Clone, PartialEq)]
546pub enum Annotation {
547    Sensitive,
548    Redact,
549    Pii,
550    Custom(String),
551}
552
553/// Schema field definition
554#[derive(Debug, Clone)]
555pub struct SchemaField {
556    pub name: String,
557    pub type_ann: TypeExpr,
558    /// Field-level doc comment (may contain @since, @deprecated annotations)
559    pub doc_comment: Option<String>,
560    /// Default value for added fields (used in migrations)
561    pub default_value: Option<Expr>,
562    /// Security annotations (@sensitive, @redact, @pii)
563    pub annotations: Vec<Annotation>,
564}
565
566/// Type expressions (Phase 0: basic types only)
567#[derive(Debug, Clone)]
568pub enum TypeExpr {
569    /// Named type: int64, string, bool, float64, User
570    Named(String),
571    /// Generic type: table<User>, list<int64>
572    Generic { name: String, args: Vec<TypeExpr> },
573    /// Optional type: T?
574    Optional(Box<TypeExpr>),
575    /// Function type: fn(int64, int64) -> int64
576    Function {
577        params: Vec<TypeExpr>,
578        return_type: Box<TypeExpr>,
579    },
580}