[fine] Classes!

It's kinda amazing this works actually
This commit is contained in:
John Doty 2024-01-20 11:03:33 -08:00
parent 0ee89bf26b
commit 4505996710
5 changed files with 319 additions and 40 deletions

View file

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