//! 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 { 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, } #[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, } /// 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, pub children: Vec, /// `Some` for the structural forms `(if …)` and `(for …)`; their /// branch/body nodes live in `children`. pub control: Option, 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, list: Expr, key: Option, }, } /// `(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, pub props: Vec, pub outputs: Vec, pub states: Vec, pub handlers: Vec, pub root: Node, pub span: Span, }