dreamstack/compiler/ds-parser/src/ast.rs
enzotar a634152318 feat: DreamStack compiler foundation — Phase 0/1
Complete compiler pipeline from .ds source to reactive browser apps:

- ds-parser: lexer (string interpolation, operators, keywords) + recursive
  descent parser with operator precedence + full AST types
- ds-analyzer: signal graph extraction (source/derived classification),
  topological sort for glitch-free propagation, DOM binding analysis
- ds-codegen: JavaScript emitter with embedded reactive runtime (~3KB
  signal/derived/effect system) and dark-theme CSS design system
- ds-cli: build (compile to HTML+JS), dev (live server), check (analyze)

Verified working: source signals, derived signals, event handlers,
conditional rendering (when), 12 unit tests passing, 6.8KB output.
2026-02-25 00:03:06 -08:00

226 lines
5 KiB
Rust

/// The AST for DreamStack.
/// Homoiconic: every node is also representable as data (tagged vectors/maps).
/// A complete DreamStack program is a list of top-level declarations.
#[derive(Debug, Clone)]
pub struct Program {
pub declarations: Vec<Declaration>,
}
/// Top-level declarations.
#[derive(Debug, Clone)]
pub enum Declaration {
/// `let name = expr`
Let(LetDecl),
/// `view name = expr` or `view name(params) = expr`
View(ViewDecl),
/// `effect name(params): ReturnType`
Effect(EffectDecl),
/// `on event_name -> body`
OnHandler(OnHandler),
}
/// `let count = 0` or `let doubled = count * 2`
#[derive(Debug, Clone)]
pub struct LetDecl {
pub name: String,
pub value: Expr,
pub span: Span,
}
/// `view counter = column [ ... ]`
/// `view profile(id: UserId) = ...`
#[derive(Debug, Clone)]
pub struct ViewDecl {
pub name: String,
pub params: Vec<Param>,
pub body: Expr,
pub span: Span,
}
/// `effect fetchUser(id: UserId): Result<User, ApiError>`
#[derive(Debug, Clone)]
pub struct EffectDecl {
pub name: String,
pub params: Vec<Param>,
pub return_type: TypeExpr,
pub span: Span,
}
/// `on toggle_sidebar -> ...`
#[derive(Debug, Clone)]
pub struct OnHandler {
pub event: String,
pub param: Option<String>,
pub body: Expr,
pub span: Span,
}
/// Function/view parameter.
#[derive(Debug, Clone)]
pub struct Param {
pub name: String,
pub type_annotation: Option<TypeExpr>,
}
/// Type expressions (simplified for Phase 0).
#[derive(Debug, Clone)]
pub enum TypeExpr {
Named(String),
Generic(String, Vec<TypeExpr>),
}
/// Expressions — the core of the language.
#[derive(Debug, Clone)]
pub enum Expr {
/// Integer literal: `42`
IntLit(i64),
/// Float literal: `3.14`
FloatLit(f64),
/// String literal: `"hello"` (may contain `{interpolation}`)
StringLit(StringLit),
/// Boolean literal: `true` / `false`
BoolLit(bool),
/// Identifier: `count`, `sidebar.width`
Ident(String),
/// Dotted access: `user.name`
DotAccess(Box<Expr>, String),
/// Binary operation: `a + b`, `count > 10`
BinOp(Box<Expr>, BinOp, Box<Expr>),
/// Unary operation: `-x`, `!flag`
UnaryOp(UnaryOp, Box<Expr>),
/// Assignment: `count += 1`, `panel_x.target = 0`
Assign(Box<Expr>, AssignOp, Box<Expr>),
/// Function call: `clamp(200, 20vw, 350)`
Call(String, Vec<Expr>),
/// Block: multiple expressions, last is the value
Block(Vec<Expr>),
/// View element: `text "hello"`, `button "+" { click: ... }`
Element(Element),
/// Container: `column [ ... ]`, `row [ ... ]`
Container(Container),
/// When conditional: `when count > 10 -> ...`
When(Box<Expr>, Box<Expr>),
/// Match expression
Match(Box<Expr>, Vec<MatchArm>),
/// Pipe: `expr | operator`
Pipe(Box<Expr>, Box<Expr>),
/// `perform effectName(args)`
Perform(String, Vec<Expr>),
/// `stream from source`
StreamFrom(String),
/// Lambda: `(x -> x * 2)`
Lambda(Vec<String>, Box<Expr>),
/// Record literal: `{ key: value, ... }`
Record(Vec<(String, Expr)>),
/// List literal: `[a, b, c]`
List(Vec<Expr>),
/// `if cond then a else b`
If(Box<Expr>, Box<Expr>, Box<Expr>),
/// Spring: `spring(target: 0, stiffness: 300, damping: 30)`
Spring(Vec<(String, Expr)>),
}
/// String literal with interpolation segments.
#[derive(Debug, Clone)]
pub struct StringLit {
pub segments: Vec<StringSegment>,
}
#[derive(Debug, Clone)]
pub enum StringSegment {
Literal(String),
Interpolation(Box<Expr>),
}
/// Binary operators.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum BinOp {
Add,
Sub,
Mul,
Div,
Mod,
Eq,
Neq,
Lt,
Gt,
Lte,
Gte,
And,
Or,
}
/// Unary operators.
#[derive(Debug, Clone, Copy)]
pub enum UnaryOp {
Neg,
Not,
}
/// Assignment operators.
#[derive(Debug, Clone, Copy)]
pub enum AssignOp {
Set,
AddAssign,
SubAssign,
}
/// A UI element: `text label`, `button "+" { click: handler }`
#[derive(Debug, Clone)]
pub struct Element {
pub tag: String,
pub args: Vec<Expr>,
pub props: Vec<(String, Expr)>,
pub modifiers: Vec<Modifier>,
}
/// A container: `column [ child1, child2 ]`
#[derive(Debug, Clone)]
pub struct Container {
pub kind: ContainerKind,
pub children: Vec<Expr>,
pub props: Vec<(String, Expr)>,
}
#[derive(Debug, Clone)]
pub enum ContainerKind {
Column,
Row,
Stack,
List,
Panel,
Form,
Custom(String),
}
/// Match arm: `Ok(u) -> column [ ... ]`
#[derive(Debug, Clone)]
pub struct MatchArm {
pub pattern: Pattern,
pub body: Expr,
}
/// Pattern matching.
#[derive(Debug, Clone)]
pub enum Pattern {
Wildcard,
Ident(String),
Constructor(String, Vec<Pattern>),
Literal(Expr),
}
/// Modifiers: `| animate fade-in 200ms`
#[derive(Debug, Clone)]
pub struct Modifier {
pub name: String,
pub args: Vec<Expr>,
}
/// Source location tracking.
#[derive(Debug, Clone, Copy, Default)]
pub struct Span {
pub start: usize,
pub end: usize,
pub line: usize,
}