// AST node definitions for the dashboard's hand-rolled Rhai parser. // // Every node carries `start` / `end` byte offsets into the source so the // editor features (autocomplete, goto-def, find-usages, format) can map // between positions in the document and nodes in the tree. // // The shape mirrors the Rhai book grammar (https://rhai.rs/book/language/) // but simplified: type annotations are absent (Rhai is dynamic), and // statement-vs-expression duality is collapsed by letting `if` / `switch` / // block expressions appear in both positions (an `ExprStmt` wrapper turns // any expression into a statement). export interface Range { start: number; end: number; } // --------------------------------------------------------------------------- // Comments — captured by the lexer with their positions and re-emitted by // the formatter. Kept off the AST tree so they don't clutter walkers. // --------------------------------------------------------------------------- export interface Comment extends Range { kind: 'LineComment' | 'BlockComment'; text: string; } // --------------------------------------------------------------------------- // Statements // --------------------------------------------------------------------------- export type Stmt = | LetStmt | ConstStmt | FnDecl | ExprStmt | ReturnStmt | WhileStmt | LoopStmt | ForStmt | BreakStmt | ContinueStmt | TryStmt; export interface LetStmt extends Range { kind: 'Let'; name: string; nameRange: Range; init: Expr | null; } export interface ConstStmt extends Range { kind: 'Const'; name: string; nameRange: Range; init: Expr | null; } export interface Param extends Range { name: string; } export interface FnDecl extends Range { kind: 'FnDecl'; name: string; nameRange: Range; params: Param[]; body: BlockExpr; } export interface ExprStmt extends Range { kind: 'ExprStmt'; expr: Expr; // Whether the statement is terminated with `;`. Block-form expressions // (`if`/`switch`/`{...}`) don't require it; everything else does. semi: boolean; } export interface ReturnStmt extends Range { kind: 'Return'; value: Expr | null; } export interface WhileStmt extends Range { kind: 'While'; cond: Expr; body: BlockExpr; } export interface LoopStmt extends Range { kind: 'Loop'; body: BlockExpr; } export interface ForStmt extends Range { kind: 'For'; varName: string; varRange: Range; iter: Expr; body: BlockExpr; } export interface BreakStmt extends Range { kind: 'Break'; } export interface ContinueStmt extends Range { kind: 'Continue'; } export interface TryStmt extends Range { kind: 'Try'; body: BlockExpr; catchVar: string | null; catchVarRange: Range | null; handler: BlockExpr; } // --------------------------------------------------------------------------- // Expressions // --------------------------------------------------------------------------- export type Expr = | IdentExpr | NumberExpr | StringExpr | BoolExpr | NullExpr | CallExpr | MemberExpr | IndexExpr | UnaryExpr | BinaryExpr | AssignExpr | ParenExpr | ObjectMapExpr | ArrayExpr | FnExpr | IfExpr | SwitchExpr | BlockExpr; export interface IdentExpr extends Range { kind: 'Ident'; name: string; } export interface NumberExpr extends Range { kind: 'Number'; raw: string; } export interface StringExpr extends Range { kind: 'String'; // The surrounding quote — `"` is escape-processed, backtick is raw and // may span multiple lines. We don't decode escapes; the formatter just // preserves the raw text between the quotes. quote: '"' | '`'; raw: string; } export interface BoolExpr extends Range { kind: 'Bool'; value: boolean; } export interface NullExpr extends Range { kind: 'Null'; } export interface CallExpr extends Range { kind: 'Call'; callee: Expr; args: Expr[]; } export interface MemberExpr extends Range { kind: 'Member'; object: Expr; property: string; propertyRange: Range; } export interface IndexExpr extends Range { kind: 'Index'; object: Expr; index: Expr; } export interface UnaryExpr extends Range { kind: 'Unary'; op: string; operand: Expr; } export interface BinaryExpr extends Range { kind: 'Binary'; op: string; left: Expr; right: Expr; } export interface AssignExpr extends Range { kind: 'Assign'; op: string; // = += -= *= /= %= ??= target: Expr; value: Expr; } export interface ParenExpr extends Range { kind: 'Paren'; expr: Expr; } export interface ObjectMapEntry extends Range { key: string; keyRange: Range; value: Expr; } export interface ObjectMapExpr extends Range { kind: 'ObjectMap'; entries: ObjectMapEntry[]; } export interface ArrayExpr extends Range { kind: 'Array'; elements: Expr[]; } export interface FnExpr extends Range { kind: 'FnExpr'; params: Param[]; body: BlockExpr; } export interface IfExpr extends Range { kind: 'IfExpr'; cond: Expr; then: BlockExpr; // else branch: either a block or another `if` for `else if` chains. else_: BlockExpr | IfExpr | null; } export interface SwitchArm extends Range { pattern: Expr | null; // null = `_` default case guard: Expr | null; value: Expr; } export interface SwitchExpr extends Range { kind: 'SwitchExpr'; subject: Expr; arms: SwitchArm[]; } export interface BlockExpr extends Range { kind: 'BlockExpr'; stmts: Stmt[]; } // --------------------------------------------------------------------------- // Top-level parse output // --------------------------------------------------------------------------- export interface ParseError extends Range { message: string; } export interface ParseResult { source: string; program: BlockExpr; errors: ParseError[]; comments: Comment[]; // Offsets at which the source contained a blank line (a whitespace // run with two or more newlines). One entry per blank run; the // formatter consults these to preserve user-intent vertical grouping. blankLines: number[]; }