[fine] Methods!

This commit is contained in:
John Doty 2024-01-24 09:03:45 -08:00
parent 2839b43f6d
commit 0c69758b11
5 changed files with 352 additions and 95 deletions

View file

@ -62,10 +62,19 @@ pub struct FieldDecl {
pub field_type: Type,
}
pub struct MethodDecl {
pub name: Rc<str>,
pub decl_type: Type,
pub declaration: TreeRef,
}
pub struct ClassDecl {
pub name: Rc<str>,
pub fields: Vec<FieldDecl>,
pub methods: Vec<MethodDecl>,
pub decl_tree: TreeRef,
pub env: EnvironmentRef,
}
#[derive(Clone)]
@ -115,6 +124,12 @@ pub enum Type {
Bool,
Function(Vec<Box<Type>>, Box<Type>),
// A method is like a function except that it takes a self parameter.
// This is how we signal the difference. We do *not* count them as the
// same.
Method(Box<Type>, Vec<Box<Type>>, Box<Type>),
List(Box<Type>),
// Classes need to be fetched explicitly from the semantics; they are
@ -161,15 +176,30 @@ impl fmt::Display for Type {
write!(f, ") -> {ret}")
}
// TODO: Better names
TypeVariable(_) => write!(f, "$_"),
Method(self_type, args, ret) => {
write!(f, "method of {self_type} (")?;
let mut first = true;
for arg in args.iter() {
if !first {
write!(f, ", ")?;
}
write!(f, "{arg}")?;
first = false;
}
write!(f, ") -> {ret}")
}
TypeVariable(_) => {
// TODO: Better names for type variable
write!(f, "$_")
}
List(t) => write!(f, "list<{t}>"),
Class(_, name) => write!(f, "class {}", name),
}
}
}
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Location {
Argument,
Local,
@ -213,6 +243,7 @@ pub struct Environment {
pub location: Location,
pub next_index: usize,
pub declarations: HashMap<Box<str>, Declaration>,
pub is_error: bool,
}
impl Environment {
@ -238,9 +269,21 @@ impl Environment {
location,
next_index,
declarations: HashMap::new(),
is_error: false,
}
}
pub fn error() -> EnvironmentRef {
// TODO: Exactly once?
EnvironmentRef::new(Environment {
parent: None,
location: Location::Local,
next_index: 0,
declarations: HashMap::new(),
is_error: true,
})
}
pub fn insert(&mut self, token: &Token, t: Type) -> Option<Declaration> {
self.insert_name(token.as_str().into(), t)
}
@ -557,7 +600,7 @@ impl<'a> Semantics<'a> {
TreeKind::ForStatement => self.environment_of_for(parent, tree),
TreeKind::MemberAccess => self.environment_of_member_access(parent, tree),
TreeKind::MemberAccess => self.environment_of_member_access(tree),
_ => parent,
};
@ -665,16 +708,12 @@ impl<'a> Semantics<'a> {
None => Type::Error,
};
// TODO: Variables cannot be of type <method>?
let location = match parent.location {
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,
};
@ -688,25 +727,41 @@ impl<'a> Semantics<'a> {
assert!(tree.kind == TreeKind::ParamList);
let mut environment = Environment::new(Some(parent), Location::Argument);
for child in tree.children.iter() {
for (i, child) in tree.children.iter().enumerate() {
let Child::Tree(ct) = child else {
continue;
};
let param = &self.syntax_tree[*ct];
if param.kind != TreeKind::Parameter {
continue;
}
match param.kind {
TreeKind::SelfParameter => {
let param_name = param.nth_token(0).unwrap();
let declaration_type = self.type_of(*ct);
if environment.insert(param_name, declaration_type).is_some() {
self.report_error_tree(
param,
format!("duplicate definition of self parameter"),
);
} else if i != 1 {
self.report_error_tree(
param,
"self parameter must be the first parameter in the list",
);
}
}
TreeKind::Parameter => {
let Some(param_name) = param.nth_token(0) else {
continue;
};
let Some(param_name) = param.nth_token(0) else {
continue;
};
let declaration_type = self.type_of(*ct);
if environment.insert(param_name, declaration_type).is_some() {
self.report_error_tree(
param,
format!("duplicate definition of parameter '{param_name}'"),
);
let declaration_type = self.type_of(*ct);
if environment.insert(param_name, declaration_type).is_some() {
self.report_error_tree(
param,
format!("duplicate definition of parameter '{param_name}'"),
);
}
}
_ => (),
}
}
@ -736,41 +791,27 @@ 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.
fn environment_of_member_access(&self, tree: &Tree) -> EnvironmentRef {
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;
return Environment::error();
};
let Some(op) = tree.nth_token(1) else {
return parent;
return Environment::error();
};
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)
class.env.clone()
}
Type::Error => parent, // TODO: WRONG
Type::Error => Environment::error(),
_ => {
// TODO: This is probably wrong, yeah?
self.report_error(op.start, format!("cannot access members of '{typ}'"));
parent
Environment::error()
}
}
}
@ -796,6 +837,8 @@ impl<'a> Semantics<'a> {
assert_eq!(tree.kind, TreeKind::ClassDecl);
let name = tree.nth_token(1).map(|t| t.as_str()).unwrap_or("<??>");
// Fields
let mut fields = Vec::new();
for field in tree.children_of_kind(self.syntax_tree, TreeKind::FieldDecl) {
let f = &self.syntax_tree[field];
@ -811,10 +854,53 @@ impl<'a> Semantics<'a> {
}
}
// Methods
let mut methods = Vec::new();
for method in tree.children_of_kind(self.syntax_tree, TreeKind::FunctionDecl) {
let m = &self.syntax_tree[method];
if let Some(method_name) = m.nth_token(1) {
methods.push(MethodDecl {
name: method_name.as_str().into(),
decl_type: self.type_of(method),
declaration: method,
});
}
}
// Build into an environment
let mut env = Environment::new(None, Location::Slot);
for (index, field) in fields.iter().enumerate() {
env.declarations.insert(
(&*field.name).into(),
Declaration::Variable {
index,
declaration_type: field.field_type.clone(),
location: Location::Slot,
},
);
}
for method in methods.iter() {
let existing = env.declarations.insert(
(&*method.name).into(),
Declaration::Function {
declaration_type: method.decl_type.clone(),
declaration: method.declaration,
},
);
if existing.is_some() {
self.report_error_tree_ref(
method.declaration,
format!("duplicate definition of method '{}'", method.name),
);
}
}
let result = ClassRef::new(ClassDecl {
name: name.into(),
fields: fields.into(),
methods: methods.into(),
decl_tree: t,
env: EnvironmentRef::new(env),
});
self.classes.borrow_mut()[t.index()] = Incremental::Complete(result.clone());
@ -881,35 +967,34 @@ impl<'a> Semantics<'a> {
let result = match tree.kind {
TreeKind::Error => Some(Type::Error),
TreeKind::UnaryExpression => self.type_of_unary(tree),
TreeKind::Argument => self.type_of_argument(tree),
TreeKind::BinaryExpression => self.type_of_binary(tree),
TreeKind::Block => self.type_of_block(tree),
TreeKind::CallExpression => self.type_of_call(tree),
TreeKind::ClassDecl => self.type_of_class_decl(t, tree),
TreeKind::ConditionalExpression => self.type_of_conditional(tree),
TreeKind::ExpressionStatement => self.type_of_expression_statement(tree),
TreeKind::FieldValue => self.type_of_field_value(t, tree),
TreeKind::ForStatement => Some(Type::Nothing),
TreeKind::FunctionDecl => self.type_of_function_decl(tree),
TreeKind::GroupingExpression => self.type_of_grouping(tree),
TreeKind::Identifier => self.type_of_identifier(t, tree),
TreeKind::IfStatement => self.type_of_if_statement(tree),
TreeKind::LetStatement => Some(Type::Nothing),
TreeKind::ListConstructor => self.type_of_list_constructor(t, tree),
TreeKind::ListConstructorElement => self.type_of_list_constructor_element(tree),
TreeKind::LiteralExpression => self.type_of_literal(tree),
TreeKind::MemberAccess => self.type_of_member_access(tree),
TreeKind::NewObjectExpression => self.type_of_new_object_expression(tree),
TreeKind::Parameter => self.type_of_parameter(tree),
TreeKind::ReturnStatement => Some(Type::Unreachable),
TreeKind::ReturnType => self.type_of_return_type(tree),
TreeKind::SelfParameter => self.type_of_self_parameter(tree),
TreeKind::SelfReference => self.type_of_self_reference(t, tree),
TreeKind::TypeExpression => self.type_of_type_expr(t, tree),
TreeKind::TypeParameter => self.type_of_type_parameter(tree),
TreeKind::Block => self.type_of_block(tree),
TreeKind::LiteralExpression => self.type_of_literal(tree),
TreeKind::GroupingExpression => self.type_of_grouping(tree),
TreeKind::ConditionalExpression => self.type_of_conditional(tree),
TreeKind::CallExpression => self.type_of_call(tree),
TreeKind::Argument => self.type_of_argument(tree),
TreeKind::MemberAccess => self.type_of_member_access(tree),
TreeKind::LetStatement => Some(Type::Nothing),
TreeKind::ReturnStatement => Some(Type::Unreachable),
TreeKind::ForStatement => Some(Type::Nothing),
TreeKind::ExpressionStatement => self.type_of_expression_statement(tree),
TreeKind::IfStatement => self.type_of_if_statement(tree),
TreeKind::Identifier => self.type_of_identifier(t, tree),
TreeKind::FunctionDecl => self.type_of_function_decl(tree),
TreeKind::ReturnType => self.type_of_return_type(tree),
TreeKind::Parameter => self.type_of_parameter(tree),
TreeKind::ListConstructorElement => self.type_of_list_constructor_element(tree),
TreeKind::ListConstructor => self.type_of_list_constructor(t, tree),
TreeKind::NewObjectExpression => self.type_of_new_object_expression(tree),
TreeKind::FieldValue => self.type_of_field_value(t, tree),
TreeKind::ClassDecl => self.type_of_class_decl(t, tree),
TreeKind::UnaryExpression => self.type_of_unary(tree),
_ => self.internal_compiler_error(Some(t), "asking for a nonsense type"),
};
@ -1122,7 +1207,9 @@ impl<'a> Semantics<'a> {
Some(Type::Error)
}
None => {
self.report_error_tree(tree, format!("Unrecognized type: '{token}'"));
if !environment.is_error {
self.report_error_tree(tree, format!("Unrecognized type: '{token}'"));
}
Some(Type::Error)
}
}
@ -1297,6 +1384,37 @@ impl<'a> Semantics<'a> {
Some(*ret.clone())
}
Type::Method(_, params, ret) => {
let mut any_errors = false;
// For the purposes of type checking ignore the self type.
if params.len() != arg_types.len() {
// TODO: Augment with function name if known
self.report_error_tree(tree, format!("expected {} parameters", params.len()));
any_errors = true;
}
for (i, ((t, a), p)) in arg_types.iter().zip(params.iter()).enumerate() {
if !self.type_compat(&a, p) {
self.report_error_tree_ref(
*t,
format!(
"parameter {i} has an incompatible type: expected {} but got {}",
p, a
),
);
any_errors = true;
}
}
if any_errors {
return Some(Type::Error);
}
Some(*ret.clone())
}
_ => {
self.report_error_tree_ref(f_ref, format!("expected a function type, got: {f}"));
Some(Type::Error)
@ -1364,15 +1482,71 @@ impl<'a> Semantics<'a> {
});
}
self.report_error_tree(tree, format!("cannot find value {id} here"));
if !environment.is_error {
self.report_error_tree(tree, format!("cannot find value {id} here"));
}
Some(Type::Error)
}
fn type_of_self_parameter(&self, tree: &Tree) -> Option<Type> {
let pl = tree.parent?;
let param_list = &self.syntax_tree[pl];
let fd = param_list.parent?;
let function_decl = &self.syntax_tree[fd];
let cd = function_decl.parent?;
let class_decl = &self.syntax_tree[cd];
if class_decl.kind != TreeKind::ClassDecl {
self.report_error_tree(tree, "self parameter only allowed in methods");
Some(Type::Error)
} else {
Some(self.type_of(cd))
}
}
fn type_of_self_reference(&self, t: TreeRef, tree: &Tree) -> Option<Type> {
assert_eq!(tree.kind, TreeKind::SelfReference);
let id = tree.nth_token(0)?;
let environment = self.environment_of(t);
if let Some(declaration) = environment.bind(id) {
return Some(match declaration {
Declaration::Variable {
declaration_type, ..
} => declaration_type.clone(),
_ => self.internal_compiler_error(
Some(t),
"how did I bind something other than a variable to self?",
),
});
}
if !environment.is_error {
self.report_error_tree(tree, "`self` is only valid in methods");
}
Some(Type::Error)
}
fn type_of_function_decl(&self, tree: &Tree) -> Option<Type> {
let param_list = tree.child_tree_of_kind(self.syntax_tree, TreeKind::ParamList)?;
// NOTE: The methodness here is determined by the presence of a self
// parameter, even if that parameter is incorrect (e.g., this
// declaration is not nested in a class, or it is not the first
// parameter.) We could have also chosen to signal it by our
// nesting but we want to extract the self parameter to a
// distinguished place in the function type.
let mut self_type = None;
let mut parameter_types = Vec::new();
for p in param_list.child_trees() {
parameter_types.push(Box::new(self.type_of(p)));
let p_type = Box::new(self.type_of(p));
if self.syntax_tree[p].kind == TreeKind::SelfParameter {
self_type = Some(p_type);
} else {
parameter_types.push(p_type);
}
}
let return_type = match tree.child_of_kind(self.syntax_tree, TreeKind::ReturnType) {
@ -1380,7 +1554,11 @@ impl<'a> Semantics<'a> {
None => Type::Nothing,
};
let return_type = Box::new(return_type);
Some(Type::Function(parameter_types, return_type))
Some(match self_type {
Some(self_type) => Type::Method(self_type, parameter_types, return_type),
None => Type::Function(parameter_types, return_type),
})
}
fn type_of_parameter(&self, tree: &Tree) -> Option<Type> {
@ -1452,7 +1630,9 @@ impl<'a> Semantics<'a> {
let declaration = match environment.bind(id) {
Some(d) => d,
None => {
self.report_error_tree(tree, format!("cannot find value {id} here"));
if !environment.is_error {
self.report_error_tree(tree, format!("cannot find value {id} here"));
}
return Some(Type::Error);
}
};