pr 05.d
This commit is contained in:
parent
4cee158206
commit
e6cfef9a77
@ -3,7 +3,7 @@ use std::collections::HashMap;
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
|
||||
pub struct NameId(pub u32);
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct NameInterner {
|
||||
names: Vec<String>,
|
||||
ids: HashMap<String, NameId>,
|
||||
@ -28,6 +28,10 @@ impl NameInterner {
|
||||
id
|
||||
}
|
||||
|
||||
pub fn get(&self, s: &str) -> Option<NameId> {
|
||||
self.ids.get(s).copied()
|
||||
}
|
||||
|
||||
pub fn resolve(&self, id: NameId) -> &str {
|
||||
&self.names[id.0 as usize]
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
use prometeu_analysis::interner::NameInterner;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
@ -11,11 +12,30 @@ pub struct SourceFile {
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FileManager {
|
||||
files: Vec<SourceFile>,
|
||||
interner: NameInterner,
|
||||
}
|
||||
|
||||
impl FileManager {
|
||||
pub fn new() -> Self {
|
||||
Self { files: Vec::new() }
|
||||
Self {
|
||||
files: Vec::new(),
|
||||
interner: NameInterner::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_interner(interner: NameInterner) -> Self {
|
||||
Self {
|
||||
files: Vec::new(),
|
||||
interner,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn interner(&self) -> &NameInterner {
|
||||
&self.interner
|
||||
}
|
||||
|
||||
pub fn interner_mut(&mut self) -> &mut NameInterner {
|
||||
&mut self.interner
|
||||
}
|
||||
|
||||
pub fn add(&mut self, path: PathBuf, source: String) -> usize {
|
||||
|
||||
@ -59,6 +59,9 @@ impl Frontend for PbsFrontend {
|
||||
}
|
||||
|
||||
let mut resolver = Resolver::new(&module_symbols, &EmptyProvider, &interner);
|
||||
// Step 3: Resolve and type expressions
|
||||
let mut interner_mut = file_manager.interner_mut();
|
||||
resolver.bootstrap_types(&mut interner_mut);
|
||||
resolver.resolve(&parsed.arena, parsed.root)?;
|
||||
let imported_symbols = resolver.imported_symbols;
|
||||
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
use crate::analysis::types::{TypeArena, TypeFacts, TypeId, TypeKind};
|
||||
use crate::common::diagnostics::{Diagnostic, DiagnosticBundle, DiagnosticLevel};
|
||||
use crate::common::spans::Span;
|
||||
use crate::frontends::pbs::ast::*;
|
||||
@ -16,6 +17,9 @@ pub struct Resolver<'a> {
|
||||
scopes: Vec<HashMap<NameId, Symbol>>,
|
||||
pub imported_symbols: ModuleSymbols,
|
||||
diagnostics: Vec<Diagnostic>,
|
||||
pub type_arena: TypeArena,
|
||||
pub type_facts: TypeFacts,
|
||||
primitives: HashMap<String, TypeId>,
|
||||
}
|
||||
|
||||
impl<'a> Resolver<'a> {
|
||||
@ -31,6 +35,18 @@ impl<'a> Resolver<'a> {
|
||||
scopes: Vec::new(),
|
||||
imported_symbols: ModuleSymbols::new(),
|
||||
diagnostics: Vec::new(),
|
||||
type_arena: TypeArena::new(),
|
||||
type_facts: TypeFacts::new(),
|
||||
primitives: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bootstrap_types(&mut self, interner: &mut NameInterner) {
|
||||
let primitive_names = ["int", "bool", "float", "string", "bounded", "void"];
|
||||
for name in primitive_names {
|
||||
let name_id = interner.intern(name);
|
||||
let type_id = self.type_arena.intern_type(TypeKind::Primitive { name: name_id });
|
||||
self.primitives.insert(name.to_string(), type_id);
|
||||
}
|
||||
}
|
||||
|
||||
@ -148,33 +164,91 @@ impl<'a> Resolver<'a> {
|
||||
self.resolve_node(arena, *arg);
|
||||
}
|
||||
}
|
||||
NodeKind::Unary(n) => self.resolve_node(arena, n.expr),
|
||||
NodeKind::Unary(n) => {
|
||||
self.resolve_node(arena, n.expr);
|
||||
match n.op.as_str() {
|
||||
"-" => {
|
||||
let int_type = self.primitives.get("int").copied();
|
||||
if let Some(expr_type) = self.type_facts.get_node_type(n.expr) {
|
||||
if Some(expr_type) == int_type {
|
||||
if let Some(id) = int_type {
|
||||
self.type_facts.set_node_type(node, id);
|
||||
}
|
||||
} else {
|
||||
self.diagnostics.push(Diagnostic {
|
||||
level: DiagnosticLevel::Error,
|
||||
code: Some("E_TYPE_MISMATCH".to_string()),
|
||||
message: "Unary '-' operator expects 'int'".to_string(),
|
||||
span: Some(arena.span(node)),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
"!" => {
|
||||
let bool_type = self.primitives.get("bool").copied();
|
||||
if let Some(expr_type) = self.type_facts.get_node_type(n.expr) {
|
||||
if Some(expr_type) == bool_type {
|
||||
if let Some(id) = bool_type {
|
||||
self.type_facts.set_node_type(node, id);
|
||||
}
|
||||
} else {
|
||||
self.diagnostics.push(Diagnostic {
|
||||
level: DiagnosticLevel::Error,
|
||||
code: Some("E_TYPE_MISMATCH".to_string()),
|
||||
message: "Unary '!' operator expects 'bool'".to_string(),
|
||||
span: Some(arena.span(node)),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
NodeKind::Binary(n) => {
|
||||
self.resolve_node(arena, n.left);
|
||||
self.resolve_node(arena, n.right);
|
||||
}
|
||||
NodeKind::Cast(n) => {
|
||||
self.resolve_node(arena, n.expr);
|
||||
self.resolve_type_ref(arena, n.ty);
|
||||
}
|
||||
NodeKind::IfExpr(n) => {
|
||||
self.resolve_node(arena, n.cond);
|
||||
self.resolve_node(arena, n.then_block);
|
||||
if let Some(else_block) = n.else_block {
|
||||
self.resolve_node(arena, else_block);
|
||||
}
|
||||
}
|
||||
NodeKind::WhenExpr(n) => {
|
||||
for arm in &n.arms {
|
||||
if let NodeKind::WhenArm(arm_node) = arena.kind(*arm) {
|
||||
self.resolve_node(arena, arm_node.cond);
|
||||
self.resolve_node(arena, arm_node.body);
|
||||
|
||||
let left_type = self.type_facts.get_node_type(n.left);
|
||||
let right_type = self.type_facts.get_node_type(n.right);
|
||||
let int_type = self.primitives.get("int").copied();
|
||||
let bool_type = self.primitives.get("bool").copied();
|
||||
|
||||
match n.op.as_str() {
|
||||
"+" | "-" | "*" | "/" => {
|
||||
if let (Some(l), Some(r)) = (left_type, right_type) {
|
||||
if Some(l) == int_type && Some(r) == int_type {
|
||||
if let Some(id) = int_type {
|
||||
self.type_facts.set_node_type(node, id);
|
||||
}
|
||||
} else {
|
||||
self.diagnostics.push(Diagnostic {
|
||||
level: DiagnosticLevel::Error,
|
||||
code: Some("E_TYPE_MISMATCH".to_string()),
|
||||
message: format!("Binary '{}' operator expects 'int' operands", n.op),
|
||||
span: Some(arena.span(node)),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
"==" | "<" | ">" | "<=" | ">=" | "!=" => {
|
||||
if let (Some(l), Some(r)) = (left_type, right_type) {
|
||||
if l == r {
|
||||
if let Some(id) = bool_type {
|
||||
self.type_facts.set_node_type(node, id);
|
||||
}
|
||||
} else {
|
||||
self.diagnostics.push(Diagnostic {
|
||||
level: DiagnosticLevel::Error,
|
||||
code: Some("E_TYPE_MISMATCH".to_string()),
|
||||
message: format!("Binary '{}' operator expects operands of the same type", n.op),
|
||||
span: Some(arena.span(node)),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
NodeKind::Ident(n) => {
|
||||
self.resolve_identifier(n.name, arena.span(node), Namespace::Value);
|
||||
}
|
||||
NodeKind::TypeName(n) => {
|
||||
self.resolve_identifier(n.name, arena.span(node), Namespace::Type);
|
||||
}
|
||||
@ -210,6 +284,51 @@ impl<'a> Resolver<'a> {
|
||||
self.resolve_node(arena, n.body);
|
||||
self.exit_scope();
|
||||
}
|
||||
NodeKind::Cast(n) => {
|
||||
self.resolve_node(arena, n.expr);
|
||||
self.resolve_type_ref(arena, n.ty);
|
||||
}
|
||||
NodeKind::IfExpr(n) => {
|
||||
self.resolve_node(arena, n.cond);
|
||||
self.resolve_node(arena, n.then_block);
|
||||
if let Some(else_block) = n.else_block {
|
||||
self.resolve_node(arena, else_block);
|
||||
}
|
||||
}
|
||||
NodeKind::WhenExpr(n) => {
|
||||
for arm in &n.arms {
|
||||
if let NodeKind::WhenArm(arm_node) = arena.kind(*arm) {
|
||||
self.resolve_node(arena, arm_node.cond);
|
||||
self.resolve_node(arena, arm_node.body);
|
||||
}
|
||||
}
|
||||
}
|
||||
NodeKind::Ident(n) => {
|
||||
let _sym = self.resolve_identifier(n.name, arena.span(node), Namespace::Value);
|
||||
// For v0 bootstrap, if we found a local variable, we don't know its type yet (no inference)
|
||||
// but if we had a way to track symbol types, we'd use it here.
|
||||
// For now, we only type literals and simple operators.
|
||||
}
|
||||
NodeKind::IntLit(_) => {
|
||||
if let Some(id) = self.primitives.get("int") {
|
||||
self.type_facts.set_node_type(node, *id);
|
||||
}
|
||||
}
|
||||
NodeKind::FloatLit(_) => {
|
||||
if let Some(id) = self.primitives.get("float") {
|
||||
self.type_facts.set_node_type(node, *id);
|
||||
}
|
||||
}
|
||||
NodeKind::BoundedLit(_) => {
|
||||
if let Some(id) = self.primitives.get("bounded") {
|
||||
self.type_facts.set_node_type(node, *id);
|
||||
}
|
||||
}
|
||||
NodeKind::StringLit(_) => {
|
||||
if let Some(id) = self.primitives.get("string") {
|
||||
self.type_facts.set_node_type(node, *id);
|
||||
}
|
||||
}
|
||||
NodeKind::MemberAccess(n) => match arena.kind(n.object) {
|
||||
NodeKind::Ident(id) => {
|
||||
let ident_span = arena.span(n.object);
|
||||
@ -231,7 +350,7 @@ impl<'a> Resolver<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_fn_decl(&mut self, arena: &AstArena, id: NodeId, n: &FnDeclNodeArena) {
|
||||
fn resolve_fn_decl(&mut self, arena: &AstArena, _id: NodeId, n: &FnDeclNodeArena) {
|
||||
self.enter_scope();
|
||||
for param in &n.params {
|
||||
self.resolve_type_ref(arena, param.ty);
|
||||
@ -507,10 +626,10 @@ mod tests {
|
||||
fn setup_test(source: &str) -> (ParsedAst, usize, NameInterner) {
|
||||
let mut fm = FileManager::new();
|
||||
let file_id = fm.add(PathBuf::from("test.pbs"), source.to_string());
|
||||
let mut interner = NameInterner::new();
|
||||
let mut parser = parser::Parser::new(source, file_id, &mut interner);
|
||||
let mut interner = fm.interner().clone();
|
||||
let mut parser = parser::Parser::new(source, file_id, fm.interner_mut());
|
||||
let parsed = parser.parse_file().expect("Parsing failed");
|
||||
(parsed, file_id, interner)
|
||||
(parsed, file_id, fm.interner().clone())
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -519,7 +638,7 @@ mod tests {
|
||||
declare struct Foo {}
|
||||
declare struct Foo {}
|
||||
";
|
||||
let (parsed, _, mut interner) = setup_test(source);
|
||||
let (parsed, _, interner) = setup_test(source);
|
||||
let mut collector = SymbolCollector::new(&interner);
|
||||
let result = collector.collect(&parsed.arena, parsed.root);
|
||||
|
||||
@ -534,7 +653,7 @@ mod tests {
|
||||
declare struct Foo {}
|
||||
fn Foo() {}
|
||||
";
|
||||
let (parsed, _, mut interner) = setup_test(source);
|
||||
let (parsed, _, interner) = setup_test(source);
|
||||
let mut collector = SymbolCollector::new(&interner);
|
||||
let result = collector.collect(&parsed.arena, parsed.root);
|
||||
|
||||
@ -550,7 +669,7 @@ mod tests {
|
||||
let x = y;
|
||||
}
|
||||
";
|
||||
let (parsed, _, mut interner) = setup_test(source);
|
||||
let (parsed, _, interner) = setup_test(source);
|
||||
let mut collector = SymbolCollector::new(&interner);
|
||||
let (ts, vs) = collector
|
||||
.collect(&parsed.arena, parsed.root)
|
||||
@ -578,7 +697,7 @@ mod tests {
|
||||
let y = x;
|
||||
}
|
||||
";
|
||||
let (parsed, _, mut interner) = setup_test(source);
|
||||
let (parsed, _, interner) = setup_test(source);
|
||||
let mut collector = SymbolCollector::new(&interner);
|
||||
let (ts, vs) = collector
|
||||
.collect(&parsed.arena, parsed.root)
|
||||
@ -713,4 +832,64 @@ mod tests {
|
||||
let bundle = result.unwrap_err();
|
||||
assert!(bundle.diagnostics.iter().any(|d| d.code == Some("E_RESOLVE_INVALID_IMPORT".to_string())));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_typing_literals_and_binary() {
|
||||
let source = "
|
||||
fn main() {
|
||||
let a = 1 + 2;
|
||||
let b = 1 == 2;
|
||||
let c = -1;
|
||||
}
|
||||
";
|
||||
let (parsed, _, mut interner) = setup_test(source);
|
||||
let mut collector = SymbolCollector::new(&interner);
|
||||
let (ts, vs) = collector.collect(&parsed.arena, parsed.root).unwrap();
|
||||
let ms = ModuleSymbols { type_symbols: ts, value_symbols: vs };
|
||||
|
||||
struct EmptyProvider;
|
||||
impl ModuleProvider for EmptyProvider {
|
||||
fn get_module_symbols(&self, _path: &str) -> Option<&ModuleSymbols> { None }
|
||||
}
|
||||
|
||||
let mut interner_for_bootstrap = interner.clone();
|
||||
let mut resolver = Resolver::new(&ms, &EmptyProvider, &interner);
|
||||
resolver.bootstrap_types(&mut interner_for_bootstrap);
|
||||
resolver.resolve(&parsed.arena, parsed.root).expect("Resolution failed");
|
||||
|
||||
// Verify types in TypeFacts using format_type for easier assertion
|
||||
use crate::analysis::types::format_type;
|
||||
|
||||
let mut found_a = false;
|
||||
let mut found_b = false;
|
||||
let mut found_c = false;
|
||||
|
||||
for i in 0..parsed.arena.nodes.len() {
|
||||
let id = NodeId(i as u32);
|
||||
let kind = parsed.arena.kind(id);
|
||||
|
||||
match kind {
|
||||
NodeKind::Binary(n) if n.op == "+" => {
|
||||
let ty = resolver.type_facts.get_node_type(id).expect("Binary + should have type");
|
||||
assert_eq!(format_type(ty, &resolver.type_arena, &interner_for_bootstrap, None), "int");
|
||||
found_a = true;
|
||||
}
|
||||
NodeKind::Binary(n) if n.op == "==" => {
|
||||
let ty = resolver.type_facts.get_node_type(id).expect("Binary == should have type");
|
||||
assert_eq!(format_type(ty, &resolver.type_arena, &interner_for_bootstrap, None), "bool");
|
||||
found_b = true;
|
||||
}
|
||||
NodeKind::Unary(n) if n.op == "-" => {
|
||||
let ty = resolver.type_facts.get_node_type(id).expect("Unary - should have type");
|
||||
assert_eq!(format_type(ty, &resolver.type_arena, &interner_for_bootstrap, None), "int");
|
||||
found_c = true;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
assert!(found_a, "Binary + node not found");
|
||||
assert!(found_b, "Binary == node not found");
|
||||
assert!(found_c, "Unary - node not found");
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user