From 4505996710c0d993861a4d34c18734b8af71ff90 Mon Sep 17 00:00:00 2001 From: John Doty Date: Sat, 20 Jan 2024 11:03:33 -0800 Subject: [PATCH] [fine] Classes! It's kinda amazing this works actually --- fine/src/compiler.rs | 118 +++++++++++++++++---- fine/src/parser.rs | 11 ++ fine/src/semantics.rs | 171 +++++++++++++++++++++++++++---- fine/src/vm.rs | 28 +++++ fine/tests/expression/class.fine | 31 +++++- 5 files changed, 319 insertions(+), 40 deletions(-) diff --git a/fine/src/compiler.rs b/fine/src/compiler.rs index 18ba5f02..c183438c 100644 --- a/fine/src/compiler.rs +++ b/fine/src/compiler.rs @@ -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), diff --git a/fine/src/parser.rs b/fine/src/parser.rs index f572f045..7d6c1cc9 100644 --- a/fine/src/parser.rs +++ b/fine/src/parser.rs @@ -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) { diff --git a/fine/src/semantics.rs b/fine/src/semantics.rs index b6af291a..739e3655 100644 --- a/fine/src/semantics.rs +++ b/fine/src/semantics.rs @@ -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 { + fn type_of_type_expr(&self, t: TreeRef, tree: &Tree) -> Option { 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 { + 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 { 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 { + 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 { + 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 { 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 => { diff --git a/fine/src/vm.rs b/fine/src/vm.rs index 18d68990..9343c0f7 100644 --- a/fine/src/vm.rs +++ b/fine/src/vm.rs @@ -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, usize), } #[derive(Debug)] @@ -45,6 +49,19 @@ pub struct Object { values: Box<[StackValue]>, } +impl Object { + pub fn get_slot(&self, index: usize) -> Result { + 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> { + 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) diff --git a/fine/tests/expression/class.fine b/fine/tests/expression/class.fine index 9cdbae8d..4d1eeb7d 100644 --- a/fine/tests/expression/class.fine +++ b/fine/tests/expression/class.fine @@ -9,5 +9,34 @@ fun test() -> f64 { z } -// @ignore // @no-errors +// @eval: Float(7.0) +// @compiles-to: +// | function << module >> (0 args, 0 locals): +// | strings (0): +// | code (2): +// | 0: PushNothing +// | 1: Return +// | function Point (4 args, 0 locals): +// | strings (1): +// | 0: Point +// | code (5): +// | 0: LoadArgument(1) +// | 1: LoadArgument(0) +// | 2: PushString(0) +// | 3: NewObject(2) +// | 4: Return +// | function test (0 args, 2 locals): +// | strings (0): +// | code (10): +// | 0: PushFloat(23.0) +// | 1: PushFloat(7.0) +// | 2: LoadFunction(1) +// | 3: Call(2) +// | 4: StoreLocal(0) +// | 5: LoadLocal(0) +// | 6: LoadSlot(0) +// | 7: StoreLocal(1) +// | 8: LoadLocal(1) +// | 9: Return +// |