[fine] Let binding
This commit is contained in:
parent
e5ac1b2718
commit
f9c7591154
2 changed files with 86 additions and 21 deletions
|
|
@ -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());
|
||||||
|
|
|
||||||
|
|
@ -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}'"
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue