[fine] Classes!
It's kinda amazing this works actually
This commit is contained in:
parent
0ee89bf26b
commit
4505996710
5 changed files with 319 additions and 40 deletions
|
|
@ -585,19 +585,35 @@ impl<'a> Semantics<'a> {
|
|||
match child {
|
||||
Child::Tree(t) => {
|
||||
let ct = &self.syntax_tree[*t];
|
||||
if ct.kind == TreeKind::FunctionDecl {
|
||||
// TODO: Should I have accessors for function decls?
|
||||
let Some(name) = ct.nth_token(1) else {
|
||||
continue;
|
||||
};
|
||||
match ct.kind {
|
||||
TreeKind::FunctionDecl => {
|
||||
// TODO: Should I have accessors for function decls?
|
||||
let Some(name) = ct.nth_token(1) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
environment.declarations.insert(
|
||||
name.as_str().into(),
|
||||
Declaration::Function {
|
||||
declaration_type: self.type_of(*t),
|
||||
declaration: *t,
|
||||
},
|
||||
);
|
||||
environment.declarations.insert(
|
||||
name.as_str().into(),
|
||||
Declaration::Function {
|
||||
declaration_type: self.type_of(*t),
|
||||
declaration: *t,
|
||||
},
|
||||
);
|
||||
}
|
||||
TreeKind::ClassDecl => {
|
||||
let Some(name) = ct.nth_token(1) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
environment.declarations.insert(
|
||||
name.as_str().into(),
|
||||
Declaration::Class {
|
||||
declaration_type: self.type_of(*t),
|
||||
declaration: *t,
|
||||
},
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
|
|
@ -742,7 +758,7 @@ impl<'a> Semantics<'a> {
|
|||
TreeKind::Error => Some(Type::Error),
|
||||
TreeKind::UnaryExpression => self.type_of_unary(tree),
|
||||
TreeKind::BinaryExpression => self.type_of_binary(tree),
|
||||
TreeKind::TypeExpression => self.type_of_type_expr(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),
|
||||
|
|
@ -750,6 +766,7 @@ impl<'a> Semantics<'a> {
|
|||
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),
|
||||
|
|
@ -766,6 +783,8 @@ impl<'a> Semantics<'a> {
|
|||
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),
|
||||
|
||||
_ => self.internal_compiler_error(Some(t), "asking for a nonsense type"),
|
||||
};
|
||||
|
|
@ -935,7 +954,7 @@ impl<'a> Semantics<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn type_of_type_expr(&self, tree: &Tree) -> Option<Type> {
|
||||
fn type_of_type_expr(&self, t: TreeRef, tree: &Tree) -> Option<Type> {
|
||||
assert_eq!(tree.kind, TreeKind::TypeExpression);
|
||||
|
||||
// TODO: This will *clearly* need to get better.
|
||||
|
|
@ -958,8 +977,30 @@ impl<'a> Semantics<'a> {
|
|||
}
|
||||
}
|
||||
_ => {
|
||||
self.report_error_tree(tree, format!("Unrecognized type: '{token}'"));
|
||||
Some(Type::Error)
|
||||
let environment = self.environment_of(t);
|
||||
match environment.bind(token) {
|
||||
Some(Declaration::Class {
|
||||
declaration_type, ..
|
||||
}) => Some(declaration_type.clone()),
|
||||
Some(Declaration::Variable { .. }) => {
|
||||
self.report_error_tree(
|
||||
tree,
|
||||
format!("'{token}' is a variable and cannot be used as a type"),
|
||||
);
|
||||
Some(Type::Error)
|
||||
}
|
||||
Some(Declaration::Function { .. } | Declaration::ExternFunction { .. }) => {
|
||||
self.report_error_tree(
|
||||
tree,
|
||||
format!("'{token}' is a function and cannot be used as a type"),
|
||||
);
|
||||
Some(Type::Error)
|
||||
}
|
||||
None => {
|
||||
self.report_error_tree(tree, format!("Unrecognized type: '{token}'"));
|
||||
Some(Type::Error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1157,6 +1198,34 @@ impl<'a> Semantics<'a> {
|
|||
Some(result)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn type_of_expression_statement(&self, tree: &Tree) -> Option<Type> {
|
||||
assert_eq!(tree.kind, TreeKind::ExpressionStatement);
|
||||
let last_is_semicolon = tree
|
||||
|
|
@ -1196,9 +1265,11 @@ impl<'a> Semantics<'a> {
|
|||
Declaration::ExternFunction {
|
||||
declaration_type, ..
|
||||
} => declaration_type.clone(),
|
||||
Declaration::Class {
|
||||
declaration_type, ..
|
||||
} => declaration_type.clone(),
|
||||
Declaration::Class { .. } => {
|
||||
// TODO: Test this case
|
||||
self.report_error_tree(tree, format!("{id} is a class, not a value (did you mean to create a new instance with `new`?)"));
|
||||
Type::Error
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -1266,6 +1337,65 @@ impl<'a> Semantics<'a> {
|
|||
Some(self.type_of(tree.nth_tree(1)?))
|
||||
}
|
||||
|
||||
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.
|
||||
|
||||
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()))
|
||||
}
|
||||
|
||||
fn type_of_field_value(&self, t: TreeRef, tree: &Tree) -> Option<Type> {
|
||||
assert_eq!(tree.kind, TreeKind::FieldValue);
|
||||
|
||||
if let Some(colon) = tree.nth_token(1) {
|
||||
if colon.kind == TokenKind::Colon {
|
||||
// Form 1: { x: e, ... }
|
||||
return Some(self.type_of(tree.nth_tree(2)?));
|
||||
}
|
||||
}
|
||||
|
||||
// Form 2: { x, ... }
|
||||
let environment = self.environment_of(t);
|
||||
let id = tree.nth_token(0)?;
|
||||
match environment.bind(id)? {
|
||||
Declaration::Variable {
|
||||
declaration_type, ..
|
||||
}
|
||||
| Declaration::Function {
|
||||
declaration_type, ..
|
||||
}
|
||||
| Declaration::ExternFunction {
|
||||
declaration_type, ..
|
||||
} => Some(declaration_type.clone()),
|
||||
|
||||
Declaration::Class { .. } => {
|
||||
self.report_error_tree(
|
||||
tree,
|
||||
format!("`{id}` is a class, and cannot be the value of a field"),
|
||||
);
|
||||
Some(Type::Error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn type_of_list_constructor_element(&self, tree: &Tree) -> Option<Type> {
|
||||
assert_eq!(tree.kind, TreeKind::ListConstructorElement);
|
||||
Some(self.type_of(tree.nth_tree(0)?))
|
||||
|
|
@ -1382,7 +1512,8 @@ pub fn check(s: &Semantics) {
|
|||
| TreeKind::GroupingExpression
|
||||
| TreeKind::UnaryExpression
|
||||
| TreeKind::ConditionalExpression
|
||||
| TreeKind::BinaryExpression => {
|
||||
| TreeKind::BinaryExpression
|
||||
| TreeKind::MemberAccess => {
|
||||
let _ = s.type_of(t);
|
||||
}
|
||||
TreeKind::CallExpression => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue