[fine] Classes!

It's kinda amazing this works actually
This commit is contained in:
John Doty 2024-01-20 11:03:33 -08:00
parent 0ee89bf26b
commit 4505996710
5 changed files with 319 additions and 40 deletions

View file

@ -19,6 +19,7 @@ pub enum Instruction {
CompareFloat,
CompareString,
Discard,
Dup,
FloatAdd,
FloatDivide,
FloatMultiply,
@ -31,6 +32,8 @@ pub enum Instruction {
LoadFunction(usize),
LoadLocal(usize),
LoadModule(usize),
LoadSlot(usize),
NewObject(usize),
PushFalse,
PushFloat(f64),
PushNothing,
@ -41,8 +44,6 @@ pub enum Instruction {
StoreLocal(usize),
StoreModule(usize),
StringAdd,
Dup,
NewObject(usize),
}
pub enum Export {
@ -287,6 +288,9 @@ fn compile_expression(c: &mut Compiler, t: TreeRef) {
TreeKind::CallExpression => compile_call_expression(c, tree),
TreeKind::Block => compile_block_expression(c, tree),
TreeKind::Argument => compile_argument(c, tree),
TreeKind::NewObjectExpression => compile_new_object_expression(c, t, tree),
TreeKind::FieldValue => compile_field_value(c, t, tree),
TreeKind::MemberAccess => compile_member_access(c, tree),
_ => ice!(c, t, "{tree:?} is not an expression, cannot compile"),
};
if matches!(cr, None) {
@ -506,6 +510,10 @@ fn compile_identifier_expression(c: &mut Compiler, t: TreeRef, tree: &Tree) -> O
let environment = c.semantics.environment_of(t);
let declaration = environment.bind(ident)?;
compile_load_declaration(c, t, declaration)
}
fn compile_load_declaration(c: &mut Compiler, t: TreeRef, declaration: &Declaration) -> CR {
// TODO: Load function declaration. :P
let instruction = match declaration {
Declaration::Variable {
@ -547,23 +555,7 @@ fn compile_identifier_expression(c: &mut Compiler, t: TreeRef, tree: &Tree) -> O
Instruction::LoadFunction(index)
}
Declaration::ExternFunction { id, .. } => Instruction::LoadExternFunction(id.id()),
Declaration::Class { declaration, .. } => {
let key = FunctionKey { tree: *declaration };
let index = match c.function_bindings.get(&key) {
Some(index) => *index,
None => {
let tree = &c.syntax[*declaration];
compiler_assert_eq!(c, t, tree.kind, TreeKind::ClassDecl);
compile_class_declaration(c, t, tree, false)?;
*c.function_bindings
.get(&key)
.expect("did not compile the class constructor!")
}
};
Instruction::LoadFunction(index)
}
Declaration::Class { .. } => Instruction::Panic,
};
c.push(instruction);
@ -609,10 +601,98 @@ fn compile_argument(c: &mut Compiler, tree: &Tree) -> CR {
OK
}
fn compile_new_object_expression(c: &mut Compiler, t: TreeRef, tree: &Tree) -> CR {
// We pass in the arguments.... by... field order?
let Type::Class(class) = c.semantics.type_of(t) else {
c.push(Instruction::Panic);
return OK;
};
let field_list = tree.child_tree_of_kind(c.syntax, TreeKind::FieldList)?;
let mut field_bindings = HashMap::new();
for field in field_list.children_of_kind(c.syntax, TreeKind::FieldValue) {
let f = &c.syntax[field];
let name = f.nth_token(0)?;
field_bindings.insert(name.as_str(), field);
}
// The fields come in this order and since arguments are backwards
// (stack!) we compile them in reverse order. Missing fields panic,
// obviously.
for field in class.fields.iter().rev() {
let binding = field_bindings.get(&*field.name)?;
compile_expression(c, *binding);
}
// Fetch the correct constructor.
let type_reference = tree.child_tree_of_kind(c.syntax, TreeKind::TypeExpression)?;
let identifier = type_reference.nth_token(0)?;
let environment = c.semantics.environment_of(t);
match environment.bind(identifier)? {
Declaration::Class { declaration, .. } => {
let key = FunctionKey { tree: *declaration };
let index = match c.function_bindings.get(&key) {
Some(index) => *index,
None => {
let tree = &c.syntax[*declaration];
compiler_assert_eq!(c, t, tree.kind, TreeKind::ClassDecl);
compile_class_declaration(c, t, tree, false)?;
*c.function_bindings
.get(&key)
.expect("did not compile the class constructor!")
}
};
c.push(Instruction::LoadFunction(index));
}
_ => return None,
}
c.push(Instruction::Call(class.fields.len()));
OK
}
fn compile_field_value(c: &mut Compiler, t: TreeRef, tree: &Tree) -> CR {
if let Some(colon) = tree.nth_token(1) {
if colon.kind == TokenKind::Colon {
compile_expression(c, tree.nth_tree(2)?);
return OK;
}
}
// Form 2: { x, ... }
let environment = c.semantics.environment_of(t);
let id = tree.nth_token(0)?;
let declaration = environment.bind(id)?;
compile_load_declaration(c, t, declaration)
}
fn compile_member_access(c: &mut Compiler, tree: &Tree) -> CR {
let lhs = tree.nth_tree(0)?;
compile_expression(c, lhs);
let id = tree.nth_token(2)?;
let Type::Class(cr) = c.semantics.type_of(lhs) else {
return None;
};
let Some((index, _fld)) = cr
.fields
.iter()
.enumerate()
.find(|(_, f)| &*f.name == id.as_str())
else {
return None;
};
c.push(Instruction::LoadSlot(index));
OK
}
fn compile_statement(c: &mut Compiler, t: TreeRef, gen_value: bool) {
let tree = &c.semantics.tree()[t];
let cr = match tree.kind {
TreeKind::FunctionDecl => compile_function_declaration(c, t, tree, gen_value),
TreeKind::ClassDecl => compile_class_declaration(c, t, tree, gen_value),
TreeKind::LetStatement => compile_let_statement(c, t, tree, gen_value),
TreeKind::ExpressionStatement => compile_expression_statement(c, tree, gen_value),
TreeKind::IfStatement => compile_if_statement(c, tree, gen_value),

View file

@ -150,6 +150,7 @@ pub enum TreeKind {
FieldList,
NewObjectExpression,
FieldValue,
MemberAccess,
}
pub struct Tree<'a> {
@ -794,6 +795,16 @@ fn expression_with_power(p: &mut CParser, minimum_power: u8) {
expression_with_power(p, power);
expr = p.end(m, TreeKind::BinaryExpression);
}
while p.at(TokenKind::Dot) {
let m = p.start_before(expr);
p.advance(); // Consume the dot
p.expect(
TokenKind::Identifier,
"expected an identifier for member access",
);
expr = p.end(m, TreeKind::MemberAccess);
}
}
fn argument_list(p: &mut CParser) {

View file

@ -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 => {

View file

@ -29,6 +29,10 @@ pub enum VMErrorCode {
FunctionOutOfRange(usize),
#[error("internal error: stack type mismatch ({0:?} is not function)")]
StackExpectedFunction(StackValue),
#[error("internal error: stack type mismatch ({0:?} is not object)")]
StackExpectedObject(StackValue),
#[error("internal error: slot {0} was out of range for object (type {1} with {2} slots)")]
SlotOutOfRange(usize, Rc<str>, usize),
}
#[derive(Debug)]
@ -45,6 +49,19 @@ pub struct Object {
values: Box<[StackValue]>,
}
impl Object {
pub fn get_slot(&self, index: usize) -> Result<StackValue> {
match self.values.get(index) {
Some(v) => Ok(v.clone()),
None => Err(VMErrorCode::SlotOutOfRange(
index,
self.name.clone(),
self.values.len(),
)),
}
}
}
#[derive(Clone, Debug)]
pub enum StackValue {
Nothing,
@ -109,6 +126,13 @@ impl Frame {
}
}
fn pop_object(&mut self) -> Result<Rc<Object>> {
match self.pop_value()? {
StackValue::Object(v) => Ok(v),
v => Err(VMErrorCode::StackExpectedObject(v)),
}
}
fn push_value(&mut self, v: StackValue) {
self.stack.push(v)
}
@ -410,6 +434,10 @@ fn eval_one(
f.push_object(object.into());
}
Instruction::LoadSlot(slot) => {
let obj = f.pop_object()?;
f.push_value(obj.get_slot(slot)?);
}
}
Ok(Flow::Continue)