[fine] Improvements to classes

- Classes are defined lazily
- Member access is via environment
- Member access is just a binary expression with a weird environment
- Slot loads look like variable loads now
- Associativity in the parser (ugh)
This commit is contained in:
John Doty 2024-01-22 23:17:02 -08:00
parent 0d48bfb113
commit 2839b43f6d
5 changed files with 286 additions and 123 deletions

View file

@ -68,6 +68,23 @@ pub struct ClassDecl {
pub decl_tree: TreeRef,
}
#[derive(Clone)]
pub struct ClassRef(Rc<ClassDecl>);
impl ClassRef {
pub fn new(class: ClassDecl) -> Self {
ClassRef(Rc::new(class))
}
}
impl std::ops::Deref for ClassRef {
type Target = ClassDecl;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Clone)]
pub enum Type {
// Signals a type error. If you receive this then you know that an error
@ -99,7 +116,10 @@ pub enum Type {
Function(Vec<Box<Type>>, Box<Type>),
List(Box<Type>),
Class(Rc<ClassDecl>),
// Classes need to be fetched explicitly from the semantics; they are
// computed lazily.
Class(TreeRef, Rc<str>),
}
impl Type {
@ -144,7 +164,7 @@ impl fmt::Display for Type {
// TODO: Better names
TypeVariable(_) => write!(f, "$_"),
List(t) => write!(f, "list<{t}>"),
Class(c) => write!(f, "class {}", c.name),
Class(_, name) => write!(f, "class {}", name),
}
}
}
@ -154,7 +174,7 @@ pub enum Location {
Argument,
Local,
Module,
// TODO: Member
Slot,
// TODO: ArrayIndex
}
@ -204,6 +224,7 @@ impl Environment {
let base = parent.as_ref().map(|p| p.next_index).unwrap_or(0);
let next_index = match (parent_location, location) {
(_, Location::Argument) => 0,
(_, Location::Slot) => 0,
(Location::Local, Location::Local) => base,
(_, Location::Local) => 0,
@ -221,8 +242,12 @@ impl Environment {
}
pub fn insert(&mut self, token: &Token, t: Type) -> Option<Declaration> {
self.insert_name(token.as_str().into(), t)
}
pub fn insert_name(&mut self, name: Box<str>, t: Type) -> Option<Declaration> {
let result = self.declarations.insert(
token.as_str().into(),
name,
Declaration::Variable {
declaration_type: t,
location: self.location,
@ -341,6 +366,17 @@ fn set_logical_parents(
}
}
}
TreeKind::MemberAccess => {
// The LHS has the environment of my parent, because I set up an
// environment containing only members of the type of the LHS. The
// RHS has that one.
if let Some(lhs) = tree.nth_tree(0) {
set_logical_parents(parents, syntax_tree, lhs, parent);
}
if let Some(rhs) = tree.nth_tree(2) {
set_logical_parents(parents, syntax_tree, rhs, Some(t));
}
}
_ => {
// By default, the parent for each child is current tree.
for child in &tree.children {
@ -375,6 +411,7 @@ pub struct Semantics<'a> {
types: RefCell<Vec<Incremental<Type>>>,
environments: RefCell<Vec<Incremental<EnvironmentRef>>>,
root_environment: EnvironmentRef,
classes: RefCell<Vec<Incremental<ClassRef>>>,
}
impl<'a> Semantics<'a> {
@ -394,6 +431,7 @@ impl<'a> Semantics<'a> {
types: RefCell::new(vec![Incremental::None; tree.len()]),
environments: RefCell::new(vec![Incremental::None; tree.len()]),
root_environment: EnvironmentRef::new(root_environment),
classes: RefCell::new(vec![Incremental::None; tree.len()]),
};
// NOTE: We ensure all the known errors are reported before we move
@ -519,6 +557,8 @@ impl<'a> Semantics<'a> {
TreeKind::ForStatement => self.environment_of_for(parent, tree),
TreeKind::MemberAccess => self.environment_of_member_access(parent, tree),
_ => parent,
};
@ -629,6 +669,13 @@ impl<'a> Semantics<'a> {
Location::Local => Location::Local,
Location::Module => Location::Module,
Location::Argument => Location::Local,
// TODO: Wait can I... do wild stuff? e.g.:
//
// foo.{let y = 23; bar}
//
// ???
Location::Slot => Location::Local,
};
let mut environment = Environment::new(Some(parent), location);
@ -689,6 +736,91 @@ impl<'a> Semantics<'a> {
EnvironmentRef::new(environment)
}
fn environment_of_member_access(&self, parent: EnvironmentRef, tree: &Tree) -> EnvironmentRef {
// TODO: Build an "error environment" and then anything that tried to
// bind in it would know to give up and not report a new error.
assert_eq!(tree.kind, TreeKind::MemberAccess);
// Build environment out of members of type of lhs
let Some(lhs) = tree.nth_tree(0) else {
return parent;
};
let Some(op) = tree.nth_token(1) else {
return parent;
};
let typ = self.type_of(lhs);
match &typ {
Type::Class(ct, _) => {
// NOTE: The parent here is None! environment search after a dot
// is constrained to the scope of the type, obviously!
let class = self.class_of(*ct);
let mut env = Environment::new(None, Location::Slot);
// TODO: Cache environment of type.
// NOTE: It is important that this go in order! The location
// index is the slot index!
for field in class.fields.iter() {
env.insert_name((&*field.name).into(), field.field_type.clone());
}
EnvironmentRef::new(env)
}
Type::Error => parent, // TODO: WRONG
_ => {
// TODO: This is probably wrong, yeah?
self.report_error(op.start, format!("cannot access members of '{typ}'"));
parent
}
}
}
pub fn class_of(&self, t: TreeRef) -> ClassRef {
{
// I want to make sure that this borrow is dropped after this block.
let mut borrow = self.classes.borrow_mut();
let state = &mut borrow[t.index()];
match state {
Incremental::None => (),
Incremental::Complete(e) => return e.clone(),
Incremental::InProgress => {
drop(borrow);
self.internal_compiler_error(Some(t), "circular class dependency");
}
}
*state = Incremental::InProgress;
}
// TODO: Right now there's only one way to make a class decl. :P
let tree = &self.syntax_tree[t];
assert_eq!(tree.kind, TreeKind::ClassDecl);
let name = tree.nth_token(1).map(|t| t.as_str()).unwrap_or("<??>");
let mut fields = Vec::new();
for field in tree.children_of_kind(self.syntax_tree, TreeKind::FieldDecl) {
let f = &self.syntax_tree[field];
if let Some(field_name) = f.nth_token(0) {
let field_type = f
.nth_tree(2)
.map(|t| self.type_of(t))
.unwrap_or(Type::Error);
fields.push(FieldDecl {
name: field_name.as_str().into(),
field_type,
});
}
}
let result = ClassRef::new(ClassDecl {
name: name.into(),
fields: fields.into(),
decl_tree: t,
});
self.classes.borrow_mut()[t.index()] = Incremental::Complete(result.clone());
result
}
pub fn type_compat(&self, a: &Type, b: &Type) -> bool {
// TODO: Convert this into "can become" or something.
// TODO: This is wrong; we because of numeric literals etc.
@ -709,14 +841,14 @@ impl<'a> Semantics<'a> {
.all(|(a, b)| self.type_compat(a, b))
}
(Type::Class(ca), Type::Class(cb)) => {
(Type::Class(ca, _), Type::Class(cb, _)) => {
// TODO: If we were doing structural comparisons here...
// maybe? MAYBE?
//
// Like if this is directional we can look for field
// subsets { ..:int, ..:int } can be { ..:int } etc.
//
ca.decl_tree == cb.decl_tree
ca == cb
}
// Avoid introducing more errors
@ -1182,29 +1314,8 @@ impl<'a> Semantics<'a> {
fn type_of_member_access(&self, tree: &Tree) -> Option<Type> {
assert_eq!(tree.kind, TreeKind::MemberAccess);
let op = tree.nth_token(1)?;
let member = tree.nth_token(2)?;
let typ = self.type_of(tree.nth_tree(0)?);
match &typ {
Type::Class(c) => {
// TODO: Accelerate?
if let Some(field) = c.fields.iter().find(|f| &*f.name == member.as_str()) {
Some(field.field_type.clone())
} else {
self.report_error(
op.start,
format!("'{typ}' does not have a member named '{member}'"),
);
Some(Type::Error)
}
}
Type::Error => Some(Type::Error),
_ => {
// TODO: This is probably wrong, yeah?
self.report_error(op.start, format!("cannot access members of '{typ}'"));
Some(Type::Error)
}
}
// Type of member access is the type of the RHS.
Some(self.type_of(tree.nth_tree(2)?))
}
fn type_of_expression_statement(&self, tree: &Tree) -> Option<Type> {
@ -1320,26 +1431,9 @@ impl<'a> Semantics<'a> {
fn type_of_class_decl(&self, t: TreeRef, tree: &Tree) -> Option<Type> {
assert_eq!(tree.kind, TreeKind::ClassDecl);
// TODO: Field types need to be lazy because of recursion.
// The type of a class is computed lazily.
let name = tree.nth_token(1)?;
let mut fields = Vec::new();
for field in tree.children_of_kind(self.syntax_tree, TreeKind::FieldDecl) {
let f = &self.syntax_tree[field];
let field_name = f.nth_token(0)?;
fields.push(FieldDecl {
name: field_name.as_str().into(),
field_type: self.type_of(f.nth_tree(2)?),
});
}
let cd = ClassDecl {
name: name.as_str().into(),
fields: fields.into(),
decl_tree: t,
};
Some(Type::Class(cd.into()))
Some(Type::Class(t, name.as_str().into()))
}
fn type_of_field_value(&self, t: TreeRef, tree: &Tree) -> Option<Type> {
@ -1533,6 +1627,8 @@ pub fn check(s: &Semantics) {
TreeKind::FieldList => {}
TreeKind::NewObjectExpression => check_new_object_expression(s, tree),
TreeKind::FieldValue => {}
TreeKind::SelfParameter => {}
TreeKind::SelfReference => {}
}
}
}
@ -1626,7 +1722,9 @@ fn check_new_object_expression(s: &Semantics, tree: &Tree) {
let class_type = s.type_of(type_expression);
match &class_type {
Type::Class(c) => {
Type::Class(c, _) => {
let class = s.class_of(*c);
let mut any_errors = false;
let mut field_bindings = HashMap::new();
for field in field_list.children_of_kind(s.syntax_tree, TreeKind::FieldValue) {
@ -1640,7 +1738,7 @@ fn check_new_object_expression(s: &Semantics, tree: &Tree) {
}
// Check individual bindings...
for f in c.fields.iter() {
for f in class.fields.iter() {
if let Some((field_tree, expr_type)) = field_bindings.get(&*f.name) {
if !s.type_compat(&f.field_type, expr_type) {
s.report_error_tree_ref(