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        params: Vec<TypedParam>,
112        return_type: Option<TypeExpr>,
113        body: Vec<SNode>,
114        is_pub: bool,
115    },
116    TypeDecl {
117        name: String,
118        type_expr: TypeExpr,
119    },
120    SpawnExpr {
121        body: Vec<SNode>,
122    },
123    /// Duration literal: 500ms, 5s, 30m, 2h
124    DurationLiteral(u64),
125    /// Range expression: start upto end (exclusive) or start thru end (inclusive)
126    RangeExpr {
127        start: Box<SNode>,
128        end: Box<SNode>,
129        inclusive: bool,
130    },
131    /// Guard clause: guard condition else { body }
132    GuardStmt {
133        condition: Box<SNode>,
134        else_body: Vec<SNode>,
135    },
136    /// Ask expression: ask { system: "...", user: "...", ... }
137    AskExpr {
138        fields: Vec<DictEntry>,
139    },
140    /// Deadline block: deadline DURATION { body }
141    DeadlineBlock {
142        duration: Box<SNode>,
143        body: Vec<SNode>,
144    },
145    /// Yield expression: yields control to host, optionally with a value.
146    YieldExpr {
147        value: Option<Box<SNode>>,
148    },
149    /// Mutex block: mutual exclusion for concurrent access.
150    MutexBlock {
151        body: Vec<SNode>,
152    },
153    /// Break out of a loop.
154    BreakStmt,
155    /// Continue to next loop iteration.
156    ContinueStmt,
157
158    // Concurrency
159    Parallel {
160        count: Box<SNode>,
161        variable: Option<String>,
162        body: Vec<SNode>,
163    },
164    ParallelMap {
165        list: Box<SNode>,
166        variable: String,
167        body: Vec<SNode>,
168    },
169
170    // Expressions
171    FunctionCall {
172        name: String,
173        args: Vec<SNode>,
174    },
175    MethodCall {
176        object: Box<SNode>,
177        method: String,
178        args: Vec<SNode>,
179    },
180    /// Optional method call: `obj?.method(args)` — returns nil if obj is nil.
181    OptionalMethodCall {
182        object: Box<SNode>,
183        method: String,
184        args: Vec<SNode>,
185    },
186    PropertyAccess {
187        object: Box<SNode>,
188        property: String,
189    },
190    /// Optional chaining: `obj?.property` — returns nil if obj is nil.
191    OptionalPropertyAccess {
192        object: Box<SNode>,
193        property: String,
194    },
195    SubscriptAccess {
196        object: Box<SNode>,
197        index: Box<SNode>,
198    },
199    SliceAccess {
200        object: Box<SNode>,
201        start: Option<Box<SNode>>,
202        end: Option<Box<SNode>>,
203    },
204    BinaryOp {
205        op: String,
206        left: Box<SNode>,
207        right: Box<SNode>,
208    },
209    UnaryOp {
210        op: String,
211        operand: Box<SNode>,
212    },
213    Ternary {
214        condition: Box<SNode>,
215        true_expr: Box<SNode>,
216        false_expr: Box<SNode>,
217    },
218    Assignment {
219        target: Box<SNode>,
220        value: Box<SNode>,
221        /// None = plain `=`, Some("+") = `+=`, etc.
222        op: Option<String>,
223    },
224    ThrowStmt {
225        value: Box<SNode>,
226    },
227
228    /// Enum variant construction: EnumName.Variant(args)
229    EnumConstruct {
230        enum_name: String,
231        variant: String,
232        args: Vec<SNode>,
233    },
234    /// Struct construction: StructName { field: value, ... }
235    StructConstruct {
236        struct_name: String,
237        fields: Vec<DictEntry>,
238    },
239
240    // Literals
241    InterpolatedString(Vec<StringSegment>),
242    StringLiteral(String),
243    IntLiteral(i64),
244    FloatLiteral(f64),
245    BoolLiteral(bool),
246    NilLiteral,
247    Identifier(String),
248    ListLiteral(Vec<SNode>),
249    DictLiteral(Vec<DictEntry>),
250    /// Spread expression `...expr` inside list/dict literals.
251    Spread(Box<SNode>),
252
253    // Blocks
254    Block(Vec<SNode>),
255    Closure {
256        params: Vec<TypedParam>,
257        body: Vec<SNode>,
258    },
259}
260
261#[derive(Debug, Clone, PartialEq)]
262pub struct MatchArm {
263    pub pattern: SNode,
264    pub body: Vec<SNode>,
265}
266
267#[derive(Debug, Clone, PartialEq)]
268pub struct DictEntry {
269    pub key: SNode,
270    pub value: SNode,
271}
272
273/// An enum variant declaration.
274#[derive(Debug, Clone, PartialEq)]
275pub struct EnumVariant {
276    pub name: String,
277    pub fields: Vec<TypedParam>,
278}
279
280/// A struct field declaration.
281#[derive(Debug, Clone, PartialEq)]
282pub struct StructField {
283    pub name: String,
284    pub type_expr: Option<TypeExpr>,
285    pub optional: bool,
286}
287
288/// An interface method signature.
289#[derive(Debug, Clone, PartialEq)]
290pub struct InterfaceMethod {
291    pub name: String,
292    pub params: Vec<TypedParam>,
293    pub return_type: Option<TypeExpr>,
294}
295
296/// A type annotation (optional, for runtime checking).
297#[derive(Debug, Clone, PartialEq)]
298pub enum TypeExpr {
299    /// A named type: int, string, float, bool, nil, list, dict, closure,
300    /// or a user-defined type name.
301    Named(String),
302    /// A union type: `string | nil`, `int | float`.
303    Union(Vec<TypeExpr>),
304    /// A dict shape type: `{name: string, age: int, active?: bool}`.
305    Shape(Vec<ShapeField>),
306    /// A list type: `list[int]`.
307    List(Box<TypeExpr>),
308    /// A dict type with key and value types: `dict[string, int]`.
309    DictType(Box<TypeExpr>, Box<TypeExpr>),
310}
311
312/// A field in a dict shape type.
313#[derive(Debug, Clone, PartialEq)]
314pub struct ShapeField {
315    pub name: String,
316    pub type_expr: TypeExpr,
317    pub optional: bool,
318}
319
320/// A binding pattern for destructuring in let/var/for-in.
321#[derive(Debug, Clone, PartialEq)]
322pub enum BindingPattern {
323    /// Simple identifier: `let x = ...`
324    Identifier(String),
325    /// Dict destructuring: `let {name, age} = ...`
326    Dict(Vec<DictPatternField>),
327    /// List destructuring: `let [a, b] = ...`
328    List(Vec<ListPatternElement>),
329}
330
331/// A field in a dict destructuring pattern.
332#[derive(Debug, Clone, PartialEq)]
333pub struct DictPatternField {
334    /// The dict key to extract.
335    pub key: String,
336    /// Renamed binding (if different from key), e.g. `{name: alias}`.
337    pub alias: Option<String>,
338    /// True for `...rest` (rest pattern).
339    pub is_rest: bool,
340}
341
342/// An element in a list destructuring pattern.
343#[derive(Debug, Clone, PartialEq)]
344pub struct ListPatternElement {
345    /// The variable name to bind.
346    pub name: String,
347    /// True for `...rest` (rest pattern).
348    pub is_rest: bool,
349}
350
351/// A parameter with an optional type annotation.
352#[derive(Debug, Clone, PartialEq)]
353pub struct TypedParam {
354    pub name: String,
355    pub type_expr: Option<TypeExpr>,
356}
357
358impl TypedParam {
359    /// Create an untyped parameter.
360    pub fn untyped(name: impl Into<String>) -> Self {
361        Self {
362            name: name.into(),
363            type_expr: None,
364        }
365    }
366
367    /// Create a typed parameter.
368    pub fn typed(name: impl Into<String>, type_expr: TypeExpr) -> Self {
369        Self {
370            name: name.into(),
371            type_expr: Some(type_expr),
372        }
373    }
374
375    /// Extract just the names from a list of typed params.
376    pub fn names(params: &[TypedParam]) -> Vec<String> {
377        params.iter().map(|p| p.name.clone()).collect()
378    }
379}