[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),
StringAdd,
Dup,
NewObject(usize),
}
pub enum Export {
@ -488,6 +489,7 @@ fn compile_binary_expression(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR {
Declaration::ExternFunction { .. } => Instruction::Panic,
Declaration::Function { .. } => Instruction::Panic,
Declaration::Class { .. } => Instruction::Panic,
};
c.push(instruction);
}
@ -545,6 +547,23 @@ 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)
}
};
c.push(instruction);
@ -705,13 +724,58 @@ fn compile_function_declaration(c: &mut Compiler, t: TreeRef, tree: &Tree, gen_v
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 {
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);
OK
}

View file

@ -125,8 +125,10 @@ pub enum TreeKind {
BinaryExpression,
Block,
CallExpression,
ClassDecl,
ConditionalExpression,
ExpressionStatement,
FieldDecl,
File,
ForStatement,
FunctionDecl,
@ -145,6 +147,9 @@ pub enum TreeKind {
TypeParameter,
TypeParameterList,
UnaryExpression,
FieldList,
NewObjectExpression,
FieldValue,
}
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> {
self.children
.iter()
.filter_map(|c| match c {
Child::Tree(t) => Some(*t),
_ => None,
})
.find(|c| s[*c].kind == kind)
self.children_of_kind(&s, kind).next()
}
pub fn child_tree_of_kind<'b>(
@ -400,6 +408,10 @@ impl<'a> CParser<'a> {
self.error(error);
}
fn expect_start(&mut self, kind: TokenKind) {
assert!(self.eat(kind));
}
fn advance_with_error<T>(&mut self, error: T) -> MarkClosed
where
T: Into<String>,
@ -498,6 +510,7 @@ fn file(p: &mut CParser) {
while !p.eof() {
match p.peek() {
TokenKind::Fun => function(p),
TokenKind::Class => class(p),
_ => statement(p),
}
}
@ -505,10 +518,9 @@ fn file(p: &mut CParser) {
}
fn function(p: &mut CParser) {
assert!(p.at(TokenKind::Fun));
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");
if p.at(TokenKind::LeftParen) {
param_list(p);
@ -523,11 +535,40 @@ fn function(p: &mut CParser) {
p.end(m, TreeKind::FunctionDecl);
}
fn param_list(p: &mut CParser) {
assert!(p.at(TokenKind::LeftParen));
fn class(p: &mut CParser) {
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() {
if p.at(TokenKind::Identifier) {
parameter(p);
@ -541,12 +582,9 @@ fn param_list(p: &mut CParser) {
}
fn parameter(p: &mut CParser) {
assert!(p.at(TokenKind::Identifier));
let m = p.start();
p.expect(
TokenKind::Identifier,
"expected an identifier for a parameter name",
);
p.expect_start(TokenKind::Identifier);
if p.eat(TokenKind::Colon) {
type_expr(p);
}
@ -558,13 +596,9 @@ fn parameter(p: &mut CParser) {
}
fn return_type(p: &mut CParser) {
assert!(p.at(TokenKind::Arrow));
let m = p.start();
p.expect(
TokenKind::Arrow,
"function return type starts with an arrow",
);
p.expect_start(TokenKind::Arrow);
type_expr(p);
p.end(m, TreeKind::ReturnType);
@ -584,10 +618,9 @@ fn type_expr(p: &mut CParser) {
}
fn type_parameter_list(p: &mut CParser) {
assert!(p.at(TokenKind::Less));
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() {
if p.at(TokenKind::Identifier) {
type_parameter(p);
@ -613,10 +646,9 @@ fn type_parameter(p: &mut CParser) {
}
fn block(p: &mut CParser) {
assert!(p.at(TokenKind::LeftBrace));
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() {
statement(p);
}
@ -650,10 +682,9 @@ fn statement_if(p: &mut CParser) {
}
fn statement_let(p: &mut CParser) {
assert!(p.at(TokenKind::Let));
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::Equal, "expected a '=' after the variable name");
expression(p);
@ -665,13 +696,9 @@ fn statement_let(p: &mut CParser) {
}
fn statement_return(p: &mut CParser) {
assert!(p.at(TokenKind::Return));
let m = p.start();
p.expect(
TokenKind::Return,
"expect 'return' to start a return statement",
);
p.expect_start(TokenKind::Return);
expression(p);
if !p.at(TokenKind::RightBrace) {
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) {
assert!(p.at(TokenKind::For));
let m = p.start();
p.expect(TokenKind::For, "expect a for to start a for loop");
p.expect_start(TokenKind::For);
p.expect(
TokenKind::Identifier,
"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) {
assert!(p.at(TokenKind::LeftParen));
let m = p.start();
p.expect(
TokenKind::LeftParen,
"expect an argument list to start with '('",
);
p.expect_start(TokenKind::LeftParen);
while !p.at(TokenKind::RightParen) && !p.eof() {
argument(p);
}
@ -818,6 +840,8 @@ fn prefix_expression(p: &mut CParser) -> MarkClosed {
TokenKind::LeftBracket => list_constructor(p),
TokenKind::New => object_constructor(p),
_ => p.advance_with_error("expected an expression"),
}
}
@ -829,10 +853,9 @@ fn literal(p: &mut CParser) -> MarkClosed {
}
fn grouping(p: &mut CParser) -> MarkClosed {
assert!(p.at(TokenKind::LeftParen));
let m = p.start();
p.expect(TokenKind::LeftParen, "expected '(' to start grouping");
p.expect_start(TokenKind::LeftParen);
expression(p);
p.expect(TokenKind::RightParen, "unmatched parentheses in expression");
@ -849,10 +872,9 @@ fn unary(p: &mut CParser) -> MarkClosed {
}
fn conditional(p: &mut CParser) -> MarkClosed {
assert!(p.at(TokenKind::If));
let m = p.start();
p.expect(TokenKind::If, "expected conditional to start with 'if'");
p.expect_start(TokenKind::If);
expression(p);
block(p);
if p.eat(TokenKind::Else) {
@ -877,13 +899,9 @@ fn identifier(p: &mut CParser) -> MarkClosed {
}
fn list_constructor(p: &mut CParser) -> MarkClosed {
assert!(p.at(TokenKind::LeftBracket));
let m = p.start();
p.expect(
TokenKind::LeftBracket,
"expect a list constructor to start with [",
);
p.expect_start(TokenKind::LeftBracket);
while !p.at(TokenKind::RightBracket) && !p.eof() {
list_constructor_element(p);
}
@ -909,6 +927,49 @@ fn list_constructor_element(p: &mut CParser) {
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)]
mod tests {
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)]
pub enum Type {
// 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>),
List(Box<Type>),
Class(Rc<ClassDecl>),
}
impl Type {
@ -136,6 +148,7 @@ impl fmt::Display for Type {
// TODO: Better names
TypeVariable(_) => write!(f, "$_"),
List(t) => write!(f, "list<{t}>"),
Class(c) => write!(f, "class {}", c.name),
}
}
}
@ -173,6 +186,10 @@ pub enum Declaration {
declaration_type: Type,
id: ExternalFunctionId,
},
Class {
declaration_type: Type,
declaration: TreeRef, //?
},
}
pub struct Environment {
@ -404,7 +421,9 @@ impl<'a> Semantics<'a> {
}
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> {
@ -681,6 +700,16 @@ impl<'a> Semantics<'a> {
.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
(Type::Error, _) => true,
(_, Type::Error) => true,
@ -736,6 +765,8 @@ impl<'a> Semantics<'a> {
TreeKind::ListConstructorElement => self.type_of_list_constructor_element(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"),
};
@ -865,6 +896,13 @@ impl<'a> Semantics<'a> {
);
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 => {
self.report_error_tree_ref(
@ -1041,6 +1079,13 @@ impl<'a> Semantics<'a> {
fn type_of_call(&self, tree: &Tree) -> Option<Type> {
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 = self.type_of(f_ref);
@ -1151,6 +1196,9 @@ impl<'a> Semantics<'a> {
Declaration::ExternFunction {
declaration_type, ..
} => 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)))
}
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> {
assert_eq!(tree.kind, TreeKind::ListConstructorElement);
Some(self.type_of(tree.nth_tree(0)?))
@ -1280,6 +1335,12 @@ impl<'a> Semantics<'a> {
} => {
eprintln!("{declaration_type:?} (extern {id:?})");
}
Declaration::Class {
declaration_type,
declaration,
} => {
eprintln!("{declaration_type:?} (class {declaration:?})");
}
};
}
environment = env.parent.clone();
@ -1321,10 +1382,13 @@ pub fn check(s: &Semantics) {
| TreeKind::GroupingExpression
| TreeKind::UnaryExpression
| TreeKind::ConditionalExpression
| TreeKind::CallExpression
| TreeKind::BinaryExpression => {
let _ = s.type_of(t);
}
TreeKind::CallExpression => {
let _ = s.type_of(t);
}
TreeKind::ArgumentList => {}
TreeKind::Argument => {
let _ = s.type_of(t);
@ -1343,6 +1407,12 @@ pub fn check(s: &Semantics) {
let _ = s.type_of(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);
}
// 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)]
mod tests {
use super::*;

View file

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

View file

@ -39,6 +39,12 @@ pub struct VMError {
type Result<T> = std::result::Result<T, VMErrorCode>;
#[derive(Clone, Debug)]
pub struct Object {
name: Rc<str>,
values: Box<[StackValue]>,
}
#[derive(Clone, Debug)]
pub enum StackValue {
Nothing,
@ -47,6 +53,7 @@ pub enum StackValue {
String(Rc<str>),
Function(Rc<Function>),
ExternFunction(usize),
Object(Rc<Object>),
}
enum FuncValue {
@ -130,6 +137,10 @@ impl Frame {
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> {
self.args
.get(i)
@ -384,6 +395,21 @@ fn eval_one(
let y = f.pop_string()?;
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)

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 {
75 // lol
let result = 0;
for v in x {
result = result + v;
}
result
}
fun test() {
fun test() -> f64 {
let val = [1, 2, 3];
sum(val);
sum(val)
}
// @no-errors