//! Abstract Syntax Tree for the Lux language #![allow(dead_code)] use std::fmt; /// Source location for error reporting #[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Hash)] pub struct Span { pub start: usize, pub end: usize, } impl Span { pub fn new(start: usize, end: usize) -> Self { Self { start, end } } pub fn merge(self, other: Span) -> Span { Span { start: self.start.min(other.start), end: self.end.max(other.end), } } } /// An identifier (variable or type name) #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Ident { pub name: String, pub span: Span, } impl Ident { pub fn new(name: impl Into, span: Span) -> Self { Self { name: name.into(), span, } } } impl fmt::Display for Ident { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.name) } } /// Visibility modifier #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] pub enum Visibility { /// Public - exported from module Public, /// Private - only visible within module (default) #[default] Private, } // ============ Schema Evolution ============ /// A version number for schema evolution (e.g., @v1, @v2) #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct Version { pub number: u32, pub span: Span, } impl PartialOrd for Version { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } impl Ord for Version { fn cmp(&self, other: &Self) -> std::cmp::Ordering { self.number.cmp(&other.number) } } impl Version { pub fn new(number: u32, span: Span) -> Self { Self { number, span } } } impl fmt::Display for Version { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "@v{}", self.number) } } /// Version constraint for type annotations #[derive(Debug, Clone, PartialEq, Eq)] pub enum VersionConstraint { /// Exactly this version: @v2 Exact(Version), /// This version or later: @v2+ AtLeast(Version), /// Latest version: @latest Latest(Span), } impl fmt::Display for VersionConstraint { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { VersionConstraint::Exact(v) => write!(f, "{}", v), VersionConstraint::AtLeast(v) => write!(f, "{}+", v), VersionConstraint::Latest(_) => write!(f, "@latest"), } } } /// Migration from one version to another #[derive(Debug, Clone)] pub struct Migration { /// Source version: from @v1 pub from_version: Version, /// Migration body (expression that transforms old to new) pub body: Expr, pub span: Span, } // ============ Behavioral Types ============ /// A behavioral property that can be attached to functions #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum BehavioralProperty { /// No side effects - function only depends on inputs Pure, /// Always terminates and doesn't throw exceptions Total, /// f(f(x)) == f(x) for all x Idempotent, /// Same inputs always produce same outputs (no randomness) Deterministic, /// Order of arguments doesn't matter: f(a, b) == f(b, a) Commutative, } impl fmt::Display for BehavioralProperty { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { BehavioralProperty::Pure => write!(f, "pure"), BehavioralProperty::Total => write!(f, "total"), BehavioralProperty::Idempotent => write!(f, "idempotent"), BehavioralProperty::Deterministic => write!(f, "deterministic"), BehavioralProperty::Commutative => write!(f, "commutative"), } } } /// A where clause constraint on a function #[derive(Debug, Clone)] pub enum WhereClause { /// Type parameter has a property: where F is pure PropertyConstraint { type_param: Ident, property: BehavioralProperty, span: Span, }, /// Result refinement: where result > 0 ResultRefinement { predicate: Box, span: Span }, /// Trait constraint: where T: Show, where T: Eq + Ord TraitConstraint(TraitConstraint), } /// Module path: foo/bar/baz #[derive(Debug, Clone, PartialEq, Eq)] pub struct ModulePath { pub segments: Vec, pub span: Span, } impl ModulePath { pub fn to_string(&self) -> String { self.segments .iter() .map(|s| s.name.as_str()) .collect::>() .join("/") } } /// Import declaration #[derive(Debug, Clone)] pub struct ImportDecl { /// The module path being imported pub path: ModulePath, /// Optional alias: import foo/bar as baz pub alias: Option, /// Specific items to import: import foo.{a, b, c} pub items: Option>, /// Import all items: import foo.* pub wildcard: bool, pub span: Span, } /// A complete program (or module) #[derive(Debug, Clone)] pub struct Program { /// Module imports pub imports: Vec, /// Top-level declarations pub declarations: Vec, } /// Top-level declarations #[derive(Debug, Clone)] pub enum Declaration { /// Function definition: fn name(params): ReturnType with {Effects} = body Function(FunctionDecl), /// Effect declaration: effect Name { fn op1(...): T, ... } Effect(EffectDecl), /// Type alias or ADT: type Name = ... Type(TypeDecl), /// Handler definition: handler name: Effect { ... } Handler(HandlerDecl), /// Let binding at top level Let(LetDecl), /// Trait declaration: trait Name { fn method(...): T, ... } Trait(TraitDecl), /// Trait implementation: impl Trait for Type { ... } Impl(ImplDecl), /// Extern function declaration (FFI): extern fn name(params): ReturnType ExternFn(ExternFnDecl), /// Extern let declaration (FFI): extern let name: Type ExternLet(ExternLetDecl), } /// Function declaration #[derive(Debug, Clone)] pub struct FunctionDecl { pub visibility: Visibility, /// Documentation comment (from /// doc comments) pub doc: Option, pub name: Ident, pub type_params: Vec, pub params: Vec, pub return_type: TypeExpr, pub effects: Vec, /// Behavioral properties: is pure, is total, etc. pub properties: Vec, /// Where clause constraints pub where_clauses: Vec, pub body: Expr, pub span: Span, } /// Function parameter #[derive(Debug, Clone)] pub struct Parameter { pub name: Ident, pub typ: TypeExpr, pub span: Span, } /// Effect declaration #[derive(Debug, Clone)] pub struct EffectDecl { /// Documentation comment pub doc: Option, pub name: Ident, pub type_params: Vec, pub operations: Vec, pub span: Span, } /// An operation within an effect #[derive(Debug, Clone)] pub struct EffectOp { pub name: Ident, pub params: Vec, pub return_type: TypeExpr, pub span: Span, } /// Type declaration (alias or ADT) #[derive(Debug, Clone)] pub struct TypeDecl { pub visibility: Visibility, /// Documentation comment pub doc: Option, pub name: Ident, pub type_params: Vec, /// Optional version annotation: type User @v2 { ... } pub version: Option, pub definition: TypeDef, /// Migrations from previous versions: from @v1 = { ... } pub migrations: Vec, pub span: Span, } /// Type definition #[derive(Debug, Clone)] pub enum TypeDef { /// Type alias: type Foo = Bar Alias(TypeExpr), /// Record type: type Foo { field: Type, ... } Record(Vec), /// Enum/ADT: type Foo = A | B(Int) | C { x: Int } Enum(Vec), } /// Record field #[derive(Debug, Clone, PartialEq, Eq)] pub struct RecordField { pub name: Ident, pub typ: TypeExpr, pub span: Span, } /// Enum variant #[derive(Debug, Clone)] pub struct Variant { pub name: Ident, pub fields: VariantFields, pub span: Span, } /// Variant field types #[derive(Debug, Clone)] pub enum VariantFields { /// Unit variant: A Unit, /// Tuple variant: A(Int, String) Tuple(Vec), /// Record variant: A { x: Int, y: String } Record(Vec), } /// Handler declaration #[derive(Debug, Clone)] pub struct HandlerDecl { pub name: Ident, pub params: Vec, pub effect: Ident, pub implementations: Vec, pub span: Span, } /// Implementation of an effect operation in a handler #[derive(Debug, Clone)] pub struct HandlerImpl { pub op_name: Ident, pub params: Vec, pub resume: Option, // The continuation parameter pub body: Expr, pub span: Span, } /// Let declaration #[derive(Debug, Clone)] pub struct LetDecl { pub visibility: Visibility, /// Documentation comment pub doc: Option, pub name: Ident, pub typ: Option, pub value: Expr, pub span: Span, } /// Trait declaration: trait Show { fn show(self): String } #[derive(Debug, Clone)] pub struct TraitDecl { pub visibility: Visibility, /// Documentation comment pub doc: Option, pub name: Ident, /// Type parameters: trait Functor { ... } pub type_params: Vec, /// Super traits: trait Ord: Eq { ... } pub super_traits: Vec, /// Method signatures pub methods: Vec, pub span: Span, } /// A trait method signature #[derive(Debug, Clone)] pub struct TraitMethod { pub name: Ident, pub type_params: Vec, pub params: Vec, pub return_type: TypeExpr, /// Optional default implementation pub default_impl: Option, pub span: Span, } /// A trait bound: Show, Eq, Ord #[derive(Debug, Clone, PartialEq, Eq)] pub struct TraitBound { pub trait_name: Ident, pub type_args: Vec, pub span: Span, } /// Trait implementation: impl Show for Int { ... } #[derive(Debug, Clone)] pub struct ImplDecl { /// Type parameters: impl Show for List { ... } pub type_params: Vec, /// Trait constraints on type parameters pub constraints: Vec, /// The trait being implemented pub trait_name: Ident, /// Type arguments for the trait: impl Functor for ... pub trait_args: Vec, /// The type implementing the trait pub target_type: TypeExpr, /// Method implementations pub methods: Vec, pub span: Span, } /// A trait constraint: T: Show, T: Eq + Ord #[derive(Debug, Clone)] pub struct TraitConstraint { pub type_param: Ident, pub bounds: Vec, pub span: Span, } /// A method implementation in an impl block #[derive(Debug, Clone)] pub struct ImplMethod { pub name: Ident, pub params: Vec, pub return_type: Option, pub body: Expr, pub span: Span, } /// Extern function declaration (FFI) #[derive(Debug, Clone)] pub struct ExternFnDecl { pub visibility: Visibility, /// Documentation comment pub doc: Option, pub name: Ident, pub type_params: Vec, pub params: Vec, pub return_type: TypeExpr, /// Optional JS name override: extern fn foo(...): T = "jsFoo" pub js_name: Option, pub span: Span, } /// Extern let declaration (FFI) #[derive(Debug, Clone)] pub struct ExternLetDecl { pub visibility: Visibility, /// Documentation comment pub doc: Option, pub name: Ident, pub typ: TypeExpr, /// Optional JS name override: extern let foo: T = "window.foo" pub js_name: Option, pub span: Span, } /// Type expressions #[derive(Debug, Clone, PartialEq, Eq)] pub enum TypeExpr { /// Named type: Int, String, List Named(Ident), /// Generic type application: List, Map App(Box, Vec), /// Function type: fn(A, B): C Function { params: Vec, return_type: Box, effects: Vec, }, /// Tuple type: (A, B, C) Tuple(Vec), /// Record type: { name: String, age: Int } Record(Vec), /// Unit type Unit, /// Versioned type: User @v2, User @v2+, User @latest Versioned { base: Box, constraint: VersionConstraint, }, } impl TypeExpr { pub fn named(name: &str) -> Self { TypeExpr::Named(Ident::new(name, Span::default())) } } /// Expressions #[derive(Debug, Clone)] pub enum Expr { /// Literal values Literal(Literal), /// Variable reference Var(Ident), /// Binary operation: a + b BinaryOp { op: BinaryOp, left: Box, right: Box, span: Span, }, /// Unary operation: -a, !a UnaryOp { op: UnaryOp, operand: Box, span: Span, }, /// Function call: foo(a, b) Call { func: Box, args: Vec, span: Span, }, /// Effect operation call: Effect.operation(args) EffectOp { effect: Ident, operation: Ident, args: Vec, span: Span, }, /// Field access: foo.bar Field { object: Box, field: Ident, span: Span, }, /// Tuple index access: tuple.0, tuple.1 TupleIndex { object: Box, index: usize, span: Span, }, /// Lambda: fn(x, y) => x + y or fn(x: Int): Int => x + 1 Lambda { params: Vec, return_type: Option>, effects: Vec, body: Box, span: Span, }, /// Let binding: let x = e1; e2 Let { name: Ident, typ: Option, value: Box, body: Box, span: Span, }, /// If expression: if cond then e1 else e2 If { condition: Box, then_branch: Box, else_branch: Box, span: Span, }, /// Match expression Match { scrutinee: Box, arms: Vec, span: Span, }, /// Block: { e1; e2; e3 } Block { statements: Vec, result: Box, span: Span, }, /// Record literal: { name: "Alice", age: 30 } /// With optional spread: { ...base, name: "Bob" } Record { spread: Option>, fields: Vec<(Ident, Expr)>, span: Span, }, /// Tuple literal: (1, "hello", true) Tuple { elements: Vec, span: Span }, /// List literal: [1, 2, 3] List { elements: Vec, span: Span }, /// Run with handlers: run expr with { Effect = handler, ... } Run { expr: Box, handlers: Vec<(Ident, Expr)>, span: Span, }, /// Resume continuation in handler (like calling the continuation) Resume { value: Box, span: Span }, } impl Expr { pub fn span(&self) -> Span { match self { Expr::Literal(lit) => lit.span, Expr::Var(ident) => ident.span, Expr::BinaryOp { span, .. } => *span, Expr::UnaryOp { span, .. } => *span, Expr::Call { span, .. } => *span, Expr::EffectOp { span, .. } => *span, Expr::Field { span, .. } => *span, Expr::TupleIndex { span, .. } => *span, Expr::Lambda { span, .. } => *span, Expr::Let { span, .. } => *span, Expr::If { span, .. } => *span, Expr::Match { span, .. } => *span, Expr::Block { span, .. } => *span, Expr::Record { span, .. } => *span, Expr::Tuple { span, .. } => *span, Expr::List { span, .. } => *span, Expr::Run { span, .. } => *span, Expr::Resume { span, .. } => *span, } } } /// Literal values #[derive(Debug, Clone)] pub struct Literal { pub kind: LiteralKind, pub span: Span, } #[derive(Debug, Clone)] pub enum LiteralKind { Int(i64), Float(f64), String(String), Char(char), Bool(bool), Unit, } /// Binary operators #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum BinaryOp { // Arithmetic Add, Sub, Mul, Div, Mod, // Comparison Eq, Ne, Lt, Le, Gt, Ge, // Logical And, Or, // Other Pipe, // |> Concat, // ++ } impl fmt::Display for BinaryOp { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { BinaryOp::Add => write!(f, "+"), BinaryOp::Sub => write!(f, "-"), BinaryOp::Mul => write!(f, "*"), BinaryOp::Div => write!(f, "/"), BinaryOp::Mod => write!(f, "%"), BinaryOp::Eq => write!(f, "=="), BinaryOp::Ne => write!(f, "!="), BinaryOp::Lt => write!(f, "<"), BinaryOp::Le => write!(f, "<="), BinaryOp::Gt => write!(f, ">"), BinaryOp::Ge => write!(f, ">="), BinaryOp::And => write!(f, "&&"), BinaryOp::Or => write!(f, "||"), BinaryOp::Pipe => write!(f, "|>"), BinaryOp::Concat => write!(f, "++"), } } } /// Unary operators #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum UnaryOp { Neg, // - Not, // ! } impl fmt::Display for UnaryOp { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { UnaryOp::Neg => write!(f, "-"), UnaryOp::Not => write!(f, "!"), } } } /// Statement in a block #[derive(Debug, Clone)] pub enum Statement { /// Expression statement Expr(Expr), /// Let binding without body (in blocks) Let { name: Ident, typ: Option, value: Expr, span: Span, }, } /// Match arm #[derive(Debug, Clone)] pub struct MatchArm { pub pattern: Pattern, pub guard: Option, pub body: Expr, pub span: Span, } /// Patterns for matching #[derive(Debug, Clone)] pub enum Pattern { /// Wildcard: _ Wildcard(Span), /// Variable binding: x Var(Ident), /// Literal: 42, "hello", true Literal(Literal), /// Constructor: Some(x), None, Ok(v), module.Constructor(x) Constructor { module: Option, name: Ident, fields: Vec, span: Span, }, /// Record pattern: { name, age: a } Record { fields: Vec<(Ident, Pattern)>, span: Span, }, /// Tuple pattern: (a, b, c) Tuple { elements: Vec, span: Span }, } impl Pattern { pub fn span(&self) -> Span { match self { Pattern::Wildcard(span) => *span, Pattern::Var(ident) => ident.span, Pattern::Literal(lit) => lit.span, Pattern::Constructor { span, .. } => *span, Pattern::Record { span, .. } => *span, Pattern::Tuple { span, .. } => *span, } } }