ast.rs
raw
//! The component AST: the surface file, structured but not yet validated.
use crate::expr::Expr;
use crate::sexpr::Span;
#[derive(Clone, Debug, PartialEq)]
pub struct Ident {
pub name: String,
pub span: Span,
}
/// The closed set of prop/state/output types. Arbitrary
/// Rust types are a planned extension riding on the same declarations.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum TypeKind {
I32,
I64,
F32,
F64,
Bool,
String,
}
impl TypeKind {
pub fn parse(name: &str) -> Option<Self> {
Some(match name {
"i32" => Self::I32,
"i64" => Self::I64,
"f32" => Self::F32,
"f64" => Self::F64,
"bool" => Self::Bool,
"String" => Self::String,
_ => return None,
})
}
pub fn rust_name(self) -> &'static str {
match self {
Self::I32 => "i32",
Self::I64 => "i64",
Self::F32 => "f32",
Self::F64 => "f64",
Self::Bool => "bool",
Self::String => "String",
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Ty {
pub kind: TyKind,
/// `(List T)` — a list of `kind` elements; nested lists are a planned
/// extension.
pub list: bool,
pub span: Span,
}
/// A type name as written: a scalar, or a capitalized record name (resolved
/// against the file's `(record …)` declarations during validation).
#[derive(Clone, Debug, PartialEq)]
pub enum TyKind {
Scalar(TypeKind),
Named(String),
}
#[derive(Clone, Debug, PartialEq)]
pub struct PropDecl {
pub name: Ident,
pub ty: Ty,
pub default: Option<Expr>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct StateDecl {
pub name: Ident,
pub ty: Ty,
pub init: Expr,
}
#[derive(Clone, Debug, PartialEq)]
pub struct OutputDecl {
pub name: Ident,
pub ty: Ty,
}
/// A declared handler: `(handler increment)`, or with payloads —
/// `(handler edited String)` for payload-carrying events like
/// `:on-change`, `(handler moved i64 i64)` for invocation wires passing
/// arguments.
#[derive(Clone, Debug, PartialEq)]
pub struct HandlerDecl {
pub name: Ident,
/// The payloads the handler receives, in order; empty for plain
/// handlers.
pub payloads: Vec<Ty>,
}
/// One `:name value` pair on a widget node. Whether it is a static style,
/// a bindable widget property, or an event wire is decided by validation
/// against the property registry.
#[derive(Clone, Debug, PartialEq)]
pub struct NodeProp {
pub name: Ident,
pub value: Expr,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Node {
pub widget: Ident,
pub props: Vec<NodeProp>,
pub children: Vec<Node>,
/// `Some` for the structural forms `(if …)` and `(for …)`; their
/// branch/body nodes live in `children`.
pub control: Option<Control>,
pub span: Span,
}
/// A structural control form in widget position.
#[derive(Clone, Debug, PartialEq)]
pub enum Control {
/// `(if cond then-node else-node?)` — children hold the branches.
If { cond: Expr },
/// `(for var list-name :index i :key expr body-node)` — the single
/// child is the row template, instantiated per item.
For {
var: Ident,
index: Option<Ident>,
list: Expr,
key: Option<Expr>,
},
}
/// `(record Todo :id i64 :label String :done bool)` — keyword-shaped,
/// mirroring instance syntax. Field names own the keyword namespace inside
/// the form (a committed consequence: record-level options can never be
/// keywords).
#[derive(Clone, Debug, PartialEq)]
pub struct RecordDecl {
pub name: Ident,
pub fields: Vec<(Ident, Ty)>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Component {
pub name: Ident,
pub records: Vec<RecordDecl>,
pub props: Vec<PropDecl>,
pub outputs: Vec<OutputDecl>,
pub states: Vec<StateDecl>,
pub handlers: Vec<HandlerDecl>,
pub root: Node,
pub span: Span,
}