[fine] Classes!
It's kinda amazing this works actually
This commit is contained in:
parent
0ee89bf26b
commit
4505996710
5 changed files with 319 additions and 40 deletions
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -150,6 +150,7 @@ pub enum TreeKind {
|
|||
FieldList,
|
||||
NewObjectExpression,
|
||||
FieldValue,
|
||||
MemberAccess,
|
||||
}
|
||||
|
||||
pub struct Tree<'a> {
|
||||
|
|
@ -794,6 +795,16 @@ fn expression_with_power(p: &mut CParser, minimum_power: u8) {
|
|||
expression_with_power(p, power);
|
||||
expr = p.end(m, TreeKind::BinaryExpression);
|
||||
}
|
||||
|
||||
while p.at(TokenKind::Dot) {
|
||||
let m = p.start_before(expr);
|
||||
p.advance(); // Consume the dot
|
||||
p.expect(
|
||||
TokenKind::Identifier,
|
||||
"expected an identifier for member access",
|
||||
);
|
||||
expr = p.end(m, TreeKind::MemberAccess);
|
||||
}
|
||||
}
|
||||
|
||||
fn argument_list(p: &mut CParser) {
|
||||
|
|
|
|||
|
|
@ -585,19 +585,35 @@ impl<'a> Semantics<'a> {
|
|||
match child {
|
||||
Child::Tree(t) => {
|
||||
let ct = &self.syntax_tree[*t];
|
||||
if ct.kind == TreeKind::FunctionDecl {
|
||||
// TODO: Should I have accessors for function decls?
|
||||
let Some(name) = ct.nth_token(1) else {
|
||||
continue;
|
||||
};
|
||||
match ct.kind {
|
||||
TreeKind::FunctionDecl => {
|
||||
// TODO: Should I have accessors for function decls?
|
||||
let Some(name) = ct.nth_token(1) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
environment.declarations.insert(
|
||||
name.as_str().into(),
|
||||
Declaration::Function {
|
||||
declaration_type: self.type_of(*t),
|
||||
declaration: *t,
|
||||
},
|
||||
);
|
||||
environment.declarations.insert(
|
||||
name.as_str().into(),
|
||||
Declaration::Function {
|
||||
declaration_type: self.type_of(*t),
|
||||
declaration: *t,
|
||||
},
|
||||
);
|
||||
}
|
||||
TreeKind::ClassDecl => {
|
||||
let Some(name) = ct.nth_token(1) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
environment.declarations.insert(
|
||||
name.as_str().into(),
|
||||
Declaration::Class {
|
||||
declaration_type: self.type_of(*t),
|
||||
declaration: *t,
|
||||
},
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
|
|
@ -742,7 +758,7 @@ impl<'a> Semantics<'a> {
|
|||
TreeKind::Error => Some(Type::Error),
|
||||
TreeKind::UnaryExpression => self.type_of_unary(tree),
|
||||
TreeKind::BinaryExpression => self.type_of_binary(tree),
|
||||
TreeKind::TypeExpression => self.type_of_type_expr(tree),
|
||||
TreeKind::TypeExpression => self.type_of_type_expr(t, tree),
|
||||
TreeKind::TypeParameter => self.type_of_type_parameter(tree),
|
||||
TreeKind::Block => self.type_of_block(tree),
|
||||
TreeKind::LiteralExpression => self.type_of_literal(tree),
|
||||
|
|
@ -750,6 +766,7 @@ impl<'a> Semantics<'a> {
|
|||
TreeKind::ConditionalExpression => self.type_of_conditional(tree),
|
||||
TreeKind::CallExpression => self.type_of_call(tree),
|
||||
TreeKind::Argument => self.type_of_argument(tree),
|
||||
TreeKind::MemberAccess => self.type_of_member_access(tree),
|
||||
|
||||
TreeKind::LetStatement => Some(Type::Nothing),
|
||||
TreeKind::ReturnStatement => Some(Type::Unreachable),
|
||||
|
|
@ -766,6 +783,8 @@ impl<'a> Semantics<'a> {
|
|||
TreeKind::ListConstructor => self.type_of_list_constructor(t, tree),
|
||||
|
||||
TreeKind::NewObjectExpression => self.type_of_new_object_expression(tree),
|
||||
TreeKind::FieldValue => self.type_of_field_value(t, tree),
|
||||
TreeKind::ClassDecl => self.type_of_class_decl(t, tree),
|
||||
|
||||
_ => self.internal_compiler_error(Some(t), "asking for a nonsense type"),
|
||||
};
|
||||
|
|
@ -935,7 +954,7 @@ impl<'a> Semantics<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn type_of_type_expr(&self, tree: &Tree) -> Option<Type> {
|
||||
fn type_of_type_expr(&self, t: TreeRef, tree: &Tree) -> Option<Type> {
|
||||
assert_eq!(tree.kind, TreeKind::TypeExpression);
|
||||
|
||||
// TODO: This will *clearly* need to get better.
|
||||
|
|
@ -958,8 +977,30 @@ impl<'a> Semantics<'a> {
|
|||
}
|
||||
}
|
||||
_ => {
|
||||
self.report_error_tree(tree, format!("Unrecognized type: '{token}'"));
|
||||
Some(Type::Error)
|
||||
let environment = self.environment_of(t);
|
||||
match environment.bind(token) {
|
||||
Some(Declaration::Class {
|
||||
declaration_type, ..
|
||||
}) => Some(declaration_type.clone()),
|
||||
Some(Declaration::Variable { .. }) => {
|
||||
self.report_error_tree(
|
||||
tree,
|
||||
format!("'{token}' is a variable and cannot be used as a type"),
|
||||
);
|
||||
Some(Type::Error)
|
||||
}
|
||||
Some(Declaration::Function { .. } | Declaration::ExternFunction { .. }) => {
|
||||
self.report_error_tree(
|
||||
tree,
|
||||
format!("'{token}' is a function and cannot be used as a type"),
|
||||
);
|
||||
Some(Type::Error)
|
||||
}
|
||||
None => {
|
||||
self.report_error_tree(tree, format!("Unrecognized type: '{token}'"));
|
||||
Some(Type::Error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1157,6 +1198,34 @@ impl<'a> Semantics<'a> {
|
|||
Some(result)
|
||||
}
|
||||
|
||||
fn type_of_member_access(&self, tree: &Tree) -> Option<Type> {
|
||||
assert_eq!(tree.kind, TreeKind::MemberAccess);
|
||||
|
||||
let op = tree.nth_token(1)?;
|
||||
let member = tree.nth_token(2)?;
|
||||
let typ = self.type_of(tree.nth_tree(0)?);
|
||||
match &typ {
|
||||
Type::Class(c) => {
|
||||
// TODO: Accelerate?
|
||||
if let Some(field) = c.fields.iter().find(|f| &*f.name == member.as_str()) {
|
||||
Some(field.field_type.clone())
|
||||
} else {
|
||||
self.report_error(
|
||||
op.start,
|
||||
format!("'{typ}' does not have a member named '{member}'"),
|
||||
);
|
||||
Some(Type::Error)
|
||||
}
|
||||
}
|
||||
Type::Error => Some(Type::Error),
|
||||
_ => {
|
||||
// TODO: This is probably wrong, yeah?
|
||||
self.report_error(op.start, format!("cannot access members of '{typ}'"));
|
||||
Some(Type::Error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn type_of_expression_statement(&self, tree: &Tree) -> Option<Type> {
|
||||
assert_eq!(tree.kind, TreeKind::ExpressionStatement);
|
||||
let last_is_semicolon = tree
|
||||
|
|
@ -1196,9 +1265,11 @@ impl<'a> Semantics<'a> {
|
|||
Declaration::ExternFunction {
|
||||
declaration_type, ..
|
||||
} => declaration_type.clone(),
|
||||
Declaration::Class {
|
||||
declaration_type, ..
|
||||
} => declaration_type.clone(),
|
||||
Declaration::Class { .. } => {
|
||||
// TODO: Test this case
|
||||
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
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -1266,6 +1337,65 @@ impl<'a> Semantics<'a> {
|
|||
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);
|
||||
|
||||
// TODO: Field types need to be lazy because of recursion.
|
||||
|
||||
let name = tree.nth_token(1)?;
|
||||
let mut fields = Vec::new();
|
||||
for field in tree.children_of_kind(self.syntax_tree, TreeKind::FieldDecl) {
|
||||
let f = &self.syntax_tree[field];
|
||||
let field_name = f.nth_token(0)?;
|
||||
fields.push(FieldDecl {
|
||||
name: field_name.as_str().into(),
|
||||
field_type: self.type_of(f.nth_tree(2)?),
|
||||
});
|
||||
}
|
||||
|
||||
let cd = ClassDecl {
|
||||
name: name.as_str().into(),
|
||||
fields: fields.into(),
|
||||
decl_tree: t,
|
||||
};
|
||||
|
||||
Some(Type::Class(cd.into()))
|
||||
}
|
||||
|
||||
fn type_of_field_value(&self, t: TreeRef, tree: &Tree) -> Option<Type> {
|
||||
assert_eq!(tree.kind, TreeKind::FieldValue);
|
||||
|
||||
if let Some(colon) = tree.nth_token(1) {
|
||||
if colon.kind == TokenKind::Colon {
|
||||
// Form 1: { x: e, ... }
|
||||
return Some(self.type_of(tree.nth_tree(2)?));
|
||||
}
|
||||
}
|
||||
|
||||
// Form 2: { x, ... }
|
||||
let environment = self.environment_of(t);
|
||||
let id = tree.nth_token(0)?;
|
||||
match environment.bind(id)? {
|
||||
Declaration::Variable {
|
||||
declaration_type, ..
|
||||
}
|
||||
| Declaration::Function {
|
||||
declaration_type, ..
|
||||
}
|
||||
| Declaration::ExternFunction {
|
||||
declaration_type, ..
|
||||
} => Some(declaration_type.clone()),
|
||||
|
||||
Declaration::Class { .. } => {
|
||||
self.report_error_tree(
|
||||
tree,
|
||||
format!("`{id}` is a class, and cannot be the value of a field"),
|
||||
);
|
||||
Some(Type::Error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn type_of_list_constructor_element(&self, tree: &Tree) -> Option<Type> {
|
||||
assert_eq!(tree.kind, TreeKind::ListConstructorElement);
|
||||
Some(self.type_of(tree.nth_tree(0)?))
|
||||
|
|
@ -1382,7 +1512,8 @@ pub fn check(s: &Semantics) {
|
|||
| TreeKind::GroupingExpression
|
||||
| TreeKind::UnaryExpression
|
||||
| TreeKind::ConditionalExpression
|
||||
| TreeKind::BinaryExpression => {
|
||||
| TreeKind::BinaryExpression
|
||||
| TreeKind::MemberAccess => {
|
||||
let _ = s.type_of(t);
|
||||
}
|
||||
TreeKind::CallExpression => {
|
||||
|
|
|
|||
|
|
@ -29,6 +29,10 @@ pub enum VMErrorCode {
|
|||
FunctionOutOfRange(usize),
|
||||
#[error("internal error: stack type mismatch ({0:?} is not function)")]
|
||||
StackExpectedFunction(StackValue),
|
||||
#[error("internal error: stack type mismatch ({0:?} is not object)")]
|
||||
StackExpectedObject(StackValue),
|
||||
#[error("internal error: slot {0} was out of range for object (type {1} with {2} slots)")]
|
||||
SlotOutOfRange(usize, Rc<str>, usize),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
@ -45,6 +49,19 @@ pub struct Object {
|
|||
values: Box<[StackValue]>,
|
||||
}
|
||||
|
||||
impl Object {
|
||||
pub fn get_slot(&self, index: usize) -> Result<StackValue> {
|
||||
match self.values.get(index) {
|
||||
Some(v) => Ok(v.clone()),
|
||||
None => Err(VMErrorCode::SlotOutOfRange(
|
||||
index,
|
||||
self.name.clone(),
|
||||
self.values.len(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum StackValue {
|
||||
Nothing,
|
||||
|
|
@ -109,6 +126,13 @@ impl Frame {
|
|||
}
|
||||
}
|
||||
|
||||
fn pop_object(&mut self) -> Result<Rc<Object>> {
|
||||
match self.pop_value()? {
|
||||
StackValue::Object(v) => Ok(v),
|
||||
v => Err(VMErrorCode::StackExpectedObject(v)),
|
||||
}
|
||||
}
|
||||
|
||||
fn push_value(&mut self, v: StackValue) {
|
||||
self.stack.push(v)
|
||||
}
|
||||
|
|
@ -410,6 +434,10 @@ fn eval_one(
|
|||
|
||||
f.push_object(object.into());
|
||||
}
|
||||
Instruction::LoadSlot(slot) => {
|
||||
let obj = f.pop_object()?;
|
||||
f.push_value(obj.get_slot(slot)?);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Flow::Continue)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue