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.
226 lines
5 KiB
Rust
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,
|
|
}
|