Compare commits

...

2 commits

Author SHA1 Message Date
3d4538c0df [fine] Starting to bind 2024-01-06 11:00:40 -08:00
56e4be9a5b [fine] Empty blocks generate nothing 2024-01-06 11:00:31 -08:00
4 changed files with 155 additions and 8 deletions

View file

@ -138,7 +138,7 @@ pub enum TreeKind {
pub struct Tree<'a> {
pub kind: TreeKind,
pub parent: Option<TreeRef>,
pub parent: Option<TreeRef>, // TODO: Do we actually need this?
pub start_pos: usize,
pub end_pos: usize,
pub children: Vec<Child<'a>>,

View file

@ -1,8 +1,8 @@
use crate::{
parser::{Child, SyntaxTree, Tree, TreeKind, TreeRef},
tokens::{Lines, TokenKind},
tokens::{Lines, Token, TokenKind},
};
use std::{cell::RefCell, collections::HashMap, fmt};
use std::{cell::RefCell, collections::HashMap, fmt, rc::Rc};
// TODO: An error should have:
//
@ -121,22 +121,126 @@ impl fmt::Display for Type {
}
}
pub struct Declaration {
declaration_type: Type,
}
pub struct Environment {
parent: Option<EnvironmentRef>,
declarations: HashMap<Box<str>, Declaration>,
}
impl Environment {
pub fn new(parent: Option<EnvironmentRef>) -> Self {
Environment {
parent,
declarations: HashMap::new(),
}
}
pub fn bind(&self, token: &Token) -> Option<&Declaration> {
if let Some(decl) = self.declarations.get(token.as_str()) {
return Some(decl);
}
let mut current = &self.parent;
while let Some(env) = current {
if let Some(decl) = env.declarations.get(token.as_str()) {
return Some(decl);
}
current = &env.parent;
}
None
}
}
#[derive(Clone)]
pub struct EnvironmentRef(Rc<Environment>);
impl EnvironmentRef {
pub fn new(environment: Environment) -> Self {
EnvironmentRef(Rc::new(environment))
}
}
impl std::ops::Deref for EnvironmentRef {
type Target = Environment;
fn deref(&self) -> &Self::Target {
&self.0
}
}
fn set_logical_parents(parents: &mut Vec<Option<TreeRef>>, syntax_tree: &SyntaxTree, t: TreeRef) {
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),
}
}
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.
let mut parent = Some(t);
for child in &tree.children {
match child {
Child::Token(_) => (),
Child::Tree(ct) => {
parents[t.index()] = parent;
parent = Some(*ct);
}
}
}
}
_ => {}
}
}
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?
syntax_tree: &'a SyntaxTree<'a>,
lines: &'a Lines,
// Instead of physical parents, this is the set of *logical* parents.
// This is what is used for binding.
logical_parents: Vec<Option<TreeRef>>,
// TODO: State should be externalized instead of this refcell nonsense.
errors: RefCell<Vec<Error>>,
types: RefCell<HashMap<TreeRef, Type>>,
environments: RefCell<HashMap<TreeRef, 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();
if let Some(root) = tree.root() {
set_logical_parents(&mut logical_parents, tree, root);
}
let mut semantics = Semantics {
syntax_tree: tree,
lines,
logical_parents,
errors: RefCell::new(vec![]),
types: RefCell::new(HashMap::new()),
environments: RefCell::new(HashMap::new()),
empty_environment: EnvironmentRef::new(Environment::new(None)),
};
// NOTE: We ensure all the known errors are reported before we move
@ -210,6 +314,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();
}
let tree = &self.syntax_tree[t];
let parent = match self.logical_parents[t.index()] {
Some(t) => self.environment_of(t),
None => self.empty_environment.clone(),
};
let result = match tree.kind {
// TODO: Things that introduce an environment!
_ => parent,
};
self.environments.borrow_mut().insert(t, result.clone());
result
}
pub fn type_of(&self, t: TreeRef) -> Option<Type> {
if let Some(existing) = self.types.borrow().get(&t) {
return Some(existing.clone());
@ -347,10 +471,10 @@ impl<'a> Semantics<'a> {
return None;
}
// if tree.children.len() == 2 {
// // Empty blocks generate Nothing.
// return Some(Type::Nothing);
// }
if tree.children.len() == 2 {
// Empty blocks generate Nothing.
return Some(Type::Nothing);
}
// The type of the block is the type of the last expression.
// (But the last child is the closing brace probably?)
@ -359,6 +483,9 @@ impl<'a> Semantics<'a> {
let mut is_unreachable = false;
for i in 1..last_index {
// TODO: if `is_unreachable` here then we actually have
// unreachable code here! We should warn about it I guess.
is_unreachable = self
.type_of(tree.nth_tree(i)?)
.map(|t| matches!(t, Type::Unreachable))
@ -488,6 +615,16 @@ impl<'a> Semantics<'a> {
fn type_of_identifier(&self, tree: &Tree) -> Option<Type> {
assert_eq!(tree.kind, TreeKind::Identifier);
let id = tree.nth_token(0)?;
if let Some(parent) = tree.parent {
let environment = self.environment_of(parent);
if let Some(declaration) = environment.bind(id) {
return Some(declaration.declaration_type);
}
}
self.report_error_tree(tree, format!("cannot find value {id} here"));
Some(Type::Error)
}
}

View file

@ -16,7 +16,7 @@ fn rebase_concrete(source_path: &str, dump: &str) {
result.push_str(line);
result.push_str("\n");
if line == "// concrete:" {
if line == "// @concrete:" {
found_concrete_section = true;
break;
}

View file

@ -0,0 +1,10 @@
// @concrete:
// | File
// | Block
// | LeftBrace:'"{"'
// | RightBrace:'"}"'
// |
{}
// @type: 94 ()