Fix hiding logic; Fix hiding with predecls

* Hiding logic is simplified and fixed so you can hide and unhide the
  same def repeatedly.
* Separates predeclared ids into its own data structure to protect them
  from hiding. Otherwise, you could hide the predeclared variable and
  the actual def would panic.
This commit is contained in:
Jakub Žádník 2021-10-01 22:29:24 +03:00
parent aa06a71e1f
commit 2af8116f50
2 changed files with 89 additions and 81 deletions

View File

@ -42,7 +42,7 @@ pub fn parse_def_predecl(working_set: &mut StateWorkingSet, spans: &[Span]) {
signature.name = name;
let decl = signature.predeclare();
working_set.add_decl(decl);
working_set.add_predecl(decl);
}
}
}
@ -95,15 +95,15 @@ pub fn parse_def(
call.positional.push(block);
if let (Some(name), Some(mut signature), Some(block_id)) =
(name, signature, block_id)
(&name, signature, block_id)
{
let decl_id = working_set
.find_decl(name.as_bytes())
.find_predecl(name.as_bytes())
.expect("internal error: predeclaration failed to add definition");
let declaration = working_set.get_decl_mut(decl_id);
signature.name = name;
signature.name = name.clone();
*declaration = signature.into_block_command(block_id);
}
@ -118,6 +118,19 @@ pub fn parse_def(
}
working_set.exit_scope();
if let Some(name) = name {
// It's OK if it returns None: The decl was already merged in previous parse
// pass.
working_set.merge_predecl(name.as_bytes());
} else {
error = error.or_else(|| {
Some(ParseError::UnknownState(
"Could not get string from string expression".into(),
*name_span,
))
});
}
call
} else {
let err_span = Span {
@ -581,18 +594,10 @@ pub fn parse_hide(
let name_bytes: Vec<u8> = working_set.get_span_contents(spans[1]).into();
// TODO: Do the import pattern stuff for bulk-hiding
// TODO: move this error into error = error.or pattern
let _decl_id = if let Some(id) = working_set.find_decl(&name_bytes) {
id
} else {
return (
garbage_statement(spans),
Some(ParseError::UnknownCommand(spans[1])),
);
};
// Hide the definitions
working_set.hide_decl(name_bytes);
if working_set.hide_decl(&name_bytes).is_none() {
error = error.or_else(|| Some(ParseError::UnknownCommand(spans[1])));
}
// Create the Hide command call
let hide_decl_id = working_set

View File

@ -1,7 +1,10 @@
use super::Command;
use crate::{ast::Block, BlockId, DeclId, Span, Type, VarId};
use core::panic;
use std::{collections::HashMap, slice::Iter};
use std::{
collections::{HashMap, HashSet},
slice::Iter,
};
pub struct EngineState {
files: Vec<(String, usize, usize)>,
@ -18,7 +21,7 @@ pub struct ScopeFrame {
decls: HashMap<Vec<u8>, DeclId>,
aliases: HashMap<Vec<u8>, Vec<Span>>,
modules: HashMap<Vec<u8>, BlockId>,
hiding: HashMap<Vec<u8>, usize>, // defines what is being hidden and its "hiding strength"
hiding: HashSet<DeclId>,
}
impl ScopeFrame {
@ -28,7 +31,7 @@ impl ScopeFrame {
decls: HashMap::new(),
aliases: HashMap::new(),
modules: HashMap::new(),
hiding: HashMap::new(),
hiding: HashSet::new(),
}
}
@ -84,7 +87,7 @@ impl EngineState {
last.modules.insert(item.0, item.1);
}
for item in first.hiding.into_iter() {
last.hiding.insert(item.0, item.1);
last.hiding.insert(item);
}
}
}
@ -129,21 +132,13 @@ impl EngineState {
}
pub fn find_decl(&self, name: &[u8]) -> Option<DeclId> {
let mut hiding_strength = 0;
// println!("state: starting finding {}", String::from_utf8_lossy(&name));
let mut hiding: HashSet<DeclId> = HashSet::new();
for scope in self.scope.iter().rev() {
// println!("hiding map: {:?}", scope.hiding);
// check if we're hiding the declin this scope
if let Some(strength) = scope.hiding.get(name) {
hiding_strength += strength;
}
hiding.extend(&scope.hiding);
if let Some(decl_id) = scope.decls.get(name) {
// if we're hiding this decl, do not return it and reduce the hiding strength
if hiding_strength > 0 {
hiding_strength -= 1;
} else {
if !hiding.contains(decl_id) {
return Some(*decl_id);
}
}
@ -242,9 +237,10 @@ pub struct StateWorkingSet<'a> {
pub struct StateDelta {
files: Vec<(String, usize, usize)>,
pub(crate) file_contents: Vec<u8>,
vars: Vec<Type>, // indexed by VarId
decls: Vec<Box<dyn Command>>, // indexed by DeclId
blocks: Vec<Block>, // indexed by BlockId
vars: Vec<Type>, // indexed by VarId
decls: Vec<Box<dyn Command>>, // indexed by DeclId
blocks: Vec<Block>, // indexed by BlockId
predecls: HashMap<Vec<u8>, DeclId>, // this should get erased after every def call
pub scope: Vec<ScopeFrame>,
}
@ -262,12 +258,10 @@ impl StateDelta {
}
pub fn enter_scope(&mut self) {
// println!("enter scope");
self.scope.push(ScopeFrame::new());
}
pub fn exit_scope(&mut self) {
// println!("exit scope");
self.scope.pop();
}
}
@ -280,6 +274,7 @@ impl<'a> StateWorkingSet<'a> {
file_contents: vec![],
vars: vec![],
decls: vec![],
predecls: HashMap::new(),
blocks: vec![],
scope: vec![ScopeFrame::new()],
},
@ -301,7 +296,6 @@ impl<'a> StateWorkingSet<'a> {
pub fn add_decl(&mut self, decl: Box<dyn Command>) -> DeclId {
let name = decl.name().as_bytes().to_vec();
// println!("adding {}", String::from_utf8_lossy(&name));
self.delta.decls.push(decl);
let decl_id = self.num_decls() - 1;
@ -312,33 +306,63 @@ impl<'a> StateWorkingSet<'a> {
.last_mut()
.expect("internal error: missing required scope frame");
// reset "hiding strength" to 0 => not hidden
if let Some(strength) = scope_frame.hiding.get_mut(&name) {
*strength = 0;
// println!(" strength: {}", strength);
}
scope_frame.decls.insert(name, decl_id);
decl_id
}
pub fn hide_decl(&mut self, name: Vec<u8>) {
let scope_frame = self
pub fn add_predecl(&mut self, decl: Box<dyn Command>) {
let name = decl.name().as_bytes().to_vec();
self.delta.decls.push(decl);
let decl_id = self.num_decls() - 1;
self.delta.predecls.insert(name, decl_id);
}
pub fn find_predecl(&mut self, name: &[u8]) -> Option<DeclId> {
self.delta.predecls.get(name).copied()
}
pub fn merge_predecl(&mut self, name: &[u8]) -> Option<DeclId> {
if let Some(decl_id) = self.delta.predecls.remove(name) {
let scope_frame = self
.delta
.scope
.last_mut()
.expect("internal error: missing required scope frame");
scope_frame.decls.insert(name.into(), decl_id);
return Some(decl_id);
}
None
}
pub fn hide_decl(&mut self, name: &[u8]) -> Option<DeclId> {
// Since we can mutate scope frames in delta, remove the id directly
for scope in self.delta.scope.iter_mut().rev() {
if let Some(decl_id) = scope.decls.remove(name) {
return Some(decl_id);
}
}
// We cannot mutate the permanent state => store the information in the current scope frame
let last_scope_frame = self
.delta
.scope
.last_mut()
.expect("internal error: missing required scope frame");
if let Some(strength) = scope_frame.hiding.get_mut(&name) {
*strength += 1;
// println!("hiding {}, strength: {}", String::from_utf8_lossy(&name), strength);
} else {
// println!("hiding {}, strength: 1", String::from_utf8_lossy(&name));
scope_frame.hiding.insert(name, 1);
for scope in self.permanent_state.scope.iter().rev() {
if let Some(decl_id) = scope.decls.get(name) {
last_scope_frame.hiding.insert(*decl_id);
return Some(*decl_id);
}
}
// println!("hiding map: {:?}", scope_frame.hiding);
None
}
pub fn add_block(&mut self, block: Block) -> BlockId {
@ -448,46 +472,25 @@ impl<'a> StateWorkingSet<'a> {
}
pub fn find_decl(&self, name: &[u8]) -> Option<DeclId> {
let mut hiding_strength = 0;
// println!("set: starting finding {}", String::from_utf8_lossy(&name));
let mut hiding: HashSet<DeclId> = HashSet::new();
if let Some(decl_id) = self.delta.predecls.get(name) {
return Some(*decl_id);
}
for scope in self.delta.scope.iter().rev() {
// println!("delta frame");
// println!("hiding map: {:?}", scope.hiding);
// check if we're hiding the declin this scope
if let Some(strength) = scope.hiding.get(name) {
hiding_strength += strength;
// println!(" was hiding, strength {}", hiding_strength);
}
hiding.extend(&scope.hiding);
if let Some(decl_id) = scope.decls.get(name) {
// if we're hiding this decl, do not return it and reduce the hiding strength
if hiding_strength > 0 {
hiding_strength -= 1;
// println!(" decl found, strength {}", hiding_strength);
} else {
// println!(" decl found, return");
return Some(*decl_id);
}
return Some(*decl_id);
}
}
for scope in self.permanent_state.scope.iter().rev() {
// println!("perma frame");
// println!("hiding map: {:?}", scope.hiding);
// check if we're hiding the declin this scope
if let Some(strength) = scope.hiding.get(name) {
hiding_strength += strength;
// println!(" was hiding, strength {}", hiding_strength);
}
hiding.extend(&scope.hiding);
if let Some(decl_id) = scope.decls.get(name) {
// if we're hiding this decl, do not return it and reduce the hiding strength
if hiding_strength > 0 {
hiding_strength -= 1;
// println!(" decl found, strength {}", hiding_strength);
} else {
// println!(" decl found, return");
if !hiding.contains(decl_id) {
return Some(*decl_id);
}
}