[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,
|
CompareFloat,
|
||||||
CompareString,
|
CompareString,
|
||||||
Discard,
|
Discard,
|
||||||
|
Dup,
|
||||||
FloatAdd,
|
FloatAdd,
|
||||||
FloatDivide,
|
FloatDivide,
|
||||||
FloatMultiply,
|
FloatMultiply,
|
||||||
|
|
@ -31,6 +32,8 @@ pub enum Instruction {
|
||||||
LoadFunction(usize),
|
LoadFunction(usize),
|
||||||
LoadLocal(usize),
|
LoadLocal(usize),
|
||||||
LoadModule(usize),
|
LoadModule(usize),
|
||||||
|
LoadSlot(usize),
|
||||||
|
NewObject(usize),
|
||||||
PushFalse,
|
PushFalse,
|
||||||
PushFloat(f64),
|
PushFloat(f64),
|
||||||
PushNothing,
|
PushNothing,
|
||||||
|
|
@ -41,8 +44,6 @@ pub enum Instruction {
|
||||||
StoreLocal(usize),
|
StoreLocal(usize),
|
||||||
StoreModule(usize),
|
StoreModule(usize),
|
||||||
StringAdd,
|
StringAdd,
|
||||||
Dup,
|
|
||||||
NewObject(usize),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Export {
|
pub enum Export {
|
||||||
|
|
@ -287,6 +288,9 @@ fn compile_expression(c: &mut Compiler, t: TreeRef) {
|
||||||
TreeKind::CallExpression => compile_call_expression(c, tree),
|
TreeKind::CallExpression => compile_call_expression(c, tree),
|
||||||
TreeKind::Block => compile_block_expression(c, tree),
|
TreeKind::Block => compile_block_expression(c, tree),
|
||||||
TreeKind::Argument => compile_argument(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"),
|
_ => ice!(c, t, "{tree:?} is not an expression, cannot compile"),
|
||||||
};
|
};
|
||||||
if matches!(cr, None) {
|
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 environment = c.semantics.environment_of(t);
|
||||||
let declaration = environment.bind(ident)?;
|
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
|
// TODO: Load function declaration. :P
|
||||||
let instruction = match declaration {
|
let instruction = match declaration {
|
||||||
Declaration::Variable {
|
Declaration::Variable {
|
||||||
|
|
@ -547,23 +555,7 @@ fn compile_identifier_expression(c: &mut Compiler, t: TreeRef, tree: &Tree) -> O
|
||||||
Instruction::LoadFunction(index)
|
Instruction::LoadFunction(index)
|
||||||
}
|
}
|
||||||
Declaration::ExternFunction { id, .. } => Instruction::LoadExternFunction(id.id()),
|
Declaration::ExternFunction { id, .. } => Instruction::LoadExternFunction(id.id()),
|
||||||
Declaration::Class { declaration, .. } => {
|
Declaration::Class { .. } => Instruction::Panic,
|
||||||
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)
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
c.push(instruction);
|
c.push(instruction);
|
||||||
|
|
@ -609,10 +601,98 @@ fn compile_argument(c: &mut Compiler, tree: &Tree) -> CR {
|
||||||
OK
|
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) {
|
fn compile_statement(c: &mut Compiler, t: TreeRef, gen_value: bool) {
|
||||||
let tree = &c.semantics.tree()[t];
|
let tree = &c.semantics.tree()[t];
|
||||||
let cr = match tree.kind {
|
let cr = match tree.kind {
|
||||||
TreeKind::FunctionDecl => compile_function_declaration(c, t, tree, gen_value),
|
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::LetStatement => compile_let_statement(c, t, tree, gen_value),
|
||||||
TreeKind::ExpressionStatement => compile_expression_statement(c, tree, gen_value),
|
TreeKind::ExpressionStatement => compile_expression_statement(c, tree, gen_value),
|
||||||
TreeKind::IfStatement => compile_if_statement(c, tree, gen_value),
|
TreeKind::IfStatement => compile_if_statement(c, tree, gen_value),
|
||||||
|
|
|
||||||
|
|
@ -150,6 +150,7 @@ pub enum TreeKind {
|
||||||
FieldList,
|
FieldList,
|
||||||
NewObjectExpression,
|
NewObjectExpression,
|
||||||
FieldValue,
|
FieldValue,
|
||||||
|
MemberAccess,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Tree<'a> {
|
pub struct Tree<'a> {
|
||||||
|
|
@ -794,6 +795,16 @@ fn expression_with_power(p: &mut CParser, minimum_power: u8) {
|
||||||
expression_with_power(p, power);
|
expression_with_power(p, power);
|
||||||
expr = p.end(m, TreeKind::BinaryExpression);
|
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) {
|
fn argument_list(p: &mut CParser) {
|
||||||
|
|
|
||||||
|
|
@ -585,19 +585,35 @@ impl<'a> Semantics<'a> {
|
||||||
match child {
|
match child {
|
||||||
Child::Tree(t) => {
|
Child::Tree(t) => {
|
||||||
let ct = &self.syntax_tree[*t];
|
let ct = &self.syntax_tree[*t];
|
||||||
if ct.kind == TreeKind::FunctionDecl {
|
match ct.kind {
|
||||||
// TODO: Should I have accessors for function decls?
|
TreeKind::FunctionDecl => {
|
||||||
let Some(name) = ct.nth_token(1) else {
|
// TODO: Should I have accessors for function decls?
|
||||||
continue;
|
let Some(name) = ct.nth_token(1) else {
|
||||||
};
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
environment.declarations.insert(
|
environment.declarations.insert(
|
||||||
name.as_str().into(),
|
name.as_str().into(),
|
||||||
Declaration::Function {
|
Declaration::Function {
|
||||||
declaration_type: self.type_of(*t),
|
declaration_type: self.type_of(*t),
|
||||||
declaration: *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::Error => Some(Type::Error),
|
||||||
TreeKind::UnaryExpression => self.type_of_unary(tree),
|
TreeKind::UnaryExpression => self.type_of_unary(tree),
|
||||||
TreeKind::BinaryExpression => self.type_of_binary(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::TypeParameter => self.type_of_type_parameter(tree),
|
||||||
TreeKind::Block => self.type_of_block(tree),
|
TreeKind::Block => self.type_of_block(tree),
|
||||||
TreeKind::LiteralExpression => self.type_of_literal(tree),
|
TreeKind::LiteralExpression => self.type_of_literal(tree),
|
||||||
|
|
@ -750,6 +766,7 @@ impl<'a> Semantics<'a> {
|
||||||
TreeKind::ConditionalExpression => self.type_of_conditional(tree),
|
TreeKind::ConditionalExpression => self.type_of_conditional(tree),
|
||||||
TreeKind::CallExpression => self.type_of_call(tree),
|
TreeKind::CallExpression => self.type_of_call(tree),
|
||||||
TreeKind::Argument => self.type_of_argument(tree),
|
TreeKind::Argument => self.type_of_argument(tree),
|
||||||
|
TreeKind::MemberAccess => self.type_of_member_access(tree),
|
||||||
|
|
||||||
TreeKind::LetStatement => Some(Type::Nothing),
|
TreeKind::LetStatement => Some(Type::Nothing),
|
||||||
TreeKind::ReturnStatement => Some(Type::Unreachable),
|
TreeKind::ReturnStatement => Some(Type::Unreachable),
|
||||||
|
|
@ -766,6 +783,8 @@ impl<'a> Semantics<'a> {
|
||||||
TreeKind::ListConstructor => self.type_of_list_constructor(t, tree),
|
TreeKind::ListConstructor => self.type_of_list_constructor(t, tree),
|
||||||
|
|
||||||
TreeKind::NewObjectExpression => self.type_of_new_object_expression(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"),
|
_ => 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);
|
assert_eq!(tree.kind, TreeKind::TypeExpression);
|
||||||
|
|
||||||
// TODO: This will *clearly* need to get better.
|
// 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}'"));
|
let environment = self.environment_of(t);
|
||||||
Some(Type::Error)
|
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)
|
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> {
|
fn type_of_expression_statement(&self, tree: &Tree) -> Option<Type> {
|
||||||
assert_eq!(tree.kind, TreeKind::ExpressionStatement);
|
assert_eq!(tree.kind, TreeKind::ExpressionStatement);
|
||||||
let last_is_semicolon = tree
|
let last_is_semicolon = tree
|
||||||
|
|
@ -1196,9 +1265,11 @@ impl<'a> Semantics<'a> {
|
||||||
Declaration::ExternFunction {
|
Declaration::ExternFunction {
|
||||||
declaration_type, ..
|
declaration_type, ..
|
||||||
} => declaration_type.clone(),
|
} => declaration_type.clone(),
|
||||||
Declaration::Class {
|
Declaration::Class { .. } => {
|
||||||
declaration_type, ..
|
// TODO: Test this case
|
||||||
} => declaration_type.clone(),
|
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)?))
|
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> {
|
fn type_of_list_constructor_element(&self, tree: &Tree) -> Option<Type> {
|
||||||
assert_eq!(tree.kind, TreeKind::ListConstructorElement);
|
assert_eq!(tree.kind, TreeKind::ListConstructorElement);
|
||||||
Some(self.type_of(tree.nth_tree(0)?))
|
Some(self.type_of(tree.nth_tree(0)?))
|
||||||
|
|
@ -1382,7 +1512,8 @@ pub fn check(s: &Semantics) {
|
||||||
| TreeKind::GroupingExpression
|
| TreeKind::GroupingExpression
|
||||||
| TreeKind::UnaryExpression
|
| TreeKind::UnaryExpression
|
||||||
| TreeKind::ConditionalExpression
|
| TreeKind::ConditionalExpression
|
||||||
| TreeKind::BinaryExpression => {
|
| TreeKind::BinaryExpression
|
||||||
|
| TreeKind::MemberAccess => {
|
||||||
let _ = s.type_of(t);
|
let _ = s.type_of(t);
|
||||||
}
|
}
|
||||||
TreeKind::CallExpression => {
|
TreeKind::CallExpression => {
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,10 @@ pub enum VMErrorCode {
|
||||||
FunctionOutOfRange(usize),
|
FunctionOutOfRange(usize),
|
||||||
#[error("internal error: stack type mismatch ({0:?} is not function)")]
|
#[error("internal error: stack type mismatch ({0:?} is not function)")]
|
||||||
StackExpectedFunction(StackValue),
|
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)]
|
#[derive(Debug)]
|
||||||
|
|
@ -45,6 +49,19 @@ pub struct Object {
|
||||||
values: Box<[StackValue]>,
|
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)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum StackValue {
|
pub enum StackValue {
|
||||||
Nothing,
|
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) {
|
fn push_value(&mut self, v: StackValue) {
|
||||||
self.stack.push(v)
|
self.stack.push(v)
|
||||||
}
|
}
|
||||||
|
|
@ -410,6 +434,10 @@ fn eval_one(
|
||||||
|
|
||||||
f.push_object(object.into());
|
f.push_object(object.into());
|
||||||
}
|
}
|
||||||
|
Instruction::LoadSlot(slot) => {
|
||||||
|
let obj = f.pop_object()?;
|
||||||
|
f.push_value(obj.get_slot(slot)?);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Flow::Continue)
|
Ok(Flow::Continue)
|
||||||
|
|
|
||||||
|
|
@ -9,5 +9,34 @@ fun test() -> f64 {
|
||||||
z
|
z
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ignore
|
|
||||||
// @no-errors
|
// @no-errors
|
||||||
|
// @eval: Float(7.0)
|
||||||
|
// @compiles-to:
|
||||||
|
// | function << module >> (0 args, 0 locals):
|
||||||
|
// | strings (0):
|
||||||
|
// | code (2):
|
||||||
|
// | 0: PushNothing
|
||||||
|
// | 1: Return
|
||||||
|
// | function Point (4 args, 0 locals):
|
||||||
|
// | strings (1):
|
||||||
|
// | 0: Point
|
||||||
|
// | code (5):
|
||||||
|
// | 0: LoadArgument(1)
|
||||||
|
// | 1: LoadArgument(0)
|
||||||
|
// | 2: PushString(0)
|
||||||
|
// | 3: NewObject(2)
|
||||||
|
// | 4: Return
|
||||||
|
// | function test (0 args, 2 locals):
|
||||||
|
// | strings (0):
|
||||||
|
// | code (10):
|
||||||
|
// | 0: PushFloat(23.0)
|
||||||
|
// | 1: PushFloat(7.0)
|
||||||
|
// | 2: LoadFunction(1)
|
||||||
|
// | 3: Call(2)
|
||||||
|
// | 4: StoreLocal(0)
|
||||||
|
// | 5: LoadLocal(0)
|
||||||
|
// | 6: LoadSlot(0)
|
||||||
|
// | 7: StoreLocal(1)
|
||||||
|
// | 8: LoadLocal(1)
|
||||||
|
// | 9: Return
|
||||||
|
// |
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue