[fine] Starting to bind
This commit is contained in:
parent
56e4be9a5b
commit
3d4538c0df
2 changed files with 137 additions and 3 deletions
|
|
@ -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>>,
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
@ -491,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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue