Compare commits
No commits in common. "0ee89bf26bd32cb8bef3d8991a6be051fcc22950" and "f20f5a5e0389f3f8ff5ff1e31da03df52cb0acd3" have entirely different histories.
0ee89bf26b
...
f20f5a5e03
9 changed files with 58 additions and 382 deletions
|
|
@ -42,7 +42,6 @@ pub enum Instruction {
|
|||
StoreModule(usize),
|
||||
StringAdd,
|
||||
Dup,
|
||||
NewObject(usize),
|
||||
}
|
||||
|
||||
pub enum Export {
|
||||
|
|
@ -489,7 +488,6 @@ 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);
|
||||
}
|
||||
|
|
@ -547,23 +545,6 @@ 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);
|
||||
|
|
@ -724,58 +705,13 @@ 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];
|
||||
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?"),
|
||||
}
|
||||
let block = tree.child_of_kind(c.syntax, TreeKind::Block)?;
|
||||
|
||||
compile_expression(c, block);
|
||||
c.push(Instruction::Return);
|
||||
|
||||
OK
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -125,10 +125,8 @@ pub enum TreeKind {
|
|||
BinaryExpression,
|
||||
Block,
|
||||
CallExpression,
|
||||
ClassDecl,
|
||||
ConditionalExpression,
|
||||
ExpressionStatement,
|
||||
FieldDecl,
|
||||
File,
|
||||
ForStatement,
|
||||
FunctionDecl,
|
||||
|
|
@ -147,9 +145,6 @@ pub enum TreeKind {
|
|||
TypeParameter,
|
||||
TypeParameterList,
|
||||
UnaryExpression,
|
||||
FieldList,
|
||||
NewObjectExpression,
|
||||
FieldValue,
|
||||
}
|
||||
|
||||
pub struct Tree<'a> {
|
||||
|
|
@ -188,17 +183,14 @@ 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_of_kind(&s, kind).next()
|
||||
self.children
|
||||
.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>(
|
||||
|
|
@ -408,10 +400,6 @@ 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>,
|
||||
|
|
@ -510,7 +498,6 @@ fn file(p: &mut CParser) {
|
|||
while !p.eof() {
|
||||
match p.peek() {
|
||||
TokenKind::Fun => function(p),
|
||||
TokenKind::Class => class(p),
|
||||
_ => statement(p),
|
||||
}
|
||||
}
|
||||
|
|
@ -518,9 +505,10 @@ fn file(p: &mut CParser) {
|
|||
}
|
||||
|
||||
fn function(p: &mut CParser) {
|
||||
assert!(p.at(TokenKind::Fun));
|
||||
let m = p.start();
|
||||
|
||||
p.expect_start(TokenKind::Fun);
|
||||
p.expect(TokenKind::Fun, "expected a function to start with 'fun'");
|
||||
p.expect(TokenKind::Identifier, "expected a function name");
|
||||
if p.at(TokenKind::LeftParen) {
|
||||
param_list(p);
|
||||
|
|
@ -535,40 +523,11 @@ fn function(p: &mut CParser) {
|
|||
p.end(m, TreeKind::FunctionDecl);
|
||||
}
|
||||
|
||||
fn class(p: &mut CParser) {
|
||||
let m = p.start();
|
||||
|
||||
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) {
|
||||
assert!(p.at(TokenKind::LeftParen));
|
||||
let m = p.start();
|
||||
|
||||
p.expect_start(TokenKind::LeftParen);
|
||||
p.expect(TokenKind::LeftParen, "expect '(' to start a parameter list");
|
||||
while !p.at(TokenKind::RightParen) && !p.eof() {
|
||||
if p.at(TokenKind::Identifier) {
|
||||
parameter(p);
|
||||
|
|
@ -582,9 +541,12 @@ fn param_list(p: &mut CParser) {
|
|||
}
|
||||
|
||||
fn parameter(p: &mut CParser) {
|
||||
assert!(p.at(TokenKind::Identifier));
|
||||
let m = p.start();
|
||||
|
||||
p.expect_start(TokenKind::Identifier);
|
||||
p.expect(
|
||||
TokenKind::Identifier,
|
||||
"expected an identifier for a parameter name",
|
||||
);
|
||||
if p.eat(TokenKind::Colon) {
|
||||
type_expr(p);
|
||||
}
|
||||
|
|
@ -596,9 +558,13 @@ fn parameter(p: &mut CParser) {
|
|||
}
|
||||
|
||||
fn return_type(p: &mut CParser) {
|
||||
assert!(p.at(TokenKind::Arrow));
|
||||
let m = p.start();
|
||||
|
||||
p.expect_start(TokenKind::Arrow);
|
||||
p.expect(
|
||||
TokenKind::Arrow,
|
||||
"function return type starts with an arrow",
|
||||
);
|
||||
type_expr(p);
|
||||
|
||||
p.end(m, TreeKind::ReturnType);
|
||||
|
|
@ -618,9 +584,10 @@ fn type_expr(p: &mut CParser) {
|
|||
}
|
||||
|
||||
fn type_parameter_list(p: &mut CParser) {
|
||||
assert!(p.at(TokenKind::Less));
|
||||
let m = p.start();
|
||||
|
||||
p.expect_start(TokenKind::Less);
|
||||
p.expect(TokenKind::Less, "expected < to start type parameter list");
|
||||
while !p.at(TokenKind::Greater) && !p.eof() {
|
||||
if p.at(TokenKind::Identifier) {
|
||||
type_parameter(p);
|
||||
|
|
@ -646,9 +613,10 @@ fn type_parameter(p: &mut CParser) {
|
|||
}
|
||||
|
||||
fn block(p: &mut CParser) {
|
||||
assert!(p.at(TokenKind::LeftBrace));
|
||||
let m = p.start();
|
||||
|
||||
p.expect_start(TokenKind::LeftBrace);
|
||||
p.expect(TokenKind::LeftBrace, "expect '{' to start a block");
|
||||
while !p.at(TokenKind::RightBrace) && !p.eof() {
|
||||
statement(p);
|
||||
}
|
||||
|
|
@ -682,9 +650,10 @@ fn statement_if(p: &mut CParser) {
|
|||
}
|
||||
|
||||
fn statement_let(p: &mut CParser) {
|
||||
assert!(p.at(TokenKind::Let));
|
||||
let m = p.start();
|
||||
|
||||
p.expect_start(TokenKind::Let);
|
||||
p.expect(TokenKind::Let, "expect 'let' to start a let statement");
|
||||
p.expect(TokenKind::Identifier, "expected a name for the variable");
|
||||
p.expect(TokenKind::Equal, "expected a '=' after the variable name");
|
||||
expression(p);
|
||||
|
|
@ -696,9 +665,13 @@ fn statement_let(p: &mut CParser) {
|
|||
}
|
||||
|
||||
fn statement_return(p: &mut CParser) {
|
||||
assert!(p.at(TokenKind::Return));
|
||||
let m = p.start();
|
||||
|
||||
p.expect_start(TokenKind::Return);
|
||||
p.expect(
|
||||
TokenKind::Return,
|
||||
"expect 'return' to start a return statement",
|
||||
);
|
||||
expression(p);
|
||||
if !p.at(TokenKind::RightBrace) {
|
||||
p.expect(TokenKind::Semicolon, "expect ';' to end a return statement");
|
||||
|
|
@ -708,9 +681,10 @@ fn statement_return(p: &mut CParser) {
|
|||
}
|
||||
|
||||
fn statement_for(p: &mut CParser) {
|
||||
assert!(p.at(TokenKind::For));
|
||||
let m = p.start();
|
||||
|
||||
p.expect_start(TokenKind::For);
|
||||
p.expect(TokenKind::For, "expect a for to start a for loop");
|
||||
p.expect(
|
||||
TokenKind::Identifier,
|
||||
"expected an identifier for the loop variable",
|
||||
|
|
@ -797,9 +771,13 @@ 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_start(TokenKind::LeftParen);
|
||||
p.expect(
|
||||
TokenKind::LeftParen,
|
||||
"expect an argument list to start with '('",
|
||||
);
|
||||
while !p.at(TokenKind::RightParen) && !p.eof() {
|
||||
argument(p);
|
||||
}
|
||||
|
|
@ -840,8 +818,6 @@ fn prefix_expression(p: &mut CParser) -> MarkClosed {
|
|||
|
||||
TokenKind::LeftBracket => list_constructor(p),
|
||||
|
||||
TokenKind::New => object_constructor(p),
|
||||
|
||||
_ => p.advance_with_error("expected an expression"),
|
||||
}
|
||||
}
|
||||
|
|
@ -853,9 +829,10 @@ fn literal(p: &mut CParser) -> MarkClosed {
|
|||
}
|
||||
|
||||
fn grouping(p: &mut CParser) -> MarkClosed {
|
||||
assert!(p.at(TokenKind::LeftParen));
|
||||
let m = p.start();
|
||||
|
||||
p.expect_start(TokenKind::LeftParen);
|
||||
p.expect(TokenKind::LeftParen, "expected '(' to start grouping");
|
||||
expression(p);
|
||||
p.expect(TokenKind::RightParen, "unmatched parentheses in expression");
|
||||
|
||||
|
|
@ -872,9 +849,10 @@ fn unary(p: &mut CParser) -> MarkClosed {
|
|||
}
|
||||
|
||||
fn conditional(p: &mut CParser) -> MarkClosed {
|
||||
assert!(p.at(TokenKind::If));
|
||||
let m = p.start();
|
||||
|
||||
p.expect_start(TokenKind::If);
|
||||
p.expect(TokenKind::If, "expected conditional to start with 'if'");
|
||||
expression(p);
|
||||
block(p);
|
||||
if p.eat(TokenKind::Else) {
|
||||
|
|
@ -899,9 +877,13 @@ fn identifier(p: &mut CParser) -> MarkClosed {
|
|||
}
|
||||
|
||||
fn list_constructor(p: &mut CParser) -> MarkClosed {
|
||||
assert!(p.at(TokenKind::LeftBracket));
|
||||
let m = p.start();
|
||||
|
||||
p.expect_start(TokenKind::LeftBracket);
|
||||
p.expect(
|
||||
TokenKind::LeftBracket,
|
||||
"expect a list constructor to start with [",
|
||||
);
|
||||
while !p.at(TokenKind::RightBracket) && !p.eof() {
|
||||
list_constructor_element(p);
|
||||
}
|
||||
|
|
@ -927,49 +909,6 @@ 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::*;
|
||||
|
|
|
|||
|
|
@ -57,17 +57,6 @@ 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
|
||||
|
|
@ -102,7 +91,6 @@ pub enum Type {
|
|||
|
||||
Function(Vec<Box<Type>>, Box<Type>),
|
||||
List(Box<Type>),
|
||||
Class(Rc<ClassDecl>),
|
||||
}
|
||||
|
||||
impl Type {
|
||||
|
|
@ -148,7 +136,6 @@ impl fmt::Display for Type {
|
|||
// TODO: Better names
|
||||
TypeVariable(_) => write!(f, "$_"),
|
||||
List(t) => write!(f, "list<{t}>"),
|
||||
Class(c) => write!(f, "class {}", c.name),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -186,10 +173,6 @@ pub enum Declaration {
|
|||
declaration_type: Type,
|
||||
id: ExternalFunctionId,
|
||||
},
|
||||
Class {
|
||||
declaration_type: Type,
|
||||
declaration: TreeRef, //?
|
||||
},
|
||||
}
|
||||
|
||||
pub struct Environment {
|
||||
|
|
@ -421,9 +404,7 @@ impl<'a> Semantics<'a> {
|
|||
}
|
||||
|
||||
pub fn snapshot_errors(&self) -> Vec<Error> {
|
||||
let mut result = (*self.errors.borrow()).clone();
|
||||
result.sort_by(|a, b| a.start.0.cmp(&b.start.0));
|
||||
result
|
||||
(*self.errors.borrow()).clone()
|
||||
}
|
||||
|
||||
pub fn logical_parent(&self, tr: TreeRef) -> Option<TreeRef> {
|
||||
|
|
@ -700,16 +681,6 @@ 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,
|
||||
|
|
@ -765,8 +736,6 @@ 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"),
|
||||
};
|
||||
|
||||
|
|
@ -896,13 +865,6 @@ 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(
|
||||
|
|
@ -1079,13 +1041,6 @@ 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);
|
||||
|
||||
|
|
@ -1196,9 +1151,6 @@ impl<'a> Semantics<'a> {
|
|||
Declaration::ExternFunction {
|
||||
declaration_type, ..
|
||||
} => declaration_type.clone(),
|
||||
Declaration::Class {
|
||||
declaration_type, ..
|
||||
} => declaration_type.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -1259,13 +1211,6 @@ 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)?))
|
||||
|
|
@ -1335,12 +1280,6 @@ impl<'a> Semantics<'a> {
|
|||
} => {
|
||||
eprintln!("{declaration_type:?} (extern {id:?})");
|
||||
}
|
||||
Declaration::Class {
|
||||
declaration_type,
|
||||
declaration,
|
||||
} => {
|
||||
eprintln!("{declaration_type:?} (class {declaration:?})");
|
||||
}
|
||||
};
|
||||
}
|
||||
environment = env.parent.clone();
|
||||
|
|
@ -1382,13 +1321,10 @@ 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);
|
||||
|
|
@ -1407,12 +1343,6 @@ 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 => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1496,72 +1426,6 @@ 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::*;
|
||||
|
|
|
|||
|
|
@ -49,7 +49,6 @@ pub enum TokenKind {
|
|||
Import,
|
||||
In,
|
||||
Let,
|
||||
New,
|
||||
Or,
|
||||
Return,
|
||||
Select,
|
||||
|
|
@ -303,11 +302,6 @@ impl<'a> Tokens<'a> {
|
|||
return TokenKind::Let;
|
||||
}
|
||||
}
|
||||
'n' => {
|
||||
if ident == "new" {
|
||||
return TokenKind::New;
|
||||
}
|
||||
}
|
||||
'o' => {
|
||||
if ident == "or" {
|
||||
return TokenKind::Or;
|
||||
|
|
@ -579,7 +573,7 @@ mod tests {
|
|||
|
||||
test_tokens!(
|
||||
more_keywords,
|
||||
"fun if import let return select this true while truewhile new",
|
||||
"fun if import let return select this true while truewhile",
|
||||
(0, Fun, "fun"),
|
||||
(4, If, "if"),
|
||||
(7, Import, "import"),
|
||||
|
|
@ -589,8 +583,7 @@ mod tests {
|
|||
(32, This, "this"),
|
||||
(37, True, "true"),
|
||||
(42, While, "while"),
|
||||
(48, Identifier, "truewhile"),
|
||||
(58, New, "new")
|
||||
(48, Identifier, "truewhile")
|
||||
);
|
||||
|
||||
test_tokens!(
|
||||
|
|
|
|||
|
|
@ -39,12 +39,6 @@ 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,
|
||||
|
|
@ -53,7 +47,6 @@ pub enum StackValue {
|
|||
String(Rc<str>),
|
||||
Function(Rc<Function>),
|
||||
ExternFunction(usize),
|
||||
Object(Rc<Object>),
|
||||
}
|
||||
|
||||
enum FuncValue {
|
||||
|
|
@ -137,10 +130,6 @@ 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)
|
||||
|
|
@ -395,21 +384,6 @@ 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)
|
||||
|
|
|
|||
|
|
@ -1,13 +0,0 @@
|
|||
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
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
class Foo {
|
||||
x: f64;
|
||||
x: f64;
|
||||
}
|
||||
|
||||
// @ignore
|
||||
// @expect-errors:
|
||||
// asdfadsf
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
fun something(x: f64, x: f64) {}
|
||||
|
||||
// @ignore
|
||||
// @expect-errors:
|
||||
// asdfadsf
|
||||
|
|
@ -1,14 +1,10 @@
|
|||
fun sum(x: list<f64>) -> f64 {
|
||||
let result = 0;
|
||||
for v in x {
|
||||
result = result + v;
|
||||
}
|
||||
result
|
||||
75 // lol
|
||||
}
|
||||
|
||||
fun test() -> f64 {
|
||||
fun test() {
|
||||
let val = [1, 2, 3];
|
||||
sum(val)
|
||||
sum(val);
|
||||
}
|
||||
|
||||
// @no-errors
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue