From 19e57db7249970cf8a92577c7fe19cebf8134afa Mon Sep 17 00:00:00 2001 From: John Doty Date: Thu, 25 Jan 2024 06:44:53 -0800 Subject: [PATCH] [fine] Static methods I guess --- fine/src/compiler.rs | 31 +++--- fine/src/semantics.rs | 94 ++++++++++++++----- fine/tests/example_tests.rs | 5 +- fine/tests/expression/class.fine | 28 ++++-- .../errors/class_as_a_variable.fine | 2 +- .../tests/expression/errors/class_errors.fine | 2 +- 6 files changed, 111 insertions(+), 51 deletions(-) diff --git a/fine/src/compiler.rs b/fine/src/compiler.rs index 32d7f0aa..25411fc2 100644 --- a/fine/src/compiler.rs +++ b/fine/src/compiler.rs @@ -216,6 +216,13 @@ macro_rules! ice { }} } +macro_rules! inst_panic { + ($($t:tt)+) => {{ + // eprintln!($($t)*); + Instruction::Panic + }}; +} + // macro_rules! ice { // ($compiler:expr, $tr:expr, $($t:tt)*) => {{}}; // } @@ -295,7 +302,7 @@ fn compile_expression(c: &mut Compiler, t: TreeRef) { _ => ice!(c, t, "{tree:?} is not an expression, cannot compile"), }; if matches!(cr, None) { - c.push(Instruction::Panic); + c.push(inst_panic!("panic compiling expression {:?}", tree)); } } @@ -330,7 +337,7 @@ fn compile_literal(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR { let index = c.add_string(result); c.push(Instruction::PushString(index)) } - Type::Error => c.push(Instruction::Panic), + Type::Error => c.push(inst_panic!("compiling literal {:?}", tr)), _ => ice!(c, t, "unsupported literal type: {t:?}"), }; OK @@ -401,7 +408,7 @@ fn compile_binary_expression(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR { TokenKind::Plus => compile_simple_binary_expression(c, tr, |_, t| match t { Type::F64 => Instruction::FloatAdd, Type::String => Instruction::StringAdd, - _ => Instruction::Panic, + _ => inst_panic!("panic adding {}", t), }), TokenKind::Minus => { compile_simple_binary_expression(c, tr, |_, _| Instruction::FloatSubtract) @@ -451,7 +458,7 @@ fn compile_binary_expression(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR { Type::F64 => Instruction::CompareFloat, Type::String => Instruction::CompareString, Type::Bool => Instruction::CompareBool, // ? - _ => Instruction::Panic, + _ => inst_panic!("panic comparing {}", arg_type), } } }) @@ -494,9 +501,9 @@ fn compile_binary_expression(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR { } } - Declaration::ExternFunction { .. } => Instruction::Panic, - Declaration::Function { .. } => Instruction::Panic, - Declaration::Class { .. } => Instruction::Panic, + Declaration::ExternFunction { .. } => inst_panic!("store ext"), + Declaration::Function { .. } => inst_panic!("store func"), + Declaration::Class { .. } => inst_panic!("store class"), }; c.push(instruction); } @@ -573,8 +580,8 @@ fn compile_load_declaration(c: &mut Compiler, t: TreeRef, declaration: &Declarat } Declaration::ExternFunction { id, .. } => Instruction::LoadExternFunction(id.id()), - // There is no universe where it's possible to use a class as a variable. - Declaration::Class { .. } => Instruction::Panic, + // Must be a static don't worry about it. + Declaration::Class { .. } => return OK, }; c.push(instruction); @@ -628,8 +635,8 @@ fn compile_argument(c: &mut Compiler, tree: &Tree) -> CR { fn compile_new_object_expression(c: &mut Compiler, t: TreeRef, tree: &Tree) -> CR { // We pass in the arguments.... by... field order? - let Type::Class(ct, _) = c.semantics.type_of(t) else { - c.push(Instruction::Panic); + let Type::Object(ct, _) = c.semantics.type_of(t) else { + c.push(inst_panic!("new obj not ob")); return OK; }; let class = c.semantics.class_of(ct); @@ -724,7 +731,7 @@ fn compile_statement(c: &mut Compiler, t: TreeRef, gen_value: bool) { _ => ice!(c, t, "unsupported tree kind {:?}", tree.kind), }; if matches!(cr, None) { - c.push(Instruction::Panic); + c.push(inst_panic!("stat {:?}", tree)); } } diff --git a/fine/src/semantics.rs b/fine/src/semantics.rs index 87c23214..40f8b355 100644 --- a/fine/src/semantics.rs +++ b/fine/src/semantics.rs @@ -66,6 +66,7 @@ pub struct MethodDecl { pub name: Rc, pub decl_type: Type, pub declaration: TreeRef, + pub is_static: bool, } pub struct ClassDecl { @@ -75,6 +76,7 @@ pub struct ClassDecl { pub decl_tree: TreeRef, pub env: EnvironmentRef, + pub static_env: EnvironmentRef, } #[derive(Clone)] @@ -132,9 +134,14 @@ pub enum Type { List(Box), - // Classes need to be fetched explicitly from the semantics; they are - // computed lazily. + // A class is the static type of a class; when the class is referred to + // by name it has this type. (Distinct from an instance!) Class(TreeRef, Rc), + + // An object is the type of an allocated object instance. Details of its + // class need to be fetched explicitly from the semantics via the + // TreeRef and `Semantics::class_of`; they are computed lazily. + Object(TreeRef, Rc), } impl Type { @@ -194,6 +201,7 @@ impl fmt::Display for Type { write!(f, "$_") } List(t) => write!(f, "list<{t}>"), + Object(_, name) => write!(f, "{} instance", name), Class(_, name) => write!(f, "class {}", name), } } @@ -813,10 +821,14 @@ impl<'a> Semantics<'a> { }; let typ = self.type_of(lhs); match &typ { - Type::Class(ct, _) => { + Type::Object(ct, _) => { let class = self.class_of(*ct); class.env.clone() } + Type::Class(ct, _) => { + let class = self.class_of(*ct); + class.static_env.clone() + } Type::Error => Environment::error(), _ => { // TODO: This is probably wrong, yeah? @@ -869,16 +881,33 @@ impl<'a> Semantics<'a> { 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, - }); + // TODO: Check to see if it is actually a method, or if it is a static function. + let decl_type = self.type_of(method); + match decl_type { + Type::Method(..) => { + methods.push(MethodDecl { + name: method_name.as_str().into(), + decl_type, + declaration: method, + is_static: false, + }); + } + _ => { + // TODO: Default to method or static? + methods.push(MethodDecl { + name: method_name.as_str().into(), + decl_type, + declaration: method, + is_static: true, + }); + } + } } } // Build into an environment let mut env = Environment::new(None, Location::Slot); + let mut static_env = Environment::new(None, Location::Slot); for (index, field) in fields.iter().enumerate() { env.declarations.insert( (&*field.name).into(), @@ -890,13 +919,23 @@ impl<'a> Semantics<'a> { ); } for method in methods.iter() { - let existing = env.declarations.insert( - (&*method.name).into(), - Declaration::Function { - declaration_type: method.decl_type.clone(), - declaration: method.declaration, - }, - ); + let existing = if method.is_static { + static_env.declarations.insert( + (&*method.name).into(), + Declaration::Function { + declaration_type: method.decl_type.clone(), + declaration: method.declaration, + }, + ) + } else { + 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, @@ -911,6 +950,7 @@ impl<'a> Semantics<'a> { methods: methods.into(), decl_tree: t, env: EnvironmentRef::new(env), + static_env: EnvironmentRef::new(static_env), }); self.classes.borrow_mut()[t.index()] = Incremental::Complete(result.clone()); @@ -937,7 +977,7 @@ impl<'a> Semantics<'a> { .all(|(a, b)| self.type_compat(a, b)) } - (Type::Class(ca, _), Type::Class(cb, _)) => { + (Type::Object(ca, _), Type::Object(cb, _)) => { // TODO: If we were doing structural comparisons here... // maybe? MAYBE? // @@ -1485,10 +1525,12 @@ impl<'a> Semantics<'a> { Declaration::ExternFunction { declaration_type, .. } => declaration_type.clone(), - Declaration::Class { .. } => { - 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 - } + Declaration::Class { + declaration_type, .. + } => match declaration_type { + Type::Object(cd, name) => Type::Class(*cd, name.clone()), + _ => self.internal_compiler_error(Some(t), "bound to a class not understood"), + }, }); } @@ -1612,16 +1654,18 @@ impl<'a> Semantics<'a> { 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. + // NOTE: Matching fields is done in the check function, as is + // reporting on the suitability of the type. 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); - // The type of a class is computed lazily. + // The details of a class are computed lazily, but this is enough of + // a belly-button. let name = tree.nth_token(1)?; - Some(Type::Class(t, name.as_str().into())) + Some(Type::Object(t, name.as_str().into())) } fn type_of_field_value(&self, t: TreeRef, tree: &Tree) -> Option { @@ -1910,9 +1954,9 @@ fn check_new_object_expression(s: &Semantics, tree: &Tree) { return; }; - let class_type = s.type_of(type_expression); + let class_type = s.type_of(type_expression); // TODO: Should yield a ClassType not an ObjectType? match &class_type { - Type::Class(c, _) => { + Type::Object(c, _) => { let class = s.class_of(*c); let mut any_errors = false; diff --git a/fine/tests/example_tests.rs b/fine/tests/example_tests.rs index f8e1117e..3f56156a 100644 --- a/fine/tests/example_tests.rs +++ b/fine/tests/example_tests.rs @@ -285,9 +285,10 @@ fn assert_eval_ok(tree: &SyntaxTree, lines: &Lines, expected: &str) { eprintln!("{actual}"); + eprintln!("Backtrace:"); for frame in e.stack.iter() { - eprintln!("{:?}", frame.func()); - eprint!(" ("); + let func = frame.func(); + eprint!(" {} (", func.name()); for arg in frame.args().iter() { eprint!("{:?},", arg); } diff --git a/fine/tests/expression/class.fine b/fine/tests/expression/class.fine index 4a1eb8c2..e756d7b2 100644 --- a/fine/tests/expression/class.fine +++ b/fine/tests/expression/class.fine @@ -2,9 +2,9 @@ class Point { x: f64; y: f64; - // fun something_static() -> f64 { - // 12 - // } + fun something_static() -> f64 { + 12 + } fun square_length(self) -> f64 { self.x * self.x + self.y * self.y @@ -23,19 +23,19 @@ fun test() -> f64 { }; let pt = line.start; - let z = line.start.x + pt.square_length();// + Point::something_static(); + let z = line.start.x + pt.square_length() + Point.something_static(); z } // @no-errors -// @eval: Float(585.0) +// @eval: Float(597.0) // @compiles-to: // | function << module >> (0 args, 0 locals): // | strings (0): // | code (2): // | 0: PushNothing // | 1: Return -// | function Point (5 args, 0 locals): +// | function Point (6 args, 0 locals): // | strings (1): // | 0: Point // | code (5): @@ -55,7 +55,7 @@ fun test() -> f64 { // | 4: Return // | function test (0 args, 3 locals): // | strings (0): -// | code (24): +// | code (27): // | 0: PushFloat(99.0) // | 1: PushFloat(999.0) // | 2: LoadFunction(1) @@ -77,9 +77,12 @@ fun test() -> f64 { // | 18: LoadFunction(4) // | 19: Call(1) // | 20: FloatAdd -// | 21: StoreLocal(2) -// | 22: LoadLocal(2) -// | 23: Return +// | 21: LoadFunction(5) +// | 22: Call(0) +// | 23: FloatAdd +// | 24: StoreLocal(2) +// | 25: LoadLocal(2) +// | 26: Return // | function square_length (1 args, 0 locals): // | strings (0): // | code (12): @@ -95,4 +98,9 @@ fun test() -> f64 { // | 9: FloatMultiply // | 10: FloatAdd // | 11: Return +// | function something_static (0 args, 0 locals): +// | strings (0): +// | code (2): +// | 0: PushFloat(12.0) +// | 1: Return // | diff --git a/fine/tests/expression/errors/class_as_a_variable.fine b/fine/tests/expression/errors/class_as_a_variable.fine index dc20e8c9..83540878 100644 --- a/fine/tests/expression/errors/class_as_a_variable.fine +++ b/fine/tests/expression/errors/class_as_a_variable.fine @@ -5,4 +5,4 @@ fun test() -> f64 { } // @expect-errors: -// | 4:2: Foo is a class, not a value (did you mean to create a new instance with 'new'?) \ No newline at end of file +// | 4:6: cannot apply binary operator '+' to expressions of type 'class Foo' (on the left) and 'f64' (on the right) \ No newline at end of file diff --git a/fine/tests/expression/errors/class_errors.fine b/fine/tests/expression/errors/class_errors.fine index fb6f16a0..678cca7b 100644 --- a/fine/tests/expression/errors/class_errors.fine +++ b/fine/tests/expression/errors/class_errors.fine @@ -18,7 +18,7 @@ fun test() { // @expect-errors: // | 7:12: missing an initializer for field y // | 8:12: missing an initializer for field x -// | 9:41: class Point does not have a field named z +// | 9:41: Point instance does not have a field named z // | 10:32: field x is of type f64, but this expression generates a string // | 12:32: cannot find value x here // | 15:31: field x is of type f64, but this expression generates a string