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