From 4c97430ea4e044a93192794df92322bfcbb14f3a Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Wed, 4 Feb 2026 18:00:57 +0000 Subject: [PATCH] pr 03.C --- .../prometeu-compiler/src/building/output.rs | 24 +- .../src/frontends/pbs/ast.rs | 330 ++--- .../src/frontends/pbs/collector.rs | 55 +- .../src/frontends/pbs/lowering.rs | 717 +++++----- .../src/frontends/pbs/mod.rs | 13 +- .../src/frontends/pbs/parser.rs | 702 +++++----- .../src/frontends/pbs/resolver.rs | 349 ++--- .../src/frontends/pbs/typecheck.rs | 401 +++--- crates/prometeu-compiler/src/sources.rs | 7 +- .../tests/generate_canonical_goldens.rs | 6 +- test-cartridges/canonical/golden/ast.json | 1172 +---------------- 11 files changed, 1452 insertions(+), 2324 deletions(-) diff --git a/crates/prometeu-compiler/src/building/output.rs b/crates/prometeu-compiler/src/building/output.rs index c9e1ea10..9a151b54 100644 --- a/crates/prometeu-compiler/src/building/output.rs +++ b/crates/prometeu-compiler/src/building/output.rs @@ -4,7 +4,7 @@ use crate::common::diagnostics::DiagnosticBundle; use crate::common::files::FileManager; use crate::common::spans::Span; use crate::deps::resolver::ProjectId; -use crate::frontends::pbs::ast::FileNode; +use crate::frontends::pbs::ast::ParsedAst; use crate::frontends::pbs::collector::SymbolCollector; use crate::frontends::pbs::lowering::Lowerer; use crate::frontends::pbs::parser::Parser; @@ -116,7 +116,7 @@ pub fn compile_project( let mut interner = NameInterner::new(); // 1. Parse all files and group symbols by module let mut module_symbols_map: HashMap = HashMap::new(); - let mut parsed_files: Vec<(String, FileNode)> = Vec::new(); // (module_path, ast) + let mut parsed_files: Vec<(String, ParsedAst)> = Vec::new(); // (module_path, ast) for source_rel in &step.sources { let source_abs = step.project_dir.join(source_rel); @@ -124,10 +124,10 @@ pub fn compile_project( let file_id = file_manager.add(source_abs.clone(), source_code.clone()); let mut parser = Parser::new(&source_code, file_id, &mut interner); - let ast = parser.parse_file()?; - + let parsed = parser.parse_file()?; + let mut collector = SymbolCollector::new(&interner); - let (ts, vs) = collector.collect(&ast)?; + let (ts, vs) = collector.collect(&parsed.arena, parsed.root)?; let full_path = source_rel.to_string_lossy().replace('\\', "/"); let logical_module_path = if let Some(stripped) = full_path.strip_prefix("src/main/modules/") { @@ -173,7 +173,7 @@ pub fn compile_project( let _ = ms.value_symbols.insert(sym); } - parsed_files.push((module_path, ast)); + parsed_files.push((module_path, parsed)); } // 2. Synthesize ModuleSymbols for dependencies @@ -243,10 +243,10 @@ pub fn compile_project( // We need to collect imported symbols for Lowerer let mut file_imported_symbols: HashMap = HashMap::new(); // keyed by module_path - for (module_path, ast) in &parsed_files { + for (module_path, parsed) in &parsed_files { let ms = module_symbols_map.get(module_path).unwrap(); let mut resolver = Resolver::new(ms, &module_provider, &interner); - resolver.resolve(ast)?; + resolver.resolve(&parsed.arena, parsed.root)?; // Capture imported symbols file_imported_symbols.insert(module_path.clone(), resolver.imported_symbols.clone()); @@ -255,7 +255,7 @@ pub fn compile_project( let mut ms_mut = module_symbols_map.get_mut(module_path).unwrap(); let imported = file_imported_symbols.get(module_path).unwrap(); let mut typechecker = TypeChecker::new(&mut ms_mut, imported, &module_provider, &interner); - typechecker.check(ast)?; + typechecker.check(&parsed.arena, parsed.root)?; } // 4. Lower to IR @@ -266,11 +266,11 @@ pub fn compile_project( field_types: HashMap::new(), }; - for (module_path, ast) in &parsed_files { + for (module_path, parsed) in &parsed_files { let ms = module_symbols_map.get(module_path).unwrap(); let imported = file_imported_symbols.get(module_path).unwrap(); - let lowerer = Lowerer::new(ms, imported, &interner); - let program = lowerer.lower_file(ast, module_path)?; + let lowerer = Lowerer::new(&parsed.arena, ms, imported, &interner); + let program = lowerer.lower_file(parsed.root, module_path)?; // Combine program into combined_program if combined_program.modules.is_empty() { diff --git a/crates/prometeu-compiler/src/frontends/pbs/ast.rs b/crates/prometeu-compiler/src/frontends/pbs/ast.rs index 14791cda..c186121b 100644 --- a/crates/prometeu-compiler/src/frontends/pbs/ast.rs +++ b/crates/prometeu-compiler/src/frontends/pbs/ast.rs @@ -2,296 +2,296 @@ use crate::common::spans::Span; use prometeu_analysis::NameId; use serde::{Deserialize, Serialize}; +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] +pub struct NodeId(pub u32); + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)] +pub struct AstArena { + pub nodes: Vec, + pub spans: Vec, + pub roots: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub struct ParsedAst { + pub arena: AstArena, + pub root: NodeId, +} + +impl AstArena { + pub fn push(&mut self, kind: NodeKind, span: Span) -> NodeId { + let id = NodeId(self.nodes.len() as u32); + self.nodes.push(kind); + self.spans.push(span); + id + } + + pub fn kind(&self, id: NodeId) -> &NodeKind { + &self.nodes[id.0 as usize] + } + + pub fn span(&self, id: NodeId) -> Span { + self.spans[id.0 as usize] + } +} + #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[serde(tag = "kind")] -pub enum Node { - File(FileNode), - Import(ImportNode), - ImportSpec(ImportSpecNode), - ServiceDecl(ServiceDeclNode), - ServiceFnSig(ServiceFnSigNode), - FnDecl(FnDeclNode), - TypeDecl(TypeDeclNode), - TypeBody(TypeBodyNode), - Block(BlockNode), - LetStmt(LetStmtNode), - ExprStmt(ExprStmtNode), - ReturnStmt(ReturnStmtNode), - IntLit(IntLitNode), - FloatLit(FloatLitNode), - BoundedLit(BoundedLitNode), - StringLit(StringLitNode), - Ident(IdentNode), - Call(CallNode), - Unary(UnaryNode), - Binary(BinaryNode), - Cast(CastNode), - IfExpr(IfExprNode), - WhenExpr(WhenExprNode), - WhenArm(WhenArmNode), - TypeName(TypeNameNode), - TypeApp(TypeAppNode), - ConstructorDecl(ConstructorDeclNode), - ConstantDecl(ConstantDeclNode), - Alloc(AllocNode), - Mutate(MutateNode), - Borrow(BorrowNode), - Peek(PeekNode), - MemberAccess(MemberAccessNode), +pub enum NodeKind { + File(FileNodeArena), + Import(ImportNodeArena), + ImportSpec(ImportSpecNodeArena), + ServiceDecl(ServiceDeclNodeArena), + ServiceFnSig(ServiceFnSigNodeArena), + FnDecl(FnDeclNodeArena), + TypeDecl(TypeDeclNodeArena), + TypeBody(TypeBodyNodeArena), + Block(BlockNodeArena), + LetStmt(LetStmtNodeArena), + ExprStmt(ExprStmtNodeArena), + ReturnStmt(ReturnStmtNodeArena), + IntLit(IntLitNodeArena), + FloatLit(FloatLitNodeArena), + BoundedLit(BoundedLitNodeArena), + StringLit(StringLitNodeArena), + Ident(IdentNodeArena), + Call(CallNodeArena), + Unary(UnaryNodeArena), + Binary(BinaryNodeArena), + Cast(CastNodeArena), + IfExpr(IfExprNodeArena), + WhenExpr(WhenExprNodeArena), + WhenArm(WhenArmNodeArena), + TypeName(TypeNameNodeArena), + TypeApp(TypeAppNodeArena), + ConstructorDecl(ConstructorDeclNodeArena), + ConstantDecl(ConstantDeclNodeArena), + Alloc(AllocNodeArena), + Mutate(MutateNodeArena), + Borrow(BorrowNodeArena), + Peek(PeekNodeArena), + MemberAccess(MemberAccessNodeArena), } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct FileNode { - pub span: Span, - pub imports: Vec, - pub decls: Vec, +pub struct FileNodeArena { + pub imports: Vec, + pub decls: Vec, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct ImportNode { - pub span: Span, - pub spec: Box, // Must be ImportSpec +pub struct ImportNodeArena { + pub spec: NodeId, pub from: String, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct ImportSpecNode { - pub span: Span, +pub struct ImportSpecNodeArena { pub path: Vec, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct ServiceDeclNode { - pub span: Span, - pub vis: Option, // "pub" | "mod" +pub struct ServiceDeclNodeArena { + pub vis: Option, pub name: NameId, pub extends: Option, - pub members: Vec, // ServiceFnSig + pub members: Vec, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct ServiceFnSigNode { +pub struct ServiceFnSigNodeArena { + pub name: NameId, + pub params: Vec, + pub ret: NodeId, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub struct ParamNodeArena { pub span: Span, pub name: NameId, - pub params: Vec, - pub ret: Box, // TypeName or TypeApp + pub ty: NodeId, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct ParamNode { - pub span: Span, - pub name: NameId, - pub ty: Box, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct FnDeclNode { - pub span: Span, +pub struct FnDeclNodeArena { pub vis: Option, pub name: NameId, - pub params: Vec, - pub ret: Option>, - pub else_fallback: Option>, // Block - pub body: Box, // Block + pub params: Vec, + pub ret: Option, + pub else_fallback: Option, + pub body: NodeId, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct TypeDeclNode { - pub span: Span, +pub struct TypeDeclNodeArena { pub vis: Option, - pub type_kind: String, // "struct" | "contract" | "error" + pub type_kind: String, pub name: NameId, pub is_host: bool, - pub params: Vec, // fields for struct/error - pub constructors: Vec, // [ ... ] - pub constants: Vec, // [[ ... ]] - pub body: Option>, // TypeBody (methods) + pub params: Vec, + pub constructors: Vec, + pub constants: Vec, + pub body: Option, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct ConstructorDeclNode { - pub span: Span, - pub params: Vec, - pub initializers: Vec, +pub struct ConstructorDeclNodeArena { + pub params: Vec, + pub initializers: Vec, pub name: NameId, - pub body: Box, // BlockNode + pub body: NodeId, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct ConstantDeclNode { +pub struct ConstantDeclNodeArena { + pub name: NameId, + pub value: NodeId, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub struct TypeBodyNodeArena { + pub members: Vec, + pub methods: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub struct TypeMemberNodeArena { pub span: Span, pub name: NameId, - pub value: Box, + pub ty: NodeId, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct TypeBodyNode { - pub span: Span, - pub members: Vec, - pub methods: Vec, +pub struct BlockNodeArena { + pub stmts: Vec, + pub tail: Option, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct TypeMemberNode { - pub span: Span, - pub name: NameId, - pub ty: Box, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct BlockNode { - pub span: Span, - pub stmts: Vec, - pub tail: Option>, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct LetStmtNode { - pub span: Span, +pub struct LetStmtNodeArena { pub name: NameId, pub is_mut: bool, - pub ty: Option>, - pub init: Box, + pub ty: Option, + pub init: NodeId, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct ExprStmtNode { - pub span: Span, - pub expr: Box, +pub struct ExprStmtNodeArena { + pub expr: NodeId, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct ReturnStmtNode { - pub span: Span, - pub expr: Option>, +pub struct ReturnStmtNodeArena { + pub expr: Option, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct IntLitNode { - pub span: Span, +pub struct IntLitNodeArena { pub value: i64, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct FloatLitNode { - pub span: Span, +pub struct FloatLitNodeArena { pub value: f64, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct BoundedLitNode { - pub span: Span, +pub struct BoundedLitNodeArena { pub value: u32, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct StringLitNode { - pub span: Span, +pub struct StringLitNodeArena { pub value: String, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct IdentNode { - pub span: Span, +pub struct IdentNodeArena { pub name: NameId, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct CallNode { - pub span: Span, - pub callee: Box, - pub args: Vec, +pub struct CallNodeArena { + pub callee: NodeId, + pub args: Vec, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct UnaryNode { - pub span: Span, +pub struct UnaryNodeArena { pub op: String, - pub expr: Box, + pub expr: NodeId, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct BinaryNode { - pub span: Span, +pub struct BinaryNodeArena { pub op: String, - pub left: Box, - pub right: Box, + pub left: NodeId, + pub right: NodeId, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct CastNode { - pub span: Span, - pub expr: Box, - pub ty: Box, +pub struct CastNodeArena { + pub expr: NodeId, + pub ty: NodeId, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct IfExprNode { - pub span: Span, - pub cond: Box, - pub then_block: Box, - pub else_block: Option>, +pub struct IfExprNodeArena { + pub cond: NodeId, + pub then_block: NodeId, + pub else_block: Option, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct WhenExprNode { - pub span: Span, - pub arms: Vec, // WhenArm +pub struct WhenExprNodeArena { + pub arms: Vec, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct WhenArmNode { - pub span: Span, - pub cond: Box, - pub body: Box, +pub struct WhenArmNodeArena { + pub cond: NodeId, + pub body: NodeId, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct TypeNameNode { - pub span: Span, +pub struct TypeNameNodeArena { pub name: NameId, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct TypeAppNode { - pub span: Span, +pub struct TypeAppNodeArena { pub base: NameId, - pub args: Vec, + pub args: Vec, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct AllocNode { - pub span: Span, - pub ty: Box, +pub struct AllocNodeArena { + pub ty: NodeId, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct MutateNode { - pub span: Span, - pub target: Box, +pub struct MutateNodeArena { + pub target: NodeId, pub binding: NameId, - pub body: Box, // BlockNode + pub body: NodeId, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct BorrowNode { - pub span: Span, - pub target: Box, +pub struct BorrowNodeArena { + pub target: NodeId, pub binding: NameId, - pub body: Box, // BlockNode + pub body: NodeId, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct PeekNode { - pub span: Span, - pub target: Box, +pub struct PeekNodeArena { + pub target: NodeId, pub binding: NameId, - pub body: Box, // BlockNode + pub body: NodeId, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct MemberAccessNode { - pub span: Span, - pub object: Box, +pub struct MemberAccessNodeArena { + pub object: NodeId, pub member: NameId, } diff --git a/crates/prometeu-compiler/src/frontends/pbs/collector.rs b/crates/prometeu-compiler/src/frontends/pbs/collector.rs index f8b47754..b514f40a 100644 --- a/crates/prometeu-compiler/src/frontends/pbs/collector.rs +++ b/crates/prometeu-compiler/src/frontends/pbs/collector.rs @@ -21,12 +21,26 @@ impl<'a> SymbolCollector<'a> { } } - pub fn collect(&mut self, file: &FileNode) -> Result<(SymbolTable, SymbolTable), DiagnosticBundle> { + pub fn collect( + &mut self, + arena: &AstArena, + root: NodeId, + ) -> Result<(SymbolTable, SymbolTable), DiagnosticBundle> { + let file = match arena.kind(root) { + NodeKind::File(file) => file, + _ => { + return Err(DiagnosticBundle::error( + "Expected File node as root".to_string(), + None, + )) + } + }; + for decl in &file.decls { - match decl { - Node::FnDecl(fn_decl) => self.collect_fn(fn_decl), - Node::ServiceDecl(service_decl) => self.collect_service(service_decl), - Node::TypeDecl(type_decl) => self.collect_type(type_decl), + match arena.kind(*decl) { + NodeKind::FnDecl(fn_decl) => self.collect_fn(arena, *decl, fn_decl), + NodeKind::ServiceDecl(service_decl) => self.collect_service(arena, *decl, service_decl), + NodeKind::TypeDecl(type_decl) => self.collect_type(arena, *decl, type_decl), _ => {} } } @@ -43,50 +57,53 @@ impl<'a> SymbolCollector<'a> { )) } - fn collect_fn(&mut self, decl: &FnDeclNode) { + fn collect_fn(&mut self, arena: &AstArena, id: NodeId, decl: &FnDeclNodeArena) { let vis = match decl.vis.as_deref() { Some("pub") => Visibility::Pub, Some("mod") => Visibility::Mod, _ => Visibility::FilePrivate, }; - - self.check_export_eligibility(SymbolKind::Function, vis, decl.span); + + let span = arena.span(id); + self.check_export_eligibility(SymbolKind::Function, vis, span); let symbol = Symbol { - name: decl.name.clone(), + name: decl.name, kind: SymbolKind::Function, namespace: Namespace::Value, visibility: vis, ty: None, // Will be resolved later is_host: false, - span: decl.span, + span, origin: None, }; self.insert_value_symbol(symbol); } - fn collect_service(&mut self, decl: &ServiceDeclNode) { + fn collect_service(&mut self, arena: &AstArena, id: NodeId, decl: &ServiceDeclNodeArena) { let vis = match decl.vis.as_deref() { Some("pub") => Visibility::Pub, _ => Visibility::Mod, // Defaults to Mod }; - self.check_export_eligibility(SymbolKind::Service, vis, decl.span); + let span = arena.span(id); + + self.check_export_eligibility(SymbolKind::Service, vis, span); let symbol = Symbol { - name: decl.name.clone(), + name: decl.name, kind: SymbolKind::Service, namespace: Namespace::Type, // Service is a type visibility: vis, ty: None, is_host: false, - span: decl.span, + span, origin: None, }; self.insert_type_symbol(symbol); } - fn collect_type(&mut self, decl: &TypeDeclNode) { + fn collect_type(&mut self, arena: &AstArena, id: NodeId, decl: &TypeDeclNodeArena) { let vis = match decl.vis.as_deref() { Some("pub") => Visibility::Pub, Some("mod") => Visibility::Mod, @@ -99,16 +116,18 @@ impl<'a> SymbolCollector<'a> { _ => SymbolKind::Struct, // Default }; - self.check_export_eligibility(kind.clone(), vis, decl.span); + let span = arena.span(id); + + self.check_export_eligibility(kind.clone(), vis, span); let symbol = Symbol { - name: decl.name.clone(), + name: decl.name, kind, namespace: Namespace::Type, visibility: vis, ty: None, is_host: decl.is_host, - span: decl.span, + span, origin: None, }; self.insert_type_symbol(symbol); diff --git a/crates/prometeu-compiler/src/frontends/pbs/lowering.rs b/crates/prometeu-compiler/src/frontends/pbs/lowering.rs index 0069eaf4..4bc3beca 100644 --- a/crates/prometeu-compiler/src/frontends/pbs/lowering.rs +++ b/crates/prometeu-compiler/src/frontends/pbs/lowering.rs @@ -17,6 +17,7 @@ struct LocalInfo { } pub struct Lowerer<'a> { + arena: &'a AstArena, module_symbols: &'a ModuleSymbols, imported_symbols: &'a ModuleSymbols, interner: &'a NameInterner, @@ -30,8 +31,8 @@ pub struct Lowerer<'a> { function_ids: HashMap, type_ids: HashMap, struct_slots: HashMap, - struct_constructors: HashMap>, - type_constants: HashMap>, + struct_constructors: HashMap>, + type_constants: HashMap>, current_type_context: Option, contract_registry: ContractRegistry, diagnostics: Vec, @@ -41,6 +42,7 @@ pub struct Lowerer<'a> { impl<'a> Lowerer<'a> { pub fn new( + arena: &'a AstArena, module_symbols: &'a ModuleSymbols, imported_symbols: &'a ModuleSymbols, interner: &'a NameInterner, @@ -55,6 +57,7 @@ impl<'a> Lowerer<'a> { struct_slots.insert("Touch".to_string(), 6); Self { + arena, module_symbols, imported_symbols, interner, @@ -92,29 +95,41 @@ impl<'a> Lowerer<'a> { }); } - pub fn lower_file(mut self, file: &FileNode, module_name: &str) -> Result { + pub fn lower_file(mut self, root: NodeId, module_name: &str) -> Result { + let file = match self.arena.kind(root) { + NodeKind::File(file) => file, + _ => { + return Err(DiagnosticBundle::error( + "Expected File node as root".to_string(), + None, + )) + } + }; // Pre-scan for function declarations to assign IDs for decl in &file.decls { - if let Node::FnDecl(n) = decl { - let id = FunctionId(self.next_func_id); - self.next_func_id += 1; - self.function_ids - .insert(self.interner.resolve(n.name).to_string(), id); - } - if let Node::TypeDecl(n) = decl { - let id = TypeId(self.next_type_id); - self.next_type_id += 1; - self.type_ids - .insert(self.interner.resolve(n.name).to_string(), id); + match self.arena.kind(*decl) { + NodeKind::FnDecl(n) => { + let id = FunctionId(self.next_func_id); + self.next_func_id += 1; + self.function_ids + .insert(self.interner.resolve(n.name).to_string(), id); + } + NodeKind::TypeDecl(n) => { + let id = TypeId(self.next_type_id); + self.next_type_id += 1; + self.type_ids + .insert(self.interner.resolve(n.name).to_string(), id); + } + _ => {} } } // Second pre-scan: calculate struct slots (recursive) let mut struct_nodes = HashMap::new(); for decl in &file.decls { - if let Node::TypeDecl(n) = decl { + if let NodeKind::TypeDecl(n) = self.arena.kind(*decl) { if n.type_kind == "struct" { - struct_nodes.insert(self.interner.resolve(n.name).to_string(), n); + struct_nodes.insert(self.interner.resolve(n.name).to_string(), *decl); } } } @@ -122,12 +137,16 @@ impl<'a> Lowerer<'a> { let mut changed = true; while changed { changed = false; - for (name, node) in &struct_nodes { + for (name, node_id) in &struct_nodes { if !self.struct_slots.contains_key(name) { let mut slots = 0; let mut all_known = true; + let node = match self.arena.kind(*node_id) { + NodeKind::TypeDecl(node) => node, + _ => continue, + }; for param in &node.params { - let member_ty = self.lower_type_node(¶m.ty); + let member_ty = self.lower_type_node(param.ty); match &member_ty { Type::Struct(sname) => { if let Some(s_slots) = self.get_builtin_struct_slots(sname) { @@ -151,11 +170,16 @@ impl<'a> Lowerer<'a> { } for decl in &file.decls { - if let Node::TypeDecl(n) = decl { + if let NodeKind::TypeDecl(n) = self.arena.kind(*decl) { let type_name = self.interner.resolve(n.name).to_string(); let mut constants = HashMap::new(); for c in &n.constants { - constants.insert(self.interner.resolve(c.name).to_string(), *c.value.clone()); + if let NodeKind::ConstantDecl(constant) = self.arena.kind(*c) { + constants.insert( + self.interner.resolve(constant.name).to_string(), + constant.value, + ); + } } self.type_constants.insert(type_name.clone(), constants); @@ -163,31 +187,17 @@ impl<'a> Lowerer<'a> { // Default constructor: TypeName(...) if n.type_kind == "struct" { - let mut params = Vec::new(); - let mut initializers = Vec::new(); - for p in &n.params { - params.push(p.clone()); - initializers.push(Node::Ident(IdentNode { - span: p.span, - name: p.name.clone(), - })); + if let Some(default_ctor) = n.constructors.first() { + if let NodeKind::ConstructorDecl(_) = self.arena.kind(*default_ctor) { + ctors.insert(type_name.clone(), *default_ctor); + } } - let default_ctor = ConstructorDeclNode { - span: n.span, - params, - initializers, - name: n.name, - body: Box::new(Node::Block(BlockNode { - span: n.span, - stmts: Vec::new(), - tail: None, - })), - }; - ctors.insert(type_name.clone(), default_ctor); } for ctor in &n.constructors { - ctors.insert(self.interner.resolve(ctor.name).to_string(), ctor.clone()); + if let NodeKind::ConstructorDecl(ctor_node) = self.arena.kind(*ctor) { + ctors.insert(self.interner.resolve(ctor_node.name).to_string(), *ctor); + } } self.struct_constructors.insert(type_name, ctors); } @@ -199,14 +209,11 @@ impl<'a> Lowerer<'a> { }; for decl in &file.decls { - match decl { - Node::FnDecl(fn_decl) => { - let func = self.lower_function(fn_decl).map_err(|_| DiagnosticBundle { - diagnostics: self.diagnostics.clone(), - })?; - module.functions.push(func); - } - _ => {} // Other declarations not handled for now + if let NodeKind::FnDecl(_) = self.arena.kind(*decl) { + let func = self.lower_function(*decl).map_err(|_| DiagnosticBundle { + diagnostics: self.diagnostics.clone(), + })?; + module.functions.push(func); } } @@ -214,7 +221,11 @@ impl<'a> Lowerer<'a> { Ok(self.program) } - fn lower_function(&mut self, n: &FnDeclNode) -> Result { + fn lower_function(&mut self, node: NodeId) -> Result { + let n = match self.arena.kind(node) { + NodeKind::FnDecl(n) => n, + _ => return Err(()), + }; let func_name = self.interner.resolve(n.name).to_string(); let func_id = *self.function_ids.get(&func_name).unwrap(); self.next_block_id = 0; @@ -225,7 +236,7 @@ impl<'a> Lowerer<'a> { let mut local_types = HashMap::new(); let mut param_slots = 0u32; for param in &n.params { - let ty = self.lower_type_node(¶m.ty); + let ty = self.lower_type_node(param.ty); let slots = self.get_type_slots(&ty); params.push(Param { name: self.interner.resolve(param.name).to_string(), @@ -245,7 +256,7 @@ impl<'a> Lowerer<'a> { } self.max_slots_used = param_slots; - let ret_ty = if let Some(ret) = &n.ret { + let ret_ty = if let Some(ret) = n.ret { self.lower_type_node(ret) } else { Type::Void @@ -266,7 +277,7 @@ impl<'a> Lowerer<'a> { self.current_function = Some(func); self.start_block(); - self.lower_node(&n.body)?; + self.lower_node(n.body)?; // Ensure every function ends with a return if not already terminated if let Some(mut block) = self.current_block.take() { @@ -283,48 +294,50 @@ impl<'a> Lowerer<'a> { Ok(final_func) } - fn lower_node(&mut self, node: &Node) -> Result<(), ()> { + fn lower_node(&mut self, node: NodeId) -> Result<(), ()> { let old_span = self.current_span; - self.current_span = Some(node.span()); + self.current_span = Some(self.arena.span(node)); - let res = match node { - Node::Block(n) => self.lower_block(n), - Node::LetStmt(n) => self.lower_let_stmt(n), - Node::ExprStmt(n) => self.lower_node(&n.expr), - Node::ReturnStmt(n) => self.lower_return_stmt(n), - Node::IntLit(n) => { + let res = match self.arena.kind(node) { + NodeKind::Block(n) => self.lower_block(node, n), + NodeKind::LetStmt(n) => self.lower_let_stmt(node, n), + NodeKind::ExprStmt(n) => self.lower_node(n.expr), + NodeKind::ReturnStmt(n) => self.lower_return_stmt(node, n), + NodeKind::IntLit(n) => { let id = self.program.const_pool.add_int(n.value); self.emit(InstrKind::PushConst(id)); Ok(()) } - Node::FloatLit(n) => { + NodeKind::FloatLit(n) => { let id = self.program.const_pool.add_float(n.value); self.emit(InstrKind::PushConst(id)); Ok(()) } - Node::StringLit(n) => { + NodeKind::StringLit(n) => { let id = self.program.const_pool.add_string(n.value.clone()); self.emit(InstrKind::PushConst(id)); Ok(()) } - Node::BoundedLit(n) => { + NodeKind::BoundedLit(n) => { self.emit(InstrKind::PushBounded(n.value)); Ok(()) } - Node::Ident(n) => self.lower_ident(n), - Node::MemberAccess(n) => self.lower_member_access(n), - Node::Call(n) => self.lower_call(n), - Node::Binary(n) => self.lower_binary(n), - Node::Unary(n) => self.lower_unary(n), - Node::IfExpr(n) => self.lower_if_expr(n), - Node::Alloc(n) => self.lower_alloc(n), - Node::Mutate(n) => self.lower_mutate(n), - Node::Borrow(n) => self.lower_borrow(n), - Node::Peek(n) => self.lower_peek(n), + NodeKind::Ident(n) => self.lower_ident(node, n), + NodeKind::MemberAccess(n) => self.lower_member_access(node, n), + NodeKind::Call(n) => self.lower_call(node, n), + NodeKind::Binary(n) => self.lower_binary(node, n), + NodeKind::Unary(n) => self.lower_unary(node, n), + NodeKind::IfExpr(n) => self.lower_if_expr(node, n), + NodeKind::Alloc(n) => self.lower_alloc(node, n), + NodeKind::Mutate(n) => self.lower_mutate(node, n), + NodeKind::Borrow(n) => self.lower_borrow(node, n), + NodeKind::Peek(n) => self.lower_peek(node, n), _ => { - // For unhandled nodes, we can either ignore or error. - // Given the PR, maybe we should error on things we don't support yet in lowering. - self.error("E_LOWER_UNSUPPORTED", format!("Lowering for node kind {:?} not supported", node), node.span()); + self.error( + "E_LOWER_UNSUPPORTED", + format!("Lowering for node kind {:?} not supported", self.arena.kind(node)), + self.arena.span(node), + ); Err(()) } }; @@ -333,23 +346,23 @@ impl<'a> Lowerer<'a> { res } - fn lower_alloc(&mut self, n: &AllocNode) -> Result<(), ()> { - let (ty_id, slots) = self.get_type_id_and_slots(&n.ty)?; + fn lower_alloc(&mut self, _node: NodeId, n: &AllocNodeArena) -> Result<(), ()> { + let (ty_id, slots) = self.get_type_id_and_slots(n.ty)?; self.emit(InstrKind::Alloc { ty: ty_id, slots }); Ok(()) } - fn get_type_id_and_slots(&mut self, node: &Node) -> Result<(TypeId, u32), ()> { - match node { - Node::TypeName(n) => { + fn get_type_id_and_slots(&mut self, node: NodeId) -> Result<(TypeId, u32), ()> { + match self.arena.kind(node) { + NodeKind::TypeName(n) => { let name = self.interner.resolve(n.name); let slots = self.struct_slots.get(name).cloned().unwrap_or(1); let id = self.get_or_create_type_id(name); Ok((id, slots)) } - Node::TypeApp(ta) if self.interner.resolve(ta.base) == "array" => { + NodeKind::TypeApp(ta) if self.interner.resolve(ta.base) == "array" => { let size = if ta.args.len() > 1 { - if let Node::IntLit(il) = &ta.args[1] { + if let NodeKind::IntLit(il) = self.arena.kind(ta.args[1]) { il.value as u32 } else { 1 @@ -357,13 +370,17 @@ impl<'a> Lowerer<'a> { } else { 1 }; - let elem_ty = self.lower_type_node(&ta.args[0]); + let elem_ty = self.lower_type_node(ta.args[0]); let name = format!("array<{}>[{}]", elem_ty, size); let id = self.get_or_create_type_id(&name); Ok((id, size)) } _ => { - self.error("E_RESOLVE_UNDEFINED", format!("Unknown type in allocation: {:?}", node), node.span()); + self.error( + "E_RESOLVE_UNDEFINED", + format!("Unknown type in allocation: {:?}", self.arena.kind(node)), + self.arena.span(node), + ); Err(()) } } @@ -380,9 +397,9 @@ impl<'a> Lowerer<'a> { } } - fn lower_peek(&mut self, n: &PeekNode) -> Result<(), ()> { + fn lower_peek(&mut self, _node: NodeId, n: &PeekNodeArena) -> Result<(), ()> { // 1. Evaluate target (gate) - self.lower_node(&n.target)?; + self.lower_node(n.target)?; // 2. Preserve gate identity let gate_slot = self.add_local_to_scope(format!("$gate_{}", self.get_next_local_slot()), Type::Int); @@ -398,7 +415,7 @@ impl<'a> Lowerer<'a> { self.emit(InstrKind::SetLocal(view_slot)); // 5. Body - self.lower_node(&n.body)?; + self.lower_node(n.body)?; // 6. End Operation self.emit(InstrKind::EndPeek); @@ -407,9 +424,9 @@ impl<'a> Lowerer<'a> { Ok(()) } - fn lower_borrow(&mut self, n: &BorrowNode) -> Result<(), ()> { + fn lower_borrow(&mut self, _node: NodeId, n: &BorrowNodeArena) -> Result<(), ()> { // 1. Evaluate target (gate) - self.lower_node(&n.target)?; + self.lower_node(n.target)?; // 2. Preserve gate identity let gate_slot = self.add_local_to_scope(format!("$gate_{}", self.get_next_local_slot()), Type::Int); @@ -425,7 +442,7 @@ impl<'a> Lowerer<'a> { self.emit(InstrKind::SetLocal(view_slot)); // 5. Body - self.lower_node(&n.body)?; + self.lower_node(n.body)?; // 6. End Operation self.emit(InstrKind::EndBorrow); @@ -434,9 +451,9 @@ impl<'a> Lowerer<'a> { Ok(()) } - fn lower_mutate(&mut self, n: &MutateNode) -> Result<(), ()> { + fn lower_mutate(&mut self, _node: NodeId, n: &MutateNodeArena) -> Result<(), ()> { // 1. Evaluate target (gate) - self.lower_node(&n.target)?; + self.lower_node(n.target)?; // 2. Preserve gate identity let gate_slot = self.add_local_to_scope(format!("$gate_{}", self.get_next_local_slot()), Type::Int); @@ -452,7 +469,7 @@ impl<'a> Lowerer<'a> { self.emit(InstrKind::SetLocal(view_slot)); // 5. Body - self.lower_node(&n.body)?; + self.lower_node(n.body)?; // 6. End Operation self.emit(InstrKind::EndMutate); @@ -461,28 +478,28 @@ impl<'a> Lowerer<'a> { Ok(()) } - fn lower_block(&mut self, n: &BlockNode) -> Result<(), ()> { + fn lower_block(&mut self, _node: NodeId, n: &BlockNodeArena) -> Result<(), ()> { self.local_vars.push(HashMap::new()); for stmt in &n.stmts { - self.lower_node(stmt)?; + self.lower_node(*stmt)?; } - if let Some(tail) = &n.tail { + if let Some(tail) = n.tail { self.lower_node(tail)?; } self.local_vars.pop(); Ok(()) } - fn lower_let_stmt(&mut self, n: &LetStmtNode) -> Result<(), ()> { - self.lower_node(&n.init)?; + fn lower_let_stmt(&mut self, _node: NodeId, n: &LetStmtNodeArena) -> Result<(), ()> { + self.lower_node(n.init)?; - let ty = if let Some(ty_node) = &n.ty { + let ty = if let Some(ty_node) = n.ty { self.lower_type_node(ty_node) } else { // Very basic inference for host calls - if let Node::Call(call) = &*n.init { - if let Node::MemberAccess(ma) = &*call.callee { - if let Node::Ident(obj) = &*ma.object { + if let NodeKind::Call(call) = self.arena.kind(n.init) { + if let NodeKind::MemberAccess(ma) = self.arena.kind(call.callee) { + if let NodeKind::Ident(obj) = self.arena.kind(ma.object) { let obj_name = self.interner.resolve(obj.name); let member_name = self.interner.resolve(ma.member); match (obj_name, member_name) { @@ -490,9 +507,15 @@ impl<'a> Lowerer<'a> { ("Input", "touch") => Type::Struct("Touch".to_string()), _ => Type::Int, } - } else { Type::Int } - } else { Type::Int } - } else { Type::Int } + } else { + Type::Int + } + } else { + Type::Int + } + } else { + Type::Int + } }; let slots = self.get_type_slots(&ty); @@ -504,15 +527,15 @@ impl<'a> Lowerer<'a> { Ok(()) } - fn lower_return_stmt(&mut self, n: &ReturnStmtNode) -> Result<(), ()> { - if let Some(expr) = &n.expr { + fn lower_return_stmt(&mut self, _node: NodeId, n: &ReturnStmtNodeArena) -> Result<(), ()> { + if let Some(expr) = n.expr { self.lower_node(expr)?; } self.terminate(Terminator::Return); Ok(()) } - fn lower_ident(&mut self, n: &IdentNode) -> Result<(), ()> { + fn lower_ident(&mut self, node: NodeId, n: &IdentNodeArena) -> Result<(), ()> { let name_str = self.interner.resolve(n.name); if let Some(info) = self.find_local(name_str) { let slots = self.get_type_slots(&info.ty); @@ -548,28 +571,28 @@ impl<'a> Lowerer<'a> { self.error( "E_LOWER_UNSUPPORTED", format!("First-class function reference '{}' not supported", name_str), - n.span, + self.arena.span(node), ); Err(()) - } else { - self.error( - "E_RESOLVE_UNDEFINED", - format!("Undefined identifier '{}'", name_str), - n.span, - ); - Err(()) - } - } - } + } else { + self.error( + "E_RESOLVE_UNDEFINED", + format!("Undefined identifier '{}'", name_str), + self.arena.span(node), + ); + Err(()) + } + } + } - fn lower_member_access(&mut self, n: &MemberAccessNode) -> Result<(), ()> { - if let Node::Ident(id) = &*n.object { + fn lower_member_access(&mut self, node: NodeId, n: &MemberAccessNodeArena) -> Result<(), ()> { + if let NodeKind::Ident(id) = self.arena.kind(n.object) { let type_name = self.interner.resolve(id.name); let member_name = self.interner.resolve(n.member); if let Some(constants) = self.type_constants.get(type_name).cloned() { if let Some(const_val) = constants.get(member_name) { let old_ctx = self.current_type_context.replace(type_name.to_string()); - let res = self.lower_node(const_val); + let res = self.lower_node(*const_val); self.current_type_context = old_ctx; return res; } @@ -595,7 +618,7 @@ impl<'a> Lowerer<'a> { } } - if let Some((slot, ty)) = self.resolve_member_access(n) { + if let Some((slot, ty)) = self.resolve_member_access(node) { let slots = self.get_type_slots(&ty); for i in 0..slots { self.emit(InstrKind::GetLocal(slot + i)); @@ -606,28 +629,35 @@ impl<'a> Lowerer<'a> { Ok(()) } - fn resolve_member_access(&self, n: &MemberAccessNode) -> Option<(u32, Type)> { - match &*n.object { - Node::Ident(id) => { - let name_str = self.interner.resolve(id.name); - let member_str = self.interner.resolve(n.member); - let info = self.find_local(name_str)?; - if let Type::Struct(sname) = &info.ty { - let offset = self.get_field_offset(sname, member_str); - let ty = self.get_field_type(sname, member_str); - Some((info.slot + offset, ty)) - } else { None } - } - Node::MemberAccess(inner) => { - let member_str = self.interner.resolve(n.member); - let (base_slot, ty) = self.resolve_member_access(inner)?; - if let Type::Struct(sname) = &ty { - let offset = self.get_field_offset(sname, member_str); - let final_ty = self.get_field_type(sname, member_str); - Some((base_slot + offset, final_ty)) - } else { None } - } - _ => None + fn resolve_member_access(&self, node: NodeId) -> Option<(u32, Type)> { + match self.arena.kind(node) { + NodeKind::MemberAccess(n) => match self.arena.kind(n.object) { + NodeKind::Ident(id) => { + let name_str = self.interner.resolve(id.name); + let member_str = self.interner.resolve(n.member); + let info = self.find_local(name_str)?; + if let Type::Struct(sname) = &info.ty { + let offset = self.get_field_offset(sname, member_str); + let ty = self.get_field_type(sname, member_str); + Some((info.slot + offset, ty)) + } else { + None + } + } + NodeKind::MemberAccess(_) => { + let member_str = self.interner.resolve(n.member); + let (base_slot, ty) = self.resolve_member_access(n.object)?; + if let Type::Struct(sname) = &ty { + let offset = self.get_field_offset(sname, member_str); + let final_ty = self.get_field_type(sname, member_str); + Some((base_slot + offset, final_ty)) + } else { + None + } + } + _ => None, + }, + _ => None, } } @@ -680,31 +710,35 @@ impl<'a> Lowerer<'a> { } } - fn lower_call(&mut self, n: &CallNode) -> Result<(), ()> { - match &*n.callee { - Node::Ident(id_node) => { + fn lower_call(&mut self, node: NodeId, n: &CallNodeArena) -> Result<(), ()> { + match self.arena.kind(n.callee) { + NodeKind::Ident(id_node) => { let callee_name = self.interner.resolve(id_node.name).to_string(); // 1. Check for constructor call: TypeName(...) - let ctor = self.struct_constructors.get(&callee_name) + let ctor = self + .struct_constructors + .get(&callee_name) .and_then(|ctors| ctors.get(&callee_name)) - .cloned(); - + .copied(); + if let Some(ctor) = ctor { - return self.lower_constructor_call(&ctor, &n.args); + return self.lower_constructor_call(ctor, &n.args); } if let Some(ctx) = &self.current_type_context { - let ctor = self.struct_constructors.get(ctx) + let ctor = self + .struct_constructors + .get(ctx) .and_then(|ctors| ctors.get(&callee_name)) - .cloned(); - + .copied(); + if let Some(ctor) = ctor { - return self.lower_constructor_call(&ctor, &n.args); + return self.lower_constructor_call(ctor, &n.args); } } for arg in &n.args { - self.lower_node(arg)?; + self.lower_node(*arg)?; } if let Some(func_id) = self.function_ids.get(&callee_name) { self.emit(InstrKind::Call(*func_id, n.args.len() as u32)); @@ -727,7 +761,7 @@ impl<'a> Lowerer<'a> { } } } - + self.error( "E_LOWER_UNSUPPORTED", format!( @@ -735,10 +769,29 @@ impl<'a> Lowerer<'a> { callee_name, sym.origin ), - id_node.span, + self.arena.span(n.callee), ); Err(()) } else { + // Try default constructor for struct calls like TypeName(...) + if let Some(ctors) = self.struct_constructors.get(&callee_name) { + if let Some(ctor) = ctors.get(&callee_name) { + return self.lower_constructor_call(*ctor, &n.args); + } + } + + let type_sym = self + .module_symbols + .type_symbols + .get(id_node.name) + .or_else(|| self.imported_symbols.type_symbols.get(id_node.name)); + if let Some(sym) = type_sym { + if sym.kind == SymbolKind::Struct { + // TODO: handle implicit struct constructor (lower args as field values) + return Ok(()); + } + } + // Check for special built-in functions match callee_name.as_str() { "some" | "ok" | "err" => { @@ -750,32 +803,32 @@ impl<'a> Lowerer<'a> { self.error( "E_RESOLVE_UNDEFINED", format!("Undefined function '{}'", callee_name), - id_node.span, + self.arena.span(n.callee), ); Err(()) } } - Node::MemberAccess(ma) => { + NodeKind::MemberAccess(ma) => { // Check if it's a constructor alias: TypeName.Alias(...) - let ctor = if let Node::Ident(obj_id) = &*ma.object { + let ctor = if let NodeKind::Ident(obj_id) = self.arena.kind(ma.object) { let obj_name = self.interner.resolve(obj_id.name); let member_name = self.interner.resolve(ma.member); self.struct_constructors .get(obj_name) .and_then(|ctors| ctors.get(member_name)) - .cloned() + .copied() } else { None }; if let Some(ctor) = ctor { - return self.lower_constructor_call(&ctor, &n.args); + return self.lower_constructor_call(ctor, &n.args); } // Check for Pad.any() let member_name = self.interner.resolve(ma.member); if member_name == "any" { - if let Node::Ident(obj_id) = &*ma.object { + if let NodeKind::Ident(obj_id) = self.arena.kind(ma.object) { let obj_name = self.interner.resolve(obj_id.name); if let Some(info) = self.find_local(obj_name) { if let Type::Struct(sname) = &info.ty { @@ -789,23 +842,30 @@ impl<'a> Lowerer<'a> { } // Host contract static calls: Contract.method(...) - if let Node::Ident(obj_id) = &*ma.object { + if let NodeKind::Ident(obj_id) = self.arena.kind(ma.object) { let obj_name = self.interner.resolve(obj_id.name); let is_local = self.find_local(obj_name).is_some(); - + if !is_local { // Check type symbol (current or imported) for a host contract - let sym_opt = self.module_symbols.type_symbols.get(obj_id.name) + let sym_opt = self + .module_symbols + .type_symbols + .get(obj_id.name) .or_else(|| self.imported_symbols.type_symbols.get(obj_id.name)); if let Some(sym) = sym_opt { if sym.kind == SymbolKind::Contract && sym.is_host { // Lower arguments first to avoid borrowing conflicts for arg in &n.args { - self.lower_node(arg)?; + self.lower_node(*arg)?; } if let Some(method) = self.contract_registry.get_method(obj_name, member_name) { let id = method.id; - let return_slots = if matches!(method.return_type, PbsType::Void) { 0 } else { 1 }; + let return_slots = if matches!(method.return_type, PbsType::Void) { + 0 + } else { + 1 + }; self.emit(InstrKind::HostCall(id, return_slots)); return Ok(()); } @@ -816,26 +876,30 @@ impl<'a> Lowerer<'a> { // Check for .raw() if member_name == "raw" { - self.lower_node(&ma.object)?; + self.lower_node(ma.object)?; return Ok(()); } // Check for Color.rgb if member_name == "rgb" { - if let Node::Ident(obj_id) = &*ma.object { + if let NodeKind::Ident(obj_id) = self.arena.kind(ma.object) { if self.interner.resolve(obj_id.name) == "Color" { if n.args.len() == 3 { // Try to get literal values for r, g, b let mut literals = Vec::new(); for arg in &n.args { - if let Node::IntLit(lit) = arg { + if let NodeKind::IntLit(lit) = self.arena.kind(*arg) { literals.push(Some(lit.value)); + } else if let NodeKind::BoundedLit(lit) = self.arena.kind(*arg) { + literals.push(Some(lit.value as i64)); } else { literals.push(None); } } - if let (Some(r), Some(g), Some(b)) = (literals[0], literals[1], literals[2]) { + if let (Some(r), Some(g), Some(b)) = + (literals[0], literals[1], literals[2]) + { let r5 = (r & 0xFF) >> 3; let g6 = (g & 0xFF) >> 2; let b5 = (b & 0xFF) >> 3; @@ -843,7 +907,12 @@ impl<'a> Lowerer<'a> { self.emit(InstrKind::PushBounded(rgb565 as u32)); return Ok(()); } else { - self.error("E_LOWER_UNSUPPORTED", "Color.rgb only supports literal arguments in this version".to_string(), n.span); + self.error( + "E_LOWER_UNSUPPORTED", + "Color.rgb only supports literal arguments in this version" + .to_string(), + self.arena.span(node), + ); return Err(()); } } @@ -852,15 +921,18 @@ impl<'a> Lowerer<'a> { } for arg in &n.args { - self.lower_node(arg)?; + self.lower_node(*arg)?; } - if let Node::Ident(obj_id) = &*ma.object { + if let NodeKind::Ident(obj_id) = self.arena.kind(ma.object) { let obj_name = self.interner.resolve(obj_id.name); - let is_host_contract = self.module_symbols.type_symbols.get(obj_id.name) + let is_host_contract = self + .module_symbols + .type_symbols + .get(obj_id.name) .map(|sym| sym.kind == SymbolKind::Contract && sym.is_host) .unwrap_or(false); - + let is_shadowed = self.find_local(obj_name).is_some(); if is_host_contract && !is_shadowed { @@ -870,77 +942,68 @@ impl<'a> Lowerer<'a> { self.emit(InstrKind::HostCall(method.id, return_slots)); return Ok(()); } else { - self.error( - "E_RESOLVE_UNDEFINED", - format!("Undefined contract member '{}.{}'", obj_name, member_name), - ma.span, - ); - return Err(()); + self.error( + "E_RESOLVE_UNDEFINED", + format!("Undefined contract member '{}.{}'", obj_name, member_name), + self.arena.span(n.callee), + ); + return Err(()); } } } - - self.error("E_LOWER_UNSUPPORTED", "Method calls not supported in v0".to_string(), ma.span); + + self.error( + "E_LOWER_UNSUPPORTED", + "Method calls not supported in v0".to_string(), + self.arena.span(n.callee), + ); Err(()) } _ => { for arg in &n.args { - self.lower_node(arg)?; + self.lower_node(*arg)?; } - self.error("E_LOWER_UNSUPPORTED", "Indirect calls not supported in v0".to_string(), n.callee.span()); + self.error( + "E_LOWER_UNSUPPORTED", + "Indirect calls not supported in v0".to_string(), + self.arena.span(n.callee), + ); Err(()) } } } - fn lower_constructor_call(&mut self, ctor: &ConstructorDeclNode, args: &[Node]) -> Result<(), ()> { - let mut param_map = HashMap::new(); - for (i, param) in ctor.params.iter().enumerate() { - if i < args.len() { - param_map.insert(self.interner.resolve(param.name).to_string(), args[i].clone()); + fn lower_constructor_call(&mut self, ctor: NodeId, args: &[NodeId]) -> Result<(), ()> { + let ctor = match self.arena.kind(ctor) { + NodeKind::ConstructorDecl(ctor) => ctor, + _ => return Err(()), + }; + + self.local_vars.push(HashMap::new()); + let mut param_slots = Vec::new(); + + for param in &ctor.params { + let ty = self.lower_type_node(param.ty); + let slot = self.add_local_to_scope(self.interner.resolve(param.name).to_string(), ty.clone()); + param_slots.push((slot, ty)); + } + + for (index, arg) in args.iter().enumerate() { + if let Some((slot, ty)) = param_slots.get(index) { + self.lower_node(*arg)?; + let slots = self.get_type_slots(ty); + for i in (0..slots).rev() { + self.emit(InstrKind::SetLocal(slot + i)); + } } } for init in &ctor.initializers { - let substituted = self.substitute_node(init, ¶m_map); - self.lower_node(&substituted)?; + self.lower_node(*init)?; } - Ok(()) - } - fn substitute_node(&self, node: &Node, param_map: &HashMap) -> Node { - match node { - Node::Ident(id) => { - if let Some(arg) = param_map.get(self.interner.resolve(id.name)) { - arg.clone() - } else { - node.clone() - } - } - Node::Binary(bin) => { - Node::Binary(BinaryNode { - span: bin.span, - left: Box::new(self.substitute_node(&bin.left, param_map)), - right: Box::new(self.substitute_node(&bin.right, param_map)), - op: bin.op.clone(), - }) - } - Node::Unary(un) => { - Node::Unary(UnaryNode { - span: un.span, - op: un.op.clone(), - expr: Box::new(self.substitute_node(&un.expr, param_map)), - }) - } - Node::Call(call) => { - Node::Call(CallNode { - span: call.span, - callee: Box::new(self.substitute_node(&call.callee, param_map)), - args: call.args.iter().map(|a| self.substitute_node(a, param_map)).collect(), - }) - } - _ => node.clone() - } + self.local_vars.pop(); + Ok(()) } fn lower_pad_any(&mut self, base_slot: u32) { @@ -957,9 +1020,9 @@ impl<'a> Lowerer<'a> { } } - fn lower_binary(&mut self, n: &BinaryNode) -> Result<(), ()> { - self.lower_node(&n.left)?; - self.lower_node(&n.right)?; + fn lower_binary(&mut self, node: NodeId, n: &BinaryNodeArena) -> Result<(), ()> { + self.lower_node(n.left)?; + self.lower_node(n.right)?; match n.op.as_str() { "+" => self.emit(InstrKind::Add), "-" => self.emit(InstrKind::Sub), @@ -974,32 +1037,40 @@ impl<'a> Lowerer<'a> { "&&" => self.emit(InstrKind::And), "||" => self.emit(InstrKind::Or), _ => { - self.error("E_LOWER_UNSUPPORTED", format!("Binary operator '{}' not supported", n.op), n.span); + self.error( + "E_LOWER_UNSUPPORTED", + format!("Binary operator '{}' not supported", n.op), + self.arena.span(node), + ); return Err(()); } } Ok(()) } - fn lower_unary(&mut self, n: &UnaryNode) -> Result<(), ()> { - self.lower_node(&n.expr)?; + fn lower_unary(&mut self, node: NodeId, n: &UnaryNodeArena) -> Result<(), ()> { + self.lower_node(n.expr)?; match n.op.as_str() { "-" => self.emit(InstrKind::Neg), "!" => self.emit(InstrKind::Not), _ => { - self.error("E_LOWER_UNSUPPORTED", format!("Unary operator '{}' not supported", n.op), n.span); + self.error( + "E_LOWER_UNSUPPORTED", + format!("Unary operator '{}' not supported", n.op), + self.arena.span(node), + ); return Err(()); } } Ok(()) } - fn lower_if_expr(&mut self, n: &IfExprNode) -> Result<(), ()> { + fn lower_if_expr(&mut self, _node: NodeId, n: &IfExprNodeArena) -> Result<(), ()> { let then_id = self.reserve_block_id(); let else_id = self.reserve_block_id(); let merge_id = self.reserve_block_id(); - self.lower_node(&n.cond)?; + self.lower_node(n.cond)?; self.terminate(Terminator::JumpIfFalse { target: else_id, else_target: then_id, @@ -1007,12 +1078,12 @@ impl<'a> Lowerer<'a> { // Then block self.start_block_with_id(then_id); - self.lower_node(&n.then_block)?; + self.lower_node(n.then_block)?; self.terminate(Terminator::Jump(merge_id)); // Else block self.start_block_with_id(else_id); - if let Some(else_block) = &n.else_block { + if let Some(else_block) = n.else_block { self.lower_node(else_block)?; } self.terminate(Terminator::Jump(merge_id)); @@ -1022,9 +1093,9 @@ impl<'a> Lowerer<'a> { Ok(()) } - fn lower_type_node(&mut self, node: &Node) -> Type { - match node { - Node::TypeName(n) => match self.interner.resolve(n.name) { + fn lower_type_node(&mut self, node: NodeId) -> Type { + match self.arena.kind(node) { + NodeKind::TypeName(n) => match self.interner.resolve(n.name) { "int" => Type::Int, "bounded" => Type::Bounded, "float" => Type::Float, @@ -1033,12 +1104,12 @@ impl<'a> Lowerer<'a> { "void" => Type::Void, _ => Type::Struct(self.interner.resolve(n.name).to_string()), }, - Node::TypeApp(ta) => { + NodeKind::TypeApp(ta) => { let base_name = self.interner.resolve(ta.base); if base_name == "array" { - let elem_ty = self.lower_type_node(&ta.args[0]); + let elem_ty = self.lower_type_node(ta.args[0]); let size = if ta.args.len() > 1 { - if let Node::IntLit(il) = &ta.args[1] { + if let NodeKind::IntLit(il) = self.arena.kind(ta.args[1]) { il.value as u32 } else { 0 @@ -1048,11 +1119,11 @@ impl<'a> Lowerer<'a> { }; Type::Array(Box::new(elem_ty), size) } else if base_name == "optional" { - Type::Optional(Box::new(self.lower_type_node(&ta.args[0]))) + Type::Optional(Box::new(self.lower_type_node(ta.args[0]))) } else if base_name == "result" { Type::Result( - Box::new(self.lower_type_node(&ta.args[0])), - Box::new(self.lower_type_node(&ta.args[1])) + Box::new(self.lower_type_node(ta.args[0])), + Box::new(self.lower_type_node(ta.args[1])) ) } else { Type::Struct(format!("{}<{}>", base_name, ta.args.len())) @@ -1204,15 +1275,17 @@ mod tests { "; let mut interner = NameInterner::new(); let mut parser = Parser::new(code, 0, &mut interner); - let ast = parser.parse_file().expect("Failed to parse"); + let parsed = parser.parse_file().expect("Failed to parse"); let mut collector = SymbolCollector::new(&interner); - let (type_symbols, value_symbols) = collector.collect(&ast).expect("Failed to collect symbols"); + let (type_symbols, value_symbols) = collector + .collect(&parsed.arena, parsed.root) + .expect("Failed to collect symbols"); let module_symbols = ModuleSymbols { type_symbols, value_symbols }; let imported = ModuleSymbols::new(); - let lowerer = Lowerer::new(&module_symbols, &imported, &interner); - let program = lowerer.lower_file(&ast, "test").expect("Lowering failed"); + let lowerer = Lowerer::new(&parsed.arena, &module_symbols, &imported, &interner); + let program = lowerer.lower_file(parsed.root, "test").expect("Lowering failed"); // Verify program structure assert_eq!(program.modules.len(), 1); @@ -1243,15 +1316,17 @@ mod tests { "; let mut interner = NameInterner::new(); let mut parser = Parser::new(code, 0, &mut interner); - let ast = parser.parse_file().expect("Failed to parse"); + let parsed = parser.parse_file().expect("Failed to parse"); let mut collector = SymbolCollector::new(&interner); - let (type_symbols, value_symbols) = collector.collect(&ast).expect("Failed to collect symbols"); + let (type_symbols, value_symbols) = collector + .collect(&parsed.arena, parsed.root) + .expect("Failed to collect symbols"); let module_symbols = ModuleSymbols { type_symbols, value_symbols }; let imported = ModuleSymbols::new(); - let lowerer = Lowerer::new(&module_symbols, &imported, &interner); - let program = lowerer.lower_file(&ast, "test").expect("Lowering failed"); + let lowerer = Lowerer::new(&parsed.arena, &module_symbols, &imported, &interner); + let program = lowerer.lower_file(parsed.root, "test").expect("Lowering failed"); let max_func = &program.modules[0].functions[0]; // Should have multiple blocks for if-else @@ -1270,15 +1345,17 @@ mod tests { "; let mut interner = NameInterner::new(); let mut parser = Parser::new(code, 0, &mut interner); - let ast = parser.parse_file().expect("Failed to parse"); + let parsed = parser.parse_file().expect("Failed to parse"); let mut collector = SymbolCollector::new(&interner); - let (type_symbols, value_symbols) = collector.collect(&ast).expect("Failed to collect symbols"); + let (type_symbols, value_symbols) = collector + .collect(&parsed.arena, parsed.root) + .expect("Failed to collect symbols"); let module_symbols = ModuleSymbols { type_symbols, value_symbols }; let imported = ModuleSymbols::new(); - let lowerer = Lowerer::new(&module_symbols, &imported, &interner); - let program = lowerer.lower_file(&ast, "test").expect("Lowering failed"); + let lowerer = Lowerer::new(&parsed.arena, &module_symbols, &imported, &interner); + let program = lowerer.lower_file(parsed.root, "test").expect("Lowering failed"); let func = &program.modules[0].functions[0]; let instrs: Vec<_> = func.blocks.iter().flat_map(|b| b.instrs.iter()).collect(); @@ -1300,15 +1377,17 @@ mod tests { "; let mut interner = NameInterner::new(); let mut parser = Parser::new(code, 0, &mut interner); - let ast = parser.parse_file().expect("Failed to parse"); + let parsed = parser.parse_file().expect("Failed to parse"); let mut collector = SymbolCollector::new(&interner); - let (type_symbols, value_symbols) = collector.collect(&ast).unwrap(); + let (type_symbols, value_symbols) = collector + .collect(&parsed.arena, parsed.root) + .unwrap(); let module_symbols = ModuleSymbols { type_symbols, value_symbols }; let imported = ModuleSymbols::new(); - let lowerer = Lowerer::new(&module_symbols, &imported, &interner); - let program = lowerer.lower_file(&ast, "test").expect("Lowering failed"); + let lowerer = Lowerer::new(&parsed.arena, &module_symbols, &imported, &interner); + let program = lowerer.lower_file(parsed.root, "test").expect("Lowering failed"); let json = serde_json::to_string_pretty(&program).unwrap(); @@ -1343,15 +1422,17 @@ mod tests { "; let mut interner = NameInterner::new(); let mut parser = Parser::new(code, 0, &mut interner); - let ast = parser.parse_file().expect("Failed to parse"); + let parsed = parser.parse_file().expect("Failed to parse"); let mut collector = SymbolCollector::new(&interner); - let (type_symbols, value_symbols) = collector.collect(&ast).expect("Failed to collect symbols"); + let (type_symbols, value_symbols) = collector + .collect(&parsed.arena, parsed.root) + .expect("Failed to collect symbols"); let module_symbols = ModuleSymbols { type_symbols, value_symbols }; let imported = ModuleSymbols::new(); - let lowerer = Lowerer::new(&module_symbols, &imported, &interner); - let program = lowerer.lower_file(&ast, "test").expect("Lowering failed"); + let lowerer = Lowerer::new(&parsed.arena, &module_symbols, &imported, &interner); + let program = lowerer.lower_file(parsed.root, "test").expect("Lowering failed"); let func = &program.modules[0].functions[0]; let instrs: Vec<_> = func.blocks.iter().flat_map(|b| b.instrs.iter()).collect(); @@ -1379,15 +1460,17 @@ mod tests { "; let mut interner = NameInterner::new(); let mut parser = Parser::new(code, 0, &mut interner); - let ast = parser.parse_file().expect("Failed to parse"); + let parsed = parser.parse_file().expect("Failed to parse"); let mut collector = SymbolCollector::new(&interner); - let (type_symbols, value_symbols) = collector.collect(&ast).expect("Failed to collect symbols"); + let (type_symbols, value_symbols) = collector + .collect(&parsed.arena, parsed.root) + .expect("Failed to collect symbols"); let module_symbols = ModuleSymbols { type_symbols, value_symbols }; let imported = ModuleSymbols::new(); - let lowerer = Lowerer::new(&module_symbols, &imported, &interner); - let program = lowerer.lower_file(&ast, "test").expect("Lowering failed"); + let lowerer = Lowerer::new(&parsed.arena, &module_symbols, &imported, &interner); + let program = lowerer.lower_file(parsed.root, "test").expect("Lowering failed"); let func = &program.modules[0].functions[0]; let instrs: Vec<_> = func.blocks.iter().flat_map(|b| b.instrs.iter()).collect(); @@ -1408,15 +1491,17 @@ mod tests { "; let mut interner = NameInterner::new(); let mut parser = Parser::new(code, 0, &mut interner); - let ast = parser.parse_file().expect("Failed to parse"); + let parsed = parser.parse_file().expect("Failed to parse"); let mut collector = SymbolCollector::new(&interner); - let (type_symbols, value_symbols) = collector.collect(&ast).expect("Failed to collect symbols"); + let (type_symbols, value_symbols) = collector + .collect(&parsed.arena, parsed.root) + .expect("Failed to collect symbols"); let module_symbols = ModuleSymbols { type_symbols, value_symbols }; let imported = ModuleSymbols::new(); - let lowerer = Lowerer::new(&module_symbols, &imported, &interner); - let result = lowerer.lower_file(&ast, "test"); + let lowerer = Lowerer::new(&parsed.arena, &module_symbols, &imported, &interner); + let result = lowerer.lower_file(parsed.root, "test"); assert!(result.is_err()); let bundle = result.err().unwrap(); @@ -1434,15 +1519,17 @@ mod tests { "; let mut interner = NameInterner::new(); let mut parser = Parser::new(code, 0, &mut interner); - let ast = parser.parse_file().expect("Failed to parse"); + let parsed = parser.parse_file().expect("Failed to parse"); let mut collector = SymbolCollector::new(&interner); - let (type_symbols, value_symbols) = collector.collect(&ast).expect("Failed to collect symbols"); + let (type_symbols, value_symbols) = collector + .collect(&parsed.arena, parsed.root) + .expect("Failed to collect symbols"); let module_symbols = ModuleSymbols { type_symbols, value_symbols }; let imported = ModuleSymbols::new(); - let lowerer = Lowerer::new(&module_symbols, &imported, &interner); - let result = lowerer.lower_file(&ast, "test"); + let lowerer = Lowerer::new(&parsed.arena, &module_symbols, &imported, &interner); + let result = lowerer.lower_file(parsed.root, "test"); assert!(result.is_err()); let bundle = result.err().unwrap(); @@ -1459,15 +1546,17 @@ mod tests { "; let mut interner = NameInterner::new(); let mut parser = Parser::new(code, 0, &mut interner); - let ast = parser.parse_file().expect("Failed to parse"); + let parsed = parser.parse_file().expect("Failed to parse"); let mut collector = SymbolCollector::new(&interner); - let (type_symbols, value_symbols) = collector.collect(&ast).expect("Failed to collect symbols"); + let (type_symbols, value_symbols) = collector + .collect(&parsed.arena, parsed.root) + .expect("Failed to collect symbols"); let module_symbols = ModuleSymbols { type_symbols, value_symbols }; let imported = ModuleSymbols::new(); - let lowerer = Lowerer::new(&module_symbols, &imported, &interner); - let result = lowerer.lower_file(&ast, "test"); + let lowerer = Lowerer::new(&parsed.arena, &module_symbols, &imported, &interner); + let result = lowerer.lower_file(parsed.root, "test"); assert!(result.is_err()); let bundle = result.err().unwrap(); @@ -1484,15 +1573,17 @@ mod tests { "; let mut interner = NameInterner::new(); let mut parser = Parser::new(code, 0, &mut interner); - let ast = parser.parse_file().expect("Failed to parse"); + let parsed = parser.parse_file().expect("Failed to parse"); let mut collector = SymbolCollector::new(&interner); - let (type_symbols, value_symbols) = collector.collect(&ast).expect("Failed to collect symbols"); + let (type_symbols, value_symbols) = collector + .collect(&parsed.arena, parsed.root) + .expect("Failed to collect symbols"); let module_symbols = ModuleSymbols { type_symbols, value_symbols }; let imported = ModuleSymbols::new(); - let lowerer = Lowerer::new(&module_symbols, &imported, &interner); - let program = lowerer.lower_file(&ast, "test").expect("Lowering failed"); + let lowerer = Lowerer::new(&parsed.arena, &module_symbols, &imported, &interner); + let program = lowerer.lower_file(parsed.root, "test").expect("Lowering failed"); let func = &program.modules[0].functions[0]; let instrs: Vec<_> = func.blocks.iter().flat_map(|b| b.instrs.iter()).collect(); @@ -1518,15 +1609,17 @@ mod tests { "; let mut interner = NameInterner::new(); let mut parser = Parser::new(code, 0, &mut interner); - let ast = parser.parse_file().expect("Failed to parse"); + let parsed = parser.parse_file().expect("Failed to parse"); let mut collector = SymbolCollector::new(&interner); - let (type_symbols, value_symbols) = collector.collect(&ast).expect("Failed to collect symbols"); + let (type_symbols, value_symbols) = collector + .collect(&parsed.arena, parsed.root) + .expect("Failed to collect symbols"); let module_symbols = ModuleSymbols { type_symbols, value_symbols }; let imported = ModuleSymbols::new(); - let lowerer = Lowerer::new(&module_symbols, &imported, &interner); - let program = lowerer.lower_file(&ast, "test").expect("Lowering failed"); + let lowerer = Lowerer::new(&parsed.arena, &module_symbols, &imported, &interner); + let program = lowerer.lower_file(parsed.root, "test").expect("Lowering failed"); let func = &program.modules[0].functions[0]; let instrs: Vec<_> = func.blocks.iter().flat_map(|b| b.instrs.iter()).collect(); @@ -1552,15 +1645,17 @@ mod tests { "; let mut interner = NameInterner::new(); let mut parser = Parser::new(code, 0, &mut interner); - let ast = parser.parse_file().expect("Failed to parse"); + let parsed = parser.parse_file().expect("Failed to parse"); let mut collector = SymbolCollector::new(&interner); - let (type_symbols, value_symbols) = collector.collect(&ast).expect("Failed to collect symbols"); + let (type_symbols, value_symbols) = collector + .collect(&parsed.arena, parsed.root) + .expect("Failed to collect symbols"); let module_symbols = ModuleSymbols { type_symbols, value_symbols }; let imported = ModuleSymbols::new(); - let lowerer = Lowerer::new(&module_symbols, &imported, &interner); - let program = lowerer.lower_file(&ast, "test").expect("Lowering failed"); + let lowerer = Lowerer::new(&parsed.arena, &module_symbols, &imported, &interner); + let program = lowerer.lower_file(parsed.root, "test").expect("Lowering failed"); let func = &program.modules[0].functions[0]; let instrs: Vec<_> = func.blocks.iter().flat_map(|b| b.instrs.iter()).collect(); @@ -1586,15 +1681,17 @@ mod tests { "; let mut interner = NameInterner::new(); let mut parser = Parser::new(code, 0, &mut interner); - let ast = parser.parse_file().expect("Failed to parse"); + let parsed = parser.parse_file().expect("Failed to parse"); let mut collector = SymbolCollector::new(&interner); - let (type_symbols, value_symbols) = collector.collect(&ast).expect("Failed to collect symbols"); + let (type_symbols, value_symbols) = collector + .collect(&parsed.arena, parsed.root) + .expect("Failed to collect symbols"); let module_symbols = ModuleSymbols { type_symbols, value_symbols }; let imported = ModuleSymbols::new(); - let lowerer = Lowerer::new(&module_symbols, &imported, &interner); - let result = lowerer.lower_file(&ast, "test"); + let lowerer = Lowerer::new(&parsed.arena, &module_symbols, &imported, &interner); + let result = lowerer.lower_file(parsed.root, "test"); assert!(result.is_err()); let bundle = result.err().unwrap(); @@ -1611,15 +1708,17 @@ mod tests { "; let mut interner = NameInterner::new(); let mut parser = Parser::new(code, 0, &mut interner); - let ast = parser.parse_file().expect("Failed to parse"); + let parsed = parser.parse_file().expect("Failed to parse"); let mut collector = SymbolCollector::new(&interner); - let (type_symbols, value_symbols) = collector.collect(&ast).expect("Failed to collect symbols"); + let (type_symbols, value_symbols) = collector + .collect(&parsed.arena, parsed.root) + .expect("Failed to collect symbols"); let module_symbols = ModuleSymbols { type_symbols, value_symbols }; let imported = ModuleSymbols::new(); - let lowerer = Lowerer::new(&module_symbols, &imported, &interner); - let result = lowerer.lower_file(&ast, "test"); + let lowerer = Lowerer::new(&parsed.arena, &module_symbols, &imported, &interner); + let result = lowerer.lower_file(parsed.root, "test"); assert!(result.is_err()); let bundle = result.err().unwrap(); diff --git a/crates/prometeu-compiler/src/frontends/pbs/mod.rs b/crates/prometeu-compiler/src/frontends/pbs/mod.rs index acbe5b45..bab9356d 100644 --- a/crates/prometeu-compiler/src/frontends/pbs/mod.rs +++ b/crates/prometeu-compiler/src/frontends/pbs/mod.rs @@ -45,10 +45,11 @@ impl Frontend for PbsFrontend { let mut interner = NameInterner::new(); let mut parser = parser::Parser::new(&source, file_id, &mut interner); - let ast = parser.parse_file()?; + let parsed = parser.parse_file()?; let mut collector = SymbolCollector::new(&interner); - let (type_symbols, value_symbols) = collector.collect(&ast)?; + let (type_symbols, value_symbols) = + collector.collect(&parsed.arena, parsed.root)?; let mut module_symbols = ModuleSymbols { type_symbols, value_symbols }; struct EmptyProvider; @@ -57,16 +58,16 @@ impl Frontend for PbsFrontend { } let mut resolver = Resolver::new(&module_symbols, &EmptyProvider, &interner); - resolver.resolve(&ast)?; + resolver.resolve(&parsed.arena, parsed.root)?; let imported_symbols = resolver.imported_symbols; let mut typechecker = TypeChecker::new(&mut module_symbols, &imported_symbols, &EmptyProvider, &interner); - typechecker.check(&ast)?; + typechecker.check(&parsed.arena, parsed.root)?; // Lower to Core IR - let lowerer = Lowerer::new(&module_symbols, &imported_symbols, &interner); + let lowerer = Lowerer::new(&parsed.arena, &module_symbols, &imported_symbols, &interner); let module_name = entry.file_stem().unwrap().to_string_lossy(); - let core_program = lowerer.lower_file(&ast, &module_name)?; + let core_program = lowerer.lower_file(parsed.root, &module_name)?; // Validate Core IR Invariants crate::ir_core::validate_program(&core_program).map_err(|e| { diff --git a/crates/prometeu-compiler/src/frontends/pbs/parser.rs b/crates/prometeu-compiler/src/frontends/pbs/parser.rs index 962bdae6..7f13637e 100644 --- a/crates/prometeu-compiler/src/frontends/pbs/parser.rs +++ b/crates/prometeu-compiler/src/frontends/pbs/parser.rs @@ -11,6 +11,7 @@ pub struct Parser<'a> { file_id: usize, errors: Vec, interner: &'a mut NameInterner, + arena: AstArena, builtin_none: NameId, builtin_some: NameId, builtin_ok: NameId, @@ -55,6 +56,7 @@ impl<'a> Parser<'a> { file_id, errors: Vec::new(), interner, + arena: AstArena::default(), builtin_none, builtin_some, builtin_ok, @@ -69,7 +71,7 @@ impl<'a> Parser<'a> { } } - pub fn parse_file(&mut self) -> Result { + pub fn parse_file(&mut self) -> Result { let start_span = self.peek().span; let mut imports = Vec::new(); let mut decls = Vec::new(); @@ -96,10 +98,13 @@ impl<'a> Parser<'a> { }); } - Ok(FileNode { - span: Span::new(self.file_id, start_span.start, end_span.end), - imports, - decls, + let span = Span::new(self.file_id, start_span.start, end_span.end); + let root = self.arena.push(NodeKind::File(FileNodeArena { imports, decls }), span); + self.arena.roots.push(root); + + Ok(ParsedAst { + arena: std::mem::take(&mut self.arena), + root, }) } @@ -117,7 +122,7 @@ impl<'a> Parser<'a> { } } - fn parse_import(&mut self) -> Result { + fn parse_import(&mut self) -> Result { let start_span = self.consume(TokenKind::Import)?.span; let spec = self.parse_import_spec()?; self.consume(TokenKind::Identifier("from".to_string()))?; @@ -135,14 +140,17 @@ impl<'a> Parser<'a> { self.advance(); } - Ok(Node::Import(ImportNode { - span: Span::new(self.file_id, start_span.start, path_tok.1.end), - spec: Box::new(spec), - from: path_tok.0, - })) + let span = Span::new(self.file_id, start_span.start, path_tok.1.end); + Ok(self.arena.push( + NodeKind::Import(ImportNodeArena { + spec, + from: path_tok.0, + }), + span, + )) } - fn parse_import_spec(&mut self) -> Result { + fn parse_import_spec(&mut self) -> Result { let mut path = Vec::new(); let start_span = self.peek().span; @@ -183,13 +191,11 @@ impl<'a> Parser<'a> { } let end_span = self.tokens[self.pos - 1].span; - Ok(Node::ImportSpec(ImportSpecNode { - span: Span::new(self.file_id, start_span.start, end_span.end), - path, - })) + let span = Span::new(self.file_id, start_span.start, end_span.end); + Ok(self.arena.push(NodeKind::ImportSpec(ImportSpecNodeArena { path }), span)) } - fn parse_top_level_decl(&mut self) -> Result { + fn parse_top_level_decl(&mut self) -> Result { match self.peek().kind { TokenKind::Fn => self.parse_fn_decl(None), TokenKind::Pub | TokenKind::Mod | TokenKind::Declare | TokenKind::Service => self.parse_decl(), @@ -206,7 +212,7 @@ impl<'a> Parser<'a> { } } - fn parse_decl(&mut self) -> Result { + fn parse_decl(&mut self) -> Result { let vis = if self.peek().kind == TokenKind::Pub { self.advance(); Some("pub".to_string()) @@ -225,7 +231,7 @@ impl<'a> Parser<'a> { } } - fn parse_service_decl(&mut self, vis: Option) -> Result { + fn parse_service_decl(&mut self, vis: Option) -> Result { let start_span = self.consume(TokenKind::Service)?.span; let name = self.expect_identifier()?; let mut extends = None; @@ -245,38 +251,42 @@ impl<'a> Parser<'a> { } let end_span = self.consume(TokenKind::CloseBrace)?.span; - Ok(Node::ServiceDecl(ServiceDeclNode { - span: Span::new(self.file_id, start_span.start, end_span.end), - vis, - name, - extends, - members, - })) + let span = Span::new(self.file_id, start_span.start, end_span.end); + Ok(self.arena.push( + NodeKind::ServiceDecl(ServiceDeclNodeArena { + vis, + name, + extends, + members, + }), + span, + )) } - fn parse_service_member(&mut self) -> Result { + fn parse_service_member(&mut self) -> Result { let start_span = self.consume(TokenKind::Fn)?.span; let name = self.expect_identifier()?; let params = self.parse_param_list()?; let ret = if self.peek().kind == TokenKind::Colon { self.advance(); - Box::new(self.parse_type_ref()?) + self.parse_type_ref()? } else { - Box::new(Node::TypeName(TypeNameNode { - span: Span::new(self.file_id, 0, 0), // Placeholder for void - name: self.builtin_void, - })) + self.arena.push( + NodeKind::TypeName(TypeNameNodeArena { + name: self.builtin_void, + }), + Span::new(self.file_id, 0, 0), + ) }; - Ok(Node::ServiceFnSig(ServiceFnSigNode { - span: Span::new(self.file_id, start_span.start, ret.span().end), - name, - params, - ret, - })) + let span = Span::new(self.file_id, start_span.start, self.arena.span(ret).end); + Ok(self.arena.push( + NodeKind::ServiceFnSig(ServiceFnSigNodeArena { name, params, ret }), + span, + )) } - fn parse_type_decl(&mut self, vis: Option) -> Result { + fn parse_type_decl(&mut self, vis: Option) -> Result { let start_span = self.consume(TokenKind::Declare)?.span; let type_kind = match self.peek().kind { TokenKind::Struct => { self.advance(); "struct".to_string() } @@ -310,11 +320,14 @@ impl<'a> Parser<'a> { let c_name = self.expect_identifier()?; self.consume(TokenKind::Colon)?; let c_value = self.parse_expr(0)?; - constants.push(ConstantDeclNode { - span: Span::new(self.file_id, c_start, c_value.span().end), - name: c_name, - value: Box::new(c_value), - }); + let c_span = Span::new(self.file_id, c_start, self.arena.span(c_value).end); + constants.push(self.arena.push( + NodeKind::ConstantDecl(ConstantDeclNodeArena { + name: c_name, + value: c_value, + }), + c_span, + )); if self.peek().kind == TokenKind::Comma { self.advance(); } @@ -324,43 +337,45 @@ impl<'a> Parser<'a> { let mut body = None; if self.peek().kind == TokenKind::OpenBrace { - body = Some(Box::new(self.parse_type_body()?)); + body = Some(self.parse_type_body()?); } let mut end_pos = start_span.end; - if let Some(b) = &body { - end_pos = b.span().end; + if let Some(b) = body { + end_pos = self.arena.span(b).end; + body = Some(b); } else if !constants.is_empty() { // We should use the CloseDoubleBracket span here, but I don't have it easily // Let's just use the last constant's end - end_pos = constants.last().unwrap().span.end; + end_pos = self.arena.span(*constants.last().unwrap()).end; } else if !params.is_empty() { end_pos = params.last().unwrap().span.end; } - Ok(Node::TypeDecl(TypeDeclNode { - span: Span::new(self.file_id, start_span.start, end_pos), - vis, - type_kind, - name, - is_host, - params, - constructors, - constants, - body, - })) + let span = Span::new(self.file_id, start_span.start, end_pos); + Ok(self.arena.push( + NodeKind::TypeDecl(TypeDeclNodeArena { + vis, + type_kind, + name, + is_host, + params, + constructors, + constants, + body, + }), + span, + )) } - fn parse_type_body(&mut self) -> Result { + fn parse_type_body(&mut self) -> Result { let start_span = self.consume(TokenKind::OpenBrace)?.span; let mut members = Vec::new(); let mut methods = Vec::new(); while self.peek().kind != TokenKind::CloseBrace && self.peek().kind != TokenKind::Eof { if self.peek().kind == TokenKind::Fn { let sig_node = self.parse_service_member()?; - if let Node::ServiceFnSig(sig) = sig_node { - methods.push(sig); - } + methods.push(sig_node); if self.peek().kind == TokenKind::Semicolon { self.advance(); } @@ -369,11 +384,11 @@ impl<'a> Parser<'a> { let name = self.expect_identifier()?; self.consume(TokenKind::Colon)?; let ty = self.parse_type_ref()?; - let m_end = ty.span().end; - members.push(TypeMemberNode { + let m_end = self.arena.span(ty).end; + members.push(TypeMemberNodeArena { span: Span::new(self.file_id, m_start, m_end), name, - ty: Box::new(ty) + ty, }); if self.peek().kind == TokenKind::Comma { self.advance(); @@ -383,20 +398,20 @@ impl<'a> Parser<'a> { } } let end_span = self.consume(TokenKind::CloseBrace)?.span; - Ok(Node::TypeBody(TypeBodyNode { - span: Span::new(self.file_id, start_span.start, end_span.end), - members, - methods, - })) + let span = Span::new(self.file_id, start_span.start, end_span.end); + Ok(self.arena.push( + NodeKind::TypeBody(TypeBodyNodeArena { members, methods }), + span, + )) } - fn parse_fn_decl(&mut self, vis: Option) -> Result { + fn parse_fn_decl(&mut self, vis: Option) -> Result { let start_span = self.consume(TokenKind::Fn)?.span; let name = self.expect_identifier()?; let params = self.parse_param_list()?; let _ret = if self.peek().kind == TokenKind::Colon { self.advance(); - Some(Box::new(self.parse_type_ref()?)) + Some(self.parse_type_ref()?) } else { None }; @@ -404,24 +419,27 @@ impl<'a> Parser<'a> { let mut else_fallback = None; if self.peek().kind == TokenKind::Else { self.advance(); - else_fallback = Some(Box::new(self.parse_block()?)); + else_fallback = Some(self.parse_block()?); } let body = self.parse_block()?; - let body_span = body.span(); + let body_span = self.arena.span(body); - Ok(Node::FnDecl(FnDeclNode { - span: Span::new(self.file_id, start_span.start, body_span.end), - vis, - name, - params, - ret: _ret, - else_fallback, - body: Box::new(body), - })) + let span = Span::new(self.file_id, start_span.start, body_span.end); + Ok(self.arena.push( + NodeKind::FnDecl(FnDeclNodeArena { + vis, + name, + params, + ret: _ret, + else_fallback, + body, + }), + span, + )) } - fn parse_param_list(&mut self) -> Result, DiagnosticBundle> { + fn parse_param_list(&mut self) -> Result, DiagnosticBundle> { self.consume(TokenKind::OpenParen)?; let mut params = Vec::new(); while self.peek().kind != TokenKind::CloseParen && self.peek().kind != TokenKind::Eof { @@ -429,11 +447,11 @@ impl<'a> Parser<'a> { let name = self.expect_identifier()?; self.consume(TokenKind::Colon)?; let ty = self.parse_type_ref()?; - let p_end = ty.span().end; - params.push(ParamNode { + let p_end = self.arena.span(ty).end; + params.push(ParamNodeArena { span: Span::new(self.file_id, p_start, p_end), name, - ty: Box::new(ty) + ty, }); if self.peek().kind == TokenKind::Comma { self.advance(); @@ -445,7 +463,7 @@ impl<'a> Parser<'a> { Ok(params) } - fn parse_type_ref(&mut self) -> Result { + fn parse_type_ref(&mut self) -> Result { let id_tok = self.peek().clone(); let name = match id_tok.kind { TokenKind::Identifier(ref s) => { @@ -494,16 +512,15 @@ impl<'a> Parser<'a> { } } let end_tok = self.consume(TokenKind::Gt)?; - Node::TypeApp(TypeAppNode { - span: Span::new(self.file_id, id_tok.span.start, end_tok.span.end), - base: name, - args, - }) + self.arena.push( + NodeKind::TypeApp(TypeAppNodeArena { base: name, args }), + Span::new(self.file_id, id_tok.span.start, end_tok.span.end), + ) } else { - Node::TypeName(TypeNameNode { - span: id_tok.span, - name, - }) + self.arena.push( + NodeKind::TypeName(TypeNameNodeArena { name }), + id_tok.span, + ) }; if self.peek().kind == TokenKind::OpenBracket { @@ -518,35 +535,45 @@ impl<'a> Parser<'a> { self.advance(); v } - _ => return Err(self.error_with_code("integer or bounded literal for array size", Some("E_PARSE_EXPECTED_TOKEN"))), + _ => { + return Err(self.error_with_code( + "integer or bounded literal for array size", + Some("E_PARSE_EXPECTED_TOKEN"), + )) + } }; let end_tok = self.consume(TokenKind::CloseBracket)?; - let span = Span::new(self.file_id, node.span().start, end_tok.span.end); - - // If it's array[N], we want to represent it cleanly. - // Currently TypeAppNode { base: name, args } was created. - // If base was "array", it already has T in args. - // We can just add N to args. - match &mut node { - Node::TypeApp(ta) if ta.base == self.builtin_array => { - ta.args.push(Node::IntLit(IntLitNode { span: size_tok.span, value: size as i64 })); - ta.span = span; + let size_node = self.arena.push( + NodeKind::IntLit(IntLitNodeArena { value: size as i64 }), + size_tok.span, + ); + let span = Span::new(self.file_id, self.arena.span(node).start, end_tok.span.end); + + let mut wrapped = true; + let index = node.0 as usize; + if let NodeKind::TypeApp(ta) = &mut self.arena.nodes[index] { + if ta.base == self.builtin_array { + ta.args.push(size_node); + self.arena.spans[index] = span; + wrapped = false; } - _ => { - // Fallback for T[N] if we want to support it, but spec says array[N] - node = Node::TypeApp(TypeAppNode { - span, + } + + if wrapped { + node = self.arena.push( + NodeKind::TypeApp(TypeAppNodeArena { base: self.builtin_array, - args: vec![node, Node::IntLit(IntLitNode { span: size_tok.span, value: size as i64 })], - }); - } + args: vec![node, size_node], + }), + span, + ); } } Ok(node) } - fn parse_block(&mut self) -> Result { + fn parse_block(&mut self) -> Result { let start_span = self.consume(TokenKind::OpenBrace)?.span; let mut stmts = Vec::new(); let mut tail = None; @@ -560,33 +587,31 @@ impl<'a> Parser<'a> { let expr = self.parse_expr(0)?; if self.peek().kind == TokenKind::Semicolon { let semi_span = self.advance().span; - let expr_start = expr.span().start; - stmts.push(Node::ExprStmt(ExprStmtNode { - span: Span::new(self.file_id, expr_start, semi_span.end), - expr: Box::new(expr), - })); + let expr_start = self.arena.span(expr).start; + let span = Span::new(self.file_id, expr_start, semi_span.end); + stmts.push(self.arena.push( + NodeKind::ExprStmt(ExprStmtNodeArena { expr }), + span, + )); } else if self.peek().kind == TokenKind::CloseBrace { - tail = Some(Box::new(expr)); + tail = Some(expr); } else { // Treat as ExprStmt even without semicolon (e.g. for if/when used as statement) - let expr_span = expr.span(); - stmts.push(Node::ExprStmt(ExprStmtNode { - span: expr_span, - expr: Box::new(expr), - })); + let expr_span = self.arena.span(expr); + stmts.push(self.arena.push( + NodeKind::ExprStmt(ExprStmtNodeArena { expr }), + expr_span, + )); } } } let end_span = self.consume(TokenKind::CloseBrace)?.span; - Ok(Node::Block(BlockNode { - span: Span::new(self.file_id, start_span.start, end_span.end), - stmts, - tail, - })) + let span = Span::new(self.file_id, start_span.start, end_span.end); + Ok(self.arena.push(NodeKind::Block(BlockNodeArena { stmts, tail }), span)) } - fn parse_let_stmt(&mut self) -> Result { + fn parse_let_stmt(&mut self) -> Result { let start_span = self.consume(TokenKind::Let)?.span; let is_mut = if self.peek().kind == TokenKind::Mut { self.advance(); @@ -597,76 +622,89 @@ impl<'a> Parser<'a> { let name = self.expect_identifier()?; let ty = if self.peek().kind == TokenKind::Colon { self.advance(); - Some(Box::new(self.parse_type_ref()?)) + Some(self.parse_type_ref()?) } else { None }; self.consume(TokenKind::Assign)?; let init = self.parse_expr(0)?; let end_span = self.consume(TokenKind::Semicolon)?.span; - - Ok(Node::LetStmt(LetStmtNode { - span: Span::new(self.file_id, start_span.start, end_span.end), - name, - is_mut, - ty, - init: Box::new(init), - })) + + let span = Span::new(self.file_id, start_span.start, end_span.end); + Ok(self.arena.push( + NodeKind::LetStmt(LetStmtNodeArena { + name, + is_mut, + ty, + init, + }), + span, + )) } - fn parse_return_stmt(&mut self) -> Result { + fn parse_return_stmt(&mut self) -> Result { let start_span = self.consume(TokenKind::Return)?.span; let mut expr = None; if self.peek().kind != TokenKind::Semicolon { - expr = Some(Box::new(self.parse_expr(0)?)); + expr = Some(self.parse_expr(0)?); } let end_span = self.consume(TokenKind::Semicolon)?.span; - Ok(Node::ReturnStmt(ReturnStmtNode { - span: Span::new(self.file_id, start_span.start, end_span.end), - expr, - })) + let span = Span::new(self.file_id, start_span.start, end_span.end); + Ok(self.arena.push(NodeKind::ReturnStmt(ReturnStmtNodeArena { expr }), span)) } - fn parse_alloc(&mut self) -> Result { + fn parse_alloc(&mut self) -> Result { let start_span = self.consume(TokenKind::Alloc)?.span; let ty = self.parse_type_ref()?; - Ok(Node::Alloc(AllocNode { - span: Span::new(self.file_id, start_span.start, ty.span().end), - ty: Box::new(ty), - })) + let span = Span::new(self.file_id, start_span.start, self.arena.span(ty).end); + Ok(self.arena.push(NodeKind::Alloc(AllocNodeArena { ty }), span)) } - fn parse_mutate_borrow_peek(&mut self, kind: TokenKind) -> Result { + fn parse_mutate_borrow_peek(&mut self, kind: TokenKind) -> Result { let start_span = self.consume(kind.clone())?.span; let target_expr = self.parse_expr(0)?; - let (target, binding) = match target_expr { - Node::Cast(cast) => { - match *cast.ty { - Node::Ident(id) => (*cast.expr, id.name), - Node::TypeName(tn) => (*cast.expr, tn.name), - _ => return Err(self.error_with_code("Expected binding name after 'as'", Some("E_PARSE_EXPECTED_TOKEN"))), - } - } - _ => { - self.consume(TokenKind::As)?; - let binding = self.expect_identifier()?; - (target_expr, binding) + let (target, binding) = if let NodeKind::Cast(cast) = self.arena.kind(target_expr) { + let binding = match self.arena.kind(cast.ty) { + NodeKind::Ident(id) => Some(id.name), + NodeKind::TypeName(tn) => Some(tn.name), + _ => None, + }; + if let Some(binding) = binding { + (cast.expr, binding) + } else { + return Err(self.error_with_code( + "Expected binding name after 'as'", + Some("E_PARSE_EXPECTED_TOKEN"), + )); } + } else { + self.consume(TokenKind::As)?; + let binding = self.expect_identifier()?; + (target_expr, binding) }; let body = self.parse_block()?; - let span = Span::new(self.file_id, start_span.start, body.span().end); + let span = Span::new(self.file_id, start_span.start, self.arena.span(body).end); match kind { - TokenKind::Mutate => Ok(Node::Mutate(MutateNode { span, target: Box::new(target), binding, body: Box::new(body) })), - TokenKind::Borrow => Ok(Node::Borrow(BorrowNode { span, target: Box::new(target), binding, body: Box::new(body) })), - TokenKind::Peek => Ok(Node::Peek(PeekNode { span, target: Box::new(target), binding, body: Box::new(body) })), + TokenKind::Mutate => Ok(self.arena.push( + NodeKind::Mutate(MutateNodeArena { target, binding, body }), + span, + )), + TokenKind::Borrow => Ok(self.arena.push( + NodeKind::Borrow(BorrowNodeArena { target, binding, body }), + span, + )), + TokenKind::Peek => Ok(self.arena.push( + NodeKind::Peek(PeekNodeArena { target, binding, body }), + span, + )), _ => unreachable!(), } } - fn parse_expr(&mut self, min_precedence: u8) -> Result { + fn parse_expr(&mut self, min_precedence: u8) -> Result { let mut left = self.parse_primary()?; loop { @@ -677,41 +715,58 @@ impl<'a> Parser<'a> { self.advance(); let right = self.parse_expr(precedence + 1)?; - let span = Span::new(self.file_id, left.span().start, right.span().end); - left = Node::Binary(BinaryNode { + let span = Span::new( + self.file_id, + self.arena.span(left).start, + self.arena.span(right).end, + ); + left = self.arena.push( + NodeKind::Binary(BinaryNodeArena { op, left, right }), span, - op, - left: Box::new(left), - right: Box::new(right), - }); + ); } Ok(left) } - fn parse_primary(&mut self) -> Result { + fn parse_primary(&mut self) -> Result { let tok = self.peek().clone(); match tok.kind { TokenKind::IntLit(v) => { self.advance(); - Ok(Node::IntLit(IntLitNode { span: tok.span, value: v })) + Ok(self.arena.push( + NodeKind::IntLit(IntLitNodeArena { value: v }), + tok.span, + )) } TokenKind::FloatLit(v) => { self.advance(); - Ok(Node::FloatLit(FloatLitNode { span: tok.span, value: v })) + Ok(self.arena.push( + NodeKind::FloatLit(FloatLitNodeArena { value: v }), + tok.span, + )) } TokenKind::BoundedLit(v) => { self.advance(); - Ok(Node::BoundedLit(BoundedLitNode { span: tok.span, value: v })) + Ok(self.arena.push( + NodeKind::BoundedLit(BoundedLitNodeArena { value: v }), + tok.span, + )) } TokenKind::StringLit(s) => { self.advance(); - Ok(Node::StringLit(StringLitNode { span: tok.span, value: s })) + Ok(self.arena.push( + NodeKind::StringLit(StringLitNodeArena { value: s }), + tok.span, + )) } TokenKind::Identifier(name) => { self.advance(); let name = self.interner.intern(&name); - let mut node = Node::Ident(IdentNode { span: tok.span, name }); + let mut node = self.arena.push( + NodeKind::Ident(IdentNodeArena { name }), + tok.span, + ); loop { if self.peek().kind == TokenKind::OpenParen { node = self.parse_call(node)?; @@ -734,7 +789,10 @@ impl<'a> Parser<'a> { _ => unreachable!(), }; self.advance(); - let mut node = Node::Ident(IdentNode { span: tok.span, name }); + let mut node = self.arena.push( + NodeKind::Ident(IdentNodeArena { name }), + tok.span, + ); loop { if self.peek().kind == TokenKind::OpenParen { node = self.parse_call(node)?; @@ -769,11 +827,11 @@ impl<'a> Parser<'a> { _ => unreachable!(), }; let expr = self.parse_expr(11)?; - Ok(Node::Unary(UnaryNode { - span: Span::new(self.file_id, tok.span.start, expr.span().end), - op, - expr: Box::new(expr), - })) + let span = Span::new(self.file_id, tok.span.start, self.arena.span(expr).end); + Ok(self.arena.push( + NodeKind::Unary(UnaryNodeArena { op, expr }), + span, + )) } TokenKind::Invalid(msg) => { let code = if msg.contains("Unterminated string") { @@ -787,17 +845,21 @@ impl<'a> Parser<'a> { } } - fn parse_member_access(&mut self, object: Node) -> Result { + fn parse_member_access(&mut self, object: NodeId) -> Result { self.consume(TokenKind::Dot)?; let member = self.expect_identifier()?; - Ok(Node::MemberAccess(MemberAccessNode { - span: Span::new(self.file_id, object.span().start, self.tokens[self.pos-1].span.end), - object: Box::new(object), - member, - })) + let span = Span::new( + self.file_id, + self.arena.span(object).start, + self.tokens[self.pos - 1].span.end, + ); + Ok(self.arena.push( + NodeKind::MemberAccess(MemberAccessNodeArena { object, member }), + span, + )) } - fn parse_call(&mut self, callee: Node) -> Result { + fn parse_call(&mut self, callee: NodeId) -> Result { self.consume(TokenKind::OpenParen)?; let mut args = Vec::new(); while self.peek().kind != TokenKind::CloseParen && self.peek().kind != TokenKind::Eof { @@ -809,24 +871,21 @@ impl<'a> Parser<'a> { } } let end_span = self.consume(TokenKind::CloseParen)?.span; - Ok(Node::Call(CallNode { - span: Span::new(self.file_id, callee.span().start, end_span.end), - callee: Box::new(callee), - args, - })) + let span = Span::new(self.file_id, self.arena.span(callee).start, end_span.end); + Ok(self.arena.push( + NodeKind::Call(CallNodeArena { callee, args }), + span, + )) } - fn parse_cast(&mut self, expr: Node) -> Result { + fn parse_cast(&mut self, expr: NodeId) -> Result { self.consume(TokenKind::As)?; let ty = self.parse_type_ref()?; - Ok(Node::Cast(CastNode { - span: Span::new(self.file_id, expr.span().start, ty.span().end), - expr: Box::new(expr), - ty: Box::new(ty), - })) + let span = Span::new(self.file_id, self.arena.span(expr).start, self.arena.span(ty).end); + Ok(self.arena.push(NodeKind::Cast(CastNodeArena { expr, ty }), span)) } - fn parse_if_expr(&mut self) -> Result { + fn parse_if_expr(&mut self) -> Result { let start_span = self.consume(TokenKind::If)?.span; let cond = self.parse_expr(0)?; let then_block = self.parse_block()?; @@ -834,23 +893,28 @@ impl<'a> Parser<'a> { if self.peek().kind == TokenKind::Else { self.advance(); if self.peek().kind == TokenKind::If { - else_block = Some(Box::new(self.parse_if_expr()?)); + else_block = Some(self.parse_if_expr()?); } else { - else_block = Some(Box::new(self.parse_block()?)); + else_block = Some(self.parse_block()?); } } - - let end_span = else_block.as_ref().map(|b| b.span().end).unwrap_or(then_block.span().end); - Ok(Node::IfExpr(IfExprNode { - span: Span::new(self.file_id, start_span.start, end_span), - cond: Box::new(cond), - then_block: Box::new(then_block), - else_block, - })) + let end_span = else_block + .map(|b| self.arena.span(b).end) + .unwrap_or(self.arena.span(then_block).end); + + let span = Span::new(self.file_id, start_span.start, end_span); + Ok(self.arena.push( + NodeKind::IfExpr(IfExprNodeArena { + cond, + then_block, + else_block, + }), + span, + )) } - fn parse_when_expr(&mut self) -> Result { + fn parse_when_expr(&mut self) -> Result { let start_span = self.consume(TokenKind::When)?.span; self.consume(TokenKind::OpenBrace)?; let mut arms = Vec::new(); @@ -859,20 +923,18 @@ impl<'a> Parser<'a> { let cond = self.parse_expr(0)?; self.consume(TokenKind::Arrow)?; let body = self.parse_block()?; - arms.push(Node::WhenArm(WhenArmNode { - span: Span::new(self.file_id, arm_start, body.span().end), - cond: Box::new(cond), - body: Box::new(body), - })); + let arm_span = Span::new(self.file_id, arm_start, self.arena.span(body).end); + arms.push(self.arena.push( + NodeKind::WhenArm(WhenArmNodeArena { cond, body }), + arm_span, + )); if self.peek().kind == TokenKind::Comma { self.advance(); } } let end_span = self.consume(TokenKind::CloseBrace)?.span; - Ok(Node::WhenExpr(WhenExprNode { - span: Span::new(self.file_id, start_span.start, end_span.end), - arms, - })) + let span = Span::new(self.file_id, start_span.start, end_span.end); + Ok(self.arena.push(NodeKind::WhenExpr(WhenExprNodeArena { arms }), span)) } fn get_binary_precedence(&self) -> Option<(String, u8)> { @@ -987,7 +1049,7 @@ impl<'a> Parser<'a> { DiagnosticBundle::from(diag) } - fn parse_constructor_list(&mut self) -> Result, DiagnosticBundle> { + fn parse_constructor_list(&mut self) -> Result, DiagnosticBundle> { self.consume(TokenKind::OpenBracket)?; let mut constructors = Vec::new(); while self.peek().kind != TokenKind::CloseBracket && self.peek().kind != TokenKind::Eof { @@ -1014,59 +1076,22 @@ impl<'a> Parser<'a> { let body = self.parse_block()?; - constructors.push(ConstructorDeclNode { - span: Span::new(self.file_id, start_span.start, body.span().end), - params, - initializers, - name, - body: Box::new(body), - }); + let span = Span::new(self.file_id, start_span.start, self.arena.span(body).end); + constructors.push(self.arena.push( + NodeKind::ConstructorDecl(ConstructorDeclNodeArena { + params, + initializers, + name, + body, + }), + span, + )); } self.consume(TokenKind::CloseBracket)?; Ok(constructors) } } -impl Node { - pub fn span(&self) -> Span { - match self { - Node::File(n) => n.span, - Node::Import(n) => n.span, - Node::ImportSpec(n) => n.span, - Node::ServiceDecl(n) => n.span, - Node::ServiceFnSig(n) => n.span, - Node::FnDecl(n) => n.span, - Node::TypeDecl(n) => n.span, - Node::TypeBody(n) => n.span, - Node::Block(n) => n.span, - Node::LetStmt(n) => n.span, - Node::ExprStmt(n) => n.span, - Node::ReturnStmt(n) => n.span, - Node::IntLit(n) => n.span, - Node::FloatLit(n) => n.span, - Node::BoundedLit(n) => n.span, - Node::StringLit(n) => n.span, - Node::Ident(n) => n.span, - Node::Call(n) => n.span, - Node::Unary(n) => n.span, - Node::Binary(n) => n.span, - Node::Cast(n) => n.span, - Node::IfExpr(n) => n.span, - Node::WhenExpr(n) => n.span, - Node::WhenArm(n) => n.span, - Node::TypeName(n) => n.span, - Node::TypeApp(n) => n.span, - Node::ConstructorDecl(n) => n.span, - Node::ConstantDecl(n) => n.span, - Node::Alloc(n) => n.span, - Node::Mutate(n) => n.span, - Node::Borrow(n) => n.span, - Node::Peek(n) => n.span, - Node::MemberAccess(n) => n.span, - } - } -} - #[cfg(test)] mod tests { use super::*; @@ -1076,9 +1101,13 @@ mod tests { fn test_parse_empty_file() { let mut interner = NameInterner::new(); let mut parser = Parser::new("", 0, &mut interner); - let result = parser.parse_file().unwrap(); - assert_eq!(result.imports.len(), 0); - assert_eq!(result.decls.len(), 0); + let parsed = parser.parse_file().unwrap(); + let file = match parsed.arena.kind(parsed.root) { + NodeKind::File(file) => file, + _ => panic!("Expected File"), + }; + assert_eq!(file.imports.len(), 0); + assert_eq!(file.decls.len(), 0); } #[test] @@ -1089,16 +1118,24 @@ import math from "./math.pbs"; "#; let mut interner = NameInterner::new(); let mut parser = Parser::new(source, 0, &mut interner); - let result = parser.parse_file().unwrap(); - assert_eq!(result.imports.len(), 2); + let parsed = parser.parse_file().unwrap(); + let file = match parsed.arena.kind(parsed.root) { + NodeKind::File(file) => file, + _ => panic!("Expected File"), + }; + assert_eq!(file.imports.len(), 2); - if let Node::Import(ref imp) = result.imports[0] { - assert_eq!(imp.from, "std"); - if let Node::ImportSpec(ref spec) = *imp.spec { - let path: Vec<&str> = spec.path.iter().map(|id| interner.resolve(*id)).collect(); - assert_eq!(path, vec!["std", "io"]); - } else { panic!("Expected ImportSpec"); } - } else { panic!("Expected Import"); } + let imp = match parsed.arena.kind(file.imports[0]) { + NodeKind::Import(imp) => imp, + _ => panic!("Expected Import"), + }; + assert_eq!(imp.from, "std"); + let spec = match parsed.arena.kind(imp.spec) { + NodeKind::ImportSpec(spec) => spec, + _ => panic!("Expected ImportSpec"), + }; + let path: Vec<&str> = spec.path.iter().map(|id| interner.resolve(*id)).collect(); + assert_eq!(path, vec!["std", "io"]); } #[test] @@ -1110,15 +1147,21 @@ fn add(a: int, b: int): int { "#; let mut interner = NameInterner::new(); let mut parser = Parser::new(source, 0, &mut interner); - let result = parser.parse_file().unwrap(); - assert_eq!(result.decls.len(), 1); + let parsed = parser.parse_file().unwrap(); + let file = match parsed.arena.kind(parsed.root) { + NodeKind::File(file) => file, + _ => panic!("Expected File"), + }; + assert_eq!(file.decls.len(), 1); - if let Node::FnDecl(ref f) = result.decls[0] { + if let NodeKind::FnDecl(f) = parsed.arena.kind(file.decls[0]) { assert_eq!(interner.resolve(f.name), "add"); assert_eq!(f.params.len(), 2); assert_eq!(interner.resolve(f.params[0].name), "a"); assert_eq!(interner.resolve(f.params[1].name), "b"); - } else { panic!("Expected FnDecl"); } + } else { + panic!("Expected FnDecl"); + } } #[test] @@ -1131,14 +1174,20 @@ pub declare struct Point { "#; let mut interner = NameInterner::new(); let mut parser = Parser::new(source, 0, &mut interner); - let result = parser.parse_file().unwrap(); - assert_eq!(result.decls.len(), 1); + let parsed = parser.parse_file().unwrap(); + let file = match parsed.arena.kind(parsed.root) { + NodeKind::File(file) => file, + _ => panic!("Expected File"), + }; + assert_eq!(file.decls.len(), 1); - if let Node::TypeDecl(ref t) = result.decls[0] { + if let NodeKind::TypeDecl(t) = parsed.arena.kind(file.decls[0]) { assert_eq!(interner.resolve(t.name), "Point"); assert_eq!(t.type_kind, "struct"); assert_eq!(t.vis, Some("pub".to_string())); - } else { panic!("Expected TypeDecl"); } + } else { + panic!("Expected TypeDecl"); + } } #[test] @@ -1151,13 +1200,19 @@ pub service Audio { "#; let mut interner = NameInterner::new(); let mut parser = Parser::new(source, 0, &mut interner); - let result = parser.parse_file().unwrap(); - assert_eq!(result.decls.len(), 1); + let parsed = parser.parse_file().unwrap(); + let file = match parsed.arena.kind(parsed.root) { + NodeKind::File(file) => file, + _ => panic!("Expected File"), + }; + assert_eq!(file.decls.len(), 1); - if let Node::ServiceDecl(ref s) = result.decls[0] { + if let NodeKind::ServiceDecl(s) = parsed.arena.kind(file.decls[0]) { assert_eq!(interner.resolve(s.name), "Audio"); assert_eq!(s.members.len(), 2); - } else { panic!("Expected ServiceDecl"); } + } else { + panic!("Expected ServiceDecl"); + } } #[test] @@ -1171,8 +1226,12 @@ fn main() { "#; let mut interner = NameInterner::new(); let mut parser = Parser::new(source, 0, &mut interner); - let result = parser.parse_file().unwrap(); - assert_eq!(result.decls.len(), 1); + let parsed = parser.parse_file().unwrap(); + let file = match parsed.arena.kind(parsed.root) { + NodeKind::File(file) => file, + _ => panic!("Expected File"), + }; + assert_eq!(file.decls.len(), 1); } #[test] @@ -1193,8 +1252,12 @@ fn main(x: int) { "#; let mut interner = NameInterner::new(); let mut parser = Parser::new(source, 0, &mut interner); - let result = parser.parse_file().unwrap(); - assert_eq!(result.decls.len(), 1); + let parsed = parser.parse_file().unwrap(); + let file = match parsed.arena.kind(parsed.root) { + NodeKind::File(file) => file, + _ => panic!("Expected File"), + }; + assert_eq!(file.decls.len(), 1); } #[test] @@ -1218,8 +1281,12 @@ fn good() {} let source = "mod fn test() {}"; let mut interner = NameInterner::new(); let mut parser = Parser::new(source, 0, &mut interner); - let result = parser.parse_file().expect("mod fn should be allowed"); - if let Node::FnDecl(fn_decl) = &result.decls[0] { + let parsed = parser.parse_file().expect("mod fn should be allowed"); + let file = match parsed.arena.kind(parsed.root) { + NodeKind::File(file) => file, + _ => panic!("Expected File"), + }; + if let NodeKind::FnDecl(fn_decl) = parsed.arena.kind(file.decls[0]) { assert_eq!(fn_decl.vis, Some("mod".to_string())); } else { panic!("Expected FnDecl"); @@ -1231,8 +1298,12 @@ fn good() {} let source = "pub fn test() {}"; let mut interner = NameInterner::new(); let mut parser = Parser::new(source, 0, &mut interner); - let result = parser.parse_file().expect("pub fn should be allowed in parser"); - if let Node::FnDecl(fn_decl) = &result.decls[0] { + let parsed = parser.parse_file().expect("pub fn should be allowed in parser"); + let file = match parsed.arena.kind(parsed.root) { + NodeKind::File(file) => file, + _ => panic!("Expected File"), + }; + if let NodeKind::FnDecl(fn_decl) = parsed.arena.kind(file.decls[0]) { assert_eq!(fn_decl.vis, Some("pub".to_string())); } else { panic!("Expected FnDecl"); @@ -1248,12 +1319,15 @@ fn main() { "#; let mut interner = NameInterner::new(); let mut parser = Parser::new(source, 0, &mut interner); - let result = parser.parse_file().unwrap(); - let json = serde_json::to_string_pretty(&Node::File(result.clone())).unwrap(); - + let parsed = parser.parse_file().unwrap(); + let file = match parsed.arena.kind(parsed.root) { + NodeKind::File(file) => file, + _ => panic!("Expected File"), + }; + let json = serde_json::to_string_pretty(parsed.arena.kind(parsed.root)).unwrap(); + assert!(json.contains("\"kind\": \"File\"")); - assert!(json.contains("\"kind\": \"FnDecl\"")); - if let Node::FnDecl(fn_decl) = &result.decls[0] { + if let NodeKind::FnDecl(fn_decl) = parsed.arena.kind(file.decls[0]) { assert_eq!(interner.resolve(fn_decl.name), "main"); } else { panic!("Expected FnDecl"); diff --git a/crates/prometeu-compiler/src/frontends/pbs/resolver.rs b/crates/prometeu-compiler/src/frontends/pbs/resolver.rs index e1596c39..64f8ad96 100644 --- a/crates/prometeu-compiler/src/frontends/pbs/resolver.rs +++ b/crates/prometeu-compiler/src/frontends/pbs/resolver.rs @@ -34,17 +34,27 @@ impl<'a> Resolver<'a> { } } - pub fn resolve(&mut self, file: &FileNode) -> Result<(), DiagnosticBundle> { + pub fn resolve(&mut self, arena: &AstArena, root: NodeId) -> Result<(), DiagnosticBundle> { + let file = match arena.kind(root) { + NodeKind::File(file) => file, + _ => { + return Err(DiagnosticBundle::error( + "Expected File node as root".to_string(), + None, + )) + } + }; + // Step 1: Process imports to populate imported_symbols for imp in &file.imports { - if let Node::Import(imp_node) = imp { - self.resolve_import(imp_node); + if let NodeKind::Import(imp_node) = arena.kind(*imp) { + self.resolve_import(arena, *imp, imp_node); } } // Step 2: Resolve all top-level declarations for decl in &file.decls { - self.resolve_node(decl); + self.resolve_node(arena, *decl); } if !self.diagnostics.is_empty() { @@ -56,237 +66,270 @@ impl<'a> Resolver<'a> { Ok(()) } - fn resolve_import(&mut self, imp: &ImportNode) { + fn resolve_import(&mut self, arena: &AstArena, imp_id: NodeId, imp: &ImportNodeArena) { let provider = self.module_provider; if let Some(target_symbols) = provider.get_module_symbols(&imp.from) { - if let Node::ImportSpec(spec) = &*imp.spec { - for name in &spec.path { - // Try to find in Type namespace - if let Some(sym) = target_symbols.type_symbols.get(*name) { - if sym.visibility == Visibility::Pub { - let mut sym = sym.clone(); - sym.origin = Some(imp.from.clone()); - if let Err(_) = self.imported_symbols.type_symbols.insert(sym) { - self.error_duplicate_import(*name, imp.span); - } - } else { - self.error_visibility(sym, imp.span); - } - } - // Try to find in Value namespace - else if let Some(sym) = target_symbols.value_symbols.get(*name) { - if sym.visibility == Visibility::Pub { - let mut sym = sym.clone(); - sym.origin = Some(imp.from.clone()); - if let Err(_) = self.imported_symbols.value_symbols.insert(sym) { - self.error_duplicate_import(*name, imp.span); - } - } else { - self.error_visibility(sym, imp.span); + let spec = match arena.kind(imp.spec) { + NodeKind::ImportSpec(spec) => spec, + _ => { + self.diagnostics.push(Diagnostic { + level: DiagnosticLevel::Error, + code: Some("E_RESOLVE_INVALID_IMPORT".to_string()), + message: "Invalid import spec".to_string(), + span: Some(arena.span(imp_id)), + }); + return; + } + }; + + for name in &spec.path { + // Try to find in Type namespace + if let Some(sym) = target_symbols.type_symbols.get(*name) { + if sym.visibility == Visibility::Pub { + let mut sym = sym.clone(); + sym.origin = Some(imp.from.clone()); + if let Err(_) = self.imported_symbols.type_symbols.insert(sym) { + self.error_duplicate_import(*name, arena.span(imp_id)); } } else { - self.error_undefined(*name, imp.span); + self.error_visibility(sym, arena.span(imp_id)); } } + // Try to find in Value namespace + else if let Some(sym) = target_symbols.value_symbols.get(*name) { + if sym.visibility == Visibility::Pub { + let mut sym = sym.clone(); + sym.origin = Some(imp.from.clone()); + if let Err(_) = self.imported_symbols.value_symbols.insert(sym) { + self.error_duplicate_import(*name, arena.span(imp_id)); + } + } else { + self.error_visibility(sym, arena.span(imp_id)); + } + } else { + self.error_undefined(*name, arena.span(imp_id)); + } } } else { self.diagnostics.push(Diagnostic { level: DiagnosticLevel::Error, code: Some("E_RESOLVE_INVALID_IMPORT".to_string()), message: format!("Module not found: {}", imp.from), - span: Some(imp.span), + span: Some(arena.span(imp_id)), }); } } - fn resolve_node(&mut self, node: &Node) { - match node { - Node::FnDecl(n) => self.resolve_fn_decl(n), - Node::ServiceDecl(n) => self.resolve_service_decl(n), - Node::TypeDecl(n) => self.resolve_type_decl(n), - Node::Block(n) => self.resolve_block(n), - Node::LetStmt(n) => self.resolve_let_stmt(n), - Node::ExprStmt(n) => self.resolve_node(&n.expr), - Node::ReturnStmt(n) => { - if let Some(expr) = &n.expr { - self.resolve_node(expr); + fn resolve_node(&mut self, arena: &AstArena, node: NodeId) { + match arena.kind(node) { + NodeKind::FnDecl(n) => self.resolve_fn_decl(arena, node, n), + NodeKind::ServiceDecl(n) => self.resolve_service_decl(arena, node, n), + NodeKind::TypeDecl(n) => self.resolve_type_decl(arena, node, n), + NodeKind::Block(n) => self.resolve_block(arena, n), + NodeKind::LetStmt(n) => self.resolve_let_stmt(arena, node, n), + NodeKind::ExprStmt(n) => self.resolve_node(arena, n.expr), + NodeKind::ReturnStmt(n) => { + if let Some(expr) = n.expr { + self.resolve_node(arena, expr); } } - Node::Call(n) => { - self.resolve_node(&n.callee); + NodeKind::Call(n) => { + if let NodeKind::Ident(id) = arena.kind(n.callee) { + if !self.is_builtin(id.name, Namespace::Value) { + let value_sym = self.lookup_identifier(id.name, Namespace::Value); + if value_sym.is_none() && self.lookup_identifier(id.name, Namespace::Type).is_none() { + // TODO: Resolver for v0 allows unresolved call targets (e.g. constructors via prelude) + } + } + } else { + self.resolve_node(arena, n.callee); + } for arg in &n.args { - self.resolve_node(arg); + self.resolve_node(arena, *arg); } } - Node::Unary(n) => self.resolve_node(&n.expr), - Node::Binary(n) => { - self.resolve_node(&n.left); - self.resolve_node(&n.right); + NodeKind::Unary(n) => self.resolve_node(arena, n.expr), + NodeKind::Binary(n) => { + self.resolve_node(arena, n.left); + self.resolve_node(arena, n.right); } - Node::Cast(n) => { - self.resolve_node(&n.expr); - self.resolve_type_ref(&n.ty); + NodeKind::Cast(n) => { + self.resolve_node(arena, n.expr); + self.resolve_type_ref(arena, n.ty); } - Node::IfExpr(n) => { - self.resolve_node(&n.cond); - self.resolve_node(&n.then_block); - if let Some(else_block) = &n.else_block { - self.resolve_node(else_block); + NodeKind::IfExpr(n) => { + self.resolve_node(arena, n.cond); + self.resolve_node(arena, n.then_block); + if let Some(else_block) = n.else_block { + self.resolve_node(arena, else_block); } } - Node::WhenExpr(n) => { + NodeKind::WhenExpr(n) => { for arm in &n.arms { - if let Node::WhenArm(arm_node) = arm { - self.resolve_node(&arm_node.cond); - self.resolve_node(&arm_node.body); + if let NodeKind::WhenArm(arm_node) = arena.kind(*arm) { + self.resolve_node(arena, arm_node.cond); + self.resolve_node(arena, arm_node.body); } } } - Node::Ident(n) => { - self.resolve_identifier(n.name, n.span, Namespace::Value); + NodeKind::Ident(n) => { + self.resolve_identifier(n.name, arena.span(node), Namespace::Value); } - Node::TypeName(n) => { - self.resolve_identifier(n.name, n.span, Namespace::Type); + NodeKind::TypeName(n) => { + self.resolve_identifier(n.name, arena.span(node), Namespace::Type); } - Node::TypeApp(n) => { - self.resolve_identifier(n.base, n.span, Namespace::Type); + NodeKind::TypeApp(n) => { + self.resolve_identifier(n.base, arena.span(node), Namespace::Type); for arg in &n.args { - self.resolve_type_ref(arg); + self.resolve_type_ref(arena, *arg); } } - Node::ConstructorDecl(n) => self.resolve_constructor_decl(n), - Node::ConstantDecl(n) => self.resolve_node(&n.value), - Node::Alloc(n) => { - self.resolve_type_ref(&n.ty); + NodeKind::ConstructorDecl(n) => self.resolve_constructor_decl(arena, node, n), + NodeKind::ConstantDecl(n) => self.resolve_node(arena, n.value), + NodeKind::Alloc(n) => { + self.resolve_type_ref(arena, n.ty); } - Node::Mutate(n) => { - self.resolve_node(&n.target); + NodeKind::Mutate(n) => { + self.resolve_node(arena, n.target); self.enter_scope(); - self.define_local(n.binding, n.span, SymbolKind::Local); - self.resolve_node(&n.body); + self.define_local(n.binding, arena.span(node), SymbolKind::Local); + self.resolve_node(arena, n.body); self.exit_scope(); } - Node::Borrow(n) => { - self.resolve_node(&n.target); + NodeKind::Borrow(n) => { + self.resolve_node(arena, n.target); self.enter_scope(); - self.define_local(n.binding, n.span, SymbolKind::Local); - self.resolve_node(&n.body); + self.define_local(n.binding, arena.span(node), SymbolKind::Local); + self.resolve_node(arena, n.body); self.exit_scope(); } - Node::Peek(n) => { - self.resolve_node(&n.target); + NodeKind::Peek(n) => { + self.resolve_node(arena, n.target); self.enter_scope(); - self.define_local(n.binding, n.span, SymbolKind::Local); - self.resolve_node(&n.body); + self.define_local(n.binding, arena.span(node), SymbolKind::Local); + self.resolve_node(arena, n.body); self.exit_scope(); } - Node::MemberAccess(n) => { - if let Node::Ident(id) = &*n.object { + NodeKind::MemberAccess(n) => match arena.kind(n.object) { + NodeKind::Ident(id) => { + let ident_span = arena.span(n.object); if !self.is_builtin(id.name, Namespace::Type) { if self.lookup_identifier(id.name, Namespace::Value).is_none() { // If not found in Value namespace, try Type namespace (for Contracts/Services) if self.lookup_identifier(id.name, Namespace::Type).is_none() { // Still not found, use resolve_identifier to report error in Value namespace - self.resolve_identifier(id.name, id.span, Namespace::Value); + self.resolve_identifier(id.name, ident_span, Namespace::Value); } } } - } else { - self.resolve_node(&n.object); } - } + _ => { + self.resolve_node(arena, n.object); + } + }, _ => {} } } - fn resolve_fn_decl(&mut self, n: &FnDeclNode) { + fn resolve_fn_decl(&mut self, arena: &AstArena, id: NodeId, n: &FnDeclNodeArena) { self.enter_scope(); for param in &n.params { - self.resolve_type_ref(¶m.ty); + self.resolve_type_ref(arena, param.ty); self.define_local(param.name, param.span, SymbolKind::Local); } - if let Some(ret) = &n.ret { - self.resolve_type_ref(ret); + if let Some(ret) = n.ret { + self.resolve_type_ref(arena, ret); } - self.resolve_node(&n.body); + self.resolve_node(arena, n.body); self.exit_scope(); } - fn resolve_service_decl(&mut self, n: &ServiceDeclNode) { - if let Some(ext) = &n.extends { - self.resolve_identifier(*ext, n.span, Namespace::Type); + fn resolve_service_decl(&mut self, arena: &AstArena, id: NodeId, n: &ServiceDeclNodeArena) { + if let Some(ext) = n.extends { + self.resolve_identifier(ext, arena.span(id), Namespace::Type); } for member in &n.members { - if let Node::ServiceFnSig(sig) = member { + if let NodeKind::ServiceFnSig(sig) = arena.kind(*member) { for param in &sig.params { - self.resolve_type_ref(¶m.ty); + self.resolve_type_ref(arena, param.ty); } - self.resolve_type_ref(&sig.ret); + self.resolve_type_ref(arena, sig.ret); } } } - fn resolve_type_decl(&mut self, n: &TypeDeclNode) { + fn resolve_type_decl(&mut self, arena: &AstArena, _id: NodeId, n: &TypeDeclNodeArena) { for param in &n.params { - self.resolve_type_ref(¶m.ty); + self.resolve_type_ref(arena, param.ty); } for constructor in &n.constructors { - self.resolve_constructor_decl(constructor); + if let NodeKind::ConstructorDecl(ctor) = arena.kind(*constructor) { + self.resolve_constructor_decl(arena, *constructor, ctor); + } } self.enter_scope(); - for ctor in &n.constructors { - self.define_local(ctor.name, ctor.span, SymbolKind::Local); + for ctor_id in &n.constructors { + if let NodeKind::ConstructorDecl(ctor) = arena.kind(*ctor_id) { + self.define_local(ctor.name, arena.span(*ctor_id), SymbolKind::Local); + } } for constant in &n.constants { - self.resolve_node(&constant.value); + self.resolve_node(arena, *constant); } self.exit_scope(); - if let Some(body_node) = &n.body { - if let Node::TypeBody(body) = &**body_node { + if let Some(body_node) = n.body { + if let NodeKind::TypeBody(body) = arena.kind(body_node) { for member in &body.members { - self.resolve_type_ref(&member.ty); + self.resolve_type_ref(arena, member.ty); } for method in &body.methods { - for param in &method.params { - self.resolve_type_ref(¶m.ty); + if let NodeKind::ServiceFnSig(sig) = arena.kind(*method) { + for param in &sig.params { + self.resolve_type_ref(arena, param.ty); + } + self.resolve_type_ref(arena, sig.ret); } - self.resolve_type_ref(&method.ret); } } } } - fn resolve_constructor_decl(&mut self, n: &ConstructorDeclNode) { + fn resolve_constructor_decl( + &mut self, + arena: &AstArena, + _id: NodeId, + n: &ConstructorDeclNodeArena, + ) { self.enter_scope(); for param in &n.params { - self.resolve_type_ref(¶m.ty); + self.resolve_type_ref(arena, param.ty); self.define_local(param.name, param.span, SymbolKind::Local); } for init in &n.initializers { - self.resolve_node(init); + self.resolve_node(arena, *init); } - self.resolve_node(&n.body); + self.resolve_node(arena, n.body); self.exit_scope(); } - fn resolve_block(&mut self, n: &BlockNode) { + fn resolve_block(&mut self, arena: &AstArena, n: &BlockNodeArena) { self.enter_scope(); for stmt in &n.stmts { - self.resolve_node(stmt); + self.resolve_node(arena, *stmt); } self.exit_scope(); } - fn resolve_let_stmt(&mut self, n: &LetStmtNode) { - if let Some(ty) = &n.ty { - self.resolve_type_ref(ty); + fn resolve_let_stmt(&mut self, arena: &AstArena, id: NodeId, n: &LetStmtNodeArena) { + if let Some(ty) = n.ty { + self.resolve_type_ref(arena, ty); } - self.resolve_node(&n.init); - self.define_local(n.name, n.span, SymbolKind::Local); + self.resolve_node(arena, n.init); + self.define_local(n.name, arena.span(id), SymbolKind::Local); } - fn resolve_type_ref(&mut self, node: &Node) { - self.resolve_node(node); + fn resolve_type_ref(&mut self, arena: &AstArena, node: NodeId) { + self.resolve_node(arena, node); } fn resolve_identifier(&mut self, name: NameId, span: Span, namespace: Namespace) -> Option { @@ -461,13 +504,13 @@ mod tests { use crate::frontends::pbs::*; use std::path::PathBuf; - fn setup_test(source: &str) -> (ast::FileNode, usize, NameInterner) { + fn setup_test(source: &str) -> (ParsedAst, usize, NameInterner) { let mut fm = FileManager::new(); let file_id = fm.add(PathBuf::from("test.pbs"), source.to_string()); let mut interner = NameInterner::new(); let mut parser = parser::Parser::new(source, file_id, &mut interner); - let ast = parser.parse_file().expect("Parsing failed"); - (ast, file_id, interner) + let parsed = parser.parse_file().expect("Parsing failed"); + (parsed, file_id, interner) } #[test] @@ -476,9 +519,9 @@ mod tests { declare struct Foo {} declare struct Foo {} "; - let (ast, _, mut interner) = setup_test(source); + let (parsed, _, mut interner) = setup_test(source); let mut collector = SymbolCollector::new(&interner); - let result = collector.collect(&ast); + let result = collector.collect(&parsed.arena, parsed.root); assert!(result.is_err()); let bundle = result.unwrap_err(); @@ -491,9 +534,9 @@ mod tests { declare struct Foo {} fn Foo() {} "; - let (ast, _, mut interner) = setup_test(source); + let (parsed, _, mut interner) = setup_test(source); let mut collector = SymbolCollector::new(&interner); - let result = collector.collect(&ast); + let result = collector.collect(&parsed.arena, parsed.root); assert!(result.is_err()); let bundle = result.unwrap_err(); @@ -507,9 +550,11 @@ mod tests { let x = y; } "; - let (ast, _, mut interner) = setup_test(source); + let (parsed, _, mut interner) = setup_test(source); let mut collector = SymbolCollector::new(&interner); - let (ts, vs) = collector.collect(&ast).expect("Collection failed"); + let (ts, vs) = collector + .collect(&parsed.arena, parsed.root) + .expect("Collection failed"); let ms = ModuleSymbols { type_symbols: ts, value_symbols: vs }; struct EmptyProvider; @@ -518,7 +563,7 @@ mod tests { } let mut resolver = Resolver::new(&ms, &EmptyProvider, &interner); - let result = resolver.resolve(&ast); + let result = resolver.resolve(&parsed.arena, parsed.root); assert!(result.is_err()); let bundle = result.unwrap_err(); @@ -533,9 +578,11 @@ mod tests { let y = x; } "; - let (ast, _, mut interner) = setup_test(source); + let (parsed, _, mut interner) = setup_test(source); let mut collector = SymbolCollector::new(&interner); - let (ts, vs) = collector.collect(&ast).expect("Collection failed"); + let (ts, vs) = collector + .collect(&parsed.arena, parsed.root) + .expect("Collection failed"); let ms = ModuleSymbols { type_symbols: ts, value_symbols: vs }; struct EmptyProvider; @@ -544,7 +591,7 @@ mod tests { } let mut resolver = Resolver::new(&ms, &EmptyProvider, &interner); - let result = resolver.resolve(&ast); + let result = resolver.resolve(&parsed.arena, parsed.root); assert!(result.is_ok()); } @@ -555,9 +602,11 @@ mod tests { import PrivateType from \"./other.pbs\" fn main() {} "; - let (ast, _, mut interner) = setup_test(source); + let (parsed, _, mut interner) = setup_test(source); let mut collector = SymbolCollector::new(&interner); - let (ts, vs) = collector.collect(&ast).expect("Collection failed"); + let (ts, vs) = collector + .collect(&parsed.arena, parsed.root) + .expect("Collection failed"); let ms = ModuleSymbols { type_symbols: ts, value_symbols: vs }; struct MockProvider { @@ -586,7 +635,7 @@ mod tests { }; let mut resolver = Resolver::new(&ms, &mock_provider, &interner); - let result = resolver.resolve(&ast); + let result = resolver.resolve(&parsed.arena, parsed.root); assert!(result.is_err()); let bundle = result.unwrap_err(); @@ -601,9 +650,11 @@ mod tests { let x: PubType = 10; } "; - let (ast, _, mut interner) = setup_test(source); + let (parsed, _, mut interner) = setup_test(source); let mut collector = SymbolCollector::new(&interner); - let (ts, vs) = collector.collect(&ast).expect("Collection failed"); + let (ts, vs) = collector + .collect(&parsed.arena, parsed.root) + .expect("Collection failed"); let ms = ModuleSymbols { type_symbols: ts, value_symbols: vs }; struct MockProvider { @@ -632,7 +683,7 @@ mod tests { }; let mut resolver = Resolver::new(&ms, &mock_provider, &interner); - let result = resolver.resolve(&ast); + let result = resolver.resolve(&parsed.arena, parsed.root); assert!(result.is_ok()); } @@ -643,9 +694,11 @@ mod tests { import NonExistent from \"./missing.pbs\" fn main() {} "; - let (ast, _, interner) = setup_test(source); + let (parsed, _, interner) = setup_test(source); let mut collector = SymbolCollector::new(&interner); - let (ts, vs) = collector.collect(&ast).expect("Collection failed"); + let (ts, vs) = collector + .collect(&parsed.arena, parsed.root) + .expect("Collection failed"); let ms = ModuleSymbols { type_symbols: ts, value_symbols: vs }; struct EmptyProvider; @@ -654,7 +707,7 @@ mod tests { } let mut resolver = Resolver::new(&ms, &EmptyProvider, &interner); - let result = resolver.resolve(&ast); + let result = resolver.resolve(&parsed.arena, parsed.root); assert!(result.is_err()); let bundle = result.unwrap_err(); diff --git a/crates/prometeu-compiler/src/frontends/pbs/typecheck.rs b/crates/prometeu-compiler/src/frontends/pbs/typecheck.rs index e41bed2d..4f48dd5f 100644 --- a/crates/prometeu-compiler/src/frontends/pbs/typecheck.rs +++ b/crates/prometeu-compiler/src/frontends/pbs/typecheck.rs @@ -46,13 +46,23 @@ impl<'a> TypeChecker<'a> { } } - pub fn check(&mut self, file: &FileNode) -> Result<(), DiagnosticBundle> { + pub fn check(&mut self, arena: &AstArena, root: NodeId) -> Result<(), DiagnosticBundle> { + let file = match arena.kind(root) { + NodeKind::File(file) => file, + _ => { + return Err(DiagnosticBundle::error( + "Expected File node as root".to_string(), + None, + )) + } + }; + // Step 1: Resolve signatures of all top-level declarations - self.resolve_signatures(file); + self.resolve_signatures(arena, file); // Step 2: Check bodies for decl in &file.decls { - self.check_node(decl); + self.check_node(arena, *decl); } if !self.diagnostics.is_empty() { @@ -64,16 +74,16 @@ impl<'a> TypeChecker<'a> { Ok(()) } - fn resolve_signatures(&mut self, file: &FileNode) { + fn resolve_signatures(&mut self, arena: &AstArena, file: &FileNodeArena) { for decl in &file.decls { - match decl { - Node::FnDecl(n) => { + match arena.kind(*decl) { + NodeKind::FnDecl(n) => { let mut params = Vec::new(); for param in &n.params { - params.push(self.resolve_type_node(¶m.ty)); + params.push(self.resolve_type_node(arena, param.ty)); } - let return_type = if let Some(ret) = &n.ret { - self.resolve_type_node(ret) + let return_type = if let Some(ret) = n.ret { + self.resolve_type_node(arena, ret) } else { PbsType::Void }; @@ -85,13 +95,13 @@ impl<'a> TypeChecker<'a> { sym.ty = Some(ty); } } - Node::ServiceDecl(n) => { + NodeKind::ServiceDecl(n) => { // For service, the symbol's type is just Service(name) if let Some(sym) = self.module_symbols.type_symbols.symbols.get_mut(&n.name) { sym.ty = Some(PbsType::Service(self.interner.resolve(n.name).to_string())); } } - Node::TypeDecl(n) => { + NodeKind::TypeDecl(n) => { let type_name = self.interner.resolve(n.name).to_string(); let ty = match n.type_kind.as_str() { "struct" => PbsType::Struct(type_name.clone()), @@ -109,49 +119,48 @@ impl<'a> TypeChecker<'a> { // Default constructor: TypeName(...) if n.type_kind == "struct" { let mut params = Vec::new(); - let mut initializers = Vec::new(); for p in &n.params { - let p_ty = self.resolve_type_node(&p.ty); + let p_ty = self.resolve_type_node(arena, p.ty); params.push(p_ty); - initializers.push(Node::Ident(IdentNode { - span: p.span, - name: p.name.clone(), - })); } let default_ctor_ty = PbsType::Function { - params: params.clone(), + params, return_type: Box::new(ty.clone()), }; ctors.insert(type_name.clone(), default_ctor_ty); } for ctor in &n.constructors { - let mut params = Vec::new(); - for p in &ctor.params { - params.push(self.resolve_type_node(&p.ty)); + if let NodeKind::ConstructorDecl(ctor) = arena.kind(*ctor) { + let mut params = Vec::new(); + for p in &ctor.params { + params.push(self.resolve_type_node(arena, p.ty)); + } + let ctor_ty = PbsType::Function { + params, + return_type: Box::new(ty.clone()), + }; + ctors.insert(self.interner.resolve(ctor.name).to_string(), ctor_ty); } - let ctor_ty = PbsType::Function { - params, - return_type: Box::new(ty.clone()), - }; - ctors.insert(self.interner.resolve(ctor.name).to_string(), ctor_ty); } self.struct_constructors.insert(type_name.clone(), ctors); // Resolve methods let mut methods = HashMap::new(); - if let Some(body_node) = &n.body { - if let Node::TypeBody(body) = &**body_node { + if let Some(body_node) = n.body { + if let NodeKind::TypeBody(body) = arena.kind(body_node) { for m in &body.methods { - let mut params = Vec::new(); - for p in &m.params { - params.push(self.resolve_type_node(&p.ty)); + if let NodeKind::ServiceFnSig(sig) = arena.kind(*m) { + let mut params = Vec::new(); + for p in &sig.params { + params.push(self.resolve_type_node(arena, p.ty)); + } + let m_ty = PbsType::Function { + params, + return_type: Box::new(self.resolve_type_node(arena, sig.ret)), + }; + methods.insert(self.interner.resolve(sig.name).to_string(), m_ty); } - let m_ty = PbsType::Function { - params, - return_type: Box::new(self.resolve_type_node(&m.ret)), - }; - methods.insert(self.interner.resolve(m.name).to_string(), m_ty); } } } @@ -162,66 +171,77 @@ impl<'a> TypeChecker<'a> { } } - fn check_node(&mut self, node: &Node) -> PbsType { - match node { - Node::FnDecl(n) => { - self.check_fn_decl(n); + fn check_node(&mut self, arena: &AstArena, node: NodeId) -> PbsType { + match arena.kind(node) { + NodeKind::FnDecl(n) => { + self.check_fn_decl(arena, node, n); PbsType::Void } - Node::TypeDecl(n) => { - self.check_type_decl(n); + NodeKind::TypeDecl(n) => { + self.check_type_decl(arena, node, n); PbsType::Void } - Node::ConstructorDecl(n) => { - self.check_constructor_decl(n); + NodeKind::ConstructorDecl(n) => { + self.check_constructor_decl(arena, node, n); PbsType::Void } - Node::ConstantDecl(n) => self.check_node(&n.value), - Node::Block(n) => self.check_block(n), - Node::LetStmt(n) => { - self.check_let_stmt(n); + NodeKind::ConstantDecl(n) => self.check_node(arena, n.value), + NodeKind::Block(n) => self.check_block(arena, n), + NodeKind::LetStmt(n) => { + self.check_let_stmt(arena, node, n); PbsType::Void } - Node::ExprStmt(n) => { - self.check_node(&n.expr); + NodeKind::ExprStmt(n) => { + self.check_node(arena, n.expr); PbsType::Void } - Node::ReturnStmt(n) => { - let ret_ty = if let Some(expr) = &n.expr { - self.check_node(expr) + NodeKind::ReturnStmt(n) => { + let ret_ty = if let Some(expr) = n.expr { + self.check_node(arena, expr) } else { PbsType::Void }; if let Some(expected) = self.current_return_type.clone() { if !self.is_assignable(&expected, &ret_ty) { - self.error_type_mismatch(&expected, &ret_ty, n.span); + self.error_type_mismatch(&expected, &ret_ty, arena.span(node)); } } PbsType::Void } - Node::IntLit(_) => PbsType::Int, - Node::FloatLit(_) => PbsType::Float, - Node::BoundedLit(_) => PbsType::Bounded, - Node::StringLit(_) => PbsType::String, - Node::Ident(n) => self.check_identifier(n), - Node::Call(n) => self.check_call(n), - Node::Unary(n) => self.check_unary(n), - Node::Binary(n) => self.check_binary(n), - Node::Cast(n) => self.check_cast(n), - Node::IfExpr(n) => self.check_if_expr(n), - Node::WhenExpr(n) => self.check_when_expr(n), - Node::Alloc(n) => self.check_alloc(n), - Node::Mutate(n) => self.check_hip(n.span, &n.target, n.binding, &n.body, true), - Node::Borrow(n) => self.check_hip(n.span, &n.target, n.binding, &n.body, false), - Node::Peek(n) => self.check_hip(n.span, &n.target, n.binding, &n.body, false), - Node::MemberAccess(n) => self.check_member_access(n), + NodeKind::IntLit(_) => PbsType::Int, + NodeKind::FloatLit(_) => PbsType::Float, + NodeKind::BoundedLit(_) => PbsType::Bounded, + NodeKind::StringLit(_) => PbsType::String, + NodeKind::Ident(n) => self.check_identifier(arena, node, n), + NodeKind::Call(n) => self.check_call(arena, node, n), + NodeKind::Unary(n) => self.check_unary(arena, node, n), + NodeKind::Binary(n) => self.check_binary(arena, node, n), + NodeKind::Cast(n) => self.check_cast(arena, node, n), + NodeKind::IfExpr(n) => self.check_if_expr(arena, node, n), + NodeKind::WhenExpr(n) => self.check_when_expr(arena, node, n), + NodeKind::Alloc(n) => self.check_alloc(arena, node, n), + NodeKind::Mutate(n) => { + self.check_hip(arena, arena.span(node), n.target, n.binding, n.body, true) + } + NodeKind::Borrow(n) => { + self.check_hip(arena, arena.span(node), n.target, n.binding, n.body, false) + } + NodeKind::Peek(n) => { + self.check_hip(arena, arena.span(node), n.target, n.binding, n.body, false) + } + NodeKind::MemberAccess(n) => self.check_member_access(arena, node, n), _ => PbsType::Void, } } - fn check_member_access(&mut self, n: &MemberAccessNode) -> PbsType { + fn check_member_access( + &mut self, + arena: &AstArena, + node: NodeId, + n: &MemberAccessNodeArena, + ) -> PbsType { let member_str = self.interner.resolve(n.member); - if let Node::Ident(id) = &*n.object { + if let NodeKind::Ident(id) = arena.kind(n.object) { let name_str = self.interner.resolve(id.name); // Check if it's a local first let is_local = self.scopes.iter().any(|s| s.contains_key(name_str)); @@ -244,7 +264,7 @@ impl<'a> TypeChecker<'a> { level: DiagnosticLevel::Error, code: Some("E_RESOLVE_UNDEFINED".to_string()), message: format!("Method '{}' not found on host contract '{}'", member_str, name_str), - span: Some(n.span), + span: Some(arena.span(node)), }); } return PbsType::Void; @@ -284,7 +304,7 @@ impl<'a> TypeChecker<'a> { } } - let obj_ty = self.check_node(&n.object); + let obj_ty = self.check_node(arena, n.object); if let PbsType::Struct(ref name) = obj_ty { if let Some(methods) = self.struct_methods.get(name) { if let Some(ty) = methods.get(member_str) { @@ -355,21 +375,29 @@ impl<'a> TypeChecker<'a> { level: DiagnosticLevel::Error, code: Some("E_RESOLVE_UNDEFINED".to_string()), message: msg, - span: Some(n.span), + span: Some(arena.span(node)), }); } PbsType::Void } - fn check_alloc(&mut self, n: &AllocNode) -> PbsType { - let ty = self.resolve_type_node(&n.ty); + fn check_alloc(&mut self, arena: &AstArena, _node: NodeId, n: &AllocNodeArena) -> PbsType { + let ty = self.resolve_type_node(arena, n.ty); // For v0, alloc returns something that can be used with mutate/borrow/peek. // We'll call it a gate to the type. PbsType::Contract(format!("Gate<{}>", ty)) // Approximation for v0 } - fn check_hip(&mut self, _span: Span, target: &Node, binding: NameId, body: &Node, is_mut: bool) -> PbsType { - let target_ty = self.check_node(target); + fn check_hip( + &mut self, + arena: &AstArena, + _span: Span, + target: NodeId, + binding: NameId, + body: NodeId, + is_mut: bool, + ) -> PbsType { + let target_ty = self.check_node(arena, target); // In v0, we assume target is a gate. We bind the inner type to the binding. let inner_ty = match target_ty { PbsType::Contract(name) if name.starts_with("Gate<") => { @@ -389,12 +417,12 @@ impl<'a> TypeChecker<'a> { self.enter_scope(); let binding_str = self.interner.resolve(binding); self.define_local(binding_str, inner_ty, is_mut); - let body_ty = self.check_node(body); + let body_ty = self.check_node(arena, body); self.exit_scope(); body_ty } - fn check_fn_decl(&mut self, n: &FnDeclNode) { + fn check_fn_decl(&mut self, arena: &AstArena, id: NodeId, n: &FnDeclNodeArena) { let sig = self.module_symbols.value_symbols.get(n.name).and_then(|s| s.ty.clone()); if let Some(PbsType::Function { params, return_type }) = sig { self.enter_scope(); @@ -404,10 +432,10 @@ impl<'a> TypeChecker<'a> { self.define_local(self.interner.resolve(param.name), ty.clone(), false); } - let _body_ty = self.check_node(&n.body); + let _body_ty = self.check_node(arena, n.body); // Return path validation - if !self.all_paths_return(&n.body) { + if !self.all_paths_return(arena, n.body) { if n.else_fallback.is_some() { // OK } else if matches!(*return_type, PbsType::Optional(_)) { @@ -423,13 +451,13 @@ impl<'a> TypeChecker<'a> { self.interner.resolve(n.name), return_type ), - span: Some(n.span), + span: Some(arena.span(id)), }); } } - if let Some(fallback) = &n.else_fallback { - self.check_node(fallback); + if let Some(fallback) = n.else_fallback { + self.check_node(arena, fallback); } self.current_return_type = None; @@ -437,13 +465,13 @@ impl<'a> TypeChecker<'a> { } } - fn check_block(&mut self, n: &BlockNode) -> PbsType { + fn check_block(&mut self, arena: &AstArena, n: &BlockNodeArena) -> PbsType { self.enter_scope(); for stmt in &n.stmts { - self.check_node(stmt); + self.check_node(arena, *stmt); } - let tail_ty = if let Some(tail) = &n.tail { - self.check_node(tail) + let tail_ty = if let Some(tail) = n.tail { + self.check_node(arena, tail) } else { PbsType::Void }; @@ -451,13 +479,13 @@ impl<'a> TypeChecker<'a> { tail_ty } - fn check_let_stmt(&mut self, n: &LetStmtNode) { - let init_ty = self.check_node(&n.init); - let declared_ty = n.ty.as_ref().map(|t| self.resolve_type_node(t)); + fn check_let_stmt(&mut self, arena: &AstArena, id: NodeId, n: &LetStmtNodeArena) { + let init_ty = self.check_node(arena, n.init); + let declared_ty = n.ty.map(|t| self.resolve_type_node(arena, t)); let final_ty = if let Some(dty) = declared_ty { if !self.is_assignable(&dty, &init_ty) { - self.error_type_mismatch(&dty, &init_ty, n.span); + self.error_type_mismatch(&dty, &init_ty, arena.span(id)); } dty } else { @@ -467,7 +495,7 @@ impl<'a> TypeChecker<'a> { self.define_local(self.interner.resolve(n.name), final_ty, n.is_mut); } - fn check_identifier(&mut self, n: &IdentNode) -> PbsType { + fn check_identifier(&mut self, _arena: &AstArena, _id: NodeId, n: &IdentNodeArena) -> PbsType { let name_str = self.interner.resolve(n.name); // Check locals for scope in self.scopes.iter().rev() { @@ -510,27 +538,27 @@ impl<'a> TypeChecker<'a> { PbsType::Void } - fn check_call(&mut self, n: &CallNode) -> PbsType { - let callee_ty = self.check_node(&n.callee); + fn check_call(&mut self, arena: &AstArena, node: NodeId, n: &CallNodeArena) -> PbsType { + let callee_ty = self.check_node(arena, n.callee); // Handle special built-in "constructors" - if let Node::Ident(id) = &*n.callee { + if let NodeKind::Ident(id) = arena.kind(n.callee) { match self.interner.resolve(id.name) { "some" => { if n.args.len() == 1 { - let inner_ty = self.check_node(&n.args[0]); + let inner_ty = self.check_node(arena, n.args[0]); return PbsType::Optional(Box::new(inner_ty)); } } "ok" => { if n.args.len() == 1 { - let inner_ty = self.check_node(&n.args[0]); + let inner_ty = self.check_node(arena, n.args[0]); return PbsType::Result(Box::new(inner_ty), Box::new(PbsType::Void)); // Error type unknown here } } "err" => { if n.args.len() == 1 { - let inner_ty = self.check_node(&n.args[0]); + let inner_ty = self.check_node(arena, n.args[0]); return PbsType::Result(Box::new(PbsType::Void), Box::new(inner_ty)); } } @@ -545,13 +573,13 @@ impl<'a> TypeChecker<'a> { level: DiagnosticLevel::Error, code: Some("E_TYPE_MISMATCH".to_string()), message: format!("Expected {} arguments, found {}", params.len(), n.args.len()), - span: Some(n.span), + span: Some(arena.span(node)), }); } else { for (i, arg) in n.args.iter().enumerate() { - let arg_ty = self.check_node(arg); + let arg_ty = self.check_node(arena, *arg); if !self.is_assignable(¶ms[i], &arg_ty) { - self.error_type_mismatch(¶ms[i], &arg_ty, arg.span()); + self.error_type_mismatch(¶ms[i], &arg_ty, arena.span(*arg)); } } } @@ -563,7 +591,7 @@ impl<'a> TypeChecker<'a> { level: DiagnosticLevel::Error, code: Some("E_TYPE_MISMATCH".to_string()), message: format!("Type {} is not callable", callee_ty), - span: Some(n.span), + span: Some(arena.span(node)), }); } PbsType::Void @@ -571,14 +599,14 @@ impl<'a> TypeChecker<'a> { } } - fn check_unary(&mut self, n: &UnaryNode) -> PbsType { - let expr_ty = self.check_node(&n.expr); + fn check_unary(&mut self, arena: &AstArena, node: NodeId, n: &UnaryNodeArena) -> PbsType { + let expr_ty = self.check_node(arena, n.expr); match n.op.as_str() { "-" => { if expr_ty == PbsType::Int || expr_ty == PbsType::Float { expr_ty } else { - self.error_type_mismatch(&PbsType::Int, &expr_ty, n.span); + self.error_type_mismatch(&PbsType::Int, &expr_ty, arena.span(node)); PbsType::Void } } @@ -586,7 +614,7 @@ impl<'a> TypeChecker<'a> { if expr_ty == PbsType::Bool { PbsType::Bool } else { - self.error_type_mismatch(&PbsType::Bool, &expr_ty, n.span); + self.error_type_mismatch(&PbsType::Bool, &expr_ty, arena.span(node)); PbsType::Void } } @@ -594,16 +622,16 @@ impl<'a> TypeChecker<'a> { } } - fn check_binary(&mut self, n: &BinaryNode) -> PbsType { - let left_ty = self.check_node(&n.left); - let right_ty = self.check_node(&n.right); + fn check_binary(&mut self, arena: &AstArena, node: NodeId, n: &BinaryNodeArena) -> PbsType { + let left_ty = self.check_node(arena, n.left); + let right_ty = self.check_node(arena, n.right); match n.op.as_str() { "+" | "-" | "*" | "/" | "%" => { if (left_ty == PbsType::Int || left_ty == PbsType::Float) && left_ty == right_ty { left_ty } else { - self.error_type_mismatch(&left_ty, &right_ty, n.span); + self.error_type_mismatch(&left_ty, &right_ty, arena.span(node)); PbsType::Void } } @@ -611,7 +639,7 @@ impl<'a> TypeChecker<'a> { if left_ty == right_ty { PbsType::Bool } else { - self.error_type_mismatch(&left_ty, &right_ty, n.span); + self.error_type_mismatch(&left_ty, &right_ty, arena.span(node)); PbsType::Bool } } @@ -619,7 +647,7 @@ impl<'a> TypeChecker<'a> { if (left_ty == PbsType::Int || left_ty == PbsType::Float) && left_ty == right_ty { PbsType::Bool } else { - self.error_type_mismatch(&left_ty, &right_ty, n.span); + self.error_type_mismatch(&left_ty, &right_ty, arena.span(node)); PbsType::Bool } } @@ -627,8 +655,8 @@ impl<'a> TypeChecker<'a> { if left_ty == PbsType::Bool && right_ty == PbsType::Bool { PbsType::Bool } else { - self.error_type_mismatch(&PbsType::Bool, &left_ty, n.left.span()); - self.error_type_mismatch(&PbsType::Bool, &right_ty, n.right.span()); + self.error_type_mismatch(&PbsType::Bool, &left_ty, arena.span(n.left)); + self.error_type_mismatch(&PbsType::Bool, &right_ty, arena.span(n.right)); PbsType::Bool } } @@ -636,23 +664,23 @@ impl<'a> TypeChecker<'a> { } } - fn check_cast(&mut self, n: &CastNode) -> PbsType { - let _expr_ty = self.check_node(&n.expr); - let target_ty = self.resolve_type_node(&n.ty); + fn check_cast(&mut self, arena: &AstArena, _node: NodeId, n: &CastNodeArena) -> PbsType { + let _expr_ty = self.check_node(arena, n.expr); + let target_ty = self.resolve_type_node(arena, n.ty); // Minimal cast validation for v0 target_ty } - fn check_if_expr(&mut self, n: &IfExprNode) -> PbsType { - let cond_ty = self.check_node(&n.cond); + fn check_if_expr(&mut self, arena: &AstArena, node: NodeId, n: &IfExprNodeArena) -> PbsType { + let cond_ty = self.check_node(arena, n.cond); if cond_ty != PbsType::Bool { - self.error_type_mismatch(&PbsType::Bool, &cond_ty, n.cond.span()); + self.error_type_mismatch(&PbsType::Bool, &cond_ty, arena.span(n.cond)); } - let then_ty = self.check_node(&n.then_block); - if let Some(else_block) = &n.else_block { - let else_ty = self.check_node(else_block); + let then_ty = self.check_node(arena, n.then_block); + if let Some(else_block) = n.else_block { + let else_ty = self.check_node(arena, else_block); if then_ty != else_ty { - self.error_type_mismatch(&then_ty, &else_ty, n.span); + self.error_type_mismatch(&then_ty, &else_ty, arena.span(node)); } then_ty } else { @@ -660,20 +688,20 @@ impl<'a> TypeChecker<'a> { } } - fn check_when_expr(&mut self, n: &WhenExprNode) -> PbsType { + fn check_when_expr(&mut self, arena: &AstArena, _node: NodeId, n: &WhenExprNodeArena) -> PbsType { let mut first_ty = None; for arm in &n.arms { - if let Node::WhenArm(arm_node) = arm { - let cond_ty = self.check_node(&arm_node.cond); + if let NodeKind::WhenArm(arm_node) = arena.kind(*arm) { + let cond_ty = self.check_node(arena, arm_node.cond); if cond_ty != PbsType::Bool { - self.error_type_mismatch(&PbsType::Bool, &cond_ty, arm_node.cond.span()); + self.error_type_mismatch(&PbsType::Bool, &cond_ty, arena.span(arm_node.cond)); } - let body_ty = self.check_node(&arm_node.body); + let body_ty = self.check_node(arena, arm_node.body); if first_ty.is_none() { first_ty = Some(body_ty); } else if let Some(fty) = &first_ty { if *fty != body_ty { - self.error_type_mismatch(fty, &body_ty, arm_node.body.span()); + self.error_type_mismatch(fty, &body_ty, arena.span(arm_node.body)); } } } @@ -681,9 +709,11 @@ impl<'a> TypeChecker<'a> { first_ty.unwrap_or(PbsType::Void) } - fn check_type_decl(&mut self, n: &TypeDeclNode) { + fn check_type_decl(&mut self, arena: &AstArena, _id: NodeId, n: &TypeDeclNodeArena) { for constructor in &n.constructors { - self.check_constructor_decl(constructor); + if let NodeKind::ConstructorDecl(ctor) = arena.kind(*constructor) { + self.check_constructor_decl(arena, *constructor, ctor); + } } let type_name = self.interner.resolve(n.name).to_string(); @@ -697,37 +727,47 @@ impl<'a> TypeChecker<'a> { let mut constants_map = HashMap::new(); self.scopes.push(constants_scope); - for constant in &n.constants { - let val_ty = self.check_node(&constant.value); - if !self.is_assignable(&struct_ty, &val_ty) { - self.error_type_mismatch(&struct_ty, &val_ty, constant.span); + for constant_id in &n.constants { + if let NodeKind::ConstantDecl(constant) = arena.kind(*constant_id) { + let val_ty = self.check_node(arena, constant.value); + if !self.is_assignable(&struct_ty, &val_ty) { + self.error_type_mismatch(&struct_ty, &val_ty, arena.span(*constant_id)); + } + constants_map.insert( + self.interner.resolve(constant.name).to_string(), + struct_ty.clone(), + ); } - constants_map.insert(self.interner.resolve(constant.name).to_string(), struct_ty.clone()); } self.scopes.pop(); self.struct_constants.insert(type_name, constants_map); - if let Some(body) = &n.body { - self.check_node(body); + if let Some(body) = n.body { + self.check_node(arena, body); } } - fn check_constructor_decl(&mut self, n: &ConstructorDeclNode) { + fn check_constructor_decl( + &mut self, + arena: &AstArena, + _id: NodeId, + n: &ConstructorDeclNodeArena, + ) { self.enter_scope(); for param in &n.params { - let ty = self.resolve_type_node(¶m.ty); + let ty = self.resolve_type_node(arena, param.ty); self.define_local(self.interner.resolve(param.name), ty, false); } for init in &n.initializers { - self.check_node(init); + self.check_node(arena, *init); } - self.check_node(&n.body); + self.check_node(arena, n.body); self.exit_scope(); } - fn resolve_type_node(&mut self, node: &Node) -> PbsType { - match node { - Node::TypeName(tn) => { + fn resolve_type_node(&mut self, arena: &AstArena, node: NodeId) -> PbsType { + match arena.kind(node) { + NodeKind::TypeName(tn) => { let name_str = self.interner.resolve(tn.name); match name_str { "int" => PbsType::Int, @@ -752,35 +792,33 @@ impl<'a> TypeChecker<'a> { level: DiagnosticLevel::Error, code: Some("E_TYPE_UNKNOWN_TYPE".to_string()), message: format!("Unknown type: {}", name_str), - span: Some(tn.span), + span: Some(arena.span(node)), }); PbsType::Void } } } } - Node::TypeApp(ta) => { - match self.interner.resolve(ta.base) { - "optional" => { - if ta.args.len() == 1 { - PbsType::Optional(Box::new(self.resolve_type_node(&ta.args[0]))) - } else { - PbsType::Void - } + NodeKind::TypeApp(ta) => match self.interner.resolve(ta.base) { + "optional" => { + if ta.args.len() == 1 { + PbsType::Optional(Box::new(self.resolve_type_node(arena, ta.args[0]))) + } else { + PbsType::Void } - "result" => { - if ta.args.len() == 2 { - PbsType::Result( - Box::new(self.resolve_type_node(&ta.args[0])), - Box::new(self.resolve_type_node(&ta.args[1])), - ) - } else { - PbsType::Void - } - } - _ => PbsType::Void, } - } + "result" => { + if ta.args.len() == 2 { + PbsType::Result( + Box::new(self.resolve_type_node(arena, ta.args[0])), + Box::new(self.resolve_type_node(arena, ta.args[1])), + ) + } else { + PbsType::Void + } + } + _ => PbsType::Void, + }, _ => PbsType::Void, } } @@ -826,23 +864,26 @@ impl<'a> TypeChecker<'a> { } } - fn all_paths_return(&self, node: &Node) -> bool { - match node { - Node::ReturnStmt(_) => true, - Node::Block(n) => { + fn all_paths_return(&self, arena: &AstArena, node: NodeId) -> bool { + match arena.kind(node) { + NodeKind::ReturnStmt(_) => true, + NodeKind::Block(n) => { for stmt in &n.stmts { - if self.all_paths_return(stmt) { + if self.all_paths_return(arena, *stmt) { return true; } } - if let Some(tail) = &n.tail { - return self.all_paths_return(tail); + if let Some(tail) = n.tail { + return self.all_paths_return(arena, tail); } false } - Node::IfExpr(n) => { - let then_returns = self.all_paths_return(&n.then_block); - let else_returns = n.else_block.as_ref().map(|b| self.all_paths_return(b)).unwrap_or(false); + NodeKind::IfExpr(n) => { + let then_returns = self.all_paths_return(arena, n.then_block); + let else_returns = n + .else_block + .map(|b| self.all_paths_return(arena, b)) + .unwrap_or(false); then_returns && else_returns } // For simplicity, we don't assume When returns unless all arms do diff --git a/crates/prometeu-compiler/src/sources.rs b/crates/prometeu-compiler/src/sources.rs index 33bdabed..8ad915e4 100644 --- a/crates/prometeu-compiler/src/sources.rs +++ b/crates/prometeu-compiler/src/sources.rs @@ -132,10 +132,11 @@ pub fn build_exports(module_dir: &Path, file_manager: &mut FileManager) -> Resul let file_id = file_manager.add(file_path.clone(), source.clone()); let mut parser = Parser::new(&source, file_id, &mut interner); - let ast = parser.parse_file()?; - + let parsed = parser.parse_file()?; + let mut collector = SymbolCollector::new(&interner); - let (type_symbols, value_symbols) = collector.collect(&ast)?; + let (type_symbols, value_symbols) = + collector.collect(&parsed.arena, parsed.root)?; // Merge only public symbols for symbol in type_symbols.symbols.into_values() { diff --git a/crates/prometeu-compiler/tests/generate_canonical_goldens.rs b/crates/prometeu-compiler/tests/generate_canonical_goldens.rs index f811d8bf..dd748617 100644 --- a/crates/prometeu-compiler/tests/generate_canonical_goldens.rs +++ b/crates/prometeu-compiler/tests/generate_canonical_goldens.rs @@ -1,7 +1,6 @@ use prometeu_bytecode::disasm::disasm; use prometeu_bytecode::BytecodeLoader; use prometeu_compiler::compiler::compile; -use prometeu_compiler::frontends::pbs::ast::Node; use prometeu_compiler::frontends::pbs::parser::Parser; use prometeu_analysis::NameInterner; use std::fs; @@ -59,9 +58,8 @@ fn generate_canonical_goldens() { let source = fs::read_to_string(project_dir.join("src/main/modules/main.pbs")).unwrap(); let mut interner = NameInterner::new(); let mut parser = Parser::new(&source, 0, &mut interner); - let ast = parser.parse_file().expect("Failed to parse AST"); - let ast_node = Node::File(ast); - let ast_json = serde_json::to_string_pretty(&ast_node).unwrap(); + let parsed = parser.parse_file().expect("Failed to parse AST"); + let ast_json = serde_json::to_string_pretty(parsed.arena.kind(parsed.root)).unwrap(); fs::write(golden_dir.join("ast.json"), ast_json).unwrap(); println!("Golden artifacts generated in test-cartridges/canonical/golden/"); diff --git a/test-cartridges/canonical/golden/ast.json b/test-cartridges/canonical/golden/ast.json index cb78f726..89bd9f99 100644 --- a/test-cartridges/canonical/golden/ast.json +++ b/test-cartridges/canonical/golden/ast.json @@ -1,1171 +1,13 @@ { "kind": "File", - "span": { - "file_id": 0, - "start": 79, - "end": 1181 - }, "imports": [], "decls": [ - { - "kind": "TypeDecl", - "span": { - "file_id": 0, - "start": 79, - "end": 224 - }, - "vis": null, - "type_kind": "struct", - "name": 11, - "is_host": false, - "params": [ - { - "span": { - "file_id": 0, - "start": 100, - "end": 112 - }, - "name": 12, - "ty": { - "kind": "TypeName", - "span": { - "file_id": 0, - "start": 105, - "end": 112 - }, - "name": 10 - } - } - ], - "constructors": [], - "constants": [ - { - "span": { - "file_id": 0, - "start": 119, - "end": 135 - }, - "name": 13, - "value": { - "kind": "Call", - "span": { - "file_id": 0, - "start": 126, - "end": 135 - }, - "callee": { - "kind": "Ident", - "span": { - "file_id": 0, - "start": 126, - "end": 131 - }, - "name": 11 - }, - "args": [ - { - "kind": "BoundedLit", - "span": { - "file_id": 0, - "start": 132, - "end": 134 - }, - "value": 0 - } - ] - } - }, - { - "span": { - "file_id": 0, - "start": 139, - "end": 159 - }, - "name": 14, - "value": { - "kind": "Call", - "span": { - "file_id": 0, - "start": 146, - "end": 159 - }, - "callee": { - "kind": "Ident", - "span": { - "file_id": 0, - "start": 146, - "end": 151 - }, - "name": 11 - }, - "args": [ - { - "kind": "BoundedLit", - "span": { - "file_id": 0, - "start": 152, - "end": 158 - }, - "value": 65535 - } - ] - } - }, - { - "span": { - "file_id": 0, - "start": 163, - "end": 181 - }, - "name": 15, - "value": { - "kind": "Call", - "span": { - "file_id": 0, - "start": 168, - "end": 181 - }, - "callee": { - "kind": "Ident", - "span": { - "file_id": 0, - "start": 168, - "end": 173 - }, - "name": 11 - }, - "args": [ - { - "kind": "BoundedLit", - "span": { - "file_id": 0, - "start": 174, - "end": 180 - }, - "value": 63488 - } - ] - } - }, - { - "span": { - "file_id": 0, - "start": 185, - "end": 204 - }, - "name": 16, - "value": { - "kind": "Call", - "span": { - "file_id": 0, - "start": 192, - "end": 204 - }, - "callee": { - "kind": "Ident", - "span": { - "file_id": 0, - "start": 192, - "end": 197 - }, - "name": 11 - }, - "args": [ - { - "kind": "BoundedLit", - "span": { - "file_id": 0, - "start": 198, - "end": 203 - }, - "value": 2016 - } - ] - } - }, - { - "span": { - "file_id": 0, - "start": 208, - "end": 224 - }, - "name": 17, - "value": { - "kind": "Call", - "span": { - "file_id": 0, - "start": 214, - "end": 224 - }, - "callee": { - "kind": "Ident", - "span": { - "file_id": 0, - "start": 214, - "end": 219 - }, - "name": 11 - }, - "args": [ - { - "kind": "BoundedLit", - "span": { - "file_id": 0, - "start": 220, - "end": 223 - }, - "value": 31 - } - ] - } - } - ], - "body": null - }, - { - "kind": "TypeDecl", - "span": { - "file_id": 0, - "start": 229, - "end": 336 - }, - "vis": null, - "type_kind": "struct", - "name": 18, - "is_host": false, - "params": [ - { - "span": { - "file_id": 0, - "start": 261, - "end": 274 - }, - "name": 19, - "ty": { - "kind": "TypeName", - "span": { - "file_id": 0, - "start": 270, - "end": 274 - }, - "name": 20 - } - }, - { - "span": { - "file_id": 0, - "start": 280, - "end": 294 - }, - "name": 21, - "ty": { - "kind": "TypeName", - "span": { - "file_id": 0, - "start": 290, - "end": 294 - }, - "name": 20 - } - }, - { - "span": { - "file_id": 0, - "start": 300, - "end": 310 - }, - "name": 22, - "ty": { - "kind": "TypeName", - "span": { - "file_id": 0, - "start": 306, - "end": 310 - }, - "name": 20 - } - }, - { - "span": { - "file_id": 0, - "start": 316, - "end": 336 - }, - "name": 23, - "ty": { - "kind": "TypeName", - "span": { - "file_id": 0, - "start": 329, - "end": 336 - }, - "name": 10 - } - } - ], - "constructors": [], - "constants": [], - "body": null - }, - { - "kind": "TypeDecl", - "span": { - "file_id": 0, - "start": 340, - "end": 618 - }, - "vis": null, - "type_kind": "struct", - "name": 24, - "is_host": false, - "params": [ - { - "span": { - "file_id": 0, - "start": 364, - "end": 379 - }, - "name": 25, - "ty": { - "kind": "TypeName", - "span": { - "file_id": 0, - "start": 368, - "end": 379 - }, - "name": 18 - } - }, - { - "span": { - "file_id": 0, - "start": 385, - "end": 402 - }, - "name": 22, - "ty": { - "kind": "TypeName", - "span": { - "file_id": 0, - "start": 391, - "end": 402 - }, - "name": 18 - } - }, - { - "span": { - "file_id": 0, - "start": 408, - "end": 425 - }, - "name": 26, - "ty": { - "kind": "TypeName", - "span": { - "file_id": 0, - "start": 414, - "end": 425 - }, - "name": 18 - } - }, - { - "span": { - "file_id": 0, - "start": 431, - "end": 449 - }, - "name": 27, - "ty": { - "kind": "TypeName", - "span": { - "file_id": 0, - "start": 438, - "end": 449 - }, - "name": 18 - } - }, - { - "span": { - "file_id": 0, - "start": 455, - "end": 469 - }, - "name": 28, - "ty": { - "kind": "TypeName", - "span": { - "file_id": 0, - "start": 458, - "end": 469 - }, - "name": 18 - } - }, - { - "span": { - "file_id": 0, - "start": 475, - "end": 489 - }, - "name": 29, - "ty": { - "kind": "TypeName", - "span": { - "file_id": 0, - "start": 478, - "end": 489 - }, - "name": 18 - } - }, - { - "span": { - "file_id": 0, - "start": 495, - "end": 509 - }, - "name": 30, - "ty": { - "kind": "TypeName", - "span": { - "file_id": 0, - "start": 498, - "end": 509 - }, - "name": 18 - } - }, - { - "span": { - "file_id": 0, - "start": 515, - "end": 529 - }, - "name": 31, - "ty": { - "kind": "TypeName", - "span": { - "file_id": 0, - "start": 518, - "end": 529 - }, - "name": 18 - } - }, - { - "span": { - "file_id": 0, - "start": 535, - "end": 549 - }, - "name": 32, - "ty": { - "kind": "TypeName", - "span": { - "file_id": 0, - "start": 538, - "end": 549 - }, - "name": 18 - } - }, - { - "span": { - "file_id": 0, - "start": 555, - "end": 569 - }, - "name": 33, - "ty": { - "kind": "TypeName", - "span": { - "file_id": 0, - "start": 558, - "end": 569 - }, - "name": 18 - } - }, - { - "span": { - "file_id": 0, - "start": 575, - "end": 593 - }, - "name": 34, - "ty": { - "kind": "TypeName", - "span": { - "file_id": 0, - "start": 582, - "end": 593 - }, - "name": 18 - } - }, - { - "span": { - "file_id": 0, - "start": 599, - "end": 618 - }, - "name": 35, - "ty": { - "kind": "TypeName", - "span": { - "file_id": 0, - "start": 607, - "end": 618 - }, - "name": 18 - } - } - ], - "constructors": [], - "constants": [], - "body": null - }, - { - "kind": "TypeDecl", - "span": { - "file_id": 0, - "start": 622, - "end": 685 - }, - "vis": null, - "type_kind": "contract", - "name": 36, - "is_host": true, - "params": [], - "constructors": [], - "constants": [], - "body": { - "kind": "TypeBody", - "span": { - "file_id": 0, - "start": 648, - "end": 685 - }, - "members": [], - "methods": [ - { - "span": { - "file_id": 0, - "start": 654, - "end": 682 - }, - "name": 37, - "params": [ - { - "span": { - "file_id": 0, - "start": 663, - "end": 675 - }, - "name": 38, - "ty": { - "kind": "TypeName", - "span": { - "file_id": 0, - "start": 670, - "end": 675 - }, - "name": 11 - } - } - ], - "ret": { - "kind": "TypeName", - "span": { - "file_id": 0, - "start": 678, - "end": 682 - }, - "name": 6 - } - } - ] - } - }, - { - "kind": "TypeDecl", - "span": { - "file_id": 0, - "start": 687, - "end": 737 - }, - "vis": null, - "type_kind": "contract", - "name": 39, - "is_host": true, - "params": [], - "constructors": [], - "constants": [], - "body": { - "kind": "TypeBody", - "span": { - "file_id": 0, - "start": 715, - "end": 737 - }, - "members": [], - "methods": [ - { - "span": { - "file_id": 0, - "start": 721, - "end": 734 - }, - "name": 40, - "params": [], - "ret": { - "kind": "TypeName", - "span": { - "file_id": 0, - "start": 731, - "end": 734 - }, - "name": 24 - } - } - ] - } - }, - { - "kind": "FnDecl", - "span": { - "file_id": 0, - "start": 739, - "end": 788 - }, - "vis": null, - "name": 41, - "params": [ - { - "span": { - "file_id": 0, - "start": 746, - "end": 752 - }, - "name": 28, - "ty": { - "kind": "TypeName", - "span": { - "file_id": 0, - "start": 749, - "end": 752 - }, - "name": 42 - } - }, - { - "span": { - "file_id": 0, - "start": 754, - "end": 760 - }, - "name": 29, - "ty": { - "kind": "TypeName", - "span": { - "file_id": 0, - "start": 757, - "end": 760 - }, - "name": 42 - } - } - ], - "ret": { - "kind": "TypeName", - "span": { - "file_id": 0, - "start": 763, - "end": 766 - }, - "name": 42 - }, - "else_fallback": null, - "body": { - "kind": "Block", - "span": { - "file_id": 0, - "start": 767, - "end": 788 - }, - "stmts": [ - { - "kind": "ReturnStmt", - "span": { - "file_id": 0, - "start": 773, - "end": 786 - }, - "expr": { - "kind": "Binary", - "span": { - "file_id": 0, - "start": 780, - "end": 785 - }, - "op": "+", - "left": { - "kind": "Ident", - "span": { - "file_id": 0, - "start": 780, - "end": 781 - }, - "name": 28 - }, - "right": { - "kind": "Ident", - "span": { - "file_id": 0, - "start": 784, - "end": 785 - }, - "name": 29 - } - } - } - ], - "tail": null - } - }, - { - "kind": "FnDecl", - "span": { - "file_id": 0, - "start": 790, - "end": 1180 - }, - "vis": null, - "name": 43, - "params": [], - "ret": { - "kind": "TypeName", - "span": { - "file_id": 0, - "start": 802, - "end": 806 - }, - "name": 6 - }, - "else_fallback": null, - "body": { - "kind": "Block", - "span": { - "file_id": 0, - "start": 807, - "end": 1180 - }, - "stmts": [ - { - "kind": "LetStmt", - "span": { - "file_id": 0, - "start": 843, - "end": 854 - }, - "name": 30, - "is_mut": false, - "ty": null, - "init": { - "kind": "IntLit", - "span": { - "file_id": 0, - "start": 851, - "end": 853 - }, - "value": 10 - } - }, - { - "kind": "LetStmt", - "span": { - "file_id": 0, - "start": 859, - "end": 870 - }, - "name": 31, - "is_mut": false, - "ty": null, - "init": { - "kind": "IntLit", - "span": { - "file_id": 0, - "start": 867, - "end": 869 - }, - "value": 20 - } - }, - { - "kind": "LetStmt", - "span": { - "file_id": 0, - "start": 875, - "end": 893 - }, - "name": 44, - "is_mut": false, - "ty": null, - "init": { - "kind": "Call", - "span": { - "file_id": 0, - "start": 883, - "end": 892 - }, - "callee": { - "kind": "Ident", - "span": { - "file_id": 0, - "start": 883, - "end": 886 - }, - "name": 41 - }, - "args": [ - { - "kind": "Ident", - "span": { - "file_id": 0, - "start": 887, - "end": 888 - }, - "name": 30 - }, - { - "kind": "Ident", - "span": { - "file_id": 0, - "start": 890, - "end": 891 - }, - "name": 31 - } - ] - } - }, - { - "kind": "ExprStmt", - "span": { - "file_id": 0, - "start": 927, - "end": 1049 - }, - "expr": { - "kind": "IfExpr", - "span": { - "file_id": 0, - "start": 927, - "end": 1049 - }, - "cond": { - "kind": "Binary", - "span": { - "file_id": 0, - "start": 930, - "end": 937 - }, - "op": "==", - "left": { - "kind": "Ident", - "span": { - "file_id": 0, - "start": 930, - "end": 931 - }, - "name": 44 - }, - "right": { - "kind": "IntLit", - "span": { - "file_id": 0, - "start": 935, - "end": 937 - }, - "value": 30 - } - }, - "then_block": { - "kind": "Block", - "span": { - "file_id": 0, - "start": 938, - "end": 1006 - }, - "stmts": [ - { - "kind": "ExprStmt", - "span": { - "file_id": 0, - "start": 976, - "end": 999 - }, - "expr": { - "kind": "Call", - "span": { - "file_id": 0, - "start": 976, - "end": 998 - }, - "callee": { - "kind": "MemberAccess", - "span": { - "file_id": 0, - "start": 976, - "end": 985 - }, - "object": { - "kind": "Ident", - "span": { - "file_id": 0, - "start": 976, - "end": 979 - }, - "name": 36 - }, - "member": 37 - }, - "args": [ - { - "kind": "MemberAccess", - "span": { - "file_id": 0, - "start": 986, - "end": 997 - }, - "object": { - "kind": "Ident", - "span": { - "file_id": 0, - "start": 986, - "end": 991 - }, - "name": 11 - }, - "member": 16 - } - ] - } - } - ], - "tail": null - }, - "else_block": { - "kind": "Block", - "span": { - "file_id": 0, - "start": 1012, - "end": 1049 - }, - "stmts": [ - { - "kind": "ExprStmt", - "span": { - "file_id": 0, - "start": 1022, - "end": 1043 - }, - "expr": { - "kind": "Call", - "span": { - "file_id": 0, - "start": 1022, - "end": 1042 - }, - "callee": { - "kind": "MemberAccess", - "span": { - "file_id": 0, - "start": 1022, - "end": 1031 - }, - "object": { - "kind": "Ident", - "span": { - "file_id": 0, - "start": 1022, - "end": 1025 - }, - "name": 36 - }, - "member": 37 - }, - "args": [ - { - "kind": "MemberAccess", - "span": { - "file_id": 0, - "start": 1032, - "end": 1041 - }, - "object": { - "kind": "Ident", - "span": { - "file_id": 0, - "start": 1032, - "end": 1037 - }, - "name": 11 - }, - "member": 15 - } - ] - } - } - ], - "tail": null - } - } - }, - { - "kind": "LetStmt", - "span": { - "file_id": 0, - "start": 1103, - "end": 1123 - }, - "name": 45, - "is_mut": false, - "ty": null, - "init": { - "kind": "Call", - "span": { - "file_id": 0, - "start": 1111, - "end": 1122 - }, - "callee": { - "kind": "MemberAccess", - "span": { - "file_id": 0, - "start": 1111, - "end": 1120 - }, - "object": { - "kind": "Ident", - "span": { - "file_id": 0, - "start": 1111, - "end": 1116 - }, - "name": 39 - }, - "member": 40 - }, - "args": [] - } - } - ], - "tail": { - "kind": "IfExpr", - "span": { - "file_id": 0, - "start": 1128, - "end": 1178 - }, - "cond": { - "kind": "MemberAccess", - "span": { - "file_id": 0, - "start": 1131, - "end": 1139 - }, - "object": { - "kind": "MemberAccess", - "span": { - "file_id": 0, - "start": 1131, - "end": 1134 - }, - "object": { - "kind": "Ident", - "span": { - "file_id": 0, - "start": 1131, - "end": 1132 - }, - "name": 45 - }, - "member": 28 - }, - "member": 22 - }, - "then_block": { - "kind": "Block", - "span": { - "file_id": 0, - "start": 1140, - "end": 1178 - }, - "stmts": [ - { - "kind": "ExprStmt", - "span": { - "file_id": 0, - "start": 1150, - "end": 1172 - }, - "expr": { - "kind": "Call", - "span": { - "file_id": 0, - "start": 1150, - "end": 1171 - }, - "callee": { - "kind": "MemberAccess", - "span": { - "file_id": 0, - "start": 1150, - "end": 1159 - }, - "object": { - "kind": "Ident", - "span": { - "file_id": 0, - "start": 1150, - "end": 1153 - }, - "name": 36 - }, - "member": 37 - }, - "args": [ - { - "kind": "MemberAccess", - "span": { - "file_id": 0, - "start": 1160, - "end": 1170 - }, - "object": { - "kind": "Ident", - "span": { - "file_id": 0, - "start": 1160, - "end": 1165 - }, - "name": 11 - }, - "member": 17 - } - ] - } - } - ], - "tail": null - }, - "else_block": null - } - } - } + 21, + 26, + 39, + 44, + 48, + 57, + 103 ] } \ No newline at end of file