diff --git a/TODO.md b/TODO.md index e4e0905..324aab7 100644 --- a/TODO.md +++ b/TODO.md @@ -1,5 +1,10 @@ #Typechecking Notes + +IDEA: - if you have a pattern-match where one variant has a variable and the other lacks it +instead of treating this as a type error, promote the bound variable to an option type + + IS BOX SYNTAX READY???? (cf. cardelli paper) diff --git a/schala-lang/language/src/symbol_table.rs b/schala-lang/language/src/symbol_table.rs index 0a370de..2634394 100644 --- a/schala-lang/language/src/symbol_table.rs +++ b/schala-lang/language/src/symbol_table.rs @@ -1,12 +1,17 @@ use std::collections::HashMap; +use std::collections::hash_map::Entry; use std::rc::Rc; use std::fmt; use std::fmt::Write; +use std::iter::IntoIterator; use crate::ast; -use crate::ast::{TypeBody, TypeSingletonName, Signature}; +use crate::ast::{Meta, TypeBody, TypeSingletonName, Signature, Statement}; use crate::typechecking::TypeName; +type LineNumber = u32; +type SymbolTrackTable = HashMap, LineNumber>; + #[derive(PartialEq, Eq, Hash, Debug)] struct SymbolPath { name: Rc, @@ -85,14 +90,43 @@ impl fmt::Display for SymbolSpec { impl SymbolTable { /* note: this adds names for *forward reference* but doesn't actually create any types. solve that problem * later */ + pub fn add_top_level_symbols(&mut self, ast: &ast::AST) -> Result<(), String> { - use self::ast::Statement; + let mut seen_identifiers = HashMap::new(); + self.add_symbols_from_scope(&ast.0, &mut seen_identifiers) + } + + fn add_symbols_from_scope<'a>(&'a mut self, statements: &Vec>, seen_identifiers: &mut SymbolTrackTable) -> Result<(), String> { use self::ast::Declaration::*; - for statement in ast.0.iter() { - let statement = statement.node(); + + fn check_symbol(table: &mut SymbolTrackTable, name: &Rc) -> Result<(), String> { + match table.entry(name.clone()) { + Entry::Occupied(o) => { + let line_number = o.get(); //TODO make this actually work + Err(format!("Duplicate definition: {}. It's already defined at {}", name, line_number)) + }, + Entry::Vacant(v) => { + let line_number = 0; //TODO should work + v.insert(line_number); + Ok(()) + } + } + } + + for meta in statements.iter() { + let statement = meta.node(); if let Statement::Declaration(decl) = statement { match decl { - FuncSig(signature) | FuncDecl(signature, _) => self.add_function_signature(signature)?, + FuncSig(ref signature) => { + check_symbol(seen_identifiers, &signature.name)?; + self.add_function_signature(signature)? + } + FuncDecl(ref signature, ref body) => { + check_symbol(seen_identifiers, &signature.name)?; + self.add_function_signature(signature)?; + let mut subscope_seen_identifiers = HashMap::new(); + self.add_symbols_from_scope(body, &mut subscope_seen_identifiers)? + }, TypeDecl { name, body, mutable } => self.add_type_decl(name, body, mutable)?, _ => () } @@ -204,5 +238,18 @@ mod symbol_table_tests { fn basic_symbol_table() { values_in_table! { "let a = 10; fn b() { 20 }", &rc!(b) }; } + + #[test] + fn no_duplicates() { + let source = r#" + fn a() { 1 } + fn b() { 2 } + fn a() { 3 } + "#; + let mut symbol_table = SymbolTable::new(); + let ast = crate::util::quick_ast(source); + let output = symbol_table.add_top_level_symbols(&ast).unwrap_err(); + assert!(output.contains("Duplicate")) + } } diff --git a/schala-lang/language/src/util.rs b/schala-lang/language/src/util.rs index 111868f..879b95d 100644 --- a/schala-lang/language/src/util.rs +++ b/schala-lang/language/src/util.rs @@ -42,12 +42,14 @@ impl<'a, T, V> ScopeStack<'a, T, V> where T: Hash + Eq { } /// this is intended for use in tests, and does no error-handling whatsoever +#[allow(dead_code)] pub fn quick_ast(input: &str) -> crate::ast::AST { let tokens = crate::tokenizing::tokenize(input); let mut parser = crate::parsing::Parser::new(tokens); parser.parse().unwrap() } +#[allow(dead_code)] macro_rules! rc { ($string:tt) => { Rc::new(stringify!($string).to_string()) } }