From f9c7591154979852b7f8d5a85a34f66fed1d8d60 Mon Sep 17 00:00:00 2001 From: John Doty Date: Sat, 6 Jan 2024 17:21:58 -0800 Subject: [PATCH] [fine] Let binding --- fine/src/semantics.rs | 57 ++++++++++++++++++++++++++++++------- fine/tests/example_tests.rs | 50 +++++++++++++++++++++++++------- 2 files changed, 86 insertions(+), 21 deletions(-) diff --git a/fine/src/semantics.rs b/fine/src/semantics.rs index 53dc1346..9bf484ea 100644 --- a/fine/src/semantics.rs +++ b/fine/src/semantics.rs @@ -122,12 +122,12 @@ impl fmt::Display for Type { } pub struct Declaration { - declaration_type: Type, + pub declaration_type: Type, } pub struct Environment { - parent: Option, - declarations: HashMap, Declaration>, + pub parent: Option, + pub declarations: HashMap, Declaration>, } impl Environment { @@ -178,8 +178,8 @@ fn set_logical_parents(parents: &mut Vec>, syntax_tree: &SyntaxT // 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(); } + parents[t.index()] = tree.parent.clone(); for child in &tree.children { match child { @@ -189,17 +189,17 @@ fn set_logical_parents(parents: &mut Vec>, syntax_tree: &SyntaxT } match tree.kind { - TreeKind::Block => { - // In a block, each child actually points to the previous child - // as the logical parent, so that variable declarations that - // occur as part of statements in the block are available to - // statements later in the block. + TreeKind::Block | TreeKind::File => { + // In a block (or at the top level), each child actually points + // to the previous child as the logical parent, so that variable + // declarations that occur as part of statements in the block are + // available to statements later in the block. let mut parent = Some(t); for child in &tree.children { match child { Child::Token(_) => (), Child::Tree(ct) => { - parents[t.index()] = parent; + parents[ct.index()] = parent; parent = Some(*ct); } } @@ -261,6 +261,14 @@ impl<'a> Semantics<'a> { (*self.errors.borrow()).clone() } + pub fn logical_parent(&self, tr: TreeRef) -> Option { + if tr.index() < self.logical_parents.len() { + self.logical_parents[tr.index()] + } else { + None + } + } + fn report_error(&self, position: usize, error: T) where T: ToString, @@ -326,7 +334,9 @@ impl<'a> Semantics<'a> { }; 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, }; @@ -334,6 +344,31 @@ impl<'a> Semantics<'a> { 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 { if let Some(existing) = self.types.borrow().get(&t) { return Some(existing.clone()); diff --git a/fine/tests/example_tests.rs b/fine/tests/example_tests.rs index ad296efd..30a5eb5a 100644 --- a/fine/tests/example_tests.rs +++ b/fine/tests/example_tests.rs @@ -1,4 +1,4 @@ -use fine::parser::SyntaxTree; +use fine::parser::{SyntaxTree, TreeRef}; use fine::semantics::{Semantics, Type}; use fine::tokens::Lines; 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, message: &str) { let tree = semantics.tree(); println!("{message}! Parsed the tree as:"); @@ -105,33 +105,52 @@ fn report_semantic_error(semantics: &Semantics, message: &str) { } 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 { - ($semantics:expr, $($t:tt)*) => {{ + ($semantics:expr, $tr:expr, $($t:tt)*) => {{ let message = format!($($t)*); - report_semantic_error($semantics, &message); + report_semantic_error($semantics, $tr, &message); panic!("{message}"); }}; } macro_rules! semantic_assert { - ($semantics:expr, $pred:expr, $($t:tt)*) => {{ + ($semantics:expr, $tr:expr, $pred:expr, $($t:tt)*) => {{ if !$pred { let message = format!($($t)*); - report_semantic_error($semantics, &message); + report_semantic_error($semantics, $tr, &message); panic!("{message}"); } }}; } 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 rr = $right; if ll != rr { let message = format!($($t)*); - report_semantic_error($semantics, &message); + report_semantic_error($semantics, $tr, &message); assert_eq!(ll, rr, "{}", message); } }}; @@ -147,13 +166,18 @@ fn assert_type_at( let semantics = Semantics::new(tree, lines); let tree_ref = match tree.find_tree_at(pos) { 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 actual = format!("{}", tree_type.unwrap_or(Type::Error)); semantic_assert_eq!( &semantics, + Some(tree_ref), expected, actual, "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 tree_ref = match tree.find_tree_at(pos) { 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); semantic_assert!( &semantics, + Some(tree_ref), matches!(tree_type, Some(Type::Error)), "The type of the {:?} tree at position {pos} was '{tree_type:?}', not an error", tree[tree_ref].kind @@ -184,6 +213,7 @@ fn assert_type_error_at( let errors = semantics.snapshot_errors(); semantic_assert!( &semantics, + Some(tree_ref), errors.iter().any(|e| e.message == expected), "Unable to find the expected error message '{expected}'" );