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", "anyhow",
"clap", "clap",
"prometeu-abi", "prometeu-abi",
"prometeu-analysis",
"prometeu-bytecode", "prometeu-bytecode",
"serde", "serde",
"serde_json", "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 ids;
pub mod span; pub mod span;
pub mod file_db; pub mod file_db;
pub mod interner;
pub use ids::FileId; pub use ids::FileId;
pub use span::Span; pub use span::Span;
pub use file_db::{FileDB, LineIndex}; pub use file_db::{FileDB, LineIndex};
pub use interner::{NameId, NameInterner};

View File

@ -16,6 +16,7 @@ include = ["../../VERSION.txt"]
[dependencies] [dependencies]
prometeu-bytecode = { path = "../prometeu-bytecode" } prometeu-bytecode = { path = "../prometeu-bytecode" }
prometeu-abi = { path = "../prometeu-abi" } prometeu-abi = { path = "../prometeu-abi" }
prometeu-analysis = { path = "../prometeu-analysis" }
clap = { version = "4.5.54", features = ["derive"] } clap = { version = "4.5.54", features = ["derive"] }
serde = { version = "1.0.228", features = ["derive"] } serde = { version = "1.0.228", features = ["derive"] }
serde_json = "1.0.149" 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::lowering::core_to_vm;
use crate::semantics::export_surface::ExportSurfaceKind; use crate::semantics::export_surface::ExportSurfaceKind;
use prometeu_bytecode::{ConstantPoolEntry, DebugInfo, FunctionMeta}; use prometeu_bytecode::{ConstantPoolEntry, DebugInfo, FunctionMeta};
use prometeu_analysis::NameInterner;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, HashMap}; use std::collections::{BTreeMap, HashMap};
@ -112,6 +113,7 @@ pub fn compile_project(
dep_modules: &HashMap<ProjectId, CompiledModule>, dep_modules: &HashMap<ProjectId, CompiledModule>,
file_manager: &mut FileManager, file_manager: &mut FileManager,
) -> Result<CompiledModule, CompileError> { ) -> Result<CompiledModule, CompileError> {
let mut interner = NameInterner::new();
// 1. Parse all files and group symbols by module // 1. Parse all files and group symbols by module
let mut module_symbols_map: HashMap<String, ModuleSymbols> = HashMap::new(); let mut module_symbols_map: HashMap<String, ModuleSymbols> = HashMap::new();
let mut parsed_files: Vec<(String, FileNode)> = Vec::new(); // (module_path, ast) let mut parsed_files: Vec<(String, 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 source_code = std::fs::read_to_string(&source_abs)?;
let file_id = file_manager.add(source_abs.clone(), source_code.clone()); 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 ast = parser.parse_file()?;
let mut collector = SymbolCollector::new(); let mut collector = SymbolCollector::new(&interner);
let (ts, vs) = collector.collect(&ast)?; let (ts, vs) = collector.collect(&ast)?;
let full_path = source_rel.to_string_lossy().replace('\\', "/"); let full_path = source_rel.to_string_lossy().replace('\\', "/");
@ -145,20 +147,30 @@ pub fn compile_project(
// Merge symbols // Merge symbols
for sym in ts.symbols.into_values() { 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( 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) Some(existing.span)
).into()); ).into());
} }
let _ = ms.type_symbols.insert(sym);
} }
for sym in vs.symbols.into_values() { 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( 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) Some(existing.span)
).into()); ).into());
} }
let _ = ms.value_symbols.insert(sym);
} }
parsed_files.push((module_path, ast)); 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 ms = all_visible_modules.entry(synthetic_module_path.clone()).or_insert_with(ModuleSymbols::new);
let sym = Symbol { let sym = Symbol {
name: key.symbol_name.clone(), name: interner.intern(&key.symbol_name),
kind: match key.kind { kind: match key.kind {
ExportSurfaceKind::Service => SymbolKind::Service, ExportSurfaceKind::Service => SymbolKind::Service,
ExportSurfaceKind::DeclareType => { ExportSurfaceKind::DeclareType => {
@ -200,21 +212,23 @@ pub fn compile_project(
}; };
if sym.namespace == Namespace::Type { 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 { return Err(CompileError::DuplicateExport {
symbol: sym.name, symbol: interner.resolve(sym.name).to_string(),
first_dep: existing.origin.unwrap_or_else(|| "unknown".to_string()), first_dep: existing.origin.clone().unwrap_or_else(|| "unknown".to_string()),
second_dep: sym.origin.unwrap_or_else(|| "unknown".to_string()), second_dep: sym.origin.unwrap_or_else(|| "unknown".to_string()),
}); });
} }
let _ = ms.type_symbols.insert(sym.clone());
} else { } 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 { return Err(CompileError::DuplicateExport {
symbol: sym.name, symbol: interner.resolve(sym.name).to_string(),
first_dep: existing.origin.unwrap_or_else(|| "unknown".to_string()), first_dep: existing.origin.clone().unwrap_or_else(|| "unknown".to_string()),
second_dep: sym.origin.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 { for (module_path, ast) in &parsed_files {
let ms = module_symbols_map.get(module_path).unwrap(); 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)?; resolver.resolve(ast)?;
// Capture imported symbols // Capture imported symbols
@ -240,7 +254,7 @@ pub fn compile_project(
// TypeChecker also needs &mut ModuleSymbols // TypeChecker also needs &mut ModuleSymbols
let mut ms_mut = module_symbols_map.get_mut(module_path).unwrap(); let mut ms_mut = module_symbols_map.get_mut(module_path).unwrap();
let imported = file_imported_symbols.get(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)?; typechecker.check(ast)?;
} }
@ -255,7 +269,7 @@ pub fn compile_project(
for (module_path, ast) in &parsed_files { for (module_path, ast) in &parsed_files {
let ms = module_symbols_map.get(module_path).unwrap(); let ms = module_symbols_map.get(module_path).unwrap();
let imported = file_imported_symbols.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)?; let program = lowerer.lower_file(ast, module_path)?;
// Combine program into combined_program // Combine program into combined_program
@ -281,7 +295,7 @@ pub fn compile_project(
if let Some(surface_kind) = ExportSurfaceKind::from_symbol_kind(sym.kind) { if let Some(surface_kind) = ExportSurfaceKind::from_symbol_kind(sym.kind) {
exports.insert(ExportKey { exports.insert(ExportKey {
module_path: module_path.clone(), module_path: module_path.clone(),
symbol_name: sym.name.clone(), symbol_name: interner.resolve(sym.name).to_string(),
kind: surface_kind, kind: surface_kind,
}, ExportMetadata { }, ExportMetadata {
func_idx: None, func_idx: None,
@ -295,11 +309,15 @@ pub fn compile_project(
if sym.visibility == Visibility::Pub { if sym.visibility == Visibility::Pub {
if let Some(surface_kind) = ExportSurfaceKind::from_symbol_kind(sym.kind) { if let Some(surface_kind) = ExportSurfaceKind::from_symbol_kind(sym.kind) {
// Find func_idx if it's a function or service // 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 { exports.insert(ExportKey {
module_path: module_path.clone(), module_path: module_path.clone(),
symbol_name: sym.name.clone(), symbol_name: interner.resolve(sym.name).to_string(),
kind: surface_kind, kind: surface_kind,
}, ExportMetadata { }, ExportMetadata {
func_idx, func_idx,
@ -312,7 +330,12 @@ pub fn compile_project(
} }
// 6. Collect symbols // 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 // 7. Collect imports from unresolved labels
let mut imports = Vec::new(); let mut imports = Vec::new();

View File

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

View File

@ -1,4 +1,5 @@
use crate::common::spans::Span; use crate::common::spans::Span;
use prometeu_analysis::NameId;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
@ -56,22 +57,22 @@ pub struct ImportNode {
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ImportSpecNode { pub struct ImportSpecNode {
pub span: Span, pub span: Span,
pub path: Vec<String>, pub path: Vec<NameId>,
} }
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ServiceDeclNode { pub struct ServiceDeclNode {
pub span: Span, pub span: Span,
pub vis: Option<String>, // "pub" | "mod" pub vis: Option<String>, // "pub" | "mod"
pub name: String, pub name: NameId,
pub extends: Option<String>, pub extends: Option<NameId>,
pub members: Vec<Node>, // ServiceFnSig pub members: Vec<Node>, // ServiceFnSig
} }
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ServiceFnSigNode { pub struct ServiceFnSigNode {
pub span: Span, pub span: Span,
pub name: String, pub name: NameId,
pub params: Vec<ParamNode>, pub params: Vec<ParamNode>,
pub ret: Box<Node>, // TypeName or TypeApp pub ret: Box<Node>, // TypeName or TypeApp
} }
@ -79,7 +80,7 @@ pub struct ServiceFnSigNode {
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ParamNode { pub struct ParamNode {
pub span: Span, pub span: Span,
pub name: String, pub name: NameId,
pub ty: Box<Node>, pub ty: Box<Node>,
} }
@ -87,7 +88,7 @@ pub struct ParamNode {
pub struct FnDeclNode { pub struct FnDeclNode {
pub span: Span, pub span: Span,
pub vis: Option<String>, pub vis: Option<String>,
pub name: String, pub name: NameId,
pub params: Vec<ParamNode>, pub params: Vec<ParamNode>,
pub ret: Option<Box<Node>>, pub ret: Option<Box<Node>>,
pub else_fallback: Option<Box<Node>>, // Block pub else_fallback: Option<Box<Node>>, // Block
@ -99,7 +100,7 @@ pub struct TypeDeclNode {
pub span: Span, pub span: Span,
pub vis: Option<String>, pub vis: Option<String>,
pub type_kind: String, // "struct" | "contract" | "error" pub type_kind: String, // "struct" | "contract" | "error"
pub name: String, pub name: NameId,
pub is_host: bool, pub is_host: bool,
pub params: Vec<ParamNode>, // fields for struct/error pub params: Vec<ParamNode>, // fields for struct/error
pub constructors: Vec<ConstructorDeclNode>, // [ ... ] pub constructors: Vec<ConstructorDeclNode>, // [ ... ]
@ -112,14 +113,14 @@ pub struct ConstructorDeclNode {
pub span: Span, pub span: Span,
pub params: Vec<ParamNode>, pub params: Vec<ParamNode>,
pub initializers: Vec<Node>, pub initializers: Vec<Node>,
pub name: String, pub name: NameId,
pub body: Box<Node>, // BlockNode pub body: Box<Node>, // BlockNode
} }
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ConstantDeclNode { pub struct ConstantDeclNode {
pub span: Span, pub span: Span,
pub name: String, pub name: NameId,
pub value: Box<Node>, pub value: Box<Node>,
} }
@ -133,7 +134,7 @@ pub struct TypeBodyNode {
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct TypeMemberNode { pub struct TypeMemberNode {
pub span: Span, pub span: Span,
pub name: String, pub name: NameId,
pub ty: Box<Node>, pub ty: Box<Node>,
} }
@ -147,7 +148,7 @@ pub struct BlockNode {
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct LetStmtNode { pub struct LetStmtNode {
pub span: Span, pub span: Span,
pub name: String, pub name: NameId,
pub is_mut: bool, pub is_mut: bool,
pub ty: Option<Box<Node>>, pub ty: Option<Box<Node>>,
pub init: Box<Node>, pub init: Box<Node>,
@ -192,7 +193,7 @@ pub struct StringLitNode {
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct IdentNode { pub struct IdentNode {
pub span: Span, pub span: Span,
pub name: String, pub name: NameId,
} }
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
@ -248,13 +249,13 @@ pub struct WhenArmNode {
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct TypeNameNode { pub struct TypeNameNode {
pub span: Span, pub span: Span,
pub name: String, pub name: NameId,
} }
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct TypeAppNode { pub struct TypeAppNode {
pub span: Span, pub span: Span,
pub base: String, pub base: NameId,
pub args: Vec<Node>, pub args: Vec<Node>,
} }
@ -268,7 +269,7 @@ pub struct AllocNode {
pub struct MutateNode { pub struct MutateNode {
pub span: Span, pub span: Span,
pub target: Box<Node>, pub target: Box<Node>,
pub binding: String, pub binding: NameId,
pub body: Box<Node>, // BlockNode pub body: Box<Node>, // BlockNode
} }
@ -276,7 +277,7 @@ pub struct MutateNode {
pub struct BorrowNode { pub struct BorrowNode {
pub span: Span, pub span: Span,
pub target: Box<Node>, pub target: Box<Node>,
pub binding: String, pub binding: NameId,
pub body: Box<Node>, // BlockNode pub body: Box<Node>, // BlockNode
} }
@ -284,7 +285,7 @@ pub struct BorrowNode {
pub struct PeekNode { pub struct PeekNode {
pub span: Span, pub span: Span,
pub target: Box<Node>, pub target: Box<Node>,
pub binding: String, pub binding: NameId,
pub body: Box<Node>, // BlockNode pub body: Box<Node>, // BlockNode
} }
@ -292,5 +293,5 @@ pub struct PeekNode {
pub struct MemberAccessNode { pub struct MemberAccessNode {
pub span: Span, pub span: Span,
pub object: Box<Node>, 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::ast::*;
use crate::frontends::pbs::symbols::*; use crate::frontends::pbs::symbols::*;
use crate::semantics::export_surface::ExportSurfaceKind; use crate::semantics::export_surface::ExportSurfaceKind;
use prometeu_analysis::NameInterner;
pub struct SymbolCollector { pub struct SymbolCollector<'a> {
interner: &'a NameInterner,
type_symbols: SymbolTable, type_symbols: SymbolTable,
value_symbols: SymbolTable, value_symbols: SymbolTable,
diagnostics: Vec<Diagnostic>, diagnostics: Vec<Diagnostic>,
} }
impl SymbolCollector { impl<'a> SymbolCollector<'a> {
pub fn new() -> Self { pub fn new(interner: &'a NameInterner) -> Self {
Self { Self {
interner,
type_symbols: SymbolTable::new(), type_symbols: SymbolTable::new(),
value_symbols: SymbolTable::new(), value_symbols: SymbolTable::new(),
diagnostics: Vec::new(), diagnostics: Vec::new(),
@ -113,35 +116,43 @@ impl SymbolCollector {
fn insert_type_symbol(&mut self, symbol: Symbol) { fn insert_type_symbol(&mut self, symbol: Symbol) {
// Check for collision in value namespace first // 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(); let existing = existing.clone();
self.error_collision(&symbol, &existing); self.error_collision(&symbol, &existing);
return; return;
} }
if let Err(existing) = self.type_symbols.insert(symbol.clone()) { 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); self.error_duplicate(&symbol, &existing);
} }
} }
}
fn insert_value_symbol(&mut self, symbol: Symbol) { fn insert_value_symbol(&mut self, symbol: Symbol) {
// Check for collision in type namespace first // 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(); let existing = existing.clone();
self.error_collision(&symbol, &existing); self.error_collision(&symbol, &existing);
return; return;
} }
if let Err(existing) = self.value_symbols.insert(symbol.clone()) { 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); self.error_duplicate(&symbol, &existing);
} }
} }
}
fn error_duplicate(&mut self, symbol: &Symbol, existing: &Symbol) { fn error_duplicate(&mut self, symbol: &Symbol, existing: &Symbol) {
self.diagnostics.push(Diagnostic { self.diagnostics.push(Diagnostic {
level: DiagnosticLevel::Error, level: DiagnosticLevel::Error,
code: Some("E_RESOLVE_DUPLICATE_SYMBOL".to_string()), 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), span: Some(symbol.span),
}); });
} }
@ -152,7 +163,9 @@ impl SymbolCollector {
code: Some("E_RESOLVE_NAMESPACE_COLLISION".to_string()), code: Some("E_RESOLVE_NAMESPACE_COLLISION".to_string()),
message: format!( message: format!(
"DebugSymbol '{}' collides with another symbol in the {:?} namespace defined at {:?}", "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), span: Some(symbol.span),
}); });

View File

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

View File

@ -23,6 +23,7 @@ use crate::common::files::FileManager;
use crate::frontends::Frontend; use crate::frontends::Frontend;
use crate::ir_vm; use crate::ir_vm;
use crate::lowering::core_to_vm; use crate::lowering::core_to_vm;
use prometeu_analysis::NameInterner;
use std::path::Path; use std::path::Path;
pub struct PbsFrontend; pub struct PbsFrontend;
@ -42,10 +43,11 @@ impl Frontend for PbsFrontend {
})?; })?;
let file_id = file_manager.add(entry.to_path_buf(), source.clone()); 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 ast = parser.parse_file()?;
let mut collector = SymbolCollector::new(); let mut collector = SymbolCollector::new(&interner);
let (type_symbols, value_symbols) = collector.collect(&ast)?; let (type_symbols, value_symbols) = collector.collect(&ast)?;
let mut module_symbols = ModuleSymbols { type_symbols, value_symbols }; 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 } 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)?; resolver.resolve(&ast)?;
let imported_symbols = resolver.imported_symbols; 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)?; typechecker.check(&ast)?;
// Lower to Core IR // 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 module_name = entry.file_stem().unwrap().to_string_lossy();
let core_program = lowerer.lower_file(&ast, &module_name)?; 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::ast::*;
use crate::frontends::pbs::lexer::Lexer; use crate::frontends::pbs::lexer::Lexer;
use crate::frontends::pbs::token::{Token, TokenKind}; use crate::frontends::pbs::token::{Token, TokenKind};
use prometeu_analysis::{NameId, NameInterner};
pub struct Parser { pub struct Parser<'a> {
tokens: Vec<Token>, tokens: Vec<Token>,
pos: usize, pos: usize,
file_id: usize, file_id: usize,
errors: Vec<Diagnostic>, 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 { impl<'a> Parser<'a> {
pub fn new(source: &str, file_id: usize) -> Self { pub fn new(source: &str, file_id: usize, interner: &'a mut NameInterner) -> Self {
let mut lexer = Lexer::new(source, file_id); let mut lexer = Lexer::new(source, file_id);
let mut tokens = Vec::new(); let mut tokens = Vec::new();
loop { 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 { Self {
tokens, tokens,
pos: 0, pos: 0,
file_id, file_id,
errors: Vec::new(), 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 { if self.peek().kind == TokenKind::OpenBrace {
self.advance(); // { self.advance(); // {
loop { loop {
if let TokenKind::Identifier(ref name) = self.peek().kind { let name = match self.peek().kind.clone() {
path.push(name.clone()); TokenKind::Identifier(name) => name,
_ => return Err(self.error("Expected identifier in import spec")),
};
self.advance(); self.advance();
} else { path.push(self.interner.intern(&name));
return Err(self.error("Expected identifier in import spec"));
}
if self.peek().kind == TokenKind::Comma { if self.peek().kind == TokenKind::Comma {
self.advance(); self.advance();
@ -130,12 +167,12 @@ impl Parser {
self.consume(TokenKind::CloseBrace)?; self.consume(TokenKind::CloseBrace)?;
} else { } else {
loop { loop {
if let TokenKind::Identifier(ref name) = self.peek().kind { let name = match self.peek().kind.clone() {
path.push(name.clone()); TokenKind::Identifier(name) => name,
_ => return Err(self.error("Expected identifier in import spec")),
};
self.advance(); self.advance();
} else { path.push(self.interner.intern(&name));
return Err(self.error("Expected identifier in import spec"));
}
if self.peek().kind == TokenKind::Dot { if self.peek().kind == TokenKind::Dot {
self.advance(); self.advance();
@ -227,7 +264,7 @@ impl Parser {
} else { } else {
Box::new(Node::TypeName(TypeNameNode { Box::new(Node::TypeName(TypeNameNode {
span: Span::new(self.file_id, 0, 0), // Placeholder for void 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 { let name = match id_tok.kind {
TokenKind::Identifier(ref s) => { TokenKind::Identifier(ref s) => {
self.advance(); self.advance();
s.clone() self.interner.intern(s)
} }
TokenKind::Optional => { TokenKind::Optional => {
self.advance(); self.advance();
"optional".to_string() self.builtin_optional
} }
TokenKind::Result => { TokenKind::Result => {
self.advance(); self.advance();
"result".to_string() self.builtin_result
} }
TokenKind::Bounded => { TokenKind::Bounded => {
self.advance(); self.advance();
"bounded".to_string() self.builtin_bounded
} }
TokenKind::None => { TokenKind::None => {
self.advance(); self.advance();
"none".to_string() self.builtin_none
} }
TokenKind::Some => { TokenKind::Some => {
self.advance(); self.advance();
"some".to_string() self.builtin_some
} }
TokenKind::Ok => { TokenKind::Ok => {
self.advance(); self.advance();
"ok".to_string() self.builtin_ok
} }
TokenKind::Err => { TokenKind::Err => {
self.advance(); self.advance();
"err".to_string() self.builtin_err
} }
_ => return Err(self.error_with_code("Expected type name", Some("E_PARSE_EXPECTED_TOKEN"))), _ => 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. // If base was "array", it already has T in args.
// We can just add N to args. // We can just add N to args.
match &mut node { 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.args.push(Node::IntLit(IntLitNode { span: size_tok.span, value: size as i64 }));
ta.span = span; 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] // Fallback for T[N] if we want to support it, but spec says array<T>[N]
node = Node::TypeApp(TypeAppNode { node = Node::TypeApp(TypeAppNode {
span, span,
base: "array".to_string(), base: self.builtin_array,
args: vec![node, Node::IntLit(IntLitNode { span: size_tok.span, value: size as i64 })], args: vec![node, Node::IntLit(IntLitNode { span: size_tok.span, value: size as i64 })],
}); });
} }
@ -673,6 +710,7 @@ impl Parser {
} }
TokenKind::Identifier(name) => { TokenKind::Identifier(name) => {
self.advance(); self.advance();
let name = self.interner.intern(&name);
let mut node = Node::Ident(IdentNode { span: tok.span, name }); let mut node = Node::Ident(IdentNode { span: tok.span, name });
loop { loop {
if self.peek().kind == TokenKind::OpenParen { if self.peek().kind == TokenKind::OpenParen {
@ -689,12 +727,12 @@ impl Parser {
} }
TokenKind::None | TokenKind::Some | TokenKind::Ok | TokenKind::Err => { TokenKind::None | TokenKind::Some | TokenKind::Ok | TokenKind::Err => {
let name = match tok.kind { let name = match tok.kind {
TokenKind::None => "none", TokenKind::None => self.builtin_none,
TokenKind::Some => "some", TokenKind::Some => self.builtin_some,
TokenKind::Ok => "ok", TokenKind::Ok => self.builtin_ok,
TokenKind::Err => "err", TokenKind::Err => self.builtin_err,
_ => unreachable!(), _ => unreachable!(),
}.to_string(); };
self.advance(); self.advance();
let mut node = Node::Ident(IdentNode { span: tok.span, name }); let mut node = Node::Ident(IdentNode { span: tok.span, name });
loop { 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(); let peeked_kind = self.peek().kind.clone();
match peeked_kind { match peeked_kind {
TokenKind::Identifier(name) => { TokenKind::Identifier(name) => {
self.advance(); self.advance();
Ok(name) Ok(self.interner.intern(&name))
} }
TokenKind::Optional => { TokenKind::Optional => {
self.advance(); self.advance();
Ok("optional".to_string()) Ok(self.builtin_optional)
} }
TokenKind::Result => { TokenKind::Result => {
self.advance(); self.advance();
Ok("result".to_string()) Ok(self.builtin_result)
} }
TokenKind::None => { TokenKind::None => {
self.advance(); self.advance();
Ok("none".to_string()) Ok(self.builtin_none)
} }
TokenKind::Some => { TokenKind::Some => {
self.advance(); self.advance();
Ok("some".to_string()) Ok(self.builtin_some)
} }
TokenKind::Ok => { TokenKind::Ok => {
self.advance(); self.advance();
Ok("ok".to_string()) Ok(self.builtin_ok)
} }
TokenKind::Err => { TokenKind::Err => {
self.advance(); self.advance();
Ok("err".to_string()) Ok(self.builtin_err)
} }
TokenKind::Bounded => { TokenKind::Bounded => {
self.advance(); self.advance();
Ok("bounded".to_string()) Ok(self.builtin_bounded)
} }
TokenKind::Invalid(msg) => { TokenKind::Invalid(msg) => {
let code = if msg.contains("Unterminated string") { let code = if msg.contains("Unterminated string") {
@ -1036,7 +1074,8 @@ mod tests {
#[test] #[test]
fn test_parse_empty_file() { 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(); let result = parser.parse_file().unwrap();
assert_eq!(result.imports.len(), 0); assert_eq!(result.imports.len(), 0);
assert_eq!(result.decls.len(), 0); assert_eq!(result.decls.len(), 0);
@ -1048,14 +1087,16 @@ mod tests {
import std.io from "std"; import std.io from "std";
import math from "./math.pbs"; 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(); let result = parser.parse_file().unwrap();
assert_eq!(result.imports.len(), 2); assert_eq!(result.imports.len(), 2);
if let Node::Import(ref imp) = result.imports[0] { if let Node::Import(ref imp) = result.imports[0] {
assert_eq!(imp.from, "std"); assert_eq!(imp.from, "std");
if let Node::ImportSpec(ref spec) = *imp.spec { 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 ImportSpec"); }
} else { panic!("Expected Import"); } } else { panic!("Expected Import"); }
} }
@ -1067,15 +1108,16 @@ fn add(a: int, b: int): int {
return a + b; 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(); let result = parser.parse_file().unwrap();
assert_eq!(result.decls.len(), 1); assert_eq!(result.decls.len(), 1);
if let Node::FnDecl(ref f) = result.decls[0] { 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.len(), 2);
assert_eq!(f.params[0].name, "a"); assert_eq!(interner.resolve(f.params[0].name), "a");
assert_eq!(f.params[1].name, "b"); assert_eq!(interner.resolve(f.params[1].name), "b");
} else { panic!("Expected FnDecl"); } } else { panic!("Expected FnDecl"); }
} }
@ -1087,12 +1129,13 @@ pub declare struct Point {
y: int 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(); let result = parser.parse_file().unwrap();
assert_eq!(result.decls.len(), 1); assert_eq!(result.decls.len(), 1);
if let Node::TypeDecl(ref t) = result.decls[0] { 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.type_kind, "struct");
assert_eq!(t.vis, Some("pub".to_string())); assert_eq!(t.vis, Some("pub".to_string()));
} else { panic!("Expected TypeDecl"); } } else { panic!("Expected TypeDecl"); }
@ -1106,12 +1149,13 @@ pub service Audio {
fn stop(): bool; 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(); let result = parser.parse_file().unwrap();
assert_eq!(result.decls.len(), 1); assert_eq!(result.decls.len(), 1);
if let Node::ServiceDecl(ref s) = result.decls[0] { 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); assert_eq!(s.members.len(), 2);
} else { panic!("Expected ServiceDecl"); } } else { panic!("Expected ServiceDecl"); }
} }
@ -1125,7 +1169,8 @@ fn main() {
foo(x, y); 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(); let result = parser.parse_file().unwrap();
assert_eq!(result.decls.len(), 1); 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(); let result = parser.parse_file().unwrap();
assert_eq!(result.decls.len(), 1); assert_eq!(result.decls.len(), 1);
} }
@ -1161,7 +1207,8 @@ fn bad() {
fn good() {} 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(); let result = parser.parse_file();
assert!(result.is_err()); assert!(result.is_err());
} }
@ -1169,7 +1216,8 @@ fn good() {}
#[test] #[test]
fn test_parse_mod_fn() { fn test_parse_mod_fn() {
let source = "mod fn test() {}"; 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"); let result = parser.parse_file().expect("mod fn should be allowed");
if let Node::FnDecl(fn_decl) = &result.decls[0] { if let Node::FnDecl(fn_decl) = &result.decls[0] {
assert_eq!(fn_decl.vis, Some("mod".to_string())); assert_eq!(fn_decl.vis, Some("mod".to_string()));
@ -1181,7 +1229,8 @@ fn good() {}
#[test] #[test]
fn test_parse_pub_fn() { fn test_parse_pub_fn() {
let source = "pub fn test() {}"; 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"); let result = parser.parse_file().expect("pub fn should be allowed in parser");
if let Node::FnDecl(fn_decl) = &result.decls[0] { if let Node::FnDecl(fn_decl) = &result.decls[0] {
assert_eq!(fn_decl.vis, Some("pub".to_string())); assert_eq!(fn_decl.vis, Some("pub".to_string()));
@ -1197,12 +1246,17 @@ fn main() {
return 42; 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 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\": \"File\""));
assert!(json.contains("\"kind\": \"FnDecl\"")); 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::common::spans::Span;
use crate::frontends::pbs::ast::*; use crate::frontends::pbs::ast::*;
use crate::frontends::pbs::symbols::*; use crate::frontends::pbs::symbols::*;
use prometeu_analysis::{NameId, NameInterner};
use std::collections::HashMap; use std::collections::HashMap;
pub trait ModuleProvider { pub trait ModuleProvider {
@ -9,9 +10,10 @@ pub trait ModuleProvider {
} }
pub struct Resolver<'a> { pub struct Resolver<'a> {
interner: &'a NameInterner,
module_provider: &'a dyn ModuleProvider, module_provider: &'a dyn ModuleProvider,
current_module: &'a ModuleSymbols, current_module: &'a ModuleSymbols,
scopes: Vec<HashMap<String, Symbol>>, scopes: Vec<HashMap<NameId, Symbol>>,
pub imported_symbols: ModuleSymbols, pub imported_symbols: ModuleSymbols,
diagnostics: Vec<Diagnostic>, diagnostics: Vec<Diagnostic>,
} }
@ -20,8 +22,10 @@ impl<'a> Resolver<'a> {
pub fn new( pub fn new(
current_module: &'a ModuleSymbols, current_module: &'a ModuleSymbols,
module_provider: &'a dyn ModuleProvider, module_provider: &'a dyn ModuleProvider,
interner: &'a NameInterner,
) -> Self { ) -> Self {
Self { Self {
interner,
module_provider, module_provider,
current_module, current_module,
scopes: Vec::new(), scopes: Vec::new(),
@ -58,30 +62,30 @@ impl<'a> Resolver<'a> {
if let Node::ImportSpec(spec) = &*imp.spec { if let Node::ImportSpec(spec) = &*imp.spec {
for name in &spec.path { for name in &spec.path {
// Try to find in Type namespace // 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 { if sym.visibility == Visibility::Pub {
let mut sym = sym.clone(); let mut sym = sym.clone();
sym.origin = Some(imp.from.clone()); sym.origin = Some(imp.from.clone());
if let Err(_) = self.imported_symbols.type_symbols.insert(sym) { 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 { } else {
self.error_visibility(sym, imp.span); self.error_visibility(sym, imp.span);
} }
} }
// Try to find in Value namespace // 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 { if sym.visibility == Visibility::Pub {
let mut sym = sym.clone(); let mut sym = sym.clone();
sym.origin = Some(imp.from.clone()); sym.origin = Some(imp.from.clone());
if let Err(_) = self.imported_symbols.value_symbols.insert(sym) { 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 { } else {
self.error_visibility(sym, imp.span); self.error_visibility(sym, imp.span);
} }
} else { } else {
self.error_undefined(name, imp.span); self.error_undefined(*name, imp.span);
} }
} }
} }
@ -139,13 +143,13 @@ impl<'a> Resolver<'a> {
} }
} }
Node::Ident(n) => { Node::Ident(n) => {
self.resolve_identifier(&n.name, n.span, Namespace::Value); self.resolve_identifier(n.name, n.span, Namespace::Value);
} }
Node::TypeName(n) => { Node::TypeName(n) => {
self.resolve_identifier(&n.name, n.span, Namespace::Type); self.resolve_identifier(n.name, n.span, Namespace::Type);
} }
Node::TypeApp(n) => { 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 { for arg in &n.args {
self.resolve_type_ref(arg); self.resolve_type_ref(arg);
} }
@ -158,32 +162,32 @@ impl<'a> Resolver<'a> {
Node::Mutate(n) => { Node::Mutate(n) => {
self.resolve_node(&n.target); self.resolve_node(&n.target);
self.enter_scope(); 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.resolve_node(&n.body);
self.exit_scope(); self.exit_scope();
} }
Node::Borrow(n) => { Node::Borrow(n) => {
self.resolve_node(&n.target); self.resolve_node(&n.target);
self.enter_scope(); 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.resolve_node(&n.body);
self.exit_scope(); self.exit_scope();
} }
Node::Peek(n) => { Node::Peek(n) => {
self.resolve_node(&n.target); self.resolve_node(&n.target);
self.enter_scope(); 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.resolve_node(&n.body);
self.exit_scope(); self.exit_scope();
} }
Node::MemberAccess(n) => { Node::MemberAccess(n) => {
if let Node::Ident(id) = &*n.object { if let Node::Ident(id) = &*n.object {
if !self.is_builtin(&id.name, Namespace::Type) { if !self.is_builtin(id.name, Namespace::Type) {
if self.lookup_identifier(&id.name, Namespace::Value).is_none() { if self.lookup_identifier(id.name, Namespace::Value).is_none() {
// If not found in Value namespace, try Type namespace (for Contracts/Services) // 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 // 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(); self.enter_scope();
for param in &n.params { for param in &n.params {
self.resolve_type_ref(&param.ty); 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 { if let Some(ret) = &n.ret {
self.resolve_type_ref(ret); self.resolve_type_ref(ret);
@ -210,7 +214,7 @@ impl<'a> Resolver<'a> {
fn resolve_service_decl(&mut self, n: &ServiceDeclNode) { fn resolve_service_decl(&mut self, n: &ServiceDeclNode) {
if let Some(ext) = &n.extends { 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 { for member in &n.members {
if let Node::ServiceFnSig(sig) = member { if let Node::ServiceFnSig(sig) = member {
@ -231,7 +235,7 @@ impl<'a> Resolver<'a> {
} }
self.enter_scope(); self.enter_scope();
for ctor in &n.constructors { 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 { for constant in &n.constants {
self.resolve_node(&constant.value); self.resolve_node(&constant.value);
@ -256,7 +260,7 @@ impl<'a> Resolver<'a> {
self.enter_scope(); self.enter_scope();
for param in &n.params { for param in &n.params {
self.resolve_type_ref(&param.ty); 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 { for init in &n.initializers {
self.resolve_node(init); self.resolve_node(init);
@ -278,14 +282,14 @@ impl<'a> Resolver<'a> {
self.resolve_type_ref(ty); self.resolve_type_ref(ty);
} }
self.resolve_node(&n.init); 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) { fn resolve_type_ref(&mut self, node: &Node) {
self.resolve_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) { if self.is_builtin(name, namespace) {
return None; return None;
} }
@ -297,7 +301,7 @@ impl<'a> Resolver<'a> {
self.diagnostics.push(Diagnostic { self.diagnostics.push(Diagnostic {
level: DiagnosticLevel::Error, level: DiagnosticLevel::Error,
code: Some("E_TYPE_UNKNOWN_TYPE".to_string()), code: Some("E_TYPE_UNKNOWN_TYPE".to_string()),
message: format!("Unknown type: {}", name), message: format!("Unknown type: {}", self.interner.resolve(name)),
span: Some(span), span: Some(span),
}); });
} else { } 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 { match namespace {
Namespace::Type => match name { Namespace::Type => match name {
"int" | "float" | "string" | "bool" | "void" | "optional" | "result" | "bounded" | "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 // 1. local bindings
if namespace == Namespace::Value { if namespace == Namespace::Value {
for scope in self.scopes.iter().rev() { 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()); return Some(sym.clone());
} }
} }
@ -369,7 +374,7 @@ impl<'a> Resolver<'a> {
None 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"); let scope = self.scopes.last_mut().expect("No scope to define local");
// Check for collision in Type namespace at top-level? // Check for collision in Type namespace at top-level?
@ -379,22 +384,25 @@ impl<'a> Resolver<'a> {
self.diagnostics.push(Diagnostic { self.diagnostics.push(Diagnostic {
level: DiagnosticLevel::Error, level: DiagnosticLevel::Error,
code: Some("E_RESOLVE_NAMESPACE_COLLISION".to_string()), 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), span: Some(span),
}); });
return; return;
} }
if scope.contains_key(name) { if scope.contains_key(&name) {
self.diagnostics.push(Diagnostic { self.diagnostics.push(Diagnostic {
level: DiagnosticLevel::Error, level: DiagnosticLevel::Error,
code: Some("E_RESOLVE_DUPLICATE_SYMBOL".to_string()), 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), span: Some(span),
}); });
} else { } else {
scope.insert(name.to_string(), Symbol { scope.insert(name, Symbol {
name: name.to_string(), name,
kind, kind,
namespace: Namespace::Value, namespace: Namespace::Value,
visibility: Visibility::FilePrivate, visibility: Visibility::FilePrivate,
@ -414,20 +422,20 @@ impl<'a> Resolver<'a> {
self.scopes.pop(); 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 { self.diagnostics.push(Diagnostic {
level: DiagnosticLevel::Error, level: DiagnosticLevel::Error,
code: Some("E_RESOLVE_UNDEFINED".to_string()), code: Some("E_RESOLVE_UNDEFINED".to_string()),
message: format!("Undefined identifier: {}", name), message: format!("Undefined identifier: {}", self.interner.resolve(name)),
span: Some(span), 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 { self.diagnostics.push(Diagnostic {
level: DiagnosticLevel::Error, level: DiagnosticLevel::Error,
code: Some("E_RESOLVE_DUPLICATE_SYMBOL".to_string()), code: Some("E_RESOLVE_DUPLICATE_SYMBOL".to_string()),
message: format!("Duplicate import: {}", name), message: format!("Duplicate import: {}", self.interner.resolve(name)),
span: Some(span), span: Some(span),
}); });
} }
@ -436,7 +444,10 @@ impl<'a> Resolver<'a> {
self.diagnostics.push(Diagnostic { self.diagnostics.push(Diagnostic {
level: DiagnosticLevel::Error, level: DiagnosticLevel::Error,
code: Some("E_RESOLVE_VISIBILITY".to_string()), 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), span: Some(span),
}); });
} }
@ -450,11 +461,13 @@ mod tests {
use crate::frontends::pbs::*; use crate::frontends::pbs::*;
use std::path::PathBuf; 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 mut fm = FileManager::new();
let file_id = fm.add(PathBuf::from("test.pbs"), source.to_string()); let file_id = fm.add(PathBuf::from("test.pbs"), source.to_string());
let mut parser = parser::Parser::new(source, file_id); let mut interner = NameInterner::new();
(parser.parse_file().expect("Parsing failed"), file_id) let mut parser = parser::Parser::new(source, file_id, &mut interner);
let ast = parser.parse_file().expect("Parsing failed");
(ast, file_id, interner)
} }
#[test] #[test]
@ -463,8 +476,8 @@ mod tests {
declare struct Foo {} declare struct Foo {}
declare struct Foo {} declare struct Foo {}
"; ";
let (ast, _) = setup_test(source); let (ast, _, mut interner) = setup_test(source);
let mut collector = SymbolCollector::new(); let mut collector = SymbolCollector::new(&interner);
let result = collector.collect(&ast); let result = collector.collect(&ast);
assert!(result.is_err()); assert!(result.is_err());
@ -478,8 +491,8 @@ mod tests {
declare struct Foo {} declare struct Foo {}
fn Foo() {} fn Foo() {}
"; ";
let (ast, _) = setup_test(source); let (ast, _, mut interner) = setup_test(source);
let mut collector = SymbolCollector::new(); let mut collector = SymbolCollector::new(&interner);
let result = collector.collect(&ast); let result = collector.collect(&ast);
assert!(result.is_err()); assert!(result.is_err());
@ -494,8 +507,8 @@ mod tests {
let x = y; let x = y;
} }
"; ";
let (ast, _) = setup_test(source); let (ast, _, mut interner) = setup_test(source);
let mut collector = SymbolCollector::new(); let mut collector = SymbolCollector::new(&interner);
let (ts, vs) = collector.collect(&ast).expect("Collection failed"); let (ts, vs) = collector.collect(&ast).expect("Collection failed");
let ms = ModuleSymbols { type_symbols: ts, value_symbols: vs }; 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 } 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); let result = resolver.resolve(&ast);
assert!(result.is_err()); assert!(result.is_err());
@ -520,8 +533,8 @@ mod tests {
let y = x; let y = x;
} }
"; ";
let (ast, _) = setup_test(source); let (ast, _, mut interner) = setup_test(source);
let mut collector = SymbolCollector::new(); let mut collector = SymbolCollector::new(&interner);
let (ts, vs) = collector.collect(&ast).expect("Collection failed"); let (ts, vs) = collector.collect(&ast).expect("Collection failed");
let ms = ModuleSymbols { type_symbols: ts, value_symbols: vs }; 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 } 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); let result = resolver.resolve(&ast);
assert!(result.is_ok()); assert!(result.is_ok());
@ -542,8 +555,8 @@ mod tests {
import PrivateType from \"./other.pbs\" import PrivateType from \"./other.pbs\"
fn main() {} fn main() {}
"; ";
let (ast, _) = setup_test(source); let (ast, _, mut interner) = setup_test(source);
let mut collector = SymbolCollector::new(); let mut collector = SymbolCollector::new(&interner);
let (ts, vs) = collector.collect(&ast).expect("Collection failed"); let (ts, vs) = collector.collect(&ast).expect("Collection failed");
let ms = ModuleSymbols { type_symbols: ts, value_symbols: vs }; let ms = ModuleSymbols { type_symbols: ts, value_symbols: vs };
@ -558,7 +571,7 @@ mod tests {
let mut other_ts = SymbolTable::new(); let mut other_ts = SymbolTable::new();
other_ts.insert(Symbol { other_ts.insert(Symbol {
name: "PrivateType".to_string(), name: interner.intern("PrivateType"),
kind: SymbolKind::Struct, kind: SymbolKind::Struct,
namespace: Namespace::Type, namespace: Namespace::Type,
visibility: Visibility::FilePrivate, visibility: Visibility::FilePrivate,
@ -572,7 +585,7 @@ mod tests {
other: ModuleSymbols { type_symbols: other_ts, value_symbols: SymbolTable::new() }, 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); let result = resolver.resolve(&ast);
assert!(result.is_err()); assert!(result.is_err());
@ -588,8 +601,8 @@ mod tests {
let x: PubType = 10; let x: PubType = 10;
} }
"; ";
let (ast, _) = setup_test(source); let (ast, _, mut interner) = setup_test(source);
let mut collector = SymbolCollector::new(); let mut collector = SymbolCollector::new(&interner);
let (ts, vs) = collector.collect(&ast).expect("Collection failed"); let (ts, vs) = collector.collect(&ast).expect("Collection failed");
let ms = ModuleSymbols { type_symbols: ts, value_symbols: vs }; let ms = ModuleSymbols { type_symbols: ts, value_symbols: vs };
@ -604,7 +617,7 @@ mod tests {
let mut other_ts = SymbolTable::new(); let mut other_ts = SymbolTable::new();
other_ts.insert(Symbol { other_ts.insert(Symbol {
name: "PubType".to_string(), name: interner.intern("PubType"),
kind: SymbolKind::Struct, kind: SymbolKind::Struct,
namespace: Namespace::Type, namespace: Namespace::Type,
visibility: Visibility::Pub, visibility: Visibility::Pub,
@ -618,7 +631,7 @@ mod tests {
other: ModuleSymbols { type_symbols: other_ts, value_symbols: SymbolTable::new() }, 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); let result = resolver.resolve(&ast);
assert!(result.is_ok()); assert!(result.is_ok());
@ -630,8 +643,8 @@ mod tests {
import NonExistent from \"./missing.pbs\" import NonExistent from \"./missing.pbs\"
fn main() {} fn main() {}
"; ";
let (ast, _) = setup_test(source); let (ast, _, interner) = setup_test(source);
let mut collector = SymbolCollector::new(); let mut collector = SymbolCollector::new(&interner);
let (ts, vs) = collector.collect(&ast).expect("Collection failed"); let (ts, vs) = collector.collect(&ast).expect("Collection failed");
let ms = ModuleSymbols { type_symbols: ts, value_symbols: vs }; 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 } 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); let result = resolver.resolve(&ast);
assert!(result.is_err()); assert!(result.is_err());

View File

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

View File

@ -2,6 +2,7 @@ use crate::common::diagnostics::DiagnosticBundle;
use crate::common::files::FileManager; use crate::common::files::FileManager;
use crate::frontends::pbs::{collector::SymbolCollector, parser::Parser, Symbol, Visibility}; use crate::frontends::pbs::{collector::SymbolCollector, parser::Parser, Symbol, Visibility};
use crate::manifest::{load_manifest, ManifestKind}; use crate::manifest::{load_manifest, ManifestKind};
use prometeu_analysis::NameInterner;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::HashMap; use std::collections::HashMap;
use std::fs; 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> { pub fn build_exports(module_dir: &Path, file_manager: &mut FileManager) -> Result<ExportTable, SourceError> {
let mut symbols = HashMap::new(); let mut symbols = HashMap::new();
let mut files = Vec::new(); let mut files = Vec::new();
let mut interner = NameInterner::new();
if module_dir.is_dir() { if module_dir.is_dir() {
discover_recursive(module_dir, &mut files)?; 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 source = fs::read_to_string(&file_path)?;
let file_id = file_manager.add(file_path.clone(), source.clone()); 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 ast = parser.parse_file()?;
let mut collector = SymbolCollector::new(); let mut collector = SymbolCollector::new(&interner);
let (type_symbols, value_symbols) = collector.collect(&ast)?; let (type_symbols, value_symbols) = collector.collect(&ast)?;
// Merge only public symbols // Merge only public symbols
for symbol in type_symbols.symbols.into_values() { for symbol in type_symbols.symbols.into_values() {
if symbol.visibility == Visibility::Pub { 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() { for symbol in value_symbols.symbols.into_values() {
if symbol.visibility == Visibility::Pub { 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::compiler::compile;
use prometeu_compiler::frontends::pbs::ast::Node; use prometeu_compiler::frontends::pbs::ast::Node;
use prometeu_compiler::frontends::pbs::parser::Parser; use prometeu_compiler::frontends::pbs::parser::Parser;
use prometeu_analysis::NameInterner;
use std::fs; use std::fs;
use std::path::Path; use std::path::Path;
@ -56,7 +57,8 @@ fn generate_canonical_goldens() {
// 3. AST JSON // 3. AST JSON
let source = fs::read_to_string(project_dir.join("src/main/modules/main.pbs")).unwrap(); 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 = parser.parse_file().expect("Failed to parse AST");
let ast_node = Node::File(ast); let ast_node = Node::File(ast);
let ast_json = serde_json::to_string_pretty(&ast_node).unwrap(); let ast_json = serde_json::to_string_pretty(&ast_node).unwrap();

View File

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