This commit is contained in:
bQUARKz 2026-02-04 13:37:38 +00:00
parent 6b372b2613
commit 4e66387f4e
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
17 changed files with 733 additions and 452 deletions

1
Cargo.lock generated
View File

@ -1907,6 +1907,7 @@ dependencies = [
"anyhow",
"clap",
"prometeu-abi",
"prometeu-analysis",
"prometeu-bytecode",
"serde",
"serde_json",

View File

@ -0,0 +1,54 @@
use std::collections::HashMap;
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
pub struct NameId(pub u32);
#[derive(Debug, Default)]
pub struct NameInterner {
names: Vec<String>,
ids: HashMap<String, NameId>,
}
impl NameInterner {
pub fn new() -> Self {
Self {
names: Vec::new(),
ids: HashMap::new(),
}
}
pub fn intern(&mut self, s: &str) -> NameId {
if let Some(id) = self.ids.get(s) {
return *id;
}
let id = NameId(self.names.len() as u32);
self.names.push(s.to_string());
self.ids.insert(self.names[id.0 as usize].clone(), id);
id
}
pub fn resolve(&self, id: NameId) -> &str {
&self.names[id.0 as usize]
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn interner_intern_resolve_roundtrip() {
let mut interner = NameInterner::new();
let id = interner.intern("foo");
assert_eq!(interner.resolve(id), "foo");
}
#[test]
fn interner_dedups_strings() {
let mut interner = NameInterner::new();
let id1 = interner.intern("bar");
let id2 = interner.intern("bar");
assert_eq!(id1, id2);
}
}

View File

@ -1,7 +1,9 @@
pub mod ids;
pub mod span;
pub mod file_db;
pub mod interner;
pub use ids::FileId;
pub use span::Span;
pub use file_db::{FileDB, LineIndex};
pub use interner::{NameId, NameInterner};

View File

@ -16,6 +16,7 @@ include = ["../../VERSION.txt"]
[dependencies]
prometeu-bytecode = { path = "../prometeu-bytecode" }
prometeu-abi = { path = "../prometeu-abi" }
prometeu-analysis = { path = "../prometeu-analysis" }
clap = { version = "4.5.54", features = ["derive"] }
serde = { version = "1.0.228", features = ["derive"] }
serde_json = "1.0.149"

View File

@ -15,6 +15,7 @@ use crate::frontends::pbs::types::PbsType;
use crate::lowering::core_to_vm;
use crate::semantics::export_surface::ExportSurfaceKind;
use prometeu_bytecode::{ConstantPoolEntry, DebugInfo, FunctionMeta};
use prometeu_analysis::NameInterner;
use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, HashMap};
@ -112,6 +113,7 @@ pub fn compile_project(
dep_modules: &HashMap<ProjectId, CompiledModule>,
file_manager: &mut FileManager,
) -> Result<CompiledModule, CompileError> {
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)
@ -121,10 +123,10 @@ pub fn compile_project(
let source_code = std::fs::read_to_string(&source_abs)?;
let file_id = file_manager.add(source_abs.clone(), source_code.clone());
let mut parser = Parser::new(&source_code, file_id);
let mut parser = Parser::new(&source_code, file_id, &mut interner);
let ast = parser.parse_file()?;
let mut collector = SymbolCollector::new();
let mut collector = SymbolCollector::new(&interner);
let (ts, vs) = collector.collect(&ast)?;
let full_path = source_rel.to_string_lossy().replace('\\', "/");
@ -145,20 +147,30 @@ pub fn compile_project(
// Merge symbols
for sym in ts.symbols.into_values() {
if let Err(existing) = ms.type_symbols.insert(sym) {
if let Some(existing) = ms.type_symbols.get(sym.name) {
return Err(DiagnosticBundle::error(
format!("Duplicate type symbol '{}' in module '{}'", existing.name, module_path),
format!(
"Duplicate type symbol '{}' in module '{}'",
interner.resolve(existing.name),
module_path
),
Some(existing.span)
).into());
}
let _ = ms.type_symbols.insert(sym);
}
for sym in vs.symbols.into_values() {
if let Err(existing) = ms.value_symbols.insert(sym) {
if let Some(existing) = ms.value_symbols.get(sym.name) {
return Err(DiagnosticBundle::error(
format!("Duplicate value symbol '{}' in module '{}'", existing.name, module_path),
format!(
"Duplicate value symbol '{}' in module '{}'",
interner.resolve(existing.name),
module_path
),
Some(existing.span)
).into());
}
let _ = ms.value_symbols.insert(sym);
}
parsed_files.push((module_path, ast));
@ -180,7 +192,7 @@ pub fn compile_project(
let ms = all_visible_modules.entry(synthetic_module_path.clone()).or_insert_with(ModuleSymbols::new);
let sym = Symbol {
name: key.symbol_name.clone(),
name: interner.intern(&key.symbol_name),
kind: match key.kind {
ExportSurfaceKind::Service => SymbolKind::Service,
ExportSurfaceKind::DeclareType => {
@ -200,21 +212,23 @@ pub fn compile_project(
};
if sym.namespace == Namespace::Type {
if let Err(existing) = ms.type_symbols.insert(sym.clone()) {
if let Some(existing) = ms.type_symbols.get(sym.name) {
return Err(CompileError::DuplicateExport {
symbol: sym.name,
first_dep: existing.origin.unwrap_or_else(|| "unknown".to_string()),
symbol: interner.resolve(sym.name).to_string(),
first_dep: existing.origin.clone().unwrap_or_else(|| "unknown".to_string()),
second_dep: sym.origin.unwrap_or_else(|| "unknown".to_string()),
});
}
let _ = ms.type_symbols.insert(sym.clone());
} else {
if let Err(existing) = ms.value_symbols.insert(sym.clone()) {
if let Some(existing) = ms.value_symbols.get(sym.name) {
return Err(CompileError::DuplicateExport {
symbol: sym.name,
first_dep: existing.origin.unwrap_or_else(|| "unknown".to_string()),
symbol: interner.resolve(sym.name).to_string(),
first_dep: existing.origin.clone().unwrap_or_else(|| "unknown".to_string()),
second_dep: sym.origin.unwrap_or_else(|| "unknown".to_string()),
});
}
let _ = ms.value_symbols.insert(sym.clone());
}
}
}
@ -231,7 +245,7 @@ pub fn compile_project(
for (module_path, ast) in &parsed_files {
let ms = module_symbols_map.get(module_path).unwrap();
let mut resolver = Resolver::new(ms, &module_provider);
let mut resolver = Resolver::new(ms, &module_provider, &interner);
resolver.resolve(ast)?;
// Capture imported symbols
@ -240,7 +254,7 @@ pub fn compile_project(
// TypeChecker also needs &mut ModuleSymbols
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);
let mut typechecker = TypeChecker::new(&mut ms_mut, imported, &module_provider, &interner);
typechecker.check(ast)?;
}
@ -255,7 +269,7 @@ pub fn compile_project(
for (module_path, ast) 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);
let lowerer = Lowerer::new(ms, imported, &interner);
let program = lowerer.lower_file(ast, module_path)?;
// Combine program into combined_program
@ -281,7 +295,7 @@ pub fn compile_project(
if let Some(surface_kind) = ExportSurfaceKind::from_symbol_kind(sym.kind) {
exports.insert(ExportKey {
module_path: module_path.clone(),
symbol_name: sym.name.clone(),
symbol_name: interner.resolve(sym.name).to_string(),
kind: surface_kind,
}, ExportMetadata {
func_idx: None,
@ -295,11 +309,15 @@ pub fn compile_project(
if sym.visibility == Visibility::Pub {
if let Some(surface_kind) = ExportSurfaceKind::from_symbol_kind(sym.kind) {
// Find func_idx if it's a function or service
let func_idx = vm_module.functions.iter().position(|f| f.name == sym.name).map(|i| i as u32);
let func_idx = vm_module
.functions
.iter()
.position(|f| f.name == interner.resolve(sym.name))
.map(|i| i as u32);
exports.insert(ExportKey {
module_path: module_path.clone(),
symbol_name: sym.name.clone(),
symbol_name: interner.resolve(sym.name).to_string(),
kind: surface_kind,
}, ExportMetadata {
func_idx,
@ -312,7 +330,12 @@ pub fn compile_project(
}
// 6. Collect symbols
let project_symbols = crate::common::symbols::collect_symbols(&step.project_id.name, &module_symbols_map, file_manager);
let project_symbols = crate::common::symbols::collect_symbols(
&step.project_id.name,
&module_symbols_map,
file_manager,
&interner,
);
// 7. Collect imports from unresolved labels
let mut imports = Vec::new();

View File

@ -1,4 +1,5 @@
use crate::common::spans::Span;
use prometeu_analysis::NameInterner;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
@ -60,19 +61,20 @@ pub fn collect_symbols(
project_id: &str,
module_symbols: &HashMap<String, crate::frontends::pbs::symbols::ModuleSymbols>,
file_manager: &crate::common::files::FileManager,
interner: &NameInterner,
) -> Vec<Symbol> {
let mut result = Vec::new();
for (module_path, ms) in module_symbols {
// Collect from type_symbols
for sym in ms.type_symbols.symbols.values() {
if let Some(s) = convert_symbol(project_id, module_path, sym, file_manager) {
if let Some(s) = convert_symbol(project_id, module_path, sym, file_manager, interner) {
result.push(s);
}
}
// Collect from value_symbols
for sym in ms.value_symbols.symbols.values() {
if let Some(s) = convert_symbol(project_id, module_path, sym, file_manager) {
if let Some(s) = convert_symbol(project_id, module_path, sym, file_manager, interner) {
result.push(s);
}
}
@ -94,6 +96,7 @@ fn convert_symbol(
module_path: &str,
sym: &crate::frontends::pbs::symbols::Symbol,
file_manager: &crate::common::files::FileManager,
interner: &NameInterner,
) -> Option<Symbol> {
use crate::frontends::pbs::symbols::{SymbolKind, Visibility};
@ -130,11 +133,12 @@ fn convert_symbol(
};
let hash = decl_span.compute_hash();
let id = format!("{}:{}:{}:{}:{:016x}", project_id, kind, module_path, sym.name, hash);
let name = interner.resolve(sym.name).to_string();
let id = format!("{}:{}:{}:{}:{:016x}", project_id, kind, module_path, name, hash);
Some(Symbol {
id,
name: sym.name.clone(),
name,
kind: kind.to_string(),
exported,
module_path: module_path.to_string(),

View File

@ -1,4 +1,5 @@
use crate::common::spans::Span;
use prometeu_analysis::NameId;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
@ -56,22 +57,22 @@ pub struct ImportNode {
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ImportSpecNode {
pub span: Span,
pub path: Vec<String>,
pub path: Vec<NameId>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ServiceDeclNode {
pub span: Span,
pub vis: Option<String>, // "pub" | "mod"
pub name: String,
pub extends: Option<String>,
pub name: NameId,
pub extends: Option<NameId>,
pub members: Vec<Node>, // ServiceFnSig
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ServiceFnSigNode {
pub span: Span,
pub name: String,
pub name: NameId,
pub params: Vec<ParamNode>,
pub ret: Box<Node>, // TypeName or TypeApp
}
@ -79,7 +80,7 @@ pub struct ServiceFnSigNode {
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ParamNode {
pub span: Span,
pub name: String,
pub name: NameId,
pub ty: Box<Node>,
}
@ -87,7 +88,7 @@ pub struct ParamNode {
pub struct FnDeclNode {
pub span: Span,
pub vis: Option<String>,
pub name: String,
pub name: NameId,
pub params: Vec<ParamNode>,
pub ret: Option<Box<Node>>,
pub else_fallback: Option<Box<Node>>, // Block
@ -99,7 +100,7 @@ pub struct TypeDeclNode {
pub span: Span,
pub vis: Option<String>,
pub type_kind: String, // "struct" | "contract" | "error"
pub name: String,
pub name: NameId,
pub is_host: bool,
pub params: Vec<ParamNode>, // fields for struct/error
pub constructors: Vec<ConstructorDeclNode>, // [ ... ]
@ -112,14 +113,14 @@ pub struct ConstructorDeclNode {
pub span: Span,
pub params: Vec<ParamNode>,
pub initializers: Vec<Node>,
pub name: String,
pub name: NameId,
pub body: Box<Node>, // BlockNode
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ConstantDeclNode {
pub span: Span,
pub name: String,
pub name: NameId,
pub value: Box<Node>,
}
@ -133,7 +134,7 @@ pub struct TypeBodyNode {
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct TypeMemberNode {
pub span: Span,
pub name: String,
pub name: NameId,
pub ty: Box<Node>,
}
@ -147,7 +148,7 @@ pub struct BlockNode {
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct LetStmtNode {
pub span: Span,
pub name: String,
pub name: NameId,
pub is_mut: bool,
pub ty: Option<Box<Node>>,
pub init: Box<Node>,
@ -192,7 +193,7 @@ pub struct StringLitNode {
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct IdentNode {
pub span: Span,
pub name: String,
pub name: NameId,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
@ -248,13 +249,13 @@ pub struct WhenArmNode {
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct TypeNameNode {
pub span: Span,
pub name: String,
pub name: NameId,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct TypeAppNode {
pub span: Span,
pub base: String,
pub base: NameId,
pub args: Vec<Node>,
}
@ -268,7 +269,7 @@ pub struct AllocNode {
pub struct MutateNode {
pub span: Span,
pub target: Box<Node>,
pub binding: String,
pub binding: NameId,
pub body: Box<Node>, // BlockNode
}
@ -276,7 +277,7 @@ pub struct MutateNode {
pub struct BorrowNode {
pub span: Span,
pub target: Box<Node>,
pub binding: String,
pub binding: NameId,
pub body: Box<Node>, // BlockNode
}
@ -284,7 +285,7 @@ pub struct BorrowNode {
pub struct PeekNode {
pub span: Span,
pub target: Box<Node>,
pub binding: String,
pub binding: NameId,
pub body: Box<Node>, // BlockNode
}
@ -292,5 +293,5 @@ pub struct PeekNode {
pub struct MemberAccessNode {
pub span: Span,
pub object: Box<Node>,
pub member: String,
pub member: NameId,
}

View File

@ -2,16 +2,19 @@ use crate::common::diagnostics::{Diagnostic, DiagnosticBundle, DiagnosticLevel};
use crate::frontends::pbs::ast::*;
use crate::frontends::pbs::symbols::*;
use crate::semantics::export_surface::ExportSurfaceKind;
use prometeu_analysis::NameInterner;
pub struct SymbolCollector {
pub struct SymbolCollector<'a> {
interner: &'a NameInterner,
type_symbols: SymbolTable,
value_symbols: SymbolTable,
diagnostics: Vec<Diagnostic>,
}
impl SymbolCollector {
pub fn new() -> Self {
impl<'a> SymbolCollector<'a> {
pub fn new(interner: &'a NameInterner) -> Self {
Self {
interner,
type_symbols: SymbolTable::new(),
value_symbols: SymbolTable::new(),
diagnostics: Vec::new(),
@ -113,27 +116,31 @@ impl SymbolCollector {
fn insert_type_symbol(&mut self, symbol: Symbol) {
// Check for collision in value namespace first
if let Some(existing) = self.value_symbols.get(&symbol.name) {
if let Some(existing) = self.value_symbols.get(symbol.name) {
let existing = existing.clone();
self.error_collision(&symbol, &existing);
return;
}
if let Err(existing) = self.type_symbols.insert(symbol.clone()) {
self.error_duplicate(&symbol, &existing);
if let Err(()) = self.type_symbols.insert(symbol.clone()) {
if let Some(existing) = self.type_symbols.get(symbol.name).cloned() {
self.error_duplicate(&symbol, &existing);
}
}
}
fn insert_value_symbol(&mut self, symbol: Symbol) {
// Check for collision in type namespace first
if let Some(existing) = self.type_symbols.get(&symbol.name) {
if let Some(existing) = self.type_symbols.get(symbol.name) {
let existing = existing.clone();
self.error_collision(&symbol, &existing);
return;
}
if let Err(existing) = self.value_symbols.insert(symbol.clone()) {
self.error_duplicate(&symbol, &existing);
if let Err(()) = self.value_symbols.insert(symbol.clone()) {
if let Some(existing) = self.value_symbols.get(symbol.name).cloned() {
self.error_duplicate(&symbol, &existing);
}
}
}
@ -141,7 +148,11 @@ impl SymbolCollector {
self.diagnostics.push(Diagnostic {
level: DiagnosticLevel::Error,
code: Some("E_RESOLVE_DUPLICATE_SYMBOL".to_string()),
message: format!("Duplicate symbol '{}' already defined at {:?}", symbol.name, existing.span),
message: format!(
"Duplicate symbol '{}' already defined at {:?}",
self.interner.resolve(symbol.name),
existing.span
),
span: Some(symbol.span),
});
}
@ -152,7 +163,9 @@ impl SymbolCollector {
code: Some("E_RESOLVE_NAMESPACE_COLLISION".to_string()),
message: format!(
"DebugSymbol '{}' collides with another symbol in the {:?} namespace defined at {:?}",
symbol.name, existing.namespace, existing.span
self.interner.resolve(symbol.name),
existing.namespace,
existing.span
),
span: Some(symbol.span),
});

View File

@ -7,6 +7,7 @@ use crate::frontends::pbs::types::PbsType;
use crate::ir_core;
use crate::ir_core::ids::{FieldId, FunctionId, TypeId, ValueId};
use crate::ir_core::{Block, Function, Instr, InstrKind, Module, Param, Program, Terminator, Type};
use prometeu_analysis::NameInterner;
use std::collections::HashMap;
#[derive(Clone)]
@ -18,6 +19,7 @@ struct LocalInfo {
pub struct Lowerer<'a> {
module_symbols: &'a ModuleSymbols,
imported_symbols: &'a ModuleSymbols,
interner: &'a NameInterner,
program: Program,
current_function: Option<Function>,
current_block: Option<Block>,
@ -38,7 +40,11 @@ pub struct Lowerer<'a> {
}
impl<'a> Lowerer<'a> {
pub fn new(module_symbols: &'a ModuleSymbols, imported_symbols: &'a ModuleSymbols) -> Self {
pub fn new(
module_symbols: &'a ModuleSymbols,
imported_symbols: &'a ModuleSymbols,
interner: &'a NameInterner,
) -> Self {
let mut field_offsets = HashMap::new();
field_offsets.insert(FieldId(0), 0); // V0 hardcoded field resolution foundation
@ -51,6 +57,7 @@ impl<'a> Lowerer<'a> {
Self {
module_symbols,
imported_symbols,
interner,
program: Program {
const_pool: ir_core::ConstPool::new(),
modules: Vec::new(),
@ -91,12 +98,14 @@ impl<'a> Lowerer<'a> {
if let Node::FnDecl(n) = decl {
let id = FunctionId(self.next_func_id);
self.next_func_id += 1;
self.function_ids.insert(n.name.clone(), id);
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(n.name.clone(), id);
self.type_ids
.insert(self.interner.resolve(n.name).to_string(), id);
}
}
@ -105,7 +114,7 @@ impl<'a> Lowerer<'a> {
for decl in &file.decls {
if let Node::TypeDecl(n) = decl {
if n.type_kind == "struct" {
struct_nodes.insert(n.name.clone(), n);
struct_nodes.insert(self.interner.resolve(n.name).to_string(), n);
}
}
}
@ -143,11 +152,12 @@ impl<'a> Lowerer<'a> {
for decl in &file.decls {
if let Node::TypeDecl(n) = decl {
let type_name = self.interner.resolve(n.name).to_string();
let mut constants = HashMap::new();
for c in &n.constants {
constants.insert(c.name.clone(), *c.value.clone());
constants.insert(self.interner.resolve(c.name).to_string(), *c.value.clone());
}
self.type_constants.insert(n.name.clone(), constants);
self.type_constants.insert(type_name.clone(), constants);
let mut ctors = HashMap::new();
@ -166,20 +176,20 @@ impl<'a> Lowerer<'a> {
span: n.span,
params,
initializers,
name: n.name.clone(),
name: n.name,
body: Box::new(Node::Block(BlockNode {
span: n.span,
stmts: Vec::new(),
tail: None,
})),
};
ctors.insert(n.name.clone(), default_ctor);
ctors.insert(type_name.clone(), default_ctor);
}
for ctor in &n.constructors {
ctors.insert(ctor.name.clone(), ctor.clone());
ctors.insert(self.interner.resolve(ctor.name).to_string(), ctor.clone());
}
self.struct_constructors.insert(n.name.clone(), ctors);
self.struct_constructors.insert(type_name, ctors);
}
}
@ -205,7 +215,8 @@ impl<'a> Lowerer<'a> {
}
fn lower_function(&mut self, n: &FnDeclNode) -> Result<Function, ()> {
let func_id = *self.function_ids.get(&n.name).unwrap();
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;
self.local_vars = vec![HashMap::new()];
self.max_slots_used = 0;
@ -217,10 +228,16 @@ impl<'a> Lowerer<'a> {
let ty = self.lower_type_node(&param.ty);
let slots = self.get_type_slots(&ty);
params.push(Param {
name: param.name.clone(),
name: self.interner.resolve(param.name).to_string(),
ty: ty.clone(),
});
self.local_vars[0].insert(param.name.clone(), LocalInfo { slot: param_slots, ty: ty.clone() });
self.local_vars[0].insert(
self.interner.resolve(param.name).to_string(),
LocalInfo {
slot: param_slots,
ty: ty.clone(),
},
);
for i in 0..slots {
local_types.insert(param_slots + i, ty.clone());
}
@ -237,7 +254,7 @@ impl<'a> Lowerer<'a> {
let func = Function {
id: func_id,
name: n.name.clone(),
name: func_name,
params,
return_type: ret_ty,
blocks: Vec::new(),
@ -325,11 +342,12 @@ impl<'a> Lowerer<'a> {
fn get_type_id_and_slots(&mut self, node: &Node) -> Result<(TypeId, u32), ()> {
match node {
Node::TypeName(n) => {
let slots = self.struct_slots.get(&n.name).cloned().unwrap_or(1);
let id = self.get_or_create_type_id(&n.name);
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 ta.base == "array" => {
Node::TypeApp(ta) if self.interner.resolve(ta.base) == "array" => {
let size = if ta.args.len() > 1 {
if let Node::IntLit(il) = &ta.args[1] {
il.value as u32
@ -376,7 +394,7 @@ impl<'a> Lowerer<'a> {
// 4. Bind view to local
self.local_vars.push(HashMap::new());
let view_slot = self.add_local_to_scope(n.binding.to_string(), Type::Int);
let view_slot = self.add_local_to_scope(self.interner.resolve(n.binding).to_string(), Type::Int);
self.emit(InstrKind::SetLocal(view_slot));
// 5. Body
@ -403,7 +421,7 @@ impl<'a> Lowerer<'a> {
// 4. Bind view to local
self.local_vars.push(HashMap::new());
let view_slot = self.add_local_to_scope(n.binding.to_string(), Type::Int);
let view_slot = self.add_local_to_scope(self.interner.resolve(n.binding).to_string(), Type::Int);
self.emit(InstrKind::SetLocal(view_slot));
// 5. Body
@ -430,7 +448,7 @@ impl<'a> Lowerer<'a> {
// 4. Bind view to local
self.local_vars.push(HashMap::new());
let view_slot = self.add_local_to_scope(n.binding.to_string(), Type::Int);
let view_slot = self.add_local_to_scope(self.interner.resolve(n.binding).to_string(), Type::Int);
self.emit(InstrKind::SetLocal(view_slot));
// 5. Body
@ -465,7 +483,9 @@ impl<'a> Lowerer<'a> {
if let Node::Call(call) = &*n.init {
if let Node::MemberAccess(ma) = &*call.callee {
if let Node::Ident(obj) = &*ma.object {
match (obj.name.as_str(), ma.member.as_str()) {
let obj_name = self.interner.resolve(obj.name);
let member_name = self.interner.resolve(ma.member);
match (obj_name, member_name) {
("Input", "pad") => Type::Struct("Pad".to_string()),
("Input", "touch") => Type::Struct("Touch".to_string()),
_ => Type::Int,
@ -476,7 +496,7 @@ impl<'a> Lowerer<'a> {
};
let slots = self.get_type_slots(&ty);
let slot = self.add_local_to_scope(n.name.clone(), ty);
let slot = self.add_local_to_scope(self.interner.resolve(n.name).to_string(), ty);
for i in (0..slots).rev() {
self.emit(InstrKind::SetLocal(slot + i));
@ -493,7 +513,8 @@ impl<'a> Lowerer<'a> {
}
fn lower_ident(&mut self, n: &IdentNode) -> Result<(), ()> {
if let Some(info) = self.find_local(&n.name) {
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);
for i in 0..slots {
self.emit(InstrKind::GetLocal(info.slot + i));
@ -501,7 +522,7 @@ impl<'a> Lowerer<'a> {
Ok(())
} else {
// Check for special identifiers
match n.name.as_str() {
match name_str {
"true" => {
let id = self.program.const_pool.add_int(1);
self.emit(InstrKind::PushConst(id));
@ -522,12 +543,20 @@ impl<'a> Lowerer<'a> {
}
// Check if it's a function (for first-class functions if supported)
if let Some(_id) = self.function_ids.get(&n.name) {
if let Some(_id) = self.function_ids.get(name_str) {
// Push function reference? Not in v0.
self.error("E_LOWER_UNSUPPORTED", format!("First-class function reference '{}' not supported", n.name), n.span);
self.error(
"E_LOWER_UNSUPPORTED",
format!("First-class function reference '{}' not supported", name_str),
n.span,
);
Err(())
} else {
self.error("E_RESOLVE_UNDEFINED", format!("Undefined identifier '{}'", n.name), n.span);
self.error(
"E_RESOLVE_UNDEFINED",
format!("Undefined identifier '{}'", name_str),
n.span,
);
Err(())
}
}
@ -535,17 +564,19 @@ impl<'a> Lowerer<'a> {
fn lower_member_access(&mut self, n: &MemberAccessNode) -> Result<(), ()> {
if let Node::Ident(id) = &*n.object {
if let Some(constants) = self.type_constants.get(&id.name).cloned() {
if let Some(const_val) = constants.get(&n.member) {
let old_ctx = self.current_type_context.replace(id.name.clone());
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);
self.current_type_context = old_ctx;
return res;
}
}
if id.name == "Color" {
let val = match n.member.as_str() {
if type_name == "Color" {
let val = match member_name {
"BLACK" => 0x0000,
"WHITE" => 0xFFFF,
"RED" => 0xF800,
@ -578,18 +609,21 @@ impl<'a> Lowerer<'a> {
fn resolve_member_access(&self, n: &MemberAccessNode) -> Option<(u32, Type)> {
match &*n.object {
Node::Ident(id) => {
let info = self.find_local(&id.name)?;
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, &n.member);
let ty = self.get_field_type(sname, &n.member);
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, &n.member);
let final_ty = self.get_field_type(sname, &n.member);
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 }
}
@ -649,9 +683,10 @@ impl<'a> Lowerer<'a> {
fn lower_call(&mut self, n: &CallNode) -> Result<(), ()> {
match &*n.callee {
Node::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(&id_node.name)
.and_then(|ctors| ctors.get(&id_node.name))
let ctor = self.struct_constructors.get(&callee_name)
.and_then(|ctors| ctors.get(&callee_name))
.cloned();
if let Some(ctor) = ctor {
@ -660,7 +695,7 @@ impl<'a> Lowerer<'a> {
if let Some(ctx) = &self.current_type_context {
let ctor = self.struct_constructors.get(ctx)
.and_then(|ctors| ctors.get(&id_node.name))
.and_then(|ctors| ctors.get(&callee_name))
.cloned();
if let Some(ctor) = ctor {
@ -671,10 +706,10 @@ impl<'a> Lowerer<'a> {
for arg in &n.args {
self.lower_node(arg)?;
}
if let Some(func_id) = self.function_ids.get(&id_node.name) {
if let Some(func_id) = self.function_ids.get(&callee_name) {
self.emit(InstrKind::Call(*func_id, n.args.len() as u32));
Ok(())
} else if let Some(sym) = self.imported_symbols.value_symbols.get(&id_node.name) {
} else if let Some(sym) = self.imported_symbols.value_symbols.get(id_node.name) {
if let Some(origin) = &sym.origin {
if origin.starts_with('@') {
// Format: @dep_alias:module_path
@ -682,32 +717,52 @@ impl<'a> Lowerer<'a> {
if parts.len() == 2 {
let dep_alias = parts[0].to_string();
let module_path = parts[1].to_string();
self.emit(InstrKind::ImportCall(dep_alias, module_path, sym.name.clone(), n.args.len() as u32));
self.emit(InstrKind::ImportCall(
dep_alias,
module_path,
self.interner.resolve(sym.name).to_string(),
n.args.len() as u32,
));
return Ok(());
}
}
}
self.error("E_LOWER_UNSUPPORTED", format!("Calling symbol '{}' with origin {:?} is not supported yet in v0", id_node.name, sym.origin), id_node.span);
self.error(
"E_LOWER_UNSUPPORTED",
format!(
"Calling symbol '{}' with origin {:?} is not supported yet in v0",
callee_name,
sym.origin
),
id_node.span,
);
Err(())
} else {
// Check for special built-in functions
match id_node.name.as_str() {
match callee_name.as_str() {
"some" | "ok" | "err" => {
return Ok(());
}
_ => {}
}
self.error("E_RESOLVE_UNDEFINED", format!("Undefined function '{}'", id_node.name), id_node.span);
self.error(
"E_RESOLVE_UNDEFINED",
format!("Undefined function '{}'", callee_name),
id_node.span,
);
Err(())
}
}
Node::MemberAccess(ma) => {
// Check if it's a constructor alias: TypeName.Alias(...)
let ctor = if let Node::Ident(obj_id) = &*ma.object {
self.struct_constructors.get(&obj_id.name)
.and_then(|ctors| ctors.get(&ma.member))
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()
} else {
None
@ -718,9 +773,11 @@ impl<'a> Lowerer<'a> {
}
// Check for Pad.any()
if ma.member == "any" {
let member_name = self.interner.resolve(ma.member);
if member_name == "any" {
if let Node::Ident(obj_id) = &*ma.object {
if let Some(info) = self.find_local(&obj_id.name) {
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 {
if sname == "Pad" {
self.lower_pad_any(info.slot);
@ -733,19 +790,20 @@ impl<'a> Lowerer<'a> {
// Host contract static calls: Contract.method(...)
if let Node::Ident(obj_id) = &*ma.object {
let is_local = self.find_local(&obj_id.name).is_some();
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)
.or_else(|| self.imported_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)?;
}
if let Some(method) = self.contract_registry.get_method(&obj_id.name, &ma.member) {
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 };
self.emit(InstrKind::HostCall(id, return_slots));
@ -757,15 +815,15 @@ impl<'a> Lowerer<'a> {
}
// Check for .raw()
if ma.member == "raw" {
if member_name == "raw" {
self.lower_node(&ma.object)?;
return Ok(());
}
// Check for Color.rgb
if ma.member == "rgb" {
if member_name == "rgb" {
if let Node::Ident(obj_id) = &*ma.object {
if obj_id.name == "Color" {
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();
@ -798,20 +856,25 @@ impl<'a> Lowerer<'a> {
}
if let Node::Ident(obj_id) = &*ma.object {
let is_host_contract = self.module_symbols.type_symbols.get(&obj_id.name)
let obj_name = self.interner.resolve(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_id.name).is_some();
let is_shadowed = self.find_local(obj_name).is_some();
if is_host_contract && !is_shadowed {
if let Some(method) = self.contract_registry.get_method(&obj_id.name, &ma.member) {
if let Some(method) = self.contract_registry.get_method(obj_name, member_name) {
let ir_ty = self.convert_pbs_type(&method.return_type);
let return_slots = self.get_type_slots(&ir_ty);
self.emit(InstrKind::HostCall(method.id, return_slots));
return Ok(());
} else {
self.error("E_RESOLVE_UNDEFINED", format!("Undefined contract member '{}.{}'", obj_id.name, ma.member), ma.span);
self.error(
"E_RESOLVE_UNDEFINED",
format!("Undefined contract member '{}.{}'", obj_name, member_name),
ma.span,
);
return Err(());
}
}
@ -834,7 +897,7 @@ impl<'a> Lowerer<'a> {
let mut param_map = HashMap::new();
for (i, param) in ctor.params.iter().enumerate() {
if i < args.len() {
param_map.insert(param.name.clone(), args[i].clone());
param_map.insert(self.interner.resolve(param.name).to_string(), args[i].clone());
}
}
@ -848,7 +911,7 @@ impl<'a> Lowerer<'a> {
fn substitute_node(&self, node: &Node, param_map: &HashMap<String, Node>) -> Node {
match node {
Node::Ident(id) => {
if let Some(arg) = param_map.get(&id.name) {
if let Some(arg) = param_map.get(self.interner.resolve(id.name)) {
arg.clone()
} else {
node.clone()
@ -961,17 +1024,18 @@ impl<'a> Lowerer<'a> {
fn lower_type_node(&mut self, node: &Node) -> Type {
match node {
Node::TypeName(n) => match n.name.as_str() {
Node::TypeName(n) => match self.interner.resolve(n.name) {
"int" => Type::Int,
"bounded" => Type::Bounded,
"float" => Type::Float,
"bool" => Type::Bool,
"string" => Type::String,
"void" => Type::Void,
_ => Type::Struct(n.name.clone()),
_ => Type::Struct(self.interner.resolve(n.name).to_string()),
},
Node::TypeApp(ta) => {
if ta.base == "array" {
let base_name = self.interner.resolve(ta.base);
if base_name == "array" {
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] {
@ -983,15 +1047,15 @@ impl<'a> Lowerer<'a> {
0
};
Type::Array(Box::new(elem_ty), size)
} else if ta.base == "optional" {
} else if base_name == "optional" {
Type::Optional(Box::new(self.lower_type_node(&ta.args[0])))
} else if ta.base == "result" {
} 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]))
)
} else {
Type::Struct(format!("{}<{}>", ta.base, ta.args.len()))
Type::Struct(format!("{}<{}>", base_name, ta.args.len()))
}
}
_ => Type::Void,
@ -1126,6 +1190,7 @@ mod tests {
use crate::frontends::pbs::parser::Parser;
use crate::frontends::pbs::symbols::ModuleSymbols;
use crate::ir_core;
use prometeu_analysis::NameInterner;
#[test]
fn test_basic_lowering() {
@ -1137,14 +1202,16 @@ mod tests {
let x = add(10, 20);
}
";
let mut parser = Parser::new(code, 0);
let mut interner = NameInterner::new();
let mut parser = Parser::new(code, 0, &mut interner);
let ast = parser.parse_file().expect("Failed to parse");
let mut collector = SymbolCollector::new();
let mut collector = SymbolCollector::new(&interner);
let (type_symbols, value_symbols) = collector.collect(&ast).expect("Failed to collect symbols");
let module_symbols = ModuleSymbols { type_symbols, value_symbols };
let imported = ModuleSymbols::new(); let lowerer = Lowerer::new(&module_symbols, &imported);
let imported = ModuleSymbols::new();
let lowerer = Lowerer::new(&module_symbols, &imported, &interner);
let program = lowerer.lower_file(&ast, "test").expect("Lowering failed");
// Verify program structure
@ -1174,14 +1241,16 @@ mod tests {
}
}
";
let mut parser = Parser::new(code, 0);
let mut interner = NameInterner::new();
let mut parser = Parser::new(code, 0, &mut interner);
let ast = parser.parse_file().expect("Failed to parse");
let mut collector = SymbolCollector::new();
let mut collector = SymbolCollector::new(&interner);
let (type_symbols, value_symbols) = collector.collect(&ast).expect("Failed to collect symbols");
let module_symbols = ModuleSymbols { type_symbols, value_symbols };
let imported = ModuleSymbols::new(); let lowerer = Lowerer::new(&module_symbols, &imported);
let imported = ModuleSymbols::new();
let lowerer = Lowerer::new(&module_symbols, &imported, &interner);
let program = lowerer.lower_file(&ast, "test").expect("Lowering failed");
let max_func = &program.modules[0].functions[0];
@ -1199,14 +1268,16 @@ mod tests {
}
}
";
let mut parser = Parser::new(code, 0);
let mut interner = NameInterner::new();
let mut parser = Parser::new(code, 0, &mut interner);
let ast = parser.parse_file().expect("Failed to parse");
let mut collector = SymbolCollector::new();
let mut collector = SymbolCollector::new(&interner);
let (type_symbols, value_symbols) = collector.collect(&ast).expect("Failed to collect symbols");
let module_symbols = ModuleSymbols { type_symbols, value_symbols };
let imported = ModuleSymbols::new(); let lowerer = Lowerer::new(&module_symbols, &imported);
let imported = ModuleSymbols::new();
let lowerer = Lowerer::new(&module_symbols, &imported, &interner);
let program = lowerer.lower_file(&ast, "test").expect("Lowering failed");
let func = &program.modules[0].functions[0];
@ -1227,14 +1298,16 @@ mod tests {
}
}
";
let mut parser = Parser::new(code, 0);
let mut interner = NameInterner::new();
let mut parser = Parser::new(code, 0, &mut interner);
let ast = parser.parse_file().expect("Failed to parse");
let mut collector = SymbolCollector::new();
let mut collector = SymbolCollector::new(&interner);
let (type_symbols, value_symbols) = collector.collect(&ast).unwrap();
let module_symbols = ModuleSymbols { type_symbols, value_symbols };
let imported = ModuleSymbols::new(); let lowerer = Lowerer::new(&module_symbols, &imported);
let imported = ModuleSymbols::new();
let lowerer = Lowerer::new(&module_symbols, &imported, &interner);
let program = lowerer.lower_file(&ast, "test").expect("Lowering failed");
let json = serde_json::to_string_pretty(&program).unwrap();
@ -1268,14 +1341,16 @@ mod tests {
}
}
";
let mut parser = Parser::new(code, 0);
let mut interner = NameInterner::new();
let mut parser = Parser::new(code, 0, &mut interner);
let ast = parser.parse_file().expect("Failed to parse");
let mut collector = SymbolCollector::new();
let mut collector = SymbolCollector::new(&interner);
let (type_symbols, value_symbols) = collector.collect(&ast).expect("Failed to collect symbols");
let module_symbols = ModuleSymbols { type_symbols, value_symbols };
let imported = ModuleSymbols::new(); let lowerer = Lowerer::new(&module_symbols, &imported);
let imported = ModuleSymbols::new();
let lowerer = Lowerer::new(&module_symbols, &imported, &interner);
let program = lowerer.lower_file(&ast, "test").expect("Lowering failed");
let func = &program.modules[0].functions[0];
@ -1302,14 +1377,16 @@ mod tests {
Log.write(2, \"Hello\");
}
";
let mut parser = Parser::new(code, 0);
let mut interner = NameInterner::new();
let mut parser = Parser::new(code, 0, &mut interner);
let ast = parser.parse_file().expect("Failed to parse");
let mut collector = SymbolCollector::new();
let mut collector = SymbolCollector::new(&interner);
let (type_symbols, value_symbols) = collector.collect(&ast).expect("Failed to collect symbols");
let module_symbols = ModuleSymbols { type_symbols, value_symbols };
let imported = ModuleSymbols::new(); let lowerer = Lowerer::new(&module_symbols, &imported);
let imported = ModuleSymbols::new();
let lowerer = Lowerer::new(&module_symbols, &imported, &interner);
let program = lowerer.lower_file(&ast, "test").expect("Lowering failed");
let func = &program.modules[0].functions[0];
@ -1329,14 +1406,16 @@ mod tests {
Gfx.clear(0);
}
";
let mut parser = Parser::new(code, 0);
let mut interner = NameInterner::new();
let mut parser = Parser::new(code, 0, &mut interner);
let ast = parser.parse_file().expect("Failed to parse");
let mut collector = SymbolCollector::new();
let mut collector = SymbolCollector::new(&interner);
let (type_symbols, value_symbols) = collector.collect(&ast).expect("Failed to collect symbols");
let module_symbols = ModuleSymbols { type_symbols, value_symbols };
let imported = ModuleSymbols::new(); let lowerer = Lowerer::new(&module_symbols, &imported);
let imported = ModuleSymbols::new();
let lowerer = Lowerer::new(&module_symbols, &imported, &interner);
let result = lowerer.lower_file(&ast, "test");
assert!(result.is_err());
@ -1353,14 +1432,16 @@ mod tests {
Gfx.clear(0);
}
";
let mut parser = Parser::new(code, 0);
let mut interner = NameInterner::new();
let mut parser = Parser::new(code, 0, &mut interner);
let ast = parser.parse_file().expect("Failed to parse");
let mut collector = SymbolCollector::new();
let mut collector = SymbolCollector::new(&interner);
let (type_symbols, value_symbols) = collector.collect(&ast).expect("Failed to collect symbols");
let module_symbols = ModuleSymbols { type_symbols, value_symbols };
let imported = ModuleSymbols::new(); let lowerer = Lowerer::new(&module_symbols, &imported);
let imported = ModuleSymbols::new();
let lowerer = Lowerer::new(&module_symbols, &imported, &interner);
let result = lowerer.lower_file(&ast, "test");
assert!(result.is_err());
@ -1376,14 +1457,16 @@ mod tests {
Gfx.invalidMethod(0);
}
";
let mut parser = Parser::new(code, 0);
let mut interner = NameInterner::new();
let mut parser = Parser::new(code, 0, &mut interner);
let ast = parser.parse_file().expect("Failed to parse");
let mut collector = SymbolCollector::new();
let mut collector = SymbolCollector::new(&interner);
let (type_symbols, value_symbols) = collector.collect(&ast).expect("Failed to collect symbols");
let module_symbols = ModuleSymbols { type_symbols, value_symbols };
let imported = ModuleSymbols::new(); let lowerer = Lowerer::new(&module_symbols, &imported);
let imported = ModuleSymbols::new();
let lowerer = Lowerer::new(&module_symbols, &imported, &interner);
let result = lowerer.lower_file(&ast, "test");
assert!(result.is_err());
@ -1399,14 +1482,16 @@ mod tests {
let v = alloc Vec3;
}
";
let mut parser = Parser::new(code, 0);
let mut interner = NameInterner::new();
let mut parser = Parser::new(code, 0, &mut interner);
let ast = parser.parse_file().expect("Failed to parse");
let mut collector = SymbolCollector::new();
let mut collector = SymbolCollector::new(&interner);
let (type_symbols, value_symbols) = collector.collect(&ast).expect("Failed to collect symbols");
let module_symbols = ModuleSymbols { type_symbols, value_symbols };
let imported = ModuleSymbols::new(); let lowerer = Lowerer::new(&module_symbols, &imported);
let imported = ModuleSymbols::new();
let lowerer = Lowerer::new(&module_symbols, &imported, &interner);
let program = lowerer.lower_file(&ast, "test").expect("Lowering failed");
let func = &program.modules[0].functions[0];
@ -1431,14 +1516,16 @@ mod tests {
let a = alloc array<int>[10b];
}
";
let mut parser = Parser::new(code, 0);
let mut interner = NameInterner::new();
let mut parser = Parser::new(code, 0, &mut interner);
let ast = parser.parse_file().expect("Failed to parse");
let mut collector = SymbolCollector::new();
let mut collector = SymbolCollector::new(&interner);
let (type_symbols, value_symbols) = collector.collect(&ast).expect("Failed to collect symbols");
let module_symbols = ModuleSymbols { type_symbols, value_symbols };
let imported = ModuleSymbols::new(); let lowerer = Lowerer::new(&module_symbols, &imported);
let imported = ModuleSymbols::new();
let lowerer = Lowerer::new(&module_symbols, &imported, &interner);
let program = lowerer.lower_file(&ast, "test").expect("Lowering failed");
let func = &program.modules[0].functions[0];
@ -1463,14 +1550,16 @@ mod tests {
let x = alloc int;
}
";
let mut parser = Parser::new(code, 0);
let mut interner = NameInterner::new();
let mut parser = Parser::new(code, 0, &mut interner);
let ast = parser.parse_file().expect("Failed to parse");
let mut collector = SymbolCollector::new();
let mut collector = SymbolCollector::new(&interner);
let (type_symbols, value_symbols) = collector.collect(&ast).expect("Failed to collect symbols");
let module_symbols = ModuleSymbols { type_symbols, value_symbols };
let imported = ModuleSymbols::new(); let lowerer = Lowerer::new(&module_symbols, &imported);
let imported = ModuleSymbols::new();
let lowerer = Lowerer::new(&module_symbols, &imported, &interner);
let program = lowerer.lower_file(&ast, "test").expect("Lowering failed");
let func = &program.modules[0].functions[0];
@ -1495,14 +1584,16 @@ mod tests {
missing_func();
}
";
let mut parser = Parser::new(code, 0);
let mut interner = NameInterner::new();
let mut parser = Parser::new(code, 0, &mut interner);
let ast = parser.parse_file().expect("Failed to parse");
let mut collector = SymbolCollector::new();
let mut collector = SymbolCollector::new(&interner);
let (type_symbols, value_symbols) = collector.collect(&ast).expect("Failed to collect symbols");
let module_symbols = ModuleSymbols { type_symbols, value_symbols };
let imported = ModuleSymbols::new(); let lowerer = Lowerer::new(&module_symbols, &imported);
let imported = ModuleSymbols::new();
let lowerer = Lowerer::new(&module_symbols, &imported, &interner);
let result = lowerer.lower_file(&ast, "test");
assert!(result.is_err());
@ -1518,14 +1609,16 @@ mod tests {
let x = undefined_var;
}
";
let mut parser = Parser::new(code, 0);
let mut interner = NameInterner::new();
let mut parser = Parser::new(code, 0, &mut interner);
let ast = parser.parse_file().expect("Failed to parse");
let mut collector = SymbolCollector::new();
let mut collector = SymbolCollector::new(&interner);
let (type_symbols, value_symbols) = collector.collect(&ast).expect("Failed to collect symbols");
let module_symbols = ModuleSymbols { type_symbols, value_symbols };
let imported = ModuleSymbols::new(); let lowerer = Lowerer::new(&module_symbols, &imported);
let imported = ModuleSymbols::new();
let lowerer = Lowerer::new(&module_symbols, &imported, &interner);
let result = lowerer.lower_file(&ast, "test");
assert!(result.is_err());

View File

@ -23,6 +23,7 @@ use crate::common::files::FileManager;
use crate::frontends::Frontend;
use crate::ir_vm;
use crate::lowering::core_to_vm;
use prometeu_analysis::NameInterner;
use std::path::Path;
pub struct PbsFrontend;
@ -42,10 +43,11 @@ impl Frontend for PbsFrontend {
})?;
let file_id = file_manager.add(entry.to_path_buf(), source.clone());
let mut parser = parser::Parser::new(&source, file_id);
let mut interner = NameInterner::new();
let mut parser = parser::Parser::new(&source, file_id, &mut interner);
let ast = parser.parse_file()?;
let mut collector = SymbolCollector::new();
let mut collector = SymbolCollector::new(&interner);
let (type_symbols, value_symbols) = collector.collect(&ast)?;
let mut module_symbols = ModuleSymbols { type_symbols, value_symbols };
@ -54,15 +56,15 @@ impl Frontend for PbsFrontend {
fn get_module_symbols(&self, _path: &str) -> Option<&ModuleSymbols> { None }
}
let mut resolver = Resolver::new(&module_symbols, &EmptyProvider);
let mut resolver = Resolver::new(&module_symbols, &EmptyProvider, &interner);
resolver.resolve(&ast)?;
let imported_symbols = resolver.imported_symbols;
let mut typechecker = TypeChecker::new(&mut module_symbols, &imported_symbols, &EmptyProvider);
let mut typechecker = TypeChecker::new(&mut module_symbols, &imported_symbols, &EmptyProvider, &interner);
typechecker.check(&ast)?;
// Lower to Core IR
let lowerer = Lowerer::new(&module_symbols, &imported_symbols);
let lowerer = Lowerer::new(&module_symbols, &imported_symbols, &interner);
let module_name = entry.file_stem().unwrap().to_string_lossy();
let core_program = lowerer.lower_file(&ast, &module_name)?;

View File

@ -3,16 +3,29 @@ use crate::common::spans::Span;
use crate::frontends::pbs::ast::*;
use crate::frontends::pbs::lexer::Lexer;
use crate::frontends::pbs::token::{Token, TokenKind};
use prometeu_analysis::{NameId, NameInterner};
pub struct Parser {
pub struct Parser<'a> {
tokens: Vec<Token>,
pos: usize,
file_id: usize,
errors: Vec<Diagnostic>,
interner: &'a mut NameInterner,
builtin_none: NameId,
builtin_some: NameId,
builtin_ok: NameId,
builtin_err: NameId,
builtin_true: NameId,
builtin_false: NameId,
builtin_void: NameId,
builtin_array: NameId,
builtin_optional: NameId,
builtin_result: NameId,
builtin_bounded: NameId,
}
impl Parser {
pub fn new(source: &str, file_id: usize) -> Self {
impl<'a> Parser<'a> {
pub fn new(source: &str, file_id: usize, interner: &'a mut NameInterner) -> Self {
let mut lexer = Lexer::new(source, file_id);
let mut tokens = Vec::new();
loop {
@ -24,11 +37,35 @@ impl Parser {
}
}
let builtin_none = interner.intern("none");
let builtin_some = interner.intern("some");
let builtin_ok = interner.intern("ok");
let builtin_err = interner.intern("err");
let builtin_true = interner.intern("true");
let builtin_false = interner.intern("false");
let builtin_void = interner.intern("void");
let builtin_array = interner.intern("array");
let builtin_optional = interner.intern("optional");
let builtin_result = interner.intern("result");
let builtin_bounded = interner.intern("bounded");
Self {
tokens,
pos: 0,
file_id,
errors: Vec::new(),
interner,
builtin_none,
builtin_some,
builtin_ok,
builtin_err,
builtin_true,
builtin_false,
builtin_void,
builtin_array,
builtin_optional,
builtin_result,
builtin_bounded,
}
}
@ -112,12 +149,12 @@ impl Parser {
if self.peek().kind == TokenKind::OpenBrace {
self.advance(); // {
loop {
if let TokenKind::Identifier(ref name) = self.peek().kind {
path.push(name.clone());
self.advance();
} else {
return Err(self.error("Expected identifier in import spec"));
}
let name = match self.peek().kind.clone() {
TokenKind::Identifier(name) => name,
_ => return Err(self.error("Expected identifier in import spec")),
};
self.advance();
path.push(self.interner.intern(&name));
if self.peek().kind == TokenKind::Comma {
self.advance();
@ -130,12 +167,12 @@ impl Parser {
self.consume(TokenKind::CloseBrace)?;
} else {
loop {
if let TokenKind::Identifier(ref name) = self.peek().kind {
path.push(name.clone());
self.advance();
} else {
return Err(self.error("Expected identifier in import spec"));
}
let name = match self.peek().kind.clone() {
TokenKind::Identifier(name) => name,
_ => return Err(self.error("Expected identifier in import spec")),
};
self.advance();
path.push(self.interner.intern(&name));
if self.peek().kind == TokenKind::Dot {
self.advance();
@ -227,7 +264,7 @@ impl Parser {
} else {
Box::new(Node::TypeName(TypeNameNode {
span: Span::new(self.file_id, 0, 0), // Placeholder for void
name: "void".to_string(),
name: self.builtin_void,
}))
};
@ -413,35 +450,35 @@ impl Parser {
let name = match id_tok.kind {
TokenKind::Identifier(ref s) => {
self.advance();
s.clone()
self.interner.intern(s)
}
TokenKind::Optional => {
self.advance();
"optional".to_string()
self.builtin_optional
}
TokenKind::Result => {
self.advance();
"result".to_string()
self.builtin_result
}
TokenKind::Bounded => {
self.advance();
"bounded".to_string()
self.builtin_bounded
}
TokenKind::None => {
self.advance();
"none".to_string()
self.builtin_none
}
TokenKind::Some => {
self.advance();
"some".to_string()
self.builtin_some
}
TokenKind::Ok => {
self.advance();
"ok".to_string()
self.builtin_ok
}
TokenKind::Err => {
self.advance();
"err".to_string()
self.builtin_err
}
_ => return Err(self.error_with_code("Expected type name", Some("E_PARSE_EXPECTED_TOKEN"))),
};
@ -491,7 +528,7 @@ impl Parser {
// 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 == "array" => {
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;
}
@ -499,7 +536,7 @@ impl Parser {
// Fallback for T[N] if we want to support it, but spec says array<T>[N]
node = Node::TypeApp(TypeAppNode {
span,
base: "array".to_string(),
base: self.builtin_array,
args: vec![node, Node::IntLit(IntLitNode { span: size_tok.span, value: size as i64 })],
});
}
@ -673,6 +710,7 @@ impl Parser {
}
TokenKind::Identifier(name) => {
self.advance();
let name = self.interner.intern(&name);
let mut node = Node::Ident(IdentNode { span: tok.span, name });
loop {
if self.peek().kind == TokenKind::OpenParen {
@ -689,12 +727,12 @@ impl Parser {
}
TokenKind::None | TokenKind::Some | TokenKind::Ok | TokenKind::Err => {
let name = match tok.kind {
TokenKind::None => "none",
TokenKind::Some => "some",
TokenKind::Ok => "ok",
TokenKind::Err => "err",
TokenKind::None => self.builtin_none,
TokenKind::Some => self.builtin_some,
TokenKind::Ok => self.builtin_ok,
TokenKind::Err => self.builtin_err,
_ => unreachable!(),
}.to_string();
};
self.advance();
let mut node = Node::Ident(IdentNode { span: tok.span, name });
loop {
@ -887,40 +925,40 @@ impl Parser {
}
}
fn expect_identifier(&mut self) -> Result<String, DiagnosticBundle> {
fn expect_identifier(&mut self) -> Result<NameId, DiagnosticBundle> {
let peeked_kind = self.peek().kind.clone();
match peeked_kind {
TokenKind::Identifier(name) => {
self.advance();
Ok(name)
Ok(self.interner.intern(&name))
}
TokenKind::Optional => {
self.advance();
Ok("optional".to_string())
Ok(self.builtin_optional)
}
TokenKind::Result => {
self.advance();
Ok("result".to_string())
Ok(self.builtin_result)
}
TokenKind::None => {
self.advance();
Ok("none".to_string())
Ok(self.builtin_none)
}
TokenKind::Some => {
self.advance();
Ok("some".to_string())
Ok(self.builtin_some)
}
TokenKind::Ok => {
self.advance();
Ok("ok".to_string())
Ok(self.builtin_ok)
}
TokenKind::Err => {
self.advance();
Ok("err".to_string())
Ok(self.builtin_err)
}
TokenKind::Bounded => {
self.advance();
Ok("bounded".to_string())
Ok(self.builtin_bounded)
}
TokenKind::Invalid(msg) => {
let code = if msg.contains("Unterminated string") {
@ -1036,7 +1074,8 @@ mod tests {
#[test]
fn test_parse_empty_file() {
let mut parser = Parser::new("", 0);
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);
@ -1048,14 +1087,16 @@ mod tests {
import std.io from "std";
import math from "./math.pbs";
"#;
let mut parser = Parser::new(source, 0);
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);
if let Node::Import(ref imp) = result.imports[0] {
assert_eq!(imp.from, "std");
if let Node::ImportSpec(ref spec) = *imp.spec {
assert_eq!(spec.path, vec!["std", "io"]);
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"); }
}
@ -1067,15 +1108,16 @@ fn add(a: int, b: int): int {
return a + b;
}
"#;
let mut parser = Parser::new(source, 0);
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);
if let Node::FnDecl(ref f) = result.decls[0] {
assert_eq!(f.name, "add");
assert_eq!(interner.resolve(f.name), "add");
assert_eq!(f.params.len(), 2);
assert_eq!(f.params[0].name, "a");
assert_eq!(f.params[1].name, "b");
assert_eq!(interner.resolve(f.params[0].name), "a");
assert_eq!(interner.resolve(f.params[1].name), "b");
} else { panic!("Expected FnDecl"); }
}
@ -1087,12 +1129,13 @@ pub declare struct Point {
y: int
}
"#;
let mut parser = Parser::new(source, 0);
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);
if let Node::TypeDecl(ref t) = result.decls[0] {
assert_eq!(t.name, "Point");
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"); }
@ -1106,12 +1149,13 @@ pub service Audio {
fn stop(): bool;
}
"#;
let mut parser = Parser::new(source, 0);
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);
if let Node::ServiceDecl(ref s) = result.decls[0] {
assert_eq!(s.name, "Audio");
assert_eq!(interner.resolve(s.name), "Audio");
assert_eq!(s.members.len(), 2);
} else { panic!("Expected ServiceDecl"); }
}
@ -1125,7 +1169,8 @@ fn main() {
foo(x, y);
}
"#;
let mut parser = Parser::new(source, 0);
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);
}
@ -1146,7 +1191,8 @@ fn main(x: int) {
};
}
"#;
let mut parser = Parser::new(source, 0);
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);
}
@ -1161,7 +1207,8 @@ fn bad() {
fn good() {}
"#;
let mut parser = Parser::new(source, 0);
let mut interner = NameInterner::new();
let mut parser = Parser::new(source, 0, &mut interner);
let result = parser.parse_file();
assert!(result.is_err());
}
@ -1169,7 +1216,8 @@ fn good() {}
#[test]
fn test_parse_mod_fn() {
let source = "mod fn test() {}";
let mut parser = Parser::new(source, 0);
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] {
assert_eq!(fn_decl.vis, Some("mod".to_string()));
@ -1181,7 +1229,8 @@ fn good() {}
#[test]
fn test_parse_pub_fn() {
let source = "pub fn test() {}";
let mut parser = Parser::new(source, 0);
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] {
assert_eq!(fn_decl.vis, Some("pub".to_string()));
@ -1197,12 +1246,17 @@ fn main() {
return 42;
}
"#;
let mut parser = Parser::new(source, 0);
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)).unwrap();
let json = serde_json::to_string_pretty(&Node::File(result.clone())).unwrap();
assert!(json.contains("\"kind\": \"File\""));
assert!(json.contains("\"kind\": \"FnDecl\""));
assert!(json.contains("\"name\": \"main\""));
if let Node::FnDecl(fn_decl) = &result.decls[0] {
assert_eq!(interner.resolve(fn_decl.name), "main");
} else {
panic!("Expected FnDecl");
}
}
}

View File

@ -2,6 +2,7 @@ use crate::common::diagnostics::{Diagnostic, DiagnosticBundle, DiagnosticLevel};
use crate::common::spans::Span;
use crate::frontends::pbs::ast::*;
use crate::frontends::pbs::symbols::*;
use prometeu_analysis::{NameId, NameInterner};
use std::collections::HashMap;
pub trait ModuleProvider {
@ -9,9 +10,10 @@ pub trait ModuleProvider {
}
pub struct Resolver<'a> {
interner: &'a NameInterner,
module_provider: &'a dyn ModuleProvider,
current_module: &'a ModuleSymbols,
scopes: Vec<HashMap<String, Symbol>>,
scopes: Vec<HashMap<NameId, Symbol>>,
pub imported_symbols: ModuleSymbols,
diagnostics: Vec<Diagnostic>,
}
@ -20,8 +22,10 @@ impl<'a> Resolver<'a> {
pub fn new(
current_module: &'a ModuleSymbols,
module_provider: &'a dyn ModuleProvider,
interner: &'a NameInterner,
) -> Self {
Self {
interner,
module_provider,
current_module,
scopes: Vec::new(),
@ -58,30 +62,30 @@ impl<'a> Resolver<'a> {
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 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);
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) {
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);
self.error_duplicate_import(*name, imp.span);
}
} else {
self.error_visibility(sym, imp.span);
}
} else {
self.error_undefined(name, imp.span);
self.error_undefined(*name, imp.span);
}
}
}
@ -139,13 +143,13 @@ impl<'a> Resolver<'a> {
}
}
Node::Ident(n) => {
self.resolve_identifier(&n.name, n.span, Namespace::Value);
self.resolve_identifier(n.name, n.span, Namespace::Value);
}
Node::TypeName(n) => {
self.resolve_identifier(&n.name, n.span, Namespace::Type);
self.resolve_identifier(n.name, n.span, Namespace::Type);
}
Node::TypeApp(n) => {
self.resolve_identifier(&n.base, n.span, Namespace::Type);
self.resolve_identifier(n.base, n.span, Namespace::Type);
for arg in &n.args {
self.resolve_type_ref(arg);
}
@ -158,32 +162,32 @@ impl<'a> Resolver<'a> {
Node::Mutate(n) => {
self.resolve_node(&n.target);
self.enter_scope();
self.define_local(&n.binding, n.span, SymbolKind::Local);
self.define_local(n.binding, n.span, SymbolKind::Local);
self.resolve_node(&n.body);
self.exit_scope();
}
Node::Borrow(n) => {
self.resolve_node(&n.target);
self.enter_scope();
self.define_local(&n.binding, n.span, SymbolKind::Local);
self.define_local(n.binding, n.span, SymbolKind::Local);
self.resolve_node(&n.body);
self.exit_scope();
}
Node::Peek(n) => {
self.resolve_node(&n.target);
self.enter_scope();
self.define_local(&n.binding, n.span, SymbolKind::Local);
self.define_local(n.binding, n.span, SymbolKind::Local);
self.resolve_node(&n.body);
self.exit_scope();
}
Node::MemberAccess(n) => {
if let Node::Ident(id) = &*n.object {
if !self.is_builtin(&id.name, Namespace::Type) {
if self.lookup_identifier(&id.name, Namespace::Value).is_none() {
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() {
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, id.span, Namespace::Value);
}
}
}
@ -199,7 +203,7 @@ impl<'a> Resolver<'a> {
self.enter_scope();
for param in &n.params {
self.resolve_type_ref(&param.ty);
self.define_local(&param.name, param.span, SymbolKind::Local);
self.define_local(param.name, param.span, SymbolKind::Local);
}
if let Some(ret) = &n.ret {
self.resolve_type_ref(ret);
@ -210,7 +214,7 @@ impl<'a> Resolver<'a> {
fn resolve_service_decl(&mut self, n: &ServiceDeclNode) {
if let Some(ext) = &n.extends {
self.resolve_identifier(ext, n.span, Namespace::Type);
self.resolve_identifier(*ext, n.span, Namespace::Type);
}
for member in &n.members {
if let Node::ServiceFnSig(sig) = member {
@ -231,7 +235,7 @@ impl<'a> Resolver<'a> {
}
self.enter_scope();
for ctor in &n.constructors {
self.define_local(&ctor.name, ctor.span, SymbolKind::Local);
self.define_local(ctor.name, ctor.span, SymbolKind::Local);
}
for constant in &n.constants {
self.resolve_node(&constant.value);
@ -256,7 +260,7 @@ impl<'a> Resolver<'a> {
self.enter_scope();
for param in &n.params {
self.resolve_type_ref(&param.ty);
self.define_local(&param.name, param.span, SymbolKind::Local);
self.define_local(param.name, param.span, SymbolKind::Local);
}
for init in &n.initializers {
self.resolve_node(init);
@ -278,14 +282,14 @@ impl<'a> Resolver<'a> {
self.resolve_type_ref(ty);
}
self.resolve_node(&n.init);
self.define_local(&n.name, n.span, SymbolKind::Local);
self.define_local(n.name, n.span, SymbolKind::Local);
}
fn resolve_type_ref(&mut self, node: &Node) {
self.resolve_node(node);
}
fn resolve_identifier(&mut self, name: &str, span: Span, namespace: Namespace) -> Option<Symbol> {
fn resolve_identifier(&mut self, name: NameId, span: Span, namespace: Namespace) -> Option<Symbol> {
if self.is_builtin(name, namespace) {
return None;
}
@ -297,7 +301,7 @@ impl<'a> Resolver<'a> {
self.diagnostics.push(Diagnostic {
level: DiagnosticLevel::Error,
code: Some("E_TYPE_UNKNOWN_TYPE".to_string()),
message: format!("Unknown type: {}", name),
message: format!("Unknown type: {}", self.interner.resolve(name)),
span: Some(span),
});
} else {
@ -307,7 +311,8 @@ impl<'a> Resolver<'a> {
}
}
fn is_builtin(&self, name: &str, namespace: Namespace) -> bool {
fn is_builtin(&self, name: NameId, namespace: Namespace) -> bool {
let name = self.interner.resolve(name);
match namespace {
Namespace::Type => match name {
"int" | "float" | "string" | "bool" | "void" | "optional" | "result" | "bounded" |
@ -321,11 +326,11 @@ impl<'a> Resolver<'a> {
}
}
fn lookup_identifier(&self, name: &str, namespace: Namespace) -> Option<Symbol> {
fn lookup_identifier(&self, name: NameId, namespace: Namespace) -> Option<Symbol> {
// 1. local bindings
if namespace == Namespace::Value {
for scope in self.scopes.iter().rev() {
if let Some(sym) = scope.get(name) {
if let Some(sym) = scope.get(&name) {
return Some(sym.clone());
}
}
@ -369,7 +374,7 @@ impl<'a> Resolver<'a> {
None
}
fn define_local(&mut self, name: &str, span: Span, kind: SymbolKind) {
fn define_local(&mut self, name: NameId, span: Span, kind: SymbolKind) {
let scope = self.scopes.last_mut().expect("No scope to define local");
// Check for collision in Type namespace at top-level?
@ -379,22 +384,25 @@ impl<'a> Resolver<'a> {
self.diagnostics.push(Diagnostic {
level: DiagnosticLevel::Error,
code: Some("E_RESOLVE_NAMESPACE_COLLISION".to_string()),
message: format!("Local variable '{}' collides with a type name", name),
message: format!(
"Local variable '{}' collides with a type name",
self.interner.resolve(name)
),
span: Some(span),
});
return;
}
if scope.contains_key(name) {
if scope.contains_key(&name) {
self.diagnostics.push(Diagnostic {
level: DiagnosticLevel::Error,
code: Some("E_RESOLVE_DUPLICATE_SYMBOL".to_string()),
message: format!("Duplicate local variable '{}'", name),
message: format!("Duplicate local variable '{}'", self.interner.resolve(name)),
span: Some(span),
});
} else {
scope.insert(name.to_string(), Symbol {
name: name.to_string(),
scope.insert(name, Symbol {
name,
kind,
namespace: Namespace::Value,
visibility: Visibility::FilePrivate,
@ -414,20 +422,20 @@ impl<'a> Resolver<'a> {
self.scopes.pop();
}
fn error_undefined(&mut self, name: &str, span: Span) {
fn error_undefined(&mut self, name: NameId, span: Span) {
self.diagnostics.push(Diagnostic {
level: DiagnosticLevel::Error,
code: Some("E_RESOLVE_UNDEFINED".to_string()),
message: format!("Undefined identifier: {}", name),
message: format!("Undefined identifier: {}", self.interner.resolve(name)),
span: Some(span),
});
}
fn error_duplicate_import(&mut self, name: &str, span: Span) {
fn error_duplicate_import(&mut self, name: NameId, span: Span) {
self.diagnostics.push(Diagnostic {
level: DiagnosticLevel::Error,
code: Some("E_RESOLVE_DUPLICATE_SYMBOL".to_string()),
message: format!("Duplicate import: {}", name),
message: format!("Duplicate import: {}", self.interner.resolve(name)),
span: Some(span),
});
}
@ -436,7 +444,10 @@ impl<'a> Resolver<'a> {
self.diagnostics.push(Diagnostic {
level: DiagnosticLevel::Error,
code: Some("E_RESOLVE_VISIBILITY".to_string()),
message: format!("DebugSymbol '{}' is not visible here", sym.name),
message: format!(
"DebugSymbol '{}' is not visible here",
self.interner.resolve(sym.name)
),
span: Some(span),
});
}
@ -450,11 +461,13 @@ mod tests {
use crate::frontends::pbs::*;
use std::path::PathBuf;
fn setup_test(source: &str) -> (ast::FileNode, usize) {
fn setup_test(source: &str) -> (ast::FileNode, usize, NameInterner) {
let mut fm = FileManager::new();
let file_id = fm.add(PathBuf::from("test.pbs"), source.to_string());
let mut parser = parser::Parser::new(source, file_id);
(parser.parse_file().expect("Parsing failed"), file_id)
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)
}
#[test]
@ -463,8 +476,8 @@ mod tests {
declare struct Foo {}
declare struct Foo {}
";
let (ast, _) = setup_test(source);
let mut collector = SymbolCollector::new();
let (ast, _, mut interner) = setup_test(source);
let mut collector = SymbolCollector::new(&interner);
let result = collector.collect(&ast);
assert!(result.is_err());
@ -478,8 +491,8 @@ mod tests {
declare struct Foo {}
fn Foo() {}
";
let (ast, _) = setup_test(source);
let mut collector = SymbolCollector::new();
let (ast, _, mut interner) = setup_test(source);
let mut collector = SymbolCollector::new(&interner);
let result = collector.collect(&ast);
assert!(result.is_err());
@ -494,8 +507,8 @@ mod tests {
let x = y;
}
";
let (ast, _) = setup_test(source);
let mut collector = SymbolCollector::new();
let (ast, _, mut interner) = setup_test(source);
let mut collector = SymbolCollector::new(&interner);
let (ts, vs) = collector.collect(&ast).expect("Collection failed");
let ms = ModuleSymbols { type_symbols: ts, value_symbols: vs };
@ -504,7 +517,7 @@ mod tests {
fn get_module_symbols(&self, _path: &str) -> Option<&ModuleSymbols> { None }
}
let mut resolver = Resolver::new(&ms, &EmptyProvider);
let mut resolver = Resolver::new(&ms, &EmptyProvider, &interner);
let result = resolver.resolve(&ast);
assert!(result.is_err());
@ -520,8 +533,8 @@ mod tests {
let y = x;
}
";
let (ast, _) = setup_test(source);
let mut collector = SymbolCollector::new();
let (ast, _, mut interner) = setup_test(source);
let mut collector = SymbolCollector::new(&interner);
let (ts, vs) = collector.collect(&ast).expect("Collection failed");
let ms = ModuleSymbols { type_symbols: ts, value_symbols: vs };
@ -530,7 +543,7 @@ mod tests {
fn get_module_symbols(&self, _path: &str) -> Option<&ModuleSymbols> { None }
}
let mut resolver = Resolver::new(&ms, &EmptyProvider);
let mut resolver = Resolver::new(&ms, &EmptyProvider, &interner);
let result = resolver.resolve(&ast);
assert!(result.is_ok());
@ -542,8 +555,8 @@ mod tests {
import PrivateType from \"./other.pbs\"
fn main() {}
";
let (ast, _) = setup_test(source);
let mut collector = SymbolCollector::new();
let (ast, _, mut interner) = setup_test(source);
let mut collector = SymbolCollector::new(&interner);
let (ts, vs) = collector.collect(&ast).expect("Collection failed");
let ms = ModuleSymbols { type_symbols: ts, value_symbols: vs };
@ -558,7 +571,7 @@ mod tests {
let mut other_ts = SymbolTable::new();
other_ts.insert(Symbol {
name: "PrivateType".to_string(),
name: interner.intern("PrivateType"),
kind: SymbolKind::Struct,
namespace: Namespace::Type,
visibility: Visibility::FilePrivate,
@ -572,7 +585,7 @@ mod tests {
other: ModuleSymbols { type_symbols: other_ts, value_symbols: SymbolTable::new() },
};
let mut resolver = Resolver::new(&ms, &mock_provider);
let mut resolver = Resolver::new(&ms, &mock_provider, &interner);
let result = resolver.resolve(&ast);
assert!(result.is_err());
@ -588,8 +601,8 @@ mod tests {
let x: PubType = 10;
}
";
let (ast, _) = setup_test(source);
let mut collector = SymbolCollector::new();
let (ast, _, mut interner) = setup_test(source);
let mut collector = SymbolCollector::new(&interner);
let (ts, vs) = collector.collect(&ast).expect("Collection failed");
let ms = ModuleSymbols { type_symbols: ts, value_symbols: vs };
@ -604,7 +617,7 @@ mod tests {
let mut other_ts = SymbolTable::new();
other_ts.insert(Symbol {
name: "PubType".to_string(),
name: interner.intern("PubType"),
kind: SymbolKind::Struct,
namespace: Namespace::Type,
visibility: Visibility::Pub,
@ -618,7 +631,7 @@ mod tests {
other: ModuleSymbols { type_symbols: other_ts, value_symbols: SymbolTable::new() },
};
let mut resolver = Resolver::new(&ms, &mock_provider);
let mut resolver = Resolver::new(&ms, &mock_provider, &interner);
let result = resolver.resolve(&ast);
assert!(result.is_ok());
@ -630,8 +643,8 @@ mod tests {
import NonExistent from \"./missing.pbs\"
fn main() {}
";
let (ast, _) = setup_test(source);
let mut collector = SymbolCollector::new();
let (ast, _, interner) = setup_test(source);
let mut collector = SymbolCollector::new(&interner);
let (ts, vs) = collector.collect(&ast).expect("Collection failed");
let ms = ModuleSymbols { type_symbols: ts, value_symbols: vs };
@ -640,7 +653,7 @@ mod tests {
fn get_module_symbols(&self, _path: &str) -> Option<&ModuleSymbols> { None }
}
let mut resolver = Resolver::new(&ms, &EmptyProvider);
let mut resolver = Resolver::new(&ms, &EmptyProvider, &interner);
let result = resolver.resolve(&ast);
assert!(result.is_err());

View File

@ -1,5 +1,6 @@
use crate::common::spans::Span;
use crate::frontends::pbs::types::PbsType;
use prometeu_analysis::NameId;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
@ -28,7 +29,7 @@ pub enum Namespace {
#[derive(Debug, Clone)]
pub struct Symbol {
pub name: String,
pub name: NameId,
pub kind: SymbolKind,
pub namespace: Namespace,
pub visibility: Visibility,
@ -40,7 +41,7 @@ pub struct Symbol {
#[derive(Debug, Clone)]
pub struct SymbolTable {
pub symbols: HashMap<String, Symbol>,
pub symbols: HashMap<NameId, Symbol>,
}
#[derive(Debug, Clone)]
@ -65,15 +66,15 @@ impl SymbolTable {
}
}
pub fn insert(&mut self, symbol: Symbol) -> Result<(), Symbol> {
if let Some(existing) = self.symbols.get(&symbol.name) {
return Err(existing.clone());
pub fn insert(&mut self, symbol: Symbol) -> Result<(), ()> {
if self.symbols.contains_key(&symbol.name) {
return Err(());
}
self.symbols.insert(symbol.name.clone(), symbol);
self.symbols.insert(symbol.name, symbol);
Ok(())
}
pub fn get(&self, name: &str) -> Option<&Symbol> {
self.symbols.get(name)
pub fn get(&self, name: NameId) -> Option<&Symbol> {
self.symbols.get(&name)
}
}

View File

@ -5,12 +5,14 @@ use crate::frontends::pbs::contracts::ContractRegistry;
use crate::frontends::pbs::resolver::ModuleProvider;
use crate::frontends::pbs::symbols::*;
use crate::frontends::pbs::types::PbsType;
use prometeu_analysis::{NameId, NameInterner};
use std::collections::HashMap;
pub struct TypeChecker<'a> {
module_symbols: &'a mut ModuleSymbols,
imported_symbols: &'a ModuleSymbols,
_module_provider: &'a dyn ModuleProvider,
interner: &'a NameInterner,
scopes: Vec<HashMap<String, PbsType>>,
mut_bindings: Vec<HashMap<String, bool>>,
current_return_type: Option<PbsType>,
@ -26,11 +28,13 @@ impl<'a> TypeChecker<'a> {
module_symbols: &'a mut ModuleSymbols,
imported_symbols: &'a ModuleSymbols,
module_provider: &'a dyn ModuleProvider,
interner: &'a NameInterner,
) -> Self {
Self {
module_symbols,
imported_symbols,
_module_provider: module_provider,
interner,
scopes: Vec::new(),
mut_bindings: Vec::new(),
current_return_type: None,
@ -84,14 +88,15 @@ impl<'a> TypeChecker<'a> {
Node::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(n.name.clone()));
sym.ty = Some(PbsType::Service(self.interner.resolve(n.name).to_string()));
}
}
Node::TypeDecl(n) => {
let type_name = self.interner.resolve(n.name).to_string();
let ty = match n.type_kind.as_str() {
"struct" => PbsType::Struct(n.name.clone()),
"contract" => PbsType::Contract(n.name.clone()),
"error" => PbsType::ErrorType(n.name.clone()),
"struct" => PbsType::Struct(type_name.clone()),
"contract" => PbsType::Contract(type_name.clone()),
"error" => PbsType::ErrorType(type_name.clone()),
_ => PbsType::Void,
};
if let Some(sym) = self.module_symbols.type_symbols.symbols.get_mut(&n.name) {
@ -117,7 +122,7 @@ impl<'a> TypeChecker<'a> {
params: params.clone(),
return_type: Box::new(ty.clone()),
};
ctors.insert(n.name.clone(), default_ctor_ty);
ctors.insert(type_name.clone(), default_ctor_ty);
}
for ctor in &n.constructors {
@ -129,9 +134,9 @@ impl<'a> TypeChecker<'a> {
params,
return_type: Box::new(ty.clone()),
};
ctors.insert(ctor.name.clone(), ctor_ty);
ctors.insert(self.interner.resolve(ctor.name).to_string(), ctor_ty);
}
self.struct_constructors.insert(n.name.clone(), ctors);
self.struct_constructors.insert(type_name.clone(), ctors);
// Resolve methods
let mut methods = HashMap::new();
@ -146,11 +151,11 @@ impl<'a> TypeChecker<'a> {
params,
return_type: Box::new(self.resolve_type_node(&m.ret)),
};
methods.insert(m.name.clone(), m_ty);
methods.insert(self.interner.resolve(m.name).to_string(), m_ty);
}
}
}
self.struct_methods.insert(n.name.clone(), methods);
self.struct_methods.insert(type_name, methods);
}
_ => {}
}
@ -206,28 +211,30 @@ impl<'a> TypeChecker<'a> {
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::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),
_ => PbsType::Void,
}
}
fn check_member_access(&mut self, n: &MemberAccessNode) -> PbsType {
let member_str = self.interner.resolve(n.member);
if let Node::Ident(id) = &*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(&id.name));
let is_local = self.scopes.iter().any(|s| s.contains_key(name_str));
if !is_local {
// Check if it's a known host contract
let sym_opt = self.module_symbols.type_symbols.get(&id.name)
.or_else(|| self.imported_symbols.type_symbols.get(&id.name));
let sym_opt = self.module_symbols.type_symbols.get(id.name)
.or_else(|| self.imported_symbols.type_symbols.get(id.name));
if let Some(sym) = sym_opt {
if sym.kind == SymbolKind::Contract && sym.is_host {
// Check if the method exists in registry
if let Some(method) = self.contract_registry.get_method(&id.name, &n.member) {
if let Some(method) = self.contract_registry.get_method(name_str, member_str) {
return PbsType::Function {
params: method.params.clone(),
return_type: Box::new(method.return_type.clone()),
@ -236,7 +243,7 @@ impl<'a> TypeChecker<'a> {
self.diagnostics.push(Diagnostic {
level: DiagnosticLevel::Error,
code: Some("E_RESOLVE_UNDEFINED".to_string()),
message: format!("Method '{}' not found on host contract '{}'", n.member, id.name),
message: format!("Method '{}' not found on host contract '{}'", member_str, name_str),
span: Some(n.span),
});
}
@ -244,8 +251,8 @@ impl<'a> TypeChecker<'a> {
}
// v0: Suporte explícito às constantes de Color
if sym.kind == SymbolKind::Struct && id.name == "Color" {
match n.member.as_str() {
if sym.kind == SymbolKind::Struct && name_str == "Color" {
match member_str {
"BLACK" | "WHITE" | "RED" | "GREEN" | "BLUE" => {
return PbsType::Struct("Color".to_string());
}
@ -255,22 +262,22 @@ impl<'a> TypeChecker<'a> {
}
// Builtin Struct Associated Members (Static/Constants)
if let Some(constants) = self.struct_constants.get(&id.name) {
if let Some(ty) = constants.get(&n.member) {
if let Some(constants) = self.struct_constants.get(name_str) {
if let Some(ty) = constants.get(member_str) {
return ty.clone();
}
}
// Fallback for constructors if used as Type.alias(...)
if let Some(ctors) = self.struct_constructors.get(&id.name) {
if let Some(ty) = ctors.get(&n.member) {
if let Some(ctors) = self.struct_constructors.get(name_str) {
if let Some(ty) = ctors.get(member_str) {
return ty.clone();
}
}
// Fallback for static methods if used as Type.method(...)
if let Some(methods) = self.struct_methods.get(&id.name) {
if let Some(ty) = methods.get(&n.member) {
if let Some(methods) = self.struct_methods.get(name_str) {
if let Some(ty) = methods.get(member_str) {
return ty.clone();
}
}
@ -280,7 +287,7 @@ impl<'a> TypeChecker<'a> {
let obj_ty = self.check_node(&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(&n.member) {
if let Some(ty) = methods.get(member_str) {
// If it's a method call on an instance, the first parameter (self) is implicit
if let PbsType::Function { mut params, return_type } = ty.clone() {
if !params.is_empty() {
@ -299,7 +306,7 @@ impl<'a> TypeChecker<'a> {
PbsType::Struct(ref name) => {
match name.as_str() {
"Color" => {
match n.member.as_str() {
match member_str {
"value" => return PbsType::Bounded,
"raw" => return PbsType::Function {
params: vec![], // self is implicit
@ -309,14 +316,14 @@ impl<'a> TypeChecker<'a> {
}
}
"ButtonState" => {
match n.member.as_str() {
match member_str {
"pressed" | "released" | "down" => return PbsType::Bool,
"hold_frames" => return PbsType::Bounded,
_ => {}
}
}
"Pad" => {
match n.member.as_str() {
match member_str {
"up" | "down" | "left" | "right" | "a" | "b" | "x" | "y" | "l" | "r" | "start" | "select" => {
return PbsType::Struct("ButtonState".to_string());
}
@ -330,7 +337,7 @@ impl<'a> TypeChecker<'a> {
}
}
"Touch" => {
match n.member.as_str() {
match member_str {
"f" => return PbsType::Struct("ButtonState".to_string()),
"x" | "y" => return PbsType::Int,
_ => {}
@ -343,7 +350,7 @@ impl<'a> TypeChecker<'a> {
}
if obj_ty != PbsType::Void {
let msg = format!("Member '{}' not found on type {:?}", n.member, obj_ty);
let msg = format!("Member '{}' not found on type {:?}", member_str, obj_ty);
self.diagnostics.push(Diagnostic {
level: DiagnosticLevel::Error,
code: Some("E_RESOLVE_UNDEFINED".to_string()),
@ -361,7 +368,7 @@ impl<'a> TypeChecker<'a> {
PbsType::Contract(format!("Gate<{}>", ty)) // Approximation for v0
}
fn check_hip(&mut self, _span: Span, target: &Node, binding: &str, body: &Node, is_mut: bool) -> PbsType {
fn check_hip(&mut self, _span: Span, target: &Node, binding: NameId, body: &Node, is_mut: bool) -> PbsType {
let target_ty = self.check_node(target);
// In v0, we assume target is a gate. We bind the inner type to the binding.
let inner_ty = match target_ty {
@ -380,20 +387,21 @@ impl<'a> TypeChecker<'a> {
};
self.enter_scope();
self.define_local(binding, inner_ty, is_mut);
let binding_str = self.interner.resolve(binding);
self.define_local(binding_str, inner_ty, is_mut);
let body_ty = self.check_node(body);
self.exit_scope();
body_ty
}
fn check_fn_decl(&mut self, n: &FnDeclNode) {
let sig = self.module_symbols.value_symbols.get(&n.name).and_then(|s| s.ty.clone());
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();
self.current_return_type = Some(*return_type.clone());
for (param, ty) in n.params.iter().zip(params.iter()) {
self.define_local(&param.name, ty.clone(), false);
self.define_local(self.interner.resolve(param.name), ty.clone(), false);
}
let _body_ty = self.check_node(&n.body);
@ -410,7 +418,11 @@ impl<'a> TypeChecker<'a> {
self.diagnostics.push(Diagnostic {
level: DiagnosticLevel::Error,
code: Some("E_TYPE_RETURN_PATH".to_string()),
message: format!("Function '{}' must return a value of type {}", n.name, return_type),
message: format!(
"Function '{}' must return a value of type {}",
self.interner.resolve(n.name),
return_type
),
span: Some(n.span),
});
}
@ -452,44 +464,45 @@ impl<'a> TypeChecker<'a> {
init_ty
};
self.define_local(&n.name, final_ty, n.is_mut);
self.define_local(self.interner.resolve(n.name), final_ty, n.is_mut);
}
fn check_identifier(&mut self, n: &IdentNode) -> PbsType {
let name_str = self.interner.resolve(n.name);
// Check locals
for scope in self.scopes.iter().rev() {
if let Some(ty) = scope.get(&n.name) {
if let Some(ty) = scope.get(name_str) {
return ty.clone();
}
}
// Check module symbols
if let Some(sym) = self.module_symbols.value_symbols.get(&n.name) {
if let Some(sym) = self.module_symbols.value_symbols.get(n.name) {
if let Some(ty) = &sym.ty {
return ty.clone();
}
}
// Check imported symbols
if let Some(sym) = self.imported_symbols.value_symbols.get(&n.name) {
if let Some(sym) = self.imported_symbols.value_symbols.get(n.name) {
if let Some(ty) = &sym.ty {
return ty.clone();
}
}
// Fallback for default constructor: check if it's a struct name
if let Some(ctors) = self.struct_constructors.get(&n.name) {
if let Some(ty) = ctors.get(&n.name) {
if let Some(ctors) = self.struct_constructors.get(name_str) {
if let Some(ty) = ctors.get(name_str) {
return ty.clone();
}
}
// Built-ins (some, none, ok, err might be handled as calls or special keywords)
// For v0, let's treat none as a special literal or identifier
if n.name == "none" {
if name_str == "none" {
return PbsType::None;
}
if n.name == "true" || n.name == "false" {
if name_str == "true" || name_str == "false" {
return PbsType::Bool;
}
@ -502,7 +515,7 @@ impl<'a> TypeChecker<'a> {
// Handle special built-in "constructors"
if let Node::Ident(id) = &*n.callee {
match id.name.as_str() {
match self.interner.resolve(id.name) {
"some" => {
if n.args.len() == 1 {
let inner_ty = self.check_node(&n.args[0]);
@ -673,9 +686,10 @@ impl<'a> TypeChecker<'a> {
self.check_constructor_decl(constructor);
}
let struct_ty = PbsType::Struct(n.name.clone());
let type_name = self.interner.resolve(n.name).to_string();
let struct_ty = PbsType::Struct(type_name.clone());
let mut constants_scope = HashMap::new();
if let Some(ctors) = self.struct_constructors.get(&n.name) {
if let Some(ctors) = self.struct_constructors.get(&type_name) {
for (name, ty) in ctors {
constants_scope.insert(name.clone(), ty.clone());
}
@ -688,10 +702,10 @@ impl<'a> TypeChecker<'a> {
if !self.is_assignable(&struct_ty, &val_ty) {
self.error_type_mismatch(&struct_ty, &val_ty, constant.span);
}
constants_map.insert(constant.name.clone(), struct_ty.clone());
constants_map.insert(self.interner.resolve(constant.name).to_string(), struct_ty.clone());
}
self.scopes.pop();
self.struct_constants.insert(n.name.clone(), constants_map);
self.struct_constants.insert(type_name, constants_map);
if let Some(body) = &n.body {
self.check_node(body);
@ -702,7 +716,7 @@ impl<'a> TypeChecker<'a> {
self.enter_scope();
for param in &n.params {
let ty = self.resolve_type_node(&param.ty);
self.define_local(&param.name, ty, false);
self.define_local(self.interner.resolve(param.name), ty, false);
}
for init in &n.initializers {
self.check_node(init);
@ -714,29 +728,30 @@ impl<'a> TypeChecker<'a> {
fn resolve_type_node(&mut self, node: &Node) -> PbsType {
match node {
Node::TypeName(tn) => {
match tn.name.as_str() {
let name_str = self.interner.resolve(tn.name);
match name_str {
"int" => PbsType::Int,
"float" => PbsType::Float,
"bool" => PbsType::Bool,
"string" => PbsType::String,
"void" => PbsType::Void,
"bounded" => PbsType::Bounded,
"Color" | "ButtonState" | "Pad" | "Touch" => PbsType::Struct(tn.name.clone()),
"Color" | "ButtonState" | "Pad" | "Touch" => PbsType::Struct(name_str.to_string()),
_ => {
// Look up in symbol table
if let Some(sym) = self.lookup_type(&tn.name) {
if let Some(sym) = self.lookup_type(tn.name) {
match sym.kind {
SymbolKind::Struct => PbsType::Struct(tn.name.clone()),
SymbolKind::Service => PbsType::Service(tn.name.clone()),
SymbolKind::Contract => PbsType::Contract(tn.name.clone()),
SymbolKind::ErrorType => PbsType::ErrorType(tn.name.clone()),
SymbolKind::Struct => PbsType::Struct(name_str.to_string()),
SymbolKind::Service => PbsType::Service(name_str.to_string()),
SymbolKind::Contract => PbsType::Contract(name_str.to_string()),
SymbolKind::ErrorType => PbsType::ErrorType(name_str.to_string()),
_ => PbsType::Void,
}
} else {
self.diagnostics.push(Diagnostic {
level: DiagnosticLevel::Error,
code: Some("E_TYPE_UNKNOWN_TYPE".to_string()),
message: format!("Unknown type: {}", tn.name),
message: format!("Unknown type: {}", name_str),
span: Some(tn.span),
});
PbsType::Void
@ -745,7 +760,7 @@ impl<'a> TypeChecker<'a> {
}
}
Node::TypeApp(ta) => {
match ta.base.as_str() {
match self.interner.resolve(ta.base) {
"optional" => {
if ta.args.len() == 1 {
PbsType::Optional(Box::new(self.resolve_type_node(&ta.args[0])))
@ -770,7 +785,7 @@ impl<'a> TypeChecker<'a> {
}
}
fn lookup_type(&self, name: &str) -> Option<&Symbol> {
fn lookup_type(&self, name: NameId) -> Option<&Symbol> {
if let Some(sym) = self.module_symbols.type_symbols.get(name) {
return Some(sym);
}

View File

@ -2,6 +2,7 @@ use crate::common::diagnostics::DiagnosticBundle;
use crate::common::files::FileManager;
use crate::frontends::pbs::{collector::SymbolCollector, parser::Parser, Symbol, Visibility};
use crate::manifest::{load_manifest, ManifestKind};
use prometeu_analysis::NameInterner;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fs;
@ -118,6 +119,7 @@ fn discover_recursive(dir: &Path, files: &mut Vec<PathBuf>) -> std::io::Result<(
pub fn build_exports(module_dir: &Path, file_manager: &mut FileManager) -> Result<ExportTable, SourceError> {
let mut symbols = HashMap::new();
let mut files = Vec::new();
let mut interner = NameInterner::new();
if module_dir.is_dir() {
discover_recursive(module_dir, &mut files)?;
@ -129,21 +131,21 @@ pub fn build_exports(module_dir: &Path, file_manager: &mut FileManager) -> Resul
let source = fs::read_to_string(&file_path)?;
let file_id = file_manager.add(file_path.clone(), source.clone());
let mut parser = Parser::new(&source, file_id);
let mut parser = Parser::new(&source, file_id, &mut interner);
let ast = parser.parse_file()?;
let mut collector = SymbolCollector::new();
let mut collector = SymbolCollector::new(&interner);
let (type_symbols, value_symbols) = collector.collect(&ast)?;
// Merge only public symbols
for symbol in type_symbols.symbols.into_values() {
if symbol.visibility == Visibility::Pub {
symbols.insert(symbol.name.clone(), symbol);
symbols.insert(interner.resolve(symbol.name).to_string(), symbol);
}
}
for symbol in value_symbols.symbols.into_values() {
if symbol.visibility == Visibility::Pub {
symbols.insert(symbol.name.clone(), symbol);
symbols.insert(interner.resolve(symbol.name).to_string(), symbol);
}
}
}

View File

@ -3,6 +3,7 @@ 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;
use std::path::Path;
@ -56,7 +57,8 @@ fn generate_canonical_goldens() {
// 3. AST JSON
let source = fs::read_to_string(project_dir.join("src/main/modules/main.pbs")).unwrap();
let mut parser = Parser::new(&source, 0);
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();

View File

@ -16,7 +16,7 @@
},
"vis": null,
"type_kind": "struct",
"name": "Color",
"name": 11,
"is_host": false,
"params": [
{
@ -25,7 +25,7 @@
"start": 100,
"end": 112
},
"name": "raw",
"name": 12,
"ty": {
"kind": "TypeName",
"span": {
@ -33,7 +33,7 @@
"start": 105,
"end": 112
},
"name": "bounded"
"name": 10
}
}
],
@ -45,7 +45,7 @@
"start": 119,
"end": 135
},
"name": "BLACK",
"name": 13,
"value": {
"kind": "Call",
"span": {
@ -60,7 +60,7 @@
"start": 126,
"end": 131
},
"name": "Color"
"name": 11
},
"args": [
{
@ -81,7 +81,7 @@
"start": 139,
"end": 159
},
"name": "WHITE",
"name": 14,
"value": {
"kind": "Call",
"span": {
@ -96,7 +96,7 @@
"start": 146,
"end": 151
},
"name": "Color"
"name": 11
},
"args": [
{
@ -117,7 +117,7 @@
"start": 163,
"end": 181
},
"name": "RED",
"name": 15,
"value": {
"kind": "Call",
"span": {
@ -132,7 +132,7 @@
"start": 168,
"end": 173
},
"name": "Color"
"name": 11
},
"args": [
{
@ -153,7 +153,7 @@
"start": 185,
"end": 204
},
"name": "GREEN",
"name": 16,
"value": {
"kind": "Call",
"span": {
@ -168,7 +168,7 @@
"start": 192,
"end": 197
},
"name": "Color"
"name": 11
},
"args": [
{
@ -189,7 +189,7 @@
"start": 208,
"end": 224
},
"name": "BLUE",
"name": 17,
"value": {
"kind": "Call",
"span": {
@ -204,7 +204,7 @@
"start": 214,
"end": 219
},
"name": "Color"
"name": 11
},
"args": [
{
@ -231,7 +231,7 @@
},
"vis": null,
"type_kind": "struct",
"name": "ButtonState",
"name": 18,
"is_host": false,
"params": [
{
@ -240,7 +240,7 @@
"start": 261,
"end": 274
},
"name": "pressed",
"name": 19,
"ty": {
"kind": "TypeName",
"span": {
@ -248,7 +248,7 @@
"start": 270,
"end": 274
},
"name": "bool"
"name": 20
}
},
{
@ -257,7 +257,7 @@
"start": 280,
"end": 294
},
"name": "released",
"name": 21,
"ty": {
"kind": "TypeName",
"span": {
@ -265,7 +265,7 @@
"start": 290,
"end": 294
},
"name": "bool"
"name": 20
}
},
{
@ -274,7 +274,7 @@
"start": 300,
"end": 310
},
"name": "down",
"name": 22,
"ty": {
"kind": "TypeName",
"span": {
@ -282,7 +282,7 @@
"start": 306,
"end": 310
},
"name": "bool"
"name": 20
}
},
{
@ -291,7 +291,7 @@
"start": 316,
"end": 336
},
"name": "hold_frames",
"name": 23,
"ty": {
"kind": "TypeName",
"span": {
@ -299,7 +299,7 @@
"start": 329,
"end": 336
},
"name": "bounded"
"name": 10
}
}
],
@ -316,7 +316,7 @@
},
"vis": null,
"type_kind": "struct",
"name": "Pad",
"name": 24,
"is_host": false,
"params": [
{
@ -325,7 +325,7 @@
"start": 364,
"end": 379
},
"name": "up",
"name": 25,
"ty": {
"kind": "TypeName",
"span": {
@ -333,7 +333,7 @@
"start": 368,
"end": 379
},
"name": "ButtonState"
"name": 18
}
},
{
@ -342,7 +342,7 @@
"start": 385,
"end": 402
},
"name": "down",
"name": 22,
"ty": {
"kind": "TypeName",
"span": {
@ -350,7 +350,7 @@
"start": 391,
"end": 402
},
"name": "ButtonState"
"name": 18
}
},
{
@ -359,7 +359,7 @@
"start": 408,
"end": 425
},
"name": "left",
"name": 26,
"ty": {
"kind": "TypeName",
"span": {
@ -367,7 +367,7 @@
"start": 414,
"end": 425
},
"name": "ButtonState"
"name": 18
}
},
{
@ -376,7 +376,7 @@
"start": 431,
"end": 449
},
"name": "right",
"name": 27,
"ty": {
"kind": "TypeName",
"span": {
@ -384,7 +384,7 @@
"start": 438,
"end": 449
},
"name": "ButtonState"
"name": 18
}
},
{
@ -393,7 +393,7 @@
"start": 455,
"end": 469
},
"name": "a",
"name": 28,
"ty": {
"kind": "TypeName",
"span": {
@ -401,7 +401,7 @@
"start": 458,
"end": 469
},
"name": "ButtonState"
"name": 18
}
},
{
@ -410,7 +410,7 @@
"start": 475,
"end": 489
},
"name": "b",
"name": 29,
"ty": {
"kind": "TypeName",
"span": {
@ -418,7 +418,7 @@
"start": 478,
"end": 489
},
"name": "ButtonState"
"name": 18
}
},
{
@ -427,7 +427,7 @@
"start": 495,
"end": 509
},
"name": "x",
"name": 30,
"ty": {
"kind": "TypeName",
"span": {
@ -435,7 +435,7 @@
"start": 498,
"end": 509
},
"name": "ButtonState"
"name": 18
}
},
{
@ -444,7 +444,7 @@
"start": 515,
"end": 529
},
"name": "y",
"name": 31,
"ty": {
"kind": "TypeName",
"span": {
@ -452,7 +452,7 @@
"start": 518,
"end": 529
},
"name": "ButtonState"
"name": 18
}
},
{
@ -461,7 +461,7 @@
"start": 535,
"end": 549
},
"name": "l",
"name": 32,
"ty": {
"kind": "TypeName",
"span": {
@ -469,7 +469,7 @@
"start": 538,
"end": 549
},
"name": "ButtonState"
"name": 18
}
},
{
@ -478,7 +478,7 @@
"start": 555,
"end": 569
},
"name": "r",
"name": 33,
"ty": {
"kind": "TypeName",
"span": {
@ -486,7 +486,7 @@
"start": 558,
"end": 569
},
"name": "ButtonState"
"name": 18
}
},
{
@ -495,7 +495,7 @@
"start": 575,
"end": 593
},
"name": "start",
"name": 34,
"ty": {
"kind": "TypeName",
"span": {
@ -503,7 +503,7 @@
"start": 582,
"end": 593
},
"name": "ButtonState"
"name": 18
}
},
{
@ -512,7 +512,7 @@
"start": 599,
"end": 618
},
"name": "select",
"name": 35,
"ty": {
"kind": "TypeName",
"span": {
@ -520,7 +520,7 @@
"start": 607,
"end": 618
},
"name": "ButtonState"
"name": 18
}
}
],
@ -537,7 +537,7 @@
},
"vis": null,
"type_kind": "contract",
"name": "Gfx",
"name": 36,
"is_host": true,
"params": [],
"constructors": [],
@ -557,7 +557,7 @@
"start": 654,
"end": 682
},
"name": "clear",
"name": 37,
"params": [
{
"span": {
@ -565,7 +565,7 @@
"start": 663,
"end": 675
},
"name": "color",
"name": 38,
"ty": {
"kind": "TypeName",
"span": {
@ -573,7 +573,7 @@
"start": 670,
"end": 675
},
"name": "Color"
"name": 11
}
}
],
@ -584,7 +584,7 @@
"start": 678,
"end": 682
},
"name": "void"
"name": 6
}
}
]
@ -599,7 +599,7 @@
},
"vis": null,
"type_kind": "contract",
"name": "Input",
"name": 39,
"is_host": true,
"params": [],
"constructors": [],
@ -619,7 +619,7 @@
"start": 721,
"end": 734
},
"name": "pad",
"name": 40,
"params": [],
"ret": {
"kind": "TypeName",
@ -628,7 +628,7 @@
"start": 731,
"end": 734
},
"name": "Pad"
"name": 24
}
}
]
@ -642,7 +642,7 @@
"end": 788
},
"vis": null,
"name": "add",
"name": 41,
"params": [
{
"span": {
@ -650,7 +650,7 @@
"start": 746,
"end": 752
},
"name": "a",
"name": 28,
"ty": {
"kind": "TypeName",
"span": {
@ -658,7 +658,7 @@
"start": 749,
"end": 752
},
"name": "int"
"name": 42
}
},
{
@ -667,7 +667,7 @@
"start": 754,
"end": 760
},
"name": "b",
"name": 29,
"ty": {
"kind": "TypeName",
"span": {
@ -675,7 +675,7 @@
"start": 757,
"end": 760
},
"name": "int"
"name": 42
}
}
],
@ -686,7 +686,7 @@
"start": 763,
"end": 766
},
"name": "int"
"name": 42
},
"else_fallback": null,
"body": {
@ -719,7 +719,7 @@
"start": 780,
"end": 781
},
"name": "a"
"name": 28
},
"right": {
"kind": "Ident",
@ -728,7 +728,7 @@
"start": 784,
"end": 785
},
"name": "b"
"name": 29
}
}
}
@ -744,7 +744,7 @@
"end": 1180
},
"vis": null,
"name": "frame",
"name": 43,
"params": [],
"ret": {
"kind": "TypeName",
@ -753,7 +753,7 @@
"start": 802,
"end": 806
},
"name": "void"
"name": 6
},
"else_fallback": null,
"body": {
@ -771,7 +771,7 @@
"start": 843,
"end": 854
},
"name": "x",
"name": 30,
"is_mut": false,
"ty": null,
"init": {
@ -791,7 +791,7 @@
"start": 859,
"end": 870
},
"name": "y",
"name": 31,
"is_mut": false,
"ty": null,
"init": {
@ -811,7 +811,7 @@
"start": 875,
"end": 893
},
"name": "z",
"name": 44,
"is_mut": false,
"ty": null,
"init": {
@ -828,7 +828,7 @@
"start": 883,
"end": 886
},
"name": "add"
"name": 41
},
"args": [
{
@ -838,7 +838,7 @@
"start": 887,
"end": 888
},
"name": "x"
"name": 30
},
{
"kind": "Ident",
@ -847,7 +847,7 @@
"start": 890,
"end": 891
},
"name": "y"
"name": 31
}
]
}
@ -881,7 +881,7 @@
"start": 930,
"end": 931
},
"name": "z"
"name": 44
},
"right": {
"kind": "IntLit",
@ -929,9 +929,9 @@
"start": 976,
"end": 979
},
"name": "Gfx"
"name": 36
},
"member": "clear"
"member": 37
},
"args": [
{
@ -948,9 +948,9 @@
"start": 986,
"end": 991
},
"name": "Color"
"name": 11
},
"member": "GREEN"
"member": 16
}
]
}
@ -994,9 +994,9 @@
"start": 1022,
"end": 1025
},
"name": "Gfx"
"name": 36
},
"member": "clear"
"member": 37
},
"args": [
{
@ -1013,9 +1013,9 @@
"start": 1032,
"end": 1037
},
"name": "Color"
"name": 11
},
"member": "RED"
"member": 15
}
]
}
@ -1032,7 +1032,7 @@
"start": 1103,
"end": 1123
},
"name": "p",
"name": 45,
"is_mut": false,
"ty": null,
"init": {
@ -1056,9 +1056,9 @@
"start": 1111,
"end": 1116
},
"name": "Input"
"name": 39
},
"member": "pad"
"member": 40
},
"args": []
}
@ -1092,11 +1092,11 @@
"start": 1131,
"end": 1132
},
"name": "p"
"name": 45
},
"member": "a"
"member": 28
},
"member": "down"
"member": 22
},
"then_block": {
"kind": "Block",
@ -1134,9 +1134,9 @@
"start": 1150,
"end": 1153
},
"name": "Gfx"
"name": 36
},
"member": "clear"
"member": 37
},
"args": [
{
@ -1153,9 +1153,9 @@
"start": 1160,
"end": 1165
},
"name": "Color"
"name": 11
},
"member": "BLUE"
"member": 17
}
]
}