This commit is contained in:
bQUARKz 2026-02-04 18:00:57 +00:00
parent 4e66387f4e
commit 4c97430ea4
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
11 changed files with 1452 additions and 2324 deletions

View File

@ -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<String, ModuleSymbols> = 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<String, ModuleSymbols> = 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() {

View File

@ -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<NodeKind>,
pub spans: Vec<Span>,
pub roots: Vec<NodeId>,
}
#[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<Node>,
pub decls: Vec<Node>,
pub struct FileNodeArena {
pub imports: Vec<NodeId>,
pub decls: Vec<NodeId>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ImportNode {
pub span: Span,
pub spec: Box<Node>, // 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<NameId>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ServiceDeclNode {
pub span: Span,
pub vis: Option<String>, // "pub" | "mod"
pub struct ServiceDeclNodeArena {
pub vis: Option<String>,
pub name: NameId,
pub extends: Option<NameId>,
pub members: Vec<Node>, // ServiceFnSig
pub members: Vec<NodeId>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ServiceFnSigNode {
pub struct ServiceFnSigNodeArena {
pub name: NameId,
pub params: Vec<ParamNodeArena>,
pub ret: NodeId,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ParamNodeArena {
pub span: Span,
pub name: NameId,
pub params: Vec<ParamNode>,
pub ret: Box<Node>, // TypeName or TypeApp
pub ty: NodeId,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ParamNode {
pub span: Span,
pub name: NameId,
pub ty: Box<Node>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct FnDeclNode {
pub span: Span,
pub struct FnDeclNodeArena {
pub vis: Option<String>,
pub name: NameId,
pub params: Vec<ParamNode>,
pub ret: Option<Box<Node>>,
pub else_fallback: Option<Box<Node>>, // Block
pub body: Box<Node>, // Block
pub params: Vec<ParamNodeArena>,
pub ret: Option<NodeId>,
pub else_fallback: Option<NodeId>,
pub body: NodeId,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct TypeDeclNode {
pub span: Span,
pub struct TypeDeclNodeArena {
pub vis: Option<String>,
pub type_kind: String, // "struct" | "contract" | "error"
pub type_kind: String,
pub name: NameId,
pub is_host: bool,
pub params: Vec<ParamNode>, // fields for struct/error
pub constructors: Vec<ConstructorDeclNode>, // [ ... ]
pub constants: Vec<ConstantDeclNode>, // [[ ... ]]
pub body: Option<Box<Node>>, // TypeBody (methods)
pub params: Vec<ParamNodeArena>,
pub constructors: Vec<NodeId>,
pub constants: Vec<NodeId>,
pub body: Option<NodeId>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ConstructorDeclNode {
pub span: Span,
pub params: Vec<ParamNode>,
pub initializers: Vec<Node>,
pub struct ConstructorDeclNodeArena {
pub params: Vec<ParamNodeArena>,
pub initializers: Vec<NodeId>,
pub name: NameId,
pub body: Box<Node>, // 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<TypeMemberNodeArena>,
pub methods: Vec<NodeId>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct TypeMemberNodeArena {
pub span: Span,
pub name: NameId,
pub value: Box<Node>,
pub ty: NodeId,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct TypeBodyNode {
pub span: Span,
pub members: Vec<TypeMemberNode>,
pub methods: Vec<ServiceFnSigNode>,
pub struct BlockNodeArena {
pub stmts: Vec<NodeId>,
pub tail: Option<NodeId>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct TypeMemberNode {
pub span: Span,
pub name: NameId,
pub ty: Box<Node>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct BlockNode {
pub span: Span,
pub stmts: Vec<Node>,
pub tail: Option<Box<Node>>,
}
#[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<Box<Node>>,
pub init: Box<Node>,
pub ty: Option<NodeId>,
pub init: NodeId,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ExprStmtNode {
pub span: Span,
pub expr: Box<Node>,
pub struct ExprStmtNodeArena {
pub expr: NodeId,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ReturnStmtNode {
pub span: Span,
pub expr: Option<Box<Node>>,
pub struct ReturnStmtNodeArena {
pub expr: Option<NodeId>,
}
#[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<Node>,
pub args: Vec<Node>,
pub struct CallNodeArena {
pub callee: NodeId,
pub args: Vec<NodeId>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct UnaryNode {
pub span: Span,
pub struct UnaryNodeArena {
pub op: String,
pub expr: Box<Node>,
pub expr: NodeId,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct BinaryNode {
pub span: Span,
pub struct BinaryNodeArena {
pub op: String,
pub left: Box<Node>,
pub right: Box<Node>,
pub left: NodeId,
pub right: NodeId,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct CastNode {
pub span: Span,
pub expr: Box<Node>,
pub ty: Box<Node>,
pub struct CastNodeArena {
pub expr: NodeId,
pub ty: NodeId,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct IfExprNode {
pub span: Span,
pub cond: Box<Node>,
pub then_block: Box<Node>,
pub else_block: Option<Box<Node>>,
pub struct IfExprNodeArena {
pub cond: NodeId,
pub then_block: NodeId,
pub else_block: Option<NodeId>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct WhenExprNode {
pub span: Span,
pub arms: Vec<Node>, // WhenArm
pub struct WhenExprNodeArena {
pub arms: Vec<NodeId>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct WhenArmNode {
pub span: Span,
pub cond: Box<Node>,
pub body: Box<Node>,
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<Node>,
pub args: Vec<NodeId>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct AllocNode {
pub span: Span,
pub ty: Box<Node>,
pub struct AllocNodeArena {
pub ty: NodeId,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct MutateNode {
pub span: Span,
pub target: Box<Node>,
pub struct MutateNodeArena {
pub target: NodeId,
pub binding: NameId,
pub body: Box<Node>, // BlockNode
pub body: NodeId,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct BorrowNode {
pub span: Span,
pub target: Box<Node>,
pub struct BorrowNodeArena {
pub target: NodeId,
pub binding: NameId,
pub body: Box<Node>, // BlockNode
pub body: NodeId,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct PeekNode {
pub span: Span,
pub target: Box<Node>,
pub struct PeekNodeArena {
pub target: NodeId,
pub binding: NameId,
pub body: Box<Node>, // BlockNode
pub body: NodeId,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct MemberAccessNode {
pub span: Span,
pub object: Box<Node>,
pub struct MemberAccessNodeArena {
pub object: NodeId,
pub member: NameId,
}

View File

@ -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);

File diff suppressed because it is too large Load Diff

View File

@ -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| {

File diff suppressed because it is too large Load Diff

View File

@ -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(&param.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(&param.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(&param.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(&param.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(&param.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<Symbol> {
@ -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();

View File

@ -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(&param.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(&params[i], &arg_ty) {
self.error_type_mismatch(&params[i], &arg_ty, arg.span());
self.error_type_mismatch(&params[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(&param.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

View File

@ -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() {

View File

@ -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/");

File diff suppressed because it is too large Load Diff