[fine] Let binding

This commit is contained in:
John Doty 2024-01-06 17:21:58 -08:00
parent e5ac1b2718
commit f9c7591154
2 changed files with 86 additions and 21 deletions

View file

@ -122,12 +122,12 @@ impl fmt::Display for Type {
} }
pub struct Declaration { pub struct Declaration {
declaration_type: Type, pub declaration_type: Type,
} }
pub struct Environment { pub struct Environment {
parent: Option<EnvironmentRef>, pub parent: Option<EnvironmentRef>,
declarations: HashMap<Box<str>, Declaration>, pub declarations: HashMap<Box<str>, Declaration>,
} }
impl Environment { impl Environment {
@ -178,8 +178,8 @@ fn set_logical_parents(parents: &mut Vec<Option<TreeRef>>, syntax_tree: &SyntaxT
// The default logical parent is the physical parent. // The default logical parent is the physical parent.
if parents.len() <= t.index() { if parents.len() <= t.index() {
parents.resize(t.index() + 1, None); parents.resize(t.index() + 1, None);
parents[t.index()] = tree.parent.clone();
} }
parents[t.index()] = tree.parent.clone();
for child in &tree.children { for child in &tree.children {
match child { match child {
@ -189,17 +189,17 @@ fn set_logical_parents(parents: &mut Vec<Option<TreeRef>>, syntax_tree: &SyntaxT
} }
match tree.kind { match tree.kind {
TreeKind::Block => { TreeKind::Block | TreeKind::File => {
// In a block, each child actually points to the previous child // In a block (or at the top level), each child actually points
// as the logical parent, so that variable declarations that // to the previous child as the logical parent, so that variable
// occur as part of statements in the block are available to // declarations that occur as part of statements in the block are
// statements later in the block. // available to statements later in the block.
let mut parent = Some(t); let mut parent = Some(t);
for child in &tree.children { for child in &tree.children {
match child { match child {
Child::Token(_) => (), Child::Token(_) => (),
Child::Tree(ct) => { Child::Tree(ct) => {
parents[t.index()] = parent; parents[ct.index()] = parent;
parent = Some(*ct); parent = Some(*ct);
} }
} }
@ -261,6 +261,14 @@ impl<'a> Semantics<'a> {
(*self.errors.borrow()).clone() (*self.errors.borrow()).clone()
} }
pub fn logical_parent(&self, tr: TreeRef) -> Option<TreeRef> {
if tr.index() < self.logical_parents.len() {
self.logical_parents[tr.index()]
} else {
None
}
}
fn report_error<T>(&self, position: usize, error: T) fn report_error<T>(&self, position: usize, error: T)
where where
T: ToString, T: ToString,
@ -326,7 +334,9 @@ impl<'a> Semantics<'a> {
}; };
let result = match tree.kind { let result = match tree.kind {
// TODO: Things that introduce an environment! TreeKind::LetStatement => self.environment_of_let(parent, tree),
// TODO: MORE Things that introduce an environment!
_ => parent, _ => parent,
}; };
@ -334,6 +344,31 @@ impl<'a> Semantics<'a> {
result 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?
};
let declaration_type = match tree.nth_tree(3) {
Some(expr) => self
.type_of(expr)
.expect("the tree in the expression should yield a type"),
// The syntax error should already have been reported, so we'll
// stick with error type here. (But bind the name, because we see
// it!)
None => Type::Error,
};
let mut environment = Environment::new(Some(parent));
environment
.declarations
.insert(name.as_str().into(), Declaration { declaration_type });
EnvironmentRef::new(environment)
}
pub fn type_of(&self, t: TreeRef) -> Option<Type> { pub fn type_of(&self, t: TreeRef) -> Option<Type> {
if let Some(existing) = self.types.borrow().get(&t) { if let Some(existing) = self.types.borrow().get(&t) {
return Some(existing.clone()); return Some(existing.clone());

View file

@ -1,4 +1,4 @@
use fine::parser::SyntaxTree; use fine::parser::{SyntaxTree, TreeRef};
use fine::semantics::{Semantics, Type}; use fine::semantics::{Semantics, Type};
use fine::tokens::Lines; use fine::tokens::Lines;
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
@ -85,7 +85,7 @@ fn assert_concrete(tree: &SyntaxTree, expected: &str, source_path: &str) {
} }
} }
fn report_semantic_error(semantics: &Semantics, message: &str) { fn report_semantic_error(semantics: &Semantics, tr: Option<TreeRef>, message: &str) {
let tree = semantics.tree(); let tree = semantics.tree();
println!("{message}! Parsed the tree as:"); println!("{message}! Parsed the tree as:");
@ -105,33 +105,52 @@ fn report_semantic_error(semantics: &Semantics, message: &str) {
} }
println!(); println!();
} }
if let Some(tr) = tr {
println!("The logical parent chain of the tree was:\n");
let mut current = Some(tr);
while let Some(c) = current {
let t = &tree[c];
println!(" {:?} [{}-{})", t.kind, t.start_pos, t.end_pos);
current = semantics.logical_parent(c);
}
println!("\nThe environment of the tree was:");
let mut environment = Some(semantics.environment_of(tr));
while let Some(env) = environment {
for (k, v) in env.declarations.iter() {
println!(" {k}: {:?}", v.declaration_type);
}
environment = env.parent.clone();
}
println!();
}
} }
macro_rules! semantic_panic { macro_rules! semantic_panic {
($semantics:expr, $($t:tt)*) => {{ ($semantics:expr, $tr:expr, $($t:tt)*) => {{
let message = format!($($t)*); let message = format!($($t)*);
report_semantic_error($semantics, &message); report_semantic_error($semantics, $tr, &message);
panic!("{message}"); panic!("{message}");
}}; }};
} }
macro_rules! semantic_assert { macro_rules! semantic_assert {
($semantics:expr, $pred:expr, $($t:tt)*) => {{ ($semantics:expr, $tr:expr, $pred:expr, $($t:tt)*) => {{
if !$pred { if !$pred {
let message = format!($($t)*); let message = format!($($t)*);
report_semantic_error($semantics, &message); report_semantic_error($semantics, $tr, &message);
panic!("{message}"); panic!("{message}");
} }
}}; }};
} }
macro_rules! semantic_assert_eq { macro_rules! semantic_assert_eq {
($semantics:expr, $left:expr, $right:expr, $($t:tt)*) => {{ ($semantics:expr, $tr:expr, $left:expr, $right:expr, $($t:tt)*) => {{
let ll = $left; let ll = $left;
let rr = $right; let rr = $right;
if ll != rr { if ll != rr {
let message = format!($($t)*); let message = format!($($t)*);
report_semantic_error($semantics, &message); report_semantic_error($semantics, $tr, &message);
assert_eq!(ll, rr, "{}", message); assert_eq!(ll, rr, "{}", message);
} }
}}; }};
@ -147,13 +166,18 @@ fn assert_type_at(
let semantics = Semantics::new(tree, lines); let semantics = Semantics::new(tree, lines);
let tree_ref = match tree.find_tree_at(pos) { let tree_ref = match tree.find_tree_at(pos) {
Some(t) => t, Some(t) => t,
None => semantic_panic!(&semantics, "Unable to find the subtee at position {pos}"), None => semantic_panic!(
&semantics,
None,
"Unable to find the subtee at position {pos}"
),
}; };
let tree_type = semantics.type_of(tree_ref); let tree_type = semantics.type_of(tree_ref);
let actual = format!("{}", tree_type.unwrap_or(Type::Error)); let actual = format!("{}", tree_type.unwrap_or(Type::Error));
semantic_assert_eq!( semantic_assert_eq!(
&semantics, &semantics,
Some(tree_ref),
expected, expected,
actual, actual,
"The type of the tree at position {pos} was incorrect" "The type of the tree at position {pos} was incorrect"
@ -170,12 +194,17 @@ fn assert_type_error_at(
let semantics = Semantics::new(tree, lines); let semantics = Semantics::new(tree, lines);
let tree_ref = match tree.find_tree_at(pos) { let tree_ref = match tree.find_tree_at(pos) {
Some(t) => t, Some(t) => t,
None => semantic_panic!(&semantics, "Unable to find the subtee at position {pos}"), None => semantic_panic!(
&semantics,
None,
"Unable to find the subtee at position {pos}"
),
}; };
let tree_type = semantics.type_of(tree_ref); let tree_type = semantics.type_of(tree_ref);
semantic_assert!( semantic_assert!(
&semantics, &semantics,
Some(tree_ref),
matches!(tree_type, Some(Type::Error)), matches!(tree_type, Some(Type::Error)),
"The type of the {:?} tree at position {pos} was '{tree_type:?}', not an error", "The type of the {:?} tree at position {pos} was '{tree_type:?}', not an error",
tree[tree_ref].kind tree[tree_ref].kind
@ -184,6 +213,7 @@ fn assert_type_error_at(
let errors = semantics.snapshot_errors(); let errors = semantics.snapshot_errors();
semantic_assert!( semantic_assert!(
&semantics, &semantics,
Some(tree_ref),
errors.iter().any(|e| e.message == expected), errors.iter().any(|e| e.message == expected),
"Unable to find the expected error message '{expected}'" "Unable to find the expected error message '{expected}'"
); );