[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 struct Tree<'a> {
|
||||||
pub kind: TreeKind,
|
pub kind: TreeKind,
|
||||||
pub parent: Option<TreeRef>,
|
pub parent: Option<TreeRef>, // TODO: Do we actually need this?
|
||||||
pub start_pos: usize,
|
pub start_pos: usize,
|
||||||
pub end_pos: usize,
|
pub end_pos: usize,
|
||||||
pub children: Vec<Child<'a>>,
|
pub children: Vec<Child<'a>>,
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
parser::{Child, SyntaxTree, Tree, TreeKind, TreeRef},
|
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:
|
// 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> {
|
pub struct Semantics<'a> {
|
||||||
// TODO: Do I really want my own copy here? Should we standardize on Arc
|
// TODO: Do I really want my own copy here? Should we standardize on Arc
|
||||||
// or Rc or some other nice sharing mechanism?
|
// or Rc or some other nice sharing mechanism?
|
||||||
syntax_tree: &'a SyntaxTree<'a>,
|
syntax_tree: &'a SyntaxTree<'a>,
|
||||||
lines: &'a Lines,
|
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>>,
|
errors: RefCell<Vec<Error>>,
|
||||||
types: RefCell<HashMap<TreeRef, Type>>,
|
types: RefCell<HashMap<TreeRef, Type>>,
|
||||||
|
environments: RefCell<HashMap<TreeRef, EnvironmentRef>>,
|
||||||
|
empty_environment: EnvironmentRef,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Semantics<'a> {
|
impl<'a> Semantics<'a> {
|
||||||
pub fn new(tree: &'a SyntaxTree<'a>, lines: &'a Lines) -> Self {
|
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 {
|
let mut semantics = Semantics {
|
||||||
syntax_tree: tree,
|
syntax_tree: tree,
|
||||||
lines,
|
lines,
|
||||||
|
logical_parents,
|
||||||
errors: RefCell::new(vec![]),
|
errors: RefCell::new(vec![]),
|
||||||
types: RefCell::new(HashMap::new()),
|
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
|
// 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> {
|
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());
|
||||||
|
|
@ -491,6 +615,16 @@ impl<'a> Semantics<'a> {
|
||||||
|
|
||||||
fn type_of_identifier(&self, tree: &Tree) -> Option<Type> {
|
fn type_of_identifier(&self, tree: &Tree) -> Option<Type> {
|
||||||
assert_eq!(tree.kind, TreeKind::Identifier);
|
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)
|
Some(Type::Error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue