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