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 { ... } finally { ... }`
198    TryCatch {
199        try_body: Vec<Stmt>,
200        catch_var: String,
201        catch_body: Vec<Stmt>,
202        finally_body: Option<Vec<Stmt>>,
203    },
204
205    /// `throw expr`
206    Throw(Expr),
207
208    /// `import "path.tl"` or `import "path.tl" as name`
209    Import { path: String, alias: Option<String> },
210
211    /// `test "name" { ... }`
212    Test { name: String, body: Vec<Stmt> },
213
214    /// `use data.transforms.clean_users` etc.
215    Use { item: UseItem, is_public: bool },
216
217    /// `mod transforms` or `pub mod transforms`
218    ModDecl { name: String, is_public: bool },
219
220    /// `trait Display<T> { fn show(self) -> string }`
221    TraitDef {
222        name: String,
223        type_params: Vec<String>,
224        methods: Vec<TraitMethod>,
225        is_public: bool,
226    },
227
228    /// `impl Display for Point { fn show(self) -> string { ... } }`
229    TraitImpl {
230        trait_name: String,
231        type_name: String,
232        type_params: Vec<String>,
233        methods: Vec<Stmt>,
234    },
235
236    /// `let { x, y } = expr` or `let [a, b] = expr`
237    LetDestructure {
238        pattern: Pattern,
239        mutable: bool,
240        value: Expr,
241        is_public: bool,
242    },
243
244    /// `type Mapper = fn(int64) -> int64`
245    TypeAlias {
246        name: String,
247        type_params: Vec<String>,
248        value: TypeExpr,
249        is_public: bool,
250    },
251
252    /// `agent name { model: "...", system: "...", tools { ... }, max_turns: N, on_tool_call { ... }, on_complete { ... } }`
253    Agent {
254        name: String,
255        model: String,
256        system_prompt: Option<String>,
257        tools: Vec<(String, Expr)>,
258        max_turns: Option<i64>,
259        temperature: Option<f64>,
260        max_tokens: Option<i64>,
261        base_url: Option<String>,
262        api_key: Option<String>,
263        output_format: Option<String>,
264        on_tool_call: Option<Vec<Stmt>>,
265        on_complete: Option<Vec<Stmt>>,
266    },
267
268    /// `break`
269    Break,
270
271    /// `continue`
272    Continue,
273}
274
275/// Enum variant definition
276#[derive(Debug, Clone)]
277pub struct EnumVariant {
278    pub name: String,
279    pub fields: Vec<TypeExpr>,
280}
281
282/// Window specification for stream processing
283#[derive(Debug, Clone)]
284pub enum WindowSpec {
285    /// `tumbling(duration)` — fixed-size, non-overlapping windows
286    Tumbling(String),
287    /// `sliding(window_size, slide_interval)` — overlapping windows
288    Sliding(String, String),
289    /// `session(gap_duration)` — session windows based on activity gap
290    Session(String),
291}
292
293/// A pattern for match arms and let-destructuring.
294/// Identifiers in pattern position are bindings (create new variables),
295/// not value references. Use literals, enum variants, or guards for comparison.
296#[derive(Debug, Clone)]
297pub enum Pattern {
298    /// `_` — matches anything, binds nothing
299    Wildcard,
300    /// Literal value: 1, "hi", true, none
301    Literal(Expr),
302    /// Binding: `x` — matches anything, binds to name
303    Binding(String),
304    /// Enum variant: `Color::Red(r, g, b)` or `None`
305    Enum {
306        type_name: String,
307        variant: String,
308        args: Vec<Pattern>,
309    },
310    /// Struct pattern: `Point { x, y }` or `{ x, y }`
311    Struct {
312        name: Option<String>,
313        fields: Vec<StructPatternField>,
314    },
315    /// List pattern: `[a, b, ...rest]`
316    List {
317        elements: Vec<Pattern>,
318        rest: Option<String>,
319    },
320    /// OR pattern: `A | B | C`
321    Or(Vec<Pattern>),
322}
323
324/// A field in a struct destructuring pattern.
325#[derive(Debug, Clone)]
326pub struct StructPatternField {
327    pub name: String,
328    /// None = shorthand `{ x }` means `{ x: x }`
329    pub pattern: Option<Pattern>,
330}
331
332/// A match arm: `pattern [if guard] => body`
333#[derive(Debug, Clone)]
334pub struct MatchArm {
335    pub pattern: Pattern,
336    pub guard: Option<Expr>,
337    pub body: Expr,
338}
339
340/// Schema migration operation
341#[derive(Debug, Clone)]
342pub enum MigrateOp {
343    /// `add_column(name: type, default: expr)`
344    AddColumn {
345        name: String,
346        type_ann: TypeExpr,
347        default: Option<Expr>,
348    },
349    /// `drop_column(name)`
350    DropColumn { name: String },
351    /// `rename_column(old_name, new_name)`
352    RenameColumn { from: String, to: String },
353    /// `alter_type(column, new_type)`
354    AlterType { column: String, new_type: TypeExpr },
355    /// `add_constraint(column, constraint_name)`
356    AddConstraint { column: String, constraint: String },
357    /// `drop_constraint(column, constraint_name)`
358    DropConstraint { column: String, constraint: String },
359}
360
361/// Closure body: either a single expression or a block with statements.
362#[derive(Debug, Clone)]
363pub enum ClosureBody {
364    /// `(x) => x * 2`
365    Expr(Box<Expr>),
366    /// `(x) -> int64 { let y = x * 2; y + 1 }`
367    Block {
368        stmts: Vec<Stmt>,
369        expr: Option<Box<Expr>>,
370    },
371}
372
373/// Expressions
374#[derive(Debug, Clone)]
375pub enum Expr {
376    // ── Literals ──
377    Int(i64),
378    Float(f64),
379    String(String),
380    Bool(bool),
381    None,
382    /// Decimal literal: `3.14d` — fixed-point decimal
383    Decimal(String),
384
385    /// Variable reference
386    Ident(String),
387
388    /// Binary operation: left op right
389    BinOp {
390        left: Box<Expr>,
391        op: BinOp,
392        right: Box<Expr>,
393    },
394
395    /// Unary operation: op expr
396    UnaryOp {
397        op: UnaryOp,
398        expr: Box<Expr>,
399    },
400
401    /// Function call: name(args)
402    Call {
403        function: Box<Expr>,
404        args: Vec<Expr>,
405    },
406
407    /// Named argument in a call: key: value
408    NamedArg {
409        name: String,
410        value: Box<Expr>,
411    },
412
413    /// Pipe: left |> right
414    Pipe {
415        left: Box<Expr>,
416        right: Box<Expr>,
417    },
418
419    /// Member access: expr.field
420    Member {
421        object: Box<Expr>,
422        field: String,
423    },
424
425    /// Index access: expr[index]
426    Index {
427        object: Box<Expr>,
428        index: Box<Expr>,
429    },
430
431    /// List literal: [a, b, c]
432    List(Vec<Expr>),
433
434    /// Map literal: { key: value, ... }
435    Map(Vec<(Expr, Expr)>),
436
437    /// Block expression: { stmts; expr }
438    Block {
439        stmts: Vec<Stmt>,
440        expr: Option<Box<Expr>>,
441    },
442
443    /// case { pattern => expr, ... }
444    Case {
445        arms: Vec<MatchArm>,
446    },
447
448    /// match expr { pattern => expr, ... }
449    Match {
450        subject: Box<Expr>,
451        arms: Vec<MatchArm>,
452    },
453
454    /// Closure: (params) => expr  or  (params) -> Type { stmts; expr }
455    Closure {
456        params: Vec<Param>,
457        return_type: Option<TypeExpr>,
458        body: ClosureBody,
459    },
460
461    /// Range: start..end
462    Range {
463        start: Box<Expr>,
464        end: Box<Expr>,
465    },
466
467    /// Null coalesce: expr ?? default
468    NullCoalesce {
469        expr: Box<Expr>,
470        default: Box<Expr>,
471    },
472
473    /// Assignment: name = value (for reassigning mut variables)
474    Assign {
475        target: Box<Expr>,
476        value: Box<Expr>,
477    },
478
479    /// Struct initialization: Name { field: value, ... }
480    StructInit {
481        name: String,
482        fields: Vec<(String, Expr)>,
483    },
484
485    /// Enum variant: Enum::Variant or Enum::Variant(args)
486    EnumVariant {
487        enum_name: String,
488        variant: String,
489        args: Vec<Expr>,
490    },
491
492    /// Await expression: `await expr`
493    Await(Box<Expr>),
494
495    /// Yield expression: `yield expr` or bare `yield`
496    Yield(Option<Box<Expr>>),
497
498    /// Try propagation: `expr?` — unwrap Result/Option or early return
499    Try(Box<Expr>),
500}
501
502/// Binary operators
503#[derive(Debug, Clone, PartialEq)]
504pub enum BinOp {
505    // Arithmetic
506    Add,
507    Sub,
508    Mul,
509    Div,
510    Mod,
511    Pow,
512    // Comparison
513    Eq,
514    Neq,
515    Lt,
516    Gt,
517    Lte,
518    Gte,
519    // Logical
520    And,
521    Or,
522}
523
524impl std::fmt::Display for BinOp {
525    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
526        match self {
527            BinOp::Add => write!(f, "+"),
528            BinOp::Sub => write!(f, "-"),
529            BinOp::Mul => write!(f, "*"),
530            BinOp::Div => write!(f, "/"),
531            BinOp::Mod => write!(f, "%"),
532            BinOp::Pow => write!(f, "**"),
533            BinOp::Eq => write!(f, "=="),
534            BinOp::Neq => write!(f, "!="),
535            BinOp::Lt => write!(f, "<"),
536            BinOp::Gt => write!(f, ">"),
537            BinOp::Lte => write!(f, "<="),
538            BinOp::Gte => write!(f, ">="),
539            BinOp::And => write!(f, "and"),
540            BinOp::Or => write!(f, "or"),
541        }
542    }
543}
544
545/// Unary operators
546#[derive(Debug, Clone, PartialEq)]
547pub enum UnaryOp {
548    Neg,
549    Not,
550    /// `&expr` — read-only reference
551    Ref,
552}
553
554/// Function parameter
555#[derive(Debug, Clone)]
556pub struct Param {
557    pub name: String,
558    pub type_ann: Option<TypeExpr>,
559}
560
561/// Annotation on schema/struct fields
562#[derive(Debug, Clone, PartialEq)]
563pub enum Annotation {
564    Sensitive,
565    Redact,
566    Pii,
567    Custom(String),
568}
569
570/// Schema field definition
571#[derive(Debug, Clone)]
572pub struct SchemaField {
573    pub name: String,
574    pub type_ann: TypeExpr,
575    /// Field-level doc comment (may contain @since, @deprecated annotations)
576    pub doc_comment: Option<String>,
577    /// Default value for added fields (used in migrations)
578    pub default_value: Option<Expr>,
579    /// Security annotations (@sensitive, @redact, @pii)
580    pub annotations: Vec<Annotation>,
581}
582
583/// Type expressions (Phase 0: basic types only)
584#[derive(Debug, Clone)]
585pub enum TypeExpr {
586    /// Named type: int64, string, bool, float64, User
587    Named(String),
588    /// Generic type: table<User>, list<int64>
589    Generic { name: String, args: Vec<TypeExpr> },
590    /// Optional type: T?
591    Optional(Box<TypeExpr>),
592    /// Function type: fn(int64, int64) -> int64
593    Function {
594        params: Vec<TypeExpr>,
595        return_type: Box<TypeExpr>,
596    },
597}