[fine] Static methods I guess
This commit is contained in:
parent
bc57978dda
commit
19e57db724
6 changed files with 111 additions and 51 deletions
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
// |
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue