[fine] WIP: Classes

This commit is contained in:
John Doty 2024-01-20 08:56:53 -08:00
parent e6c96fde38
commit 0ee89bf26b
7 changed files with 370 additions and 59 deletions

View file

@ -42,6 +42,7 @@ pub enum Instruction {
StoreModule(usize), StoreModule(usize),
StringAdd, StringAdd,
Dup, Dup,
NewObject(usize),
} }
pub enum Export { pub enum Export {
@ -488,6 +489,7 @@ fn compile_binary_expression(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR {
Declaration::ExternFunction { .. } => Instruction::Panic, Declaration::ExternFunction { .. } => Instruction::Panic,
Declaration::Function { .. } => Instruction::Panic, Declaration::Function { .. } => Instruction::Panic,
Declaration::Class { .. } => Instruction::Panic,
}; };
c.push(instruction); c.push(instruction);
} }
@ -545,6 +547,23 @@ 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, .. } => {
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);
@ -705,13 +724,58 @@ fn compile_function_declaration(c: &mut Compiler, t: TreeRef, tree: &Tree, gen_v
OK OK
} }
fn compile_class_declaration(c: &mut Compiler, t: TreeRef, tree: &Tree, gen_value: bool) -> CR {
// Only compile a given function once.
// Classes get compiled as constructor functions which get called.
let fk = FunctionKey { tree: t };
if !c.function_bindings.contains_key(&fk) {
let name = tree.nth_token(1)?;
let field_count = tree.children.len() - 2;
let function_index = c.temp_functions.len();
c.temp_functions.push(None);
c.pending_functions.push((
fk.clone(),
function_index,
Function::new(name.as_str(), field_count),
));
c.function_bindings.insert(fk, function_index);
c.module
.exports
.insert(name.to_string(), Export::Function(function_index));
}
if gen_value {
c.push(Instruction::PushNothing);
}
OK
}
fn compile_function(c: &mut Compiler, t: TreeRef) -> CR { fn compile_function(c: &mut Compiler, t: TreeRef) -> CR {
let tree = &c.syntax[t]; let tree = &c.syntax[t];
let block = tree.child_of_kind(c.syntax, TreeKind::Block)?; match tree.kind {
TreeKind::FunctionDecl => {
let block = tree.child_of_kind(c.syntax, TreeKind::Block)?;
compile_expression(c, block);
}
TreeKind::ClassDecl => {
let count = tree.children_of_kind(c.syntax, TreeKind::FieldDecl).count();
for i in 0..count {
c.push(Instruction::LoadArgument(count - 1 - i));
}
let name = tree.nth_token(1)?.as_str();
let name_index = c.add_string(name.to_string());
c.push(Instruction::PushString(name_index));
c.push(Instruction::NewObject(count));
}
_ => ice!(c, t, "what is this tree doing in compile_function?"),
}
compile_expression(c, block);
c.push(Instruction::Return); c.push(Instruction::Return);
OK OK
} }

View file

@ -125,8 +125,10 @@ pub enum TreeKind {
BinaryExpression, BinaryExpression,
Block, Block,
CallExpression, CallExpression,
ClassDecl,
ConditionalExpression, ConditionalExpression,
ExpressionStatement, ExpressionStatement,
FieldDecl,
File, File,
ForStatement, ForStatement,
FunctionDecl, FunctionDecl,
@ -145,6 +147,9 @@ pub enum TreeKind {
TypeParameter, TypeParameter,
TypeParameterList, TypeParameterList,
UnaryExpression, UnaryExpression,
FieldList,
NewObjectExpression,
FieldValue,
} }
pub struct Tree<'a> { pub struct Tree<'a> {
@ -183,14 +188,17 @@ impl<'a> Tree<'a> {
}) })
} }
pub fn children_of_kind<'b>(
&'b self,
s: &'b SyntaxTree,
kind: TreeKind,
) -> impl Iterator<Item = TreeRef> + 'b {
self.child_trees()
.filter_map(move |t| if s[t].kind == kind { Some(t) } else { None })
}
pub fn child_of_kind(&self, s: &SyntaxTree, kind: TreeKind) -> Option<TreeRef> { pub fn child_of_kind(&self, s: &SyntaxTree, kind: TreeKind) -> Option<TreeRef> {
self.children self.children_of_kind(&s, kind).next()
.iter()
.filter_map(|c| match c {
Child::Tree(t) => Some(*t),
_ => None,
})
.find(|c| s[*c].kind == kind)
} }
pub fn child_tree_of_kind<'b>( pub fn child_tree_of_kind<'b>(
@ -400,6 +408,10 @@ impl<'a> CParser<'a> {
self.error(error); self.error(error);
} }
fn expect_start(&mut self, kind: TokenKind) {
assert!(self.eat(kind));
}
fn advance_with_error<T>(&mut self, error: T) -> MarkClosed fn advance_with_error<T>(&mut self, error: T) -> MarkClosed
where where
T: Into<String>, T: Into<String>,
@ -498,6 +510,7 @@ fn file(p: &mut CParser) {
while !p.eof() { while !p.eof() {
match p.peek() { match p.peek() {
TokenKind::Fun => function(p), TokenKind::Fun => function(p),
TokenKind::Class => class(p),
_ => statement(p), _ => statement(p),
} }
} }
@ -505,10 +518,9 @@ fn file(p: &mut CParser) {
} }
fn function(p: &mut CParser) { fn function(p: &mut CParser) {
assert!(p.at(TokenKind::Fun));
let m = p.start(); let m = p.start();
p.expect(TokenKind::Fun, "expected a function to start with 'fun'"); p.expect_start(TokenKind::Fun);
p.expect(TokenKind::Identifier, "expected a function name"); p.expect(TokenKind::Identifier, "expected a function name");
if p.at(TokenKind::LeftParen) { if p.at(TokenKind::LeftParen) {
param_list(p); param_list(p);
@ -523,11 +535,40 @@ fn function(p: &mut CParser) {
p.end(m, TreeKind::FunctionDecl); p.end(m, TreeKind::FunctionDecl);
} }
fn param_list(p: &mut CParser) { fn class(p: &mut CParser) {
assert!(p.at(TokenKind::LeftParen));
let m = p.start(); let m = p.start();
p.expect(TokenKind::LeftParen, "expect '(' to start a parameter list"); p.expect_start(TokenKind::Class);
p.expect(TokenKind::Identifier, "expected a class name");
if p.eat(TokenKind::LeftBrace) {
while !p.at(TokenKind::RightBrace) && !p.eof() {
field_decl(p);
}
}
p.expect(TokenKind::RightBrace, "expected a class to end with a '}'");
p.end(m, TreeKind::ClassDecl);
}
fn field_decl(p: &mut CParser) {
let m = p.start();
p.expect(TokenKind::Identifier, "expected a field name");
if p.eat(TokenKind::Colon) {
type_expr(p);
}
p.expect(
TokenKind::Semicolon,
"expect a ';' after field declarations",
);
p.end(m, TreeKind::FieldDecl);
}
fn param_list(p: &mut CParser) {
let m = p.start();
p.expect_start(TokenKind::LeftParen);
while !p.at(TokenKind::RightParen) && !p.eof() { while !p.at(TokenKind::RightParen) && !p.eof() {
if p.at(TokenKind::Identifier) { if p.at(TokenKind::Identifier) {
parameter(p); parameter(p);
@ -541,12 +582,9 @@ fn param_list(p: &mut CParser) {
} }
fn parameter(p: &mut CParser) { fn parameter(p: &mut CParser) {
assert!(p.at(TokenKind::Identifier));
let m = p.start(); let m = p.start();
p.expect(
TokenKind::Identifier, p.expect_start(TokenKind::Identifier);
"expected an identifier for a parameter name",
);
if p.eat(TokenKind::Colon) { if p.eat(TokenKind::Colon) {
type_expr(p); type_expr(p);
} }
@ -558,13 +596,9 @@ fn parameter(p: &mut CParser) {
} }
fn return_type(p: &mut CParser) { fn return_type(p: &mut CParser) {
assert!(p.at(TokenKind::Arrow));
let m = p.start(); let m = p.start();
p.expect( p.expect_start(TokenKind::Arrow);
TokenKind::Arrow,
"function return type starts with an arrow",
);
type_expr(p); type_expr(p);
p.end(m, TreeKind::ReturnType); p.end(m, TreeKind::ReturnType);
@ -584,10 +618,9 @@ fn type_expr(p: &mut CParser) {
} }
fn type_parameter_list(p: &mut CParser) { fn type_parameter_list(p: &mut CParser) {
assert!(p.at(TokenKind::Less));
let m = p.start(); let m = p.start();
p.expect(TokenKind::Less, "expected < to start type parameter list"); p.expect_start(TokenKind::Less);
while !p.at(TokenKind::Greater) && !p.eof() { while !p.at(TokenKind::Greater) && !p.eof() {
if p.at(TokenKind::Identifier) { if p.at(TokenKind::Identifier) {
type_parameter(p); type_parameter(p);
@ -613,10 +646,9 @@ fn type_parameter(p: &mut CParser) {
} }
fn block(p: &mut CParser) { fn block(p: &mut CParser) {
assert!(p.at(TokenKind::LeftBrace));
let m = p.start(); let m = p.start();
p.expect(TokenKind::LeftBrace, "expect '{' to start a block"); p.expect_start(TokenKind::LeftBrace);
while !p.at(TokenKind::RightBrace) && !p.eof() { while !p.at(TokenKind::RightBrace) && !p.eof() {
statement(p); statement(p);
} }
@ -650,10 +682,9 @@ fn statement_if(p: &mut CParser) {
} }
fn statement_let(p: &mut CParser) { fn statement_let(p: &mut CParser) {
assert!(p.at(TokenKind::Let));
let m = p.start(); let m = p.start();
p.expect(TokenKind::Let, "expect 'let' to start a let statement"); p.expect_start(TokenKind::Let);
p.expect(TokenKind::Identifier, "expected a name for the variable"); p.expect(TokenKind::Identifier, "expected a name for the variable");
p.expect(TokenKind::Equal, "expected a '=' after the variable name"); p.expect(TokenKind::Equal, "expected a '=' after the variable name");
expression(p); expression(p);
@ -665,13 +696,9 @@ fn statement_let(p: &mut CParser) {
} }
fn statement_return(p: &mut CParser) { fn statement_return(p: &mut CParser) {
assert!(p.at(TokenKind::Return));
let m = p.start(); let m = p.start();
p.expect( p.expect_start(TokenKind::Return);
TokenKind::Return,
"expect 'return' to start a return statement",
);
expression(p); expression(p);
if !p.at(TokenKind::RightBrace) { if !p.at(TokenKind::RightBrace) {
p.expect(TokenKind::Semicolon, "expect ';' to end a return statement"); p.expect(TokenKind::Semicolon, "expect ';' to end a return statement");
@ -681,10 +708,9 @@ fn statement_return(p: &mut CParser) {
} }
fn statement_for(p: &mut CParser) { fn statement_for(p: &mut CParser) {
assert!(p.at(TokenKind::For));
let m = p.start(); let m = p.start();
p.expect(TokenKind::For, "expect a for to start a for loop"); p.expect_start(TokenKind::For);
p.expect( p.expect(
TokenKind::Identifier, TokenKind::Identifier,
"expected an identifier for the loop variable", "expected an identifier for the loop variable",
@ -771,13 +797,9 @@ fn expression_with_power(p: &mut CParser, minimum_power: u8) {
} }
fn argument_list(p: &mut CParser) { fn argument_list(p: &mut CParser) {
assert!(p.at(TokenKind::LeftParen));
let m = p.start(); let m = p.start();
p.expect( p.expect_start(TokenKind::LeftParen);
TokenKind::LeftParen,
"expect an argument list to start with '('",
);
while !p.at(TokenKind::RightParen) && !p.eof() { while !p.at(TokenKind::RightParen) && !p.eof() {
argument(p); argument(p);
} }
@ -818,6 +840,8 @@ fn prefix_expression(p: &mut CParser) -> MarkClosed {
TokenKind::LeftBracket => list_constructor(p), TokenKind::LeftBracket => list_constructor(p),
TokenKind::New => object_constructor(p),
_ => p.advance_with_error("expected an expression"), _ => p.advance_with_error("expected an expression"),
} }
} }
@ -829,10 +853,9 @@ fn literal(p: &mut CParser) -> MarkClosed {
} }
fn grouping(p: &mut CParser) -> MarkClosed { fn grouping(p: &mut CParser) -> MarkClosed {
assert!(p.at(TokenKind::LeftParen));
let m = p.start(); let m = p.start();
p.expect(TokenKind::LeftParen, "expected '(' to start grouping"); p.expect_start(TokenKind::LeftParen);
expression(p); expression(p);
p.expect(TokenKind::RightParen, "unmatched parentheses in expression"); p.expect(TokenKind::RightParen, "unmatched parentheses in expression");
@ -849,10 +872,9 @@ fn unary(p: &mut CParser) -> MarkClosed {
} }
fn conditional(p: &mut CParser) -> MarkClosed { fn conditional(p: &mut CParser) -> MarkClosed {
assert!(p.at(TokenKind::If));
let m = p.start(); let m = p.start();
p.expect(TokenKind::If, "expected conditional to start with 'if'"); p.expect_start(TokenKind::If);
expression(p); expression(p);
block(p); block(p);
if p.eat(TokenKind::Else) { if p.eat(TokenKind::Else) {
@ -877,13 +899,9 @@ fn identifier(p: &mut CParser) -> MarkClosed {
} }
fn list_constructor(p: &mut CParser) -> MarkClosed { fn list_constructor(p: &mut CParser) -> MarkClosed {
assert!(p.at(TokenKind::LeftBracket));
let m = p.start(); let m = p.start();
p.expect( p.expect_start(TokenKind::LeftBracket);
TokenKind::LeftBracket,
"expect a list constructor to start with [",
);
while !p.at(TokenKind::RightBracket) && !p.eof() { while !p.at(TokenKind::RightBracket) && !p.eof() {
list_constructor_element(p); list_constructor_element(p);
} }
@ -909,6 +927,49 @@ fn list_constructor_element(p: &mut CParser) {
p.end(m, TreeKind::ListConstructorElement); p.end(m, TreeKind::ListConstructorElement);
} }
fn object_constructor(p: &mut CParser) -> MarkClosed {
let m = p.start();
p.expect_start(TokenKind::New);
type_expr(p);
if p.at(TokenKind::LeftBrace) {
field_list(p);
} else {
p.error("expected a '{' to start the field list after the class type");
}
p.end(m, TreeKind::NewObjectExpression)
}
fn field_list(p: &mut CParser) {
let m = p.start();
p.expect_start(TokenKind::LeftBrace);
while !p.at(TokenKind::RightBrace) && !p.eof() {
field_value(p);
}
p.expect(
TokenKind::RightBrace,
"expected the field list to end with '}'",
);
p.end(m, TreeKind::FieldList);
}
fn field_value(p: &mut CParser) {
let m = p.start();
p.expect(TokenKind::Identifier, "expected a field name");
if p.eat(TokenKind::Colon) {
expression(p);
}
if !p.at(TokenKind::RightBrace) {
p.expect(TokenKind::Comma, "expect a ',' between fields");
}
p.end(m, TreeKind::FieldValue);
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View file

@ -57,6 +57,17 @@ impl fmt::Display for Error {
} }
} }
pub struct FieldDecl {
pub name: Rc<str>,
pub field_type: Type,
}
pub struct ClassDecl {
pub name: Rc<str>,
pub fields: Vec<FieldDecl>,
pub decl_tree: TreeRef,
}
#[derive(Clone)] #[derive(Clone)]
pub enum Type { pub enum Type {
// Signals a type error. If you receive this then you know that an error // Signals a type error. If you receive this then you know that an error
@ -91,6 +102,7 @@ pub enum Type {
Function(Vec<Box<Type>>, Box<Type>), Function(Vec<Box<Type>>, Box<Type>),
List(Box<Type>), List(Box<Type>),
Class(Rc<ClassDecl>),
} }
impl Type { impl Type {
@ -136,6 +148,7 @@ impl fmt::Display for Type {
// TODO: Better names // TODO: Better names
TypeVariable(_) => write!(f, "$_"), TypeVariable(_) => write!(f, "$_"),
List(t) => write!(f, "list<{t}>"), List(t) => write!(f, "list<{t}>"),
Class(c) => write!(f, "class {}", c.name),
} }
} }
} }
@ -173,6 +186,10 @@ pub enum Declaration {
declaration_type: Type, declaration_type: Type,
id: ExternalFunctionId, id: ExternalFunctionId,
}, },
Class {
declaration_type: Type,
declaration: TreeRef, //?
},
} }
pub struct Environment { pub struct Environment {
@ -404,7 +421,9 @@ impl<'a> Semantics<'a> {
} }
pub fn snapshot_errors(&self) -> Vec<Error> { pub fn snapshot_errors(&self) -> Vec<Error> {
(*self.errors.borrow()).clone() let mut result = (*self.errors.borrow()).clone();
result.sort_by(|a, b| a.start.0.cmp(&b.start.0));
result
} }
pub fn logical_parent(&self, tr: TreeRef) -> Option<TreeRef> { pub fn logical_parent(&self, tr: TreeRef) -> Option<TreeRef> {
@ -681,6 +700,16 @@ impl<'a> Semantics<'a> {
.all(|(a, b)| self.type_compat(a, b)) .all(|(a, b)| self.type_compat(a, b))
} }
(Type::Class(ca), Type::Class(cb)) => {
// TODO: If we were doing structural comparisons here...
// maybe? MAYBE?
//
// Like if this is directional we can look for field
// subsets { ..:int, ..:int } can be { ..:int } etc.
//
ca.decl_tree == cb.decl_tree
}
// Avoid introducing more errors // Avoid introducing more errors
(Type::Error, _) => true, (Type::Error, _) => true,
(_, Type::Error) => true, (_, Type::Error) => true,
@ -736,6 +765,8 @@ impl<'a> Semantics<'a> {
TreeKind::ListConstructorElement => self.type_of_list_constructor_element(tree), TreeKind::ListConstructorElement => self.type_of_list_constructor_element(tree),
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),
_ => self.internal_compiler_error(Some(t), "asking for a nonsense type"), _ => self.internal_compiler_error(Some(t), "asking for a nonsense type"),
}; };
@ -865,6 +896,13 @@ impl<'a> Semantics<'a> {
); );
return Some(Type::Error); return Some(Type::Error);
} }
Declaration::Class { .. } => {
self.report_error_tree_ref(
left_tree,
"cannot assign a new value to a class declaration",
);
return Some(Type::Error);
}
}, },
None => { None => {
self.report_error_tree_ref( self.report_error_tree_ref(
@ -1041,6 +1079,13 @@ impl<'a> Semantics<'a> {
fn type_of_call(&self, tree: &Tree) -> Option<Type> { fn type_of_call(&self, tree: &Tree) -> Option<Type> {
assert_eq!(tree.kind, TreeKind::CallExpression); assert_eq!(tree.kind, TreeKind::CallExpression);
// TODO: Move the vast majority of error checking out of this
// function: once you know that the 0th tree (the function
// expression) yields a function type, assume the type of the
// call is the type of the function return. Don't bother
// matching argument types &c; do that in an explicit
// check_call_expression function below.
let f_ref = tree.nth_tree(0)?; let f_ref = tree.nth_tree(0)?;
let f = self.type_of(f_ref); let f = self.type_of(f_ref);
@ -1151,6 +1196,9 @@ impl<'a> Semantics<'a> {
Declaration::ExternFunction { Declaration::ExternFunction {
declaration_type, .. declaration_type, ..
} => declaration_type.clone(), } => declaration_type.clone(),
Declaration::Class {
declaration_type, ..
} => declaration_type.clone(),
}); });
} }
@ -1211,6 +1259,13 @@ impl<'a> Semantics<'a> {
Some(Type::List(Box::new(element_type))) Some(Type::List(Box::new(element_type)))
} }
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.
Some(self.type_of(tree.nth_tree(1)?))
}
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)?))
@ -1280,6 +1335,12 @@ impl<'a> Semantics<'a> {
} => { } => {
eprintln!("{declaration_type:?} (extern {id:?})"); eprintln!("{declaration_type:?} (extern {id:?})");
} }
Declaration::Class {
declaration_type,
declaration,
} => {
eprintln!("{declaration_type:?} (class {declaration:?})");
}
}; };
} }
environment = env.parent.clone(); environment = env.parent.clone();
@ -1321,10 +1382,13 @@ pub fn check(s: &Semantics) {
| TreeKind::GroupingExpression | TreeKind::GroupingExpression
| TreeKind::UnaryExpression | TreeKind::UnaryExpression
| TreeKind::ConditionalExpression | TreeKind::ConditionalExpression
| TreeKind::CallExpression
| TreeKind::BinaryExpression => { | TreeKind::BinaryExpression => {
let _ = s.type_of(t); let _ = s.type_of(t);
} }
TreeKind::CallExpression => {
let _ = s.type_of(t);
}
TreeKind::ArgumentList => {} TreeKind::ArgumentList => {}
TreeKind::Argument => { TreeKind::Argument => {
let _ = s.type_of(t); let _ = s.type_of(t);
@ -1343,6 +1407,12 @@ pub fn check(s: &Semantics) {
let _ = s.type_of(t); let _ = s.type_of(t);
} }
TreeKind::ForStatement => check_for_statement(s, t), TreeKind::ForStatement => check_for_statement(s, t),
TreeKind::ClassDecl => {}
TreeKind::FieldDecl => {}
TreeKind::FieldList => {}
TreeKind::NewObjectExpression => check_new_object_expression(s, tree),
TreeKind::FieldValue => {}
} }
} }
} }
@ -1426,6 +1496,72 @@ fn check_for_statement(s: &Semantics, t: TreeRef) {
let _ = s.environment_of(t); let _ = s.environment_of(t);
} }
// TODO: TEST: Check mutual recursion with function calls
// TODO: TEST: Missing fields
// TODO: TEST: Extra fields
// TODO: TEST: Existing and type mismatch
fn check_new_object_expression(s: &Semantics, tree: &Tree) {
let Some(type_expression) = tree.nth_tree(1) else {
return;
};
let Some(field_list) = tree.child_tree_of_kind(s.syntax_tree, TreeKind::FieldList) else {
return;
};
let class_type = s.type_of(type_expression);
match &class_type {
Type::Class(c) => {
let mut any_errors = false;
let mut field_bindings = HashMap::new();
for field in field_list.children_of_kind(s.syntax_tree, TreeKind::FieldValue) {
let f = &s.syntax_tree[field];
if let Some(name) = f.nth_token(0) {
let field_type = s.type_of(field);
field_bindings.insert(name.as_str(), (field, field_type));
} else {
any_errors = true;
}
}
// Check individual bindings...
for f in c.fields.iter() {
if let Some((field_tree, expr_type)) = field_bindings.get(&*f.name) {
if !s.type_compat(&f.field_type, expr_type) {
s.report_error_tree_ref(
*field_tree,
format!(
"field {} has is of type {}, but this expression generates a {}",
f.name, f.field_type, expr_type,
),
);
}
field_bindings.remove(&*f.name);
} else if !any_errors {
s.report_error_tree(
tree,
format!("missing an initializer for field {}", f.name),
);
}
}
if !any_errors {
for (n, (field_tree, _)) in field_bindings.iter() {
s.report_error_tree_ref(
*field_tree,
format!("{} does not have a field named {}", class_type, n),
);
}
}
}
Type::Error => (),
ct => s.report_error_tree_ref(
type_expression,
format!("expected this to be a class type, but it is {ct}"),
),
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View file

@ -49,6 +49,7 @@ pub enum TokenKind {
Import, Import,
In, In,
Let, Let,
New,
Or, Or,
Return, Return,
Select, Select,
@ -302,6 +303,11 @@ impl<'a> Tokens<'a> {
return TokenKind::Let; return TokenKind::Let;
} }
} }
'n' => {
if ident == "new" {
return TokenKind::New;
}
}
'o' => { 'o' => {
if ident == "or" { if ident == "or" {
return TokenKind::Or; return TokenKind::Or;
@ -573,7 +579,7 @@ mod tests {
test_tokens!( test_tokens!(
more_keywords, more_keywords,
"fun if import let return select this true while truewhile", "fun if import let return select this true while truewhile new",
(0, Fun, "fun"), (0, Fun, "fun"),
(4, If, "if"), (4, If, "if"),
(7, Import, "import"), (7, Import, "import"),
@ -583,7 +589,8 @@ mod tests {
(32, This, "this"), (32, This, "this"),
(37, True, "true"), (37, True, "true"),
(42, While, "while"), (42, While, "while"),
(48, Identifier, "truewhile") (48, Identifier, "truewhile"),
(58, New, "new")
); );
test_tokens!( test_tokens!(

View file

@ -39,6 +39,12 @@ pub struct VMError {
type Result<T> = std::result::Result<T, VMErrorCode>; type Result<T> = std::result::Result<T, VMErrorCode>;
#[derive(Clone, Debug)]
pub struct Object {
name: Rc<str>,
values: Box<[StackValue]>,
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum StackValue { pub enum StackValue {
Nothing, Nothing,
@ -47,6 +53,7 @@ pub enum StackValue {
String(Rc<str>), String(Rc<str>),
Function(Rc<Function>), Function(Rc<Function>),
ExternFunction(usize), ExternFunction(usize),
Object(Rc<Object>),
} }
enum FuncValue { enum FuncValue {
@ -130,6 +137,10 @@ impl Frame {
self.push_value(StackValue::ExternFunction(v)); self.push_value(StackValue::ExternFunction(v));
} }
fn push_object(&mut self, v: Rc<Object>) {
self.push_value(StackValue::Object(v));
}
fn get_argument(&self, i: usize) -> Result<StackValue> { fn get_argument(&self, i: usize) -> Result<StackValue> {
self.args self.args
.get(i) .get(i)
@ -384,6 +395,21 @@ fn eval_one(
let y = f.pop_string()?; let y = f.pop_string()?;
f.push_bool(x == y); f.push_bool(x == y);
} }
Instruction::NewObject(slots) => {
let name = f.pop_string()?;
let mut values = Vec::with_capacity(slots);
for _ in 0..slots {
values.push(f.pop_value()?);
}
let object = Object {
name,
values: values.into(),
};
f.push_object(object.into());
}
} }
Ok(Flow::Continue) Ok(Flow::Continue)

View file

@ -0,0 +1,13 @@
class Point {
x: f64;
y: f64;
}
fun test() -> f64 {
let pt = new Point { x: 7, y: 23 };
let z = pt.x;
z
}
// @ignore
// @no-errors

View file

@ -1,10 +1,14 @@
fun sum(x: list<f64>) -> f64 { fun sum(x: list<f64>) -> f64 {
75 // lol let result = 0;
for v in x {
result = result + v;
}
result
} }
fun test() { fun test() -> f64 {
let val = [1, 2, 3]; let val = [1, 2, 3];
sum(val); sum(val)
} }
// @no-errors // @no-errors