From 0ee89bf26bd32cb8bef3d8991a6be051fcc22950 Mon Sep 17 00:00:00 2001 From: John Doty Date: Sat, 20 Jan 2024 08:56:53 -0800 Subject: [PATCH] [fine] WIP: Classes --- fine/src/compiler.rs | 70 +++++++++++++- fine/src/parser.rs | 159 +++++++++++++++++++++---------- fine/src/semantics.rs | 140 ++++++++++++++++++++++++++- fine/src/tokens.rs | 11 ++- fine/src/vm.rs | 26 +++++ fine/tests/expression/class.fine | 13 +++ fine/tests/expression/lists.fine | 10 +- 7 files changed, 370 insertions(+), 59 deletions(-) create mode 100644 fine/tests/expression/class.fine diff --git a/fine/src/compiler.rs b/fine/src/compiler.rs index 0c986125..18ba5f02 100644 --- a/fine/src/compiler.rs +++ b/fine/src/compiler.rs @@ -42,6 +42,7 @@ pub enum Instruction { StoreModule(usize), StringAdd, Dup, + NewObject(usize), } pub enum Export { @@ -488,6 +489,7 @@ fn compile_binary_expression(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR { Declaration::ExternFunction { .. } => Instruction::Panic, Declaration::Function { .. } => Instruction::Panic, + Declaration::Class { .. } => Instruction::Panic, }; c.push(instruction); } @@ -545,6 +547,23 @@ 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) + } }; c.push(instruction); @@ -705,13 +724,58 @@ fn compile_function_declaration(c: &mut Compiler, t: TreeRef, tree: &Tree, gen_v OK } +fn compile_class_declaration(c: &mut Compiler, t: TreeRef, tree: &Tree, gen_value: bool) -> CR { + // Only compile a given function once. + // Classes get compiled as constructor functions which get called. + let fk = FunctionKey { tree: t }; + if !c.function_bindings.contains_key(&fk) { + let name = tree.nth_token(1)?; + + let field_count = tree.children.len() - 2; + + let function_index = c.temp_functions.len(); + c.temp_functions.push(None); + + c.pending_functions.push(( + fk.clone(), + function_index, + Function::new(name.as_str(), field_count), + )); + c.function_bindings.insert(fk, function_index); + c.module + .exports + .insert(name.to_string(), Export::Function(function_index)); + } + + if gen_value { + c.push(Instruction::PushNothing); + } + + OK +} + fn compile_function(c: &mut Compiler, t: TreeRef) -> CR { let tree = &c.syntax[t]; - let block = tree.child_of_kind(c.syntax, TreeKind::Block)?; + match tree.kind { + TreeKind::FunctionDecl => { + let block = tree.child_of_kind(c.syntax, TreeKind::Block)?; + compile_expression(c, block); + } + TreeKind::ClassDecl => { + let count = tree.children_of_kind(c.syntax, TreeKind::FieldDecl).count(); + for i in 0..count { + c.push(Instruction::LoadArgument(count - 1 - i)); + } + + let name = tree.nth_token(1)?.as_str(); + let name_index = c.add_string(name.to_string()); + c.push(Instruction::PushString(name_index)); + c.push(Instruction::NewObject(count)); + } + _ => ice!(c, t, "what is this tree doing in compile_function?"), + } - compile_expression(c, block); c.push(Instruction::Return); - OK } diff --git a/fine/src/parser.rs b/fine/src/parser.rs index 68bdf198..f572f045 100644 --- a/fine/src/parser.rs +++ b/fine/src/parser.rs @@ -125,8 +125,10 @@ pub enum TreeKind { BinaryExpression, Block, CallExpression, + ClassDecl, ConditionalExpression, ExpressionStatement, + FieldDecl, File, ForStatement, FunctionDecl, @@ -145,6 +147,9 @@ pub enum TreeKind { TypeParameter, TypeParameterList, UnaryExpression, + FieldList, + NewObjectExpression, + FieldValue, } pub struct Tree<'a> { @@ -183,14 +188,17 @@ impl<'a> Tree<'a> { }) } + pub fn children_of_kind<'b>( + &'b self, + s: &'b SyntaxTree, + kind: TreeKind, + ) -> impl Iterator + 'b { + self.child_trees() + .filter_map(move |t| if s[t].kind == kind { Some(t) } else { None }) + } + pub fn child_of_kind(&self, s: &SyntaxTree, kind: TreeKind) -> Option { - self.children - .iter() - .filter_map(|c| match c { - Child::Tree(t) => Some(*t), - _ => None, - }) - .find(|c| s[*c].kind == kind) + self.children_of_kind(&s, kind).next() } pub fn child_tree_of_kind<'b>( @@ -400,6 +408,10 @@ impl<'a> CParser<'a> { self.error(error); } + fn expect_start(&mut self, kind: TokenKind) { + assert!(self.eat(kind)); + } + fn advance_with_error(&mut self, error: T) -> MarkClosed where T: Into, @@ -498,6 +510,7 @@ fn file(p: &mut CParser) { while !p.eof() { match p.peek() { TokenKind::Fun => function(p), + TokenKind::Class => class(p), _ => statement(p), } } @@ -505,10 +518,9 @@ fn file(p: &mut CParser) { } fn function(p: &mut CParser) { - assert!(p.at(TokenKind::Fun)); let m = p.start(); - p.expect(TokenKind::Fun, "expected a function to start with 'fun'"); + p.expect_start(TokenKind::Fun); p.expect(TokenKind::Identifier, "expected a function name"); if p.at(TokenKind::LeftParen) { param_list(p); @@ -523,11 +535,40 @@ fn function(p: &mut CParser) { p.end(m, TreeKind::FunctionDecl); } -fn param_list(p: &mut CParser) { - assert!(p.at(TokenKind::LeftParen)); +fn class(p: &mut CParser) { let m = p.start(); - p.expect(TokenKind::LeftParen, "expect '(' to start a parameter list"); + p.expect_start(TokenKind::Class); + p.expect(TokenKind::Identifier, "expected a class name"); + if p.eat(TokenKind::LeftBrace) { + while !p.at(TokenKind::RightBrace) && !p.eof() { + field_decl(p); + } + } + p.expect(TokenKind::RightBrace, "expected a class to end with a '}'"); + + p.end(m, TreeKind::ClassDecl); +} + +fn field_decl(p: &mut CParser) { + let m = p.start(); + + p.expect(TokenKind::Identifier, "expected a field name"); + if p.eat(TokenKind::Colon) { + type_expr(p); + } + p.expect( + TokenKind::Semicolon, + "expect a ';' after field declarations", + ); + + p.end(m, TreeKind::FieldDecl); +} + +fn param_list(p: &mut CParser) { + let m = p.start(); + + p.expect_start(TokenKind::LeftParen); while !p.at(TokenKind::RightParen) && !p.eof() { if p.at(TokenKind::Identifier) { parameter(p); @@ -541,12 +582,9 @@ fn param_list(p: &mut CParser) { } fn parameter(p: &mut CParser) { - assert!(p.at(TokenKind::Identifier)); let m = p.start(); - p.expect( - TokenKind::Identifier, - "expected an identifier for a parameter name", - ); + + p.expect_start(TokenKind::Identifier); if p.eat(TokenKind::Colon) { type_expr(p); } @@ -558,13 +596,9 @@ fn parameter(p: &mut CParser) { } fn return_type(p: &mut CParser) { - assert!(p.at(TokenKind::Arrow)); let m = p.start(); - p.expect( - TokenKind::Arrow, - "function return type starts with an arrow", - ); + p.expect_start(TokenKind::Arrow); type_expr(p); p.end(m, TreeKind::ReturnType); @@ -584,10 +618,9 @@ fn type_expr(p: &mut CParser) { } fn type_parameter_list(p: &mut CParser) { - assert!(p.at(TokenKind::Less)); let m = p.start(); - p.expect(TokenKind::Less, "expected < to start type parameter list"); + p.expect_start(TokenKind::Less); while !p.at(TokenKind::Greater) && !p.eof() { if p.at(TokenKind::Identifier) { type_parameter(p); @@ -613,10 +646,9 @@ fn type_parameter(p: &mut CParser) { } fn block(p: &mut CParser) { - assert!(p.at(TokenKind::LeftBrace)); let m = p.start(); - p.expect(TokenKind::LeftBrace, "expect '{' to start a block"); + p.expect_start(TokenKind::LeftBrace); while !p.at(TokenKind::RightBrace) && !p.eof() { statement(p); } @@ -650,10 +682,9 @@ fn statement_if(p: &mut CParser) { } fn statement_let(p: &mut CParser) { - assert!(p.at(TokenKind::Let)); let m = p.start(); - p.expect(TokenKind::Let, "expect 'let' to start a let statement"); + p.expect_start(TokenKind::Let); p.expect(TokenKind::Identifier, "expected a name for the variable"); p.expect(TokenKind::Equal, "expected a '=' after the variable name"); expression(p); @@ -665,13 +696,9 @@ fn statement_let(p: &mut CParser) { } fn statement_return(p: &mut CParser) { - assert!(p.at(TokenKind::Return)); let m = p.start(); - p.expect( - TokenKind::Return, - "expect 'return' to start a return statement", - ); + p.expect_start(TokenKind::Return); expression(p); if !p.at(TokenKind::RightBrace) { p.expect(TokenKind::Semicolon, "expect ';' to end a return statement"); @@ -681,10 +708,9 @@ fn statement_return(p: &mut CParser) { } fn statement_for(p: &mut CParser) { - assert!(p.at(TokenKind::For)); let m = p.start(); - p.expect(TokenKind::For, "expect a for to start a for loop"); + p.expect_start(TokenKind::For); p.expect( TokenKind::Identifier, "expected an identifier for the loop variable", @@ -771,13 +797,9 @@ fn expression_with_power(p: &mut CParser, minimum_power: u8) { } fn argument_list(p: &mut CParser) { - assert!(p.at(TokenKind::LeftParen)); let m = p.start(); - p.expect( - TokenKind::LeftParen, - "expect an argument list to start with '('", - ); + p.expect_start(TokenKind::LeftParen); while !p.at(TokenKind::RightParen) && !p.eof() { argument(p); } @@ -818,6 +840,8 @@ fn prefix_expression(p: &mut CParser) -> MarkClosed { TokenKind::LeftBracket => list_constructor(p), + TokenKind::New => object_constructor(p), + _ => p.advance_with_error("expected an expression"), } } @@ -829,10 +853,9 @@ fn literal(p: &mut CParser) -> MarkClosed { } fn grouping(p: &mut CParser) -> MarkClosed { - assert!(p.at(TokenKind::LeftParen)); let m = p.start(); - p.expect(TokenKind::LeftParen, "expected '(' to start grouping"); + p.expect_start(TokenKind::LeftParen); expression(p); p.expect(TokenKind::RightParen, "unmatched parentheses in expression"); @@ -849,10 +872,9 @@ fn unary(p: &mut CParser) -> MarkClosed { } fn conditional(p: &mut CParser) -> MarkClosed { - assert!(p.at(TokenKind::If)); let m = p.start(); - p.expect(TokenKind::If, "expected conditional to start with 'if'"); + p.expect_start(TokenKind::If); expression(p); block(p); if p.eat(TokenKind::Else) { @@ -877,13 +899,9 @@ fn identifier(p: &mut CParser) -> MarkClosed { } fn list_constructor(p: &mut CParser) -> MarkClosed { - assert!(p.at(TokenKind::LeftBracket)); let m = p.start(); - p.expect( - TokenKind::LeftBracket, - "expect a list constructor to start with [", - ); + p.expect_start(TokenKind::LeftBracket); while !p.at(TokenKind::RightBracket) && !p.eof() { list_constructor_element(p); } @@ -909,6 +927,49 @@ fn list_constructor_element(p: &mut CParser) { p.end(m, TreeKind::ListConstructorElement); } +fn object_constructor(p: &mut CParser) -> MarkClosed { + let m = p.start(); + + p.expect_start(TokenKind::New); + type_expr(p); + if p.at(TokenKind::LeftBrace) { + field_list(p); + } else { + p.error("expected a '{' to start the field list after the class type"); + } + + p.end(m, TreeKind::NewObjectExpression) +} + +fn field_list(p: &mut CParser) { + let m = p.start(); + + p.expect_start(TokenKind::LeftBrace); + while !p.at(TokenKind::RightBrace) && !p.eof() { + field_value(p); + } + p.expect( + TokenKind::RightBrace, + "expected the field list to end with '}'", + ); + + p.end(m, TreeKind::FieldList); +} + +fn field_value(p: &mut CParser) { + let m = p.start(); + + p.expect(TokenKind::Identifier, "expected a field name"); + if p.eat(TokenKind::Colon) { + expression(p); + } + if !p.at(TokenKind::RightBrace) { + p.expect(TokenKind::Comma, "expect a ',' between fields"); + } + + p.end(m, TreeKind::FieldValue); +} + #[cfg(test)] mod tests { use super::*; diff --git a/fine/src/semantics.rs b/fine/src/semantics.rs index 598ebcdc..b6af291a 100644 --- a/fine/src/semantics.rs +++ b/fine/src/semantics.rs @@ -57,6 +57,17 @@ impl fmt::Display for Error { } } +pub struct FieldDecl { + pub name: Rc, + pub field_type: Type, +} + +pub struct ClassDecl { + pub name: Rc, + pub fields: Vec, + pub decl_tree: TreeRef, +} + #[derive(Clone)] pub enum Type { // Signals a type error. If you receive this then you know that an error @@ -91,6 +102,7 @@ pub enum Type { Function(Vec>, Box), List(Box), + Class(Rc), } impl Type { @@ -136,6 +148,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), } } } @@ -173,6 +186,10 @@ pub enum Declaration { declaration_type: Type, id: ExternalFunctionId, }, + Class { + declaration_type: Type, + declaration: TreeRef, //? + }, } pub struct Environment { @@ -404,7 +421,9 @@ impl<'a> Semantics<'a> { } pub fn snapshot_errors(&self) -> Vec { - (*self.errors.borrow()).clone() + let mut result = (*self.errors.borrow()).clone(); + result.sort_by(|a, b| a.start.0.cmp(&b.start.0)); + result } pub fn logical_parent(&self, tr: TreeRef) -> Option { @@ -681,6 +700,16 @@ impl<'a> Semantics<'a> { .all(|(a, b)| self.type_compat(a, b)) } + (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 + } + // Avoid introducing more errors (Type::Error, _) => true, (_, Type::Error) => true, @@ -736,6 +765,8 @@ impl<'a> Semantics<'a> { 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), + _ => self.internal_compiler_error(Some(t), "asking for a nonsense type"), }; @@ -865,6 +896,13 @@ impl<'a> Semantics<'a> { ); return Some(Type::Error); } + Declaration::Class { .. } => { + self.report_error_tree_ref( + left_tree, + "cannot assign a new value to a class declaration", + ); + return Some(Type::Error); + } }, None => { self.report_error_tree_ref( @@ -1041,6 +1079,13 @@ impl<'a> Semantics<'a> { fn type_of_call(&self, tree: &Tree) -> Option { assert_eq!(tree.kind, TreeKind::CallExpression); + // TODO: Move the vast majority of error checking out of this + // function: once you know that the 0th tree (the function + // expression) yields a function type, assume the type of the + // call is the type of the function return. Don't bother + // matching argument types &c; do that in an explicit + // check_call_expression function below. + let f_ref = tree.nth_tree(0)?; let f = self.type_of(f_ref); @@ -1151,6 +1196,9 @@ impl<'a> Semantics<'a> { Declaration::ExternFunction { declaration_type, .. } => declaration_type.clone(), + Declaration::Class { + declaration_type, .. + } => declaration_type.clone(), }); } @@ -1211,6 +1259,13 @@ impl<'a> Semantics<'a> { Some(Type::List(Box::new(element_type))) } + fn type_of_new_object_expression(&self, tree: &Tree) -> Option { + assert_eq!(tree.kind, TreeKind::NewObjectExpression); + + // NOTE: Matching fields is done in the check function. + Some(self.type_of(tree.nth_tree(1)?)) + } + fn type_of_list_constructor_element(&self, tree: &Tree) -> Option { assert_eq!(tree.kind, TreeKind::ListConstructorElement); Some(self.type_of(tree.nth_tree(0)?)) @@ -1280,6 +1335,12 @@ impl<'a> Semantics<'a> { } => { eprintln!("{declaration_type:?} (extern {id:?})"); } + Declaration::Class { + declaration_type, + declaration, + } => { + eprintln!("{declaration_type:?} (class {declaration:?})"); + } }; } environment = env.parent.clone(); @@ -1321,10 +1382,13 @@ pub fn check(s: &Semantics) { | TreeKind::GroupingExpression | TreeKind::UnaryExpression | TreeKind::ConditionalExpression - | TreeKind::CallExpression | TreeKind::BinaryExpression => { let _ = s.type_of(t); } + TreeKind::CallExpression => { + let _ = s.type_of(t); + } + TreeKind::ArgumentList => {} TreeKind::Argument => { let _ = s.type_of(t); @@ -1343,6 +1407,12 @@ pub fn check(s: &Semantics) { let _ = s.type_of(t); } TreeKind::ForStatement => check_for_statement(s, t), + + TreeKind::ClassDecl => {} + TreeKind::FieldDecl => {} + TreeKind::FieldList => {} + TreeKind::NewObjectExpression => check_new_object_expression(s, tree), + TreeKind::FieldValue => {} } } } @@ -1426,6 +1496,72 @@ fn check_for_statement(s: &Semantics, t: TreeRef) { let _ = s.environment_of(t); } +// TODO: TEST: Check mutual recursion with function calls +// TODO: TEST: Missing fields +// TODO: TEST: Extra fields +// TODO: TEST: Existing and type mismatch + +fn check_new_object_expression(s: &Semantics, tree: &Tree) { + let Some(type_expression) = tree.nth_tree(1) else { + return; + }; + let Some(field_list) = tree.child_tree_of_kind(s.syntax_tree, TreeKind::FieldList) else { + return; + }; + + let class_type = s.type_of(type_expression); + match &class_type { + Type::Class(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) { + let f = &s.syntax_tree[field]; + if let Some(name) = f.nth_token(0) { + let field_type = s.type_of(field); + field_bindings.insert(name.as_str(), (field, field_type)); + } else { + any_errors = true; + } + } + + // Check individual bindings... + for f in c.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( + *field_tree, + format!( + "field {} has is of type {}, but this expression generates a {}", + f.name, f.field_type, expr_type, + ), + ); + } + field_bindings.remove(&*f.name); + } else if !any_errors { + s.report_error_tree( + tree, + format!("missing an initializer for field {}", f.name), + ); + } + } + + if !any_errors { + for (n, (field_tree, _)) in field_bindings.iter() { + s.report_error_tree_ref( + *field_tree, + format!("{} does not have a field named {}", class_type, n), + ); + } + } + } + Type::Error => (), + ct => s.report_error_tree_ref( + type_expression, + format!("expected this to be a class type, but it is {ct}"), + ), + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/fine/src/tokens.rs b/fine/src/tokens.rs index 707b65d9..de00ecc5 100644 --- a/fine/src/tokens.rs +++ b/fine/src/tokens.rs @@ -49,6 +49,7 @@ pub enum TokenKind { Import, In, Let, + New, Or, Return, Select, @@ -302,6 +303,11 @@ impl<'a> Tokens<'a> { return TokenKind::Let; } } + 'n' => { + if ident == "new" { + return TokenKind::New; + } + } 'o' => { if ident == "or" { return TokenKind::Or; @@ -573,7 +579,7 @@ mod tests { test_tokens!( more_keywords, - "fun if import let return select this true while truewhile", + "fun if import let return select this true while truewhile new", (0, Fun, "fun"), (4, If, "if"), (7, Import, "import"), @@ -583,7 +589,8 @@ mod tests { (32, This, "this"), (37, True, "true"), (42, While, "while"), - (48, Identifier, "truewhile") + (48, Identifier, "truewhile"), + (58, New, "new") ); test_tokens!( diff --git a/fine/src/vm.rs b/fine/src/vm.rs index 68d05a2c..18d68990 100644 --- a/fine/src/vm.rs +++ b/fine/src/vm.rs @@ -39,6 +39,12 @@ pub struct VMError { type Result = std::result::Result; +#[derive(Clone, Debug)] +pub struct Object { + name: Rc, + values: Box<[StackValue]>, +} + #[derive(Clone, Debug)] pub enum StackValue { Nothing, @@ -47,6 +53,7 @@ pub enum StackValue { String(Rc), Function(Rc), ExternFunction(usize), + Object(Rc), } enum FuncValue { @@ -130,6 +137,10 @@ impl Frame { self.push_value(StackValue::ExternFunction(v)); } + fn push_object(&mut self, v: Rc) { + self.push_value(StackValue::Object(v)); + } + fn get_argument(&self, i: usize) -> Result { self.args .get(i) @@ -384,6 +395,21 @@ fn eval_one( let y = f.pop_string()?; f.push_bool(x == y); } + + Instruction::NewObject(slots) => { + let name = f.pop_string()?; + let mut values = Vec::with_capacity(slots); + for _ in 0..slots { + values.push(f.pop_value()?); + } + + let object = Object { + name, + values: values.into(), + }; + + f.push_object(object.into()); + } } Ok(Flow::Continue) diff --git a/fine/tests/expression/class.fine b/fine/tests/expression/class.fine new file mode 100644 index 00000000..9cdbae8d --- /dev/null +++ b/fine/tests/expression/class.fine @@ -0,0 +1,13 @@ +class Point { + x: f64; + y: f64; +} + +fun test() -> f64 { + let pt = new Point { x: 7, y: 23 }; + let z = pt.x; + z +} + +// @ignore +// @no-errors diff --git a/fine/tests/expression/lists.fine b/fine/tests/expression/lists.fine index 3c9c577b..2630735b 100644 --- a/fine/tests/expression/lists.fine +++ b/fine/tests/expression/lists.fine @@ -1,10 +1,14 @@ fun sum(x: list) -> f64 { - 75 // lol + let result = 0; + for v in x { + result = result + v; + } + result } -fun test() { +fun test() -> f64 { let val = [1, 2, 3]; - sum(val); + sum(val) } // @no-errors