[fine] Fixup let environment
Also handle circular references in types and environments without exploding, and tweak test output a little bit.
This commit is contained in:
parent
ebad7fe295
commit
308114f8cf
4 changed files with 119 additions and 45 deletions
|
|
@ -66,6 +66,10 @@ impl<'a> SyntaxTree<'a> {
|
|||
self[t].end_pos
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.trees.len()
|
||||
}
|
||||
|
||||
pub fn trees(&self) -> impl Iterator<Item = TreeRef> {
|
||||
(0..self.trees.len()).map(|i| TreeRef::from_index(i))
|
||||
}
|
||||
|
|
@ -164,6 +168,23 @@ impl<'a> Tree<'a> {
|
|||
})
|
||||
.flatten()
|
||||
}
|
||||
|
||||
pub fn dump(&self, tree: &SyntaxTree<'a>, with_positions: bool, output: &mut String) {
|
||||
let _ = write!(output, "{:?}", self.kind);
|
||||
if with_positions {
|
||||
let _ = write!(output, " [{}, {})", self.start_pos, self.end_pos);
|
||||
}
|
||||
let _ = write!(output, "\n");
|
||||
for child in self.children.iter() {
|
||||
child.dump_rec(2, tree, with_positions, output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> std::fmt::Debug for Tree<'a> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:?} [{}-{})", self.kind, self.start_pos, self.end_pos)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||
|
|
@ -181,19 +202,6 @@ impl TreeRef {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Tree<'a> {
|
||||
pub fn dump(&self, tree: &SyntaxTree<'a>, with_positions: bool, output: &mut String) {
|
||||
let _ = write!(output, "{:?}", self.kind);
|
||||
if with_positions {
|
||||
let _ = write!(output, " [{}, {})", self.start_pos, self.end_pos);
|
||||
}
|
||||
let _ = write!(output, "\n");
|
||||
for child in self.children.iter() {
|
||||
child.dump_rec(2, tree, with_positions, output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Child<'a> {
|
||||
Token(Token<'a>),
|
||||
Tree(TreeRef),
|
||||
|
|
|
|||
|
|
@ -172,22 +172,16 @@ impl std::ops::Deref for EnvironmentRef {
|
|||
}
|
||||
}
|
||||
|
||||
fn set_logical_parents(parents: &mut Vec<Option<TreeRef>>, syntax_tree: &SyntaxTree, t: TreeRef) {
|
||||
fn set_logical_parents(
|
||||
parents: &mut Vec<Option<TreeRef>>,
|
||||
syntax_tree: &SyntaxTree,
|
||||
t: TreeRef,
|
||||
parent: Option<TreeRef>,
|
||||
) {
|
||||
parents[t.index()] = parent.clone();
|
||||
|
||||
let tree = &syntax_tree[t];
|
||||
|
||||
// The default logical parent is the physical parent.
|
||||
if parents.len() <= t.index() {
|
||||
parents.resize(t.index() + 1, None);
|
||||
}
|
||||
parents[t.index()] = tree.parent.clone();
|
||||
|
||||
for child in &tree.children {
|
||||
match child {
|
||||
Child::Token(_) => (),
|
||||
Child::Tree(ct) => set_logical_parents(parents, syntax_tree, *ct),
|
||||
}
|
||||
}
|
||||
|
||||
// eprintln!("SET PARENT {parent:?} => CHILD {tree:?} ({t:?})");
|
||||
match tree.kind {
|
||||
TreeKind::Block | TreeKind::File => {
|
||||
// In a block (or at the top level), each child actually points
|
||||
|
|
@ -199,16 +193,40 @@ fn set_logical_parents(parents: &mut Vec<Option<TreeRef>>, syntax_tree: &SyntaxT
|
|||
match child {
|
||||
Child::Token(_) => (),
|
||||
Child::Tree(ct) => {
|
||||
parents[ct.index()] = parent;
|
||||
set_logical_parents(parents, syntax_tree, *ct, parent);
|
||||
parent = Some(*ct);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
TreeKind::LetStatement => {
|
||||
// In a let statement, the logical parent of the children is
|
||||
// actually the logical parent of the let statement, so that the
|
||||
// variable doesn't have itself in scope. :P
|
||||
for child in &tree.children {
|
||||
match child {
|
||||
Child::Token(_) => (),
|
||||
Child::Tree(ct) => set_logical_parents(parents, syntax_tree, *ct, parent),
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// By default, the parent for each child is current tree.
|
||||
for child in &tree.children {
|
||||
match child {
|
||||
Child::Token(_) => (),
|
||||
Child::Tree(ct) => set_logical_parents(parents, syntax_tree, *ct, Some(t)),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum Incremental<T> {
|
||||
InProgress,
|
||||
Complete(T),
|
||||
}
|
||||
|
||||
pub struct Semantics<'a> {
|
||||
// TODO: Do I really want my own copy here? Should we standardize on Arc
|
||||
// or Rc or some other nice sharing mechanism?
|
||||
|
|
@ -221,16 +239,17 @@ pub struct Semantics<'a> {
|
|||
|
||||
// TODO: State should be externalized instead of this refcell nonsense.
|
||||
errors: RefCell<Vec<Error>>,
|
||||
types: RefCell<HashMap<TreeRef, Type>>,
|
||||
environments: RefCell<HashMap<TreeRef, EnvironmentRef>>,
|
||||
types: RefCell<HashMap<TreeRef, Incremental<Type>>>,
|
||||
environments: RefCell<HashMap<TreeRef, Incremental<EnvironmentRef>>>,
|
||||
empty_environment: EnvironmentRef,
|
||||
}
|
||||
|
||||
impl<'a> Semantics<'a> {
|
||||
pub fn new(tree: &'a SyntaxTree<'a>, lines: &'a Lines) -> Self {
|
||||
let mut logical_parents = Vec::new();
|
||||
let mut logical_parents = Vec::with_capacity(tree.len());
|
||||
logical_parents.resize(tree.len(), None);
|
||||
if let Some(root) = tree.root() {
|
||||
set_logical_parents(&mut logical_parents, tree, root);
|
||||
set_logical_parents(&mut logical_parents, tree, root, None);
|
||||
}
|
||||
|
||||
let mut semantics = Semantics {
|
||||
|
|
@ -323,11 +342,26 @@ impl<'a> Semantics<'a> {
|
|||
}
|
||||
|
||||
pub fn environment_of(&self, t: TreeRef) -> EnvironmentRef {
|
||||
if let Some(existing) = self.environments.borrow().get(&t) {
|
||||
return existing.clone();
|
||||
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();
|
||||
}
|
||||
}
|
||||
self.environments
|
||||
.borrow_mut()
|
||||
.insert(t, Incremental::InProgress);
|
||||
|
||||
let tree = &self.syntax_tree[t];
|
||||
// eprintln!("environment_of => {tree:?}");
|
||||
|
||||
let parent = match self.logical_parents[t.index()] {
|
||||
Some(t) => self.environment_of(t),
|
||||
None => self.empty_environment.clone(),
|
||||
|
|
@ -340,12 +374,13 @@ impl<'a> Semantics<'a> {
|
|||
_ => parent,
|
||||
};
|
||||
|
||||
self.environments.borrow_mut().insert(t, result.clone());
|
||||
self.environments
|
||||
.borrow_mut()
|
||||
.insert(t, Incremental::Complete(result.clone()));
|
||||
result
|
||||
}
|
||||
|
||||
fn environment_of_let(&self, parent: EnvironmentRef, tree: &Tree) -> EnvironmentRef {
|
||||
// 0 is the ... let keyword
|
||||
let Some(name) = tree.nth_token(1) else {
|
||||
return parent; // Error is already reported?
|
||||
};
|
||||
|
|
@ -370,11 +405,24 @@ impl<'a> Semantics<'a> {
|
|||
}
|
||||
|
||||
pub fn type_of(&self, t: TreeRef) -> Option<Type> {
|
||||
if let Some(existing) = self.types.borrow().get(&t) {
|
||||
return Some(existing.clone());
|
||||
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);
|
||||
}
|
||||
}
|
||||
self.types.borrow_mut().insert(t, Incremental::InProgress);
|
||||
|
||||
let tree = &self.syntax_tree[t];
|
||||
// eprintln!("type_of => {tree:?}");
|
||||
|
||||
let result = match tree.kind {
|
||||
TreeKind::Error => Some(Type::Error),
|
||||
TreeKind::UnaryExpression => self.type_of_unary(tree),
|
||||
|
|
@ -391,13 +439,22 @@ impl<'a> Semantics<'a> {
|
|||
TreeKind::ReturnStatement => Some(Type::Unreachable),
|
||||
TreeKind::ExpressionStatement => self.type_of_expression_statement(tree),
|
||||
TreeKind::Identifier => self.type_of_identifier(tree),
|
||||
_ => return None,
|
||||
|
||||
// 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, result.clone());
|
||||
self.types
|
||||
.borrow_mut()
|
||||
.insert(t, Incremental::Complete(result.clone()));
|
||||
Some(result)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -107,6 +107,7 @@ fn report_semantic_error(semantics: &Semantics, tr: Option<TreeRef>, message: &s
|
|||
}
|
||||
|
||||
if let Some(tr) = tr {
|
||||
println!("About the tree: {:?}", &tree[tr]);
|
||||
println!("The logical parent chain of the tree was:\n");
|
||||
let mut current = Some(tr);
|
||||
while let Some(c) = current {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,10 @@
|
|||
// | LiteralExpression
|
||||
// | Number:'"23"'
|
||||
// | Semicolon:'";"'
|
||||
// | ExpressionStatement
|
||||
// | LetStatement
|
||||
// | Let:'"let"'
|
||||
// | Identifier:'"y"'
|
||||
// | Equal:'"="'
|
||||
// | BinaryExpression
|
||||
// | Identifier
|
||||
// | Identifier:'"x"'
|
||||
|
|
@ -15,9 +18,14 @@
|
|||
// | LiteralExpression
|
||||
// | Number:'"2"'
|
||||
// | Semicolon:'";"'
|
||||
// | ExpressionStatement
|
||||
// | Identifier
|
||||
// | Identifier:'"y"'
|
||||
// | Semicolon:'";"'
|
||||
// |
|
||||
|
||||
let x = 23;
|
||||
x * 2;
|
||||
let y = x * 2;
|
||||
y;
|
||||
|
||||
// @type: 416 f64
|
||||
// @type: 590 f64
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue