[fine] Static methods I guess

This commit is contained in:
John Doty 2024-01-25 06:44:53 -08:00
parent bc57978dda
commit 19e57db724
6 changed files with 111 additions and 51 deletions

View file

@ -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));
}
}

View file

@ -66,6 +66,7 @@ pub struct MethodDecl {
pub name: Rc<str>,
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<Type>),
// 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<str>),
// 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<str>),
}
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<Type> {
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<Type> {
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<Type> {
@ -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;

View file

@ -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);
}

View file

@ -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
// |

View file

@ -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'?)
// | 4:6: cannot apply binary operator '+' to expressions of type 'class Foo' (on the left) and 'f64' (on the right)

View file

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