Skip to main content

harn_parser/
ast.rs

1use harn_lexer::{Span, StringSegment};
2
3/// A node wrapped with source location information.
4#[derive(Debug, Clone, PartialEq)]
5pub struct Spanned<T> {
6    pub node: T,
7    pub span: Span,
8}
9
10impl<T> Spanned<T> {
11    pub fn new(node: T, span: Span) -> Self {
12        Self { node, span }
13    }
14
15    pub fn dummy(node: T) -> Self {
16        Self {
17            node,
18            span: Span::dummy(),
19        }
20    }
21}
22
23/// A spanned AST node — the primary unit throughout the compiler.
24pub type SNode = Spanned<Node>;
25
26/// Helper to wrap a node with a span.
27pub fn spanned(node: Node, span: Span) -> SNode {
28    SNode::new(node, span)
29}
30
31/// AST nodes for the Harn language.
32#[derive(Debug, Clone, PartialEq)]
33pub enum Node {
34    // Declarations
35    Pipeline {
36        name: String,
37        params: Vec<String>,
38        body: Vec<SNode>,
39        extends: Option<String>,
40    },
41    LetBinding {
42        pattern: BindingPattern,
43        type_ann: Option<TypeExpr>,
44        value: Box<SNode>,
45    },
46    VarBinding {
47        pattern: BindingPattern,
48        type_ann: Option<TypeExpr>,
49        value: Box<SNode>,
50    },
51    OverrideDecl {
52        name: String,
53        params: Vec<String>,
54        body: Vec<SNode>,
55    },
56    ImportDecl {
57        path: String,
58    },
59    /// Selective import: import { foo, bar } from "module"
60    SelectiveImport {
61        names: Vec<String>,
62        path: String,
63    },
64    EnumDecl {
65        name: String,
66        variants: Vec<EnumVariant>,
67    },
68    StructDecl {
69        name: String,
70        fields: Vec<StructField>,
71    },
72    InterfaceDecl {
73        name: String,
74        methods: Vec<InterfaceMethod>,
75    },
76
77    // Control flow
78    IfElse {
79        condition: Box<SNode>,
80        then_body: Vec<SNode>,
81        else_body: Option<Vec<SNode>>,
82    },
83    ForIn {
84        pattern: BindingPattern,
85        iterable: Box<SNode>,
86        body: Vec<SNode>,
87    },
88    MatchExpr {
89        value: Box<SNode>,
90        arms: Vec<MatchArm>,
91    },
92    WhileLoop {
93        condition: Box<SNode>,
94        body: Vec<SNode>,
95    },
96    Retry {
97        count: Box<SNode>,
98        body: Vec<SNode>,
99    },
100    ReturnStmt {
101        value: Option<Box<SNode>>,
102    },
103    TryCatch {
104        body: Vec<SNode>,
105        error_var: Option<String>,
106        error_type: Option<TypeExpr>,
107        catch_body: Vec<SNode>,
108    },
109    FnDecl {
110        name: String,
111        type_params: Vec<TypeParam>,
112        params: Vec<TypedParam>,
113        return_type: Option<TypeExpr>,
114        where_clauses: Vec<WhereClause>,
115        body: Vec<SNode>,
116        is_pub: bool,
117    },
118    TypeDecl {
119        name: String,
120        type_expr: TypeExpr,
121    },
122    SpawnExpr {
123        body: Vec<SNode>,
124    },
125    /// Duration literal: 500ms, 5s, 30m, 2h
126    DurationLiteral(u64),
127    /// Range expression: start upto end (exclusive) or start thru end (inclusive)
128    RangeExpr {
129        start: Box<SNode>,
130        end: Box<SNode>,
131        inclusive: bool,
132    },
133    /// Guard clause: guard condition else { body }
134    GuardStmt {
135        condition: Box<SNode>,
136        else_body: Vec<SNode>,
137    },
138    /// Ask expression: ask { system: "...", user: "...", ... }
139    AskExpr {
140        fields: Vec<DictEntry>,
141    },
142    /// Deadline block: deadline DURATION { body }
143    DeadlineBlock {
144        duration: Box<SNode>,
145        body: Vec<SNode>,
146    },
147    /// Yield expression: yields control to host, optionally with a value.
148    YieldExpr {
149        value: Option<Box<SNode>>,
150    },
151    /// Mutex block: mutual exclusion for concurrent access.
152    MutexBlock {
153        body: Vec<SNode>,
154    },
155    /// Break out of a loop.
156    BreakStmt,
157    /// Continue to next loop iteration.
158    ContinueStmt,
159
160    // Concurrency
161    Parallel {
162        count: Box<SNode>,
163        variable: Option<String>,
164        body: Vec<SNode>,
165    },
166    ParallelMap {
167        list: Box<SNode>,
168        variable: String,
169        body: Vec<SNode>,
170    },
171
172    // Expressions
173    FunctionCall {
174        name: String,
175        args: Vec<SNode>,
176    },
177    MethodCall {
178        object: Box<SNode>,
179        method: String,
180        args: Vec<SNode>,
181    },
182    /// Optional method call: `obj?.method(args)` — returns nil if obj is nil.
183    OptionalMethodCall {
184        object: Box<SNode>,
185        method: String,
186        args: Vec<SNode>,
187    },
188    PropertyAccess {
189        object: Box<SNode>,
190        property: String,
191    },
192    /// Optional chaining: `obj?.property` — returns nil if obj is nil.
193    OptionalPropertyAccess {
194        object: Box<SNode>,
195        property: String,
196    },
197    SubscriptAccess {
198        object: Box<SNode>,
199        index: Box<SNode>,
200    },
201    SliceAccess {
202        object: Box<SNode>,
203        start: Option<Box<SNode>>,
204        end: Option<Box<SNode>>,
205    },
206    BinaryOp {
207        op: String,
208        left: Box<SNode>,
209        right: Box<SNode>,
210    },
211    UnaryOp {
212        op: String,
213        operand: Box<SNode>,
214    },
215    Ternary {
216        condition: Box<SNode>,
217        true_expr: Box<SNode>,
218        false_expr: Box<SNode>,
219    },
220    Assignment {
221        target: Box<SNode>,
222        value: Box<SNode>,
223        /// None = plain `=`, Some("+") = `+=`, etc.
224        op: Option<String>,
225    },
226    ThrowStmt {
227        value: Box<SNode>,
228    },
229
230    /// Enum variant construction: EnumName.Variant(args)
231    EnumConstruct {
232        enum_name: String,
233        variant: String,
234        args: Vec<SNode>,
235    },
236    /// Struct construction: StructName { field: value, ... }
237    StructConstruct {
238        struct_name: String,
239        fields: Vec<DictEntry>,
240    },
241
242    // Literals
243    InterpolatedString(Vec<StringSegment>),
244    StringLiteral(String),
245    IntLiteral(i64),
246    FloatLiteral(f64),
247    BoolLiteral(bool),
248    NilLiteral,
249    Identifier(String),
250    ListLiteral(Vec<SNode>),
251    DictLiteral(Vec<DictEntry>),
252    /// Spread expression `...expr` inside list/dict literals.
253    Spread(Box<SNode>),
254
255    // Blocks
256    Block(Vec<SNode>),
257    Closure {
258        params: Vec<TypedParam>,
259        body: Vec<SNode>,
260    },
261}
262
263#[derive(Debug, Clone, PartialEq)]
264pub struct MatchArm {
265    pub pattern: SNode,
266    pub body: Vec<SNode>,
267}
268
269#[derive(Debug, Clone, PartialEq)]
270pub struct DictEntry {
271    pub key: SNode,
272    pub value: SNode,
273}
274
275/// An enum variant declaration.
276#[derive(Debug, Clone, PartialEq)]
277pub struct EnumVariant {
278    pub name: String,
279    pub fields: Vec<TypedParam>,
280}
281
282/// A struct field declaration.
283#[derive(Debug, Clone, PartialEq)]
284pub struct StructField {
285    pub name: String,
286    pub type_expr: Option<TypeExpr>,
287    pub optional: bool,
288}
289
290/// An interface method signature.
291#[derive(Debug, Clone, PartialEq)]
292pub struct InterfaceMethod {
293    pub name: String,
294    pub params: Vec<TypedParam>,
295    pub return_type: Option<TypeExpr>,
296}
297
298/// A type annotation (optional, for runtime checking).
299#[derive(Debug, Clone, PartialEq)]
300pub enum TypeExpr {
301    /// A named type: int, string, float, bool, nil, list, dict, closure,
302    /// or a user-defined type name.
303    Named(String),
304    /// A union type: `string | nil`, `int | float`.
305    Union(Vec<TypeExpr>),
306    /// A dict shape type: `{name: string, age: int, active?: bool}`.
307    Shape(Vec<ShapeField>),
308    /// A list type: `list<int>`.
309    List(Box<TypeExpr>),
310    /// A dict type with key and value types: `dict<string, int>`.
311    DictType(Box<TypeExpr>, Box<TypeExpr>),
312    /// A function type: `fn(int, string) -> bool`.
313    FnType {
314        params: Vec<TypeExpr>,
315        return_type: Box<TypeExpr>,
316    },
317}
318
319/// A field in a dict shape type.
320#[derive(Debug, Clone, PartialEq)]
321pub struct ShapeField {
322    pub name: String,
323    pub type_expr: TypeExpr,
324    pub optional: bool,
325}
326
327/// A binding pattern for destructuring in let/var/for-in.
328#[derive(Debug, Clone, PartialEq)]
329pub enum BindingPattern {
330    /// Simple identifier: `let x = ...`
331    Identifier(String),
332    /// Dict destructuring: `let {name, age} = ...`
333    Dict(Vec<DictPatternField>),
334    /// List destructuring: `let [a, b] = ...`
335    List(Vec<ListPatternElement>),
336}
337
338/// A field in a dict destructuring pattern.
339#[derive(Debug, Clone, PartialEq)]
340pub struct DictPatternField {
341    /// The dict key to extract.
342    pub key: String,
343    /// Renamed binding (if different from key), e.g. `{name: alias}`.
344    pub alias: Option<String>,
345    /// True for `...rest` (rest pattern).
346    pub is_rest: bool,
347}
348
349/// An element in a list destructuring pattern.
350#[derive(Debug, Clone, PartialEq)]
351pub struct ListPatternElement {
352    /// The variable name to bind.
353    pub name: String,
354    /// True for `...rest` (rest pattern).
355    pub is_rest: bool,
356}
357
358/// A generic type parameter on a function or pipeline declaration.
359#[derive(Debug, Clone, PartialEq)]
360pub struct TypeParam {
361    pub name: String,
362}
363
364/// A where-clause constraint on a generic type parameter.
365#[derive(Debug, Clone, PartialEq)]
366pub struct WhereClause {
367    pub type_name: String,
368    pub bound: String,
369}
370
371/// A parameter with an optional type annotation.
372#[derive(Debug, Clone, PartialEq)]
373pub struct TypedParam {
374    pub name: String,
375    pub type_expr: Option<TypeExpr>,
376}
377
378impl TypedParam {
379    /// Create an untyped parameter.
380    pub fn untyped(name: impl Into<String>) -> Self {
381        Self {
382            name: name.into(),
383            type_expr: None,
384        }
385    }
386
387    /// Create a typed parameter.
388    pub fn typed(name: impl Into<String>, type_expr: TypeExpr) -> Self {
389        Self {
390            name: name.into(),
391            type_expr: Some(type_expr),
392        }
393    }
394
395    /// Extract just the names from a list of typed params.
396    pub fn names(params: &[TypedParam]) -> Vec<String> {
397        params.iter().map(|p| p.name.clone()).collect()
398    }
399}