diff --git a/fine/src/semantics.rs b/fine/src/semantics.rs index af3a4598..8b2f2cdf 100644 --- a/fine/src/semantics.rs +++ b/fine/src/semantics.rs @@ -222,7 +222,9 @@ fn set_logical_parents( } } +#[derive(Clone, Copy, Debug)] enum Incremental { + None, InProgress, Complete(T), } @@ -239,15 +241,14 @@ pub struct Semantics<'a> { // TODO: State should be externalized instead of this refcell nonsense. errors: RefCell>, - types: RefCell>>, - environments: RefCell>>, + types: RefCell>>, + environments: RefCell>>, empty_environment: EnvironmentRef, } impl<'a> Semantics<'a> { pub fn new(tree: &'a SyntaxTree<'a>, lines: &'a Lines) -> Self { - let mut logical_parents = Vec::with_capacity(tree.len()); - logical_parents.resize(tree.len(), None); + let mut logical_parents = vec![None; tree.len()]; if let Some(root) = tree.root() { set_logical_parents(&mut logical_parents, tree, root, None); } @@ -257,8 +258,8 @@ impl<'a> Semantics<'a> { lines, logical_parents, errors: RefCell::new(vec![]), - types: RefCell::new(HashMap::new()), - environments: RefCell::new(HashMap::new()), + types: RefCell::new(vec![Incremental::None; tree.len()]), + environments: RefCell::new(vec![Incremental::None; tree.len()]), empty_environment: EnvironmentRef::new(Environment::new(None)), }; @@ -342,22 +343,23 @@ impl<'a> Semantics<'a> { } pub fn environment_of(&self, t: TreeRef) -> EnvironmentRef { - match self.environments.borrow().get(&t) { - None => (), - Some(Incremental::Complete(e)) => return e.clone(), - Some(Incremental::InProgress) => { - // TODO: Rewrite as complete with empty after reporting error. - // eprintln!("environment_of circular => {t:?}"); - self.report_error_tree_ref( - t, - "INTERNAL COMPILER ERROR: Circular dependency detected: environment", - ); - return self.empty_environment.clone(); + { + let state = &mut self.environments.borrow_mut()[t.index()]; + match state { + Incremental::None => (), + Incremental::Complete(e) => return e.clone(), + Incremental::InProgress => { + // eprintln!("environment_of circular => {t:?}"); + self.report_error_tree_ref( + t, + "INTERNAL COMPILER ERROR: Circular dependency detected: environment", + ); + *state = Incremental::Complete(self.empty_environment.clone()); + return self.empty_environment.clone(); + } } + *state = Incremental::InProgress; } - self.environments - .borrow_mut() - .insert(t, Incremental::InProgress); let tree = &self.syntax_tree[t]; // eprintln!("environment_of => {tree:?}"); @@ -375,9 +377,7 @@ impl<'a> Semantics<'a> { _ => parent, }; - self.environments - .borrow_mut() - .insert(t, Incremental::Complete(result.clone())); + self.environments.borrow_mut()[t.index()] = Incremental::Complete(result.clone()); result } @@ -444,20 +444,20 @@ impl<'a> Semantics<'a> { } pub fn type_of(&self, t: TreeRef) -> Option { - match self.types.borrow().get(&t) { - None => (), - Some(Incremental::Complete(existing)) => return Some(existing.clone()), - Some(Incremental::InProgress) => { - // TODO: Rewrite as complete with error after reporting error. - // eprintln!("type_of circular => {t:?}"); - self.report_error_tree_ref( - t, - "INTERNAL COMPILER ERROR: Circular dependency detected: type", - ); - return Some(Type::Error); + { + let state = &mut self.types.borrow_mut()[t.index()]; + match state { + Incremental::None => (), + Incremental::Complete(existing) => return Some(existing.clone()), + Incremental::InProgress => { + // eprintln!("type_of circular => {t:?}"); + self.report_error_tree_ref(t, "The type of this expression depends on itself"); + *state = Incremental::Complete(Type::Error); + return Some(Type::Error); + } } + *state = Incremental::InProgress; } - self.types.borrow_mut().insert(t, Incremental::InProgress); let tree = &self.syntax_tree[t]; // eprintln!("type_of => {tree:?}"); @@ -479,21 +479,13 @@ impl<'a> Semantics<'a> { TreeKind::ExpressionStatement => self.type_of_expression_statement(tree), TreeKind::Identifier => self.type_of_identifier(tree), - // TODO: Previously I had short-circuited here and not put anything - // in the table if this node isn't the kind that I would - // normally compute a type for. I should keep doing that to - // detect nonsense without blowing out the hash table. If - // we're going to be computing a type for every node it - // should just be an array instead of a hash table. _ => None, }; // NOTE: These return `None` if they encounter some problem. let result = result.unwrap_or(Type::Error); - self.types - .borrow_mut() - .insert(t, Incremental::Complete(result.clone())); + self.types.borrow_mut()[t.index()] = Incremental::Complete(result.clone()); Some(result) }