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),
|
StoreModule(usize),
|
||||||
StringAdd,
|
StringAdd,
|
||||||
Dup,
|
Dup,
|
||||||
NewObject(usize),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Export {
|
pub enum Export {
|
||||||
|
|
@ -489,7 +488,6 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
@ -547,23 +545,6 @@ 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);
|
||||||
|
|
@ -724,58 +705,13 @@ 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];
|
||||||
match tree.kind {
|
let block = tree.child_of_kind(c.syntax, TreeKind::Block)?;
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -125,10 +125,8 @@ pub enum TreeKind {
|
||||||
BinaryExpression,
|
BinaryExpression,
|
||||||
Block,
|
Block,
|
||||||
CallExpression,
|
CallExpression,
|
||||||
ClassDecl,
|
|
||||||
ConditionalExpression,
|
ConditionalExpression,
|
||||||
ExpressionStatement,
|
ExpressionStatement,
|
||||||
FieldDecl,
|
|
||||||
File,
|
File,
|
||||||
ForStatement,
|
ForStatement,
|
||||||
FunctionDecl,
|
FunctionDecl,
|
||||||
|
|
@ -147,9 +145,6 @@ pub enum TreeKind {
|
||||||
TypeParameter,
|
TypeParameter,
|
||||||
TypeParameterList,
|
TypeParameterList,
|
||||||
UnaryExpression,
|
UnaryExpression,
|
||||||
FieldList,
|
|
||||||
NewObjectExpression,
|
|
||||||
FieldValue,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Tree<'a> {
|
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> {
|
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>(
|
pub fn child_tree_of_kind<'b>(
|
||||||
|
|
@ -408,10 +400,6 @@ 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>,
|
||||||
|
|
@ -510,7 +498,6 @@ 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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -518,9 +505,10 @@ 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_start(TokenKind::Fun);
|
p.expect(TokenKind::Fun, "expected a function to start with '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);
|
||||||
|
|
@ -535,40 +523,11 @@ fn function(p: &mut CParser) {
|
||||||
p.end(m, TreeKind::FunctionDecl);
|
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) {
|
fn param_list(p: &mut CParser) {
|
||||||
|
assert!(p.at(TokenKind::LeftParen));
|
||||||
let m = p.start();
|
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() {
|
while !p.at(TokenKind::RightParen) && !p.eof() {
|
||||||
if p.at(TokenKind::Identifier) {
|
if p.at(TokenKind::Identifier) {
|
||||||
parameter(p);
|
parameter(p);
|
||||||
|
|
@ -582,9 +541,12 @@ 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(
|
||||||
p.expect_start(TokenKind::Identifier);
|
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);
|
||||||
}
|
}
|
||||||
|
|
@ -596,9 +558,13 @@ 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_start(TokenKind::Arrow);
|
p.expect(
|
||||||
|
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);
|
||||||
|
|
@ -618,9 +584,10 @@ 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_start(TokenKind::Less);
|
p.expect(TokenKind::Less, "expected < to start type parameter list");
|
||||||
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);
|
||||||
|
|
@ -646,9 +613,10 @@ 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_start(TokenKind::LeftBrace);
|
p.expect(TokenKind::LeftBrace, "expect '{' to start a block");
|
||||||
while !p.at(TokenKind::RightBrace) && !p.eof() {
|
while !p.at(TokenKind::RightBrace) && !p.eof() {
|
||||||
statement(p);
|
statement(p);
|
||||||
}
|
}
|
||||||
|
|
@ -682,9 +650,10 @@ 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_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::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);
|
||||||
|
|
@ -696,9 +665,13 @@ 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_start(TokenKind::Return);
|
p.expect(
|
||||||
|
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");
|
||||||
|
|
@ -708,9 +681,10 @@ 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_start(TokenKind::For);
|
p.expect(TokenKind::For, "expect a for to start a for loop");
|
||||||
p.expect(
|
p.expect(
|
||||||
TokenKind::Identifier,
|
TokenKind::Identifier,
|
||||||
"expected an identifier for the loop variable",
|
"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) {
|
fn argument_list(p: &mut CParser) {
|
||||||
|
assert!(p.at(TokenKind::LeftParen));
|
||||||
let m = p.start();
|
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() {
|
while !p.at(TokenKind::RightParen) && !p.eof() {
|
||||||
argument(p);
|
argument(p);
|
||||||
}
|
}
|
||||||
|
|
@ -840,8 +818,6 @@ 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"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -853,9 +829,10 @@ 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_start(TokenKind::LeftParen);
|
p.expect(TokenKind::LeftParen, "expected '(' to start grouping");
|
||||||
expression(p);
|
expression(p);
|
||||||
p.expect(TokenKind::RightParen, "unmatched parentheses in expression");
|
p.expect(TokenKind::RightParen, "unmatched parentheses in expression");
|
||||||
|
|
||||||
|
|
@ -872,9 +849,10 @@ 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_start(TokenKind::If);
|
p.expect(TokenKind::If, "expected conditional to start with 'if'");
|
||||||
expression(p);
|
expression(p);
|
||||||
block(p);
|
block(p);
|
||||||
if p.eat(TokenKind::Else) {
|
if p.eat(TokenKind::Else) {
|
||||||
|
|
@ -899,9 +877,13 @@ 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_start(TokenKind::LeftBracket);
|
p.expect(
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
@ -927,49 +909,6 @@ 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::*;
|
||||||
|
|
|
||||||
|
|
@ -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)]
|
#[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
|
||||||
|
|
@ -102,7 +91,6 @@ 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 {
|
||||||
|
|
@ -148,7 +136,6 @@ 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),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -186,10 +173,6 @@ 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 {
|
||||||
|
|
@ -421,9 +404,7 @@ impl<'a> Semantics<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn snapshot_errors(&self) -> Vec<Error> {
|
pub fn snapshot_errors(&self) -> Vec<Error> {
|
||||||
let mut result = (*self.errors.borrow()).clone();
|
(*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> {
|
||||||
|
|
@ -700,16 +681,6 @@ 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,
|
||||||
|
|
@ -765,8 +736,6 @@ 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"),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -896,13 +865,6 @@ 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(
|
||||||
|
|
@ -1079,13 +1041,6 @@ 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);
|
||||||
|
|
||||||
|
|
@ -1196,9 +1151,6 @@ 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(),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1259,13 +1211,6 @@ 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)?))
|
||||||
|
|
@ -1335,12 +1280,6 @@ 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();
|
||||||
|
|
@ -1382,13 +1321,10 @@ 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);
|
||||||
|
|
@ -1407,12 +1343,6 @@ 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 => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1496,72 +1426,6 @@ 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::*;
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,6 @@ pub enum TokenKind {
|
||||||
Import,
|
Import,
|
||||||
In,
|
In,
|
||||||
Let,
|
Let,
|
||||||
New,
|
|
||||||
Or,
|
Or,
|
||||||
Return,
|
Return,
|
||||||
Select,
|
Select,
|
||||||
|
|
@ -303,11 +302,6 @@ 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;
|
||||||
|
|
@ -579,7 +573,7 @@ mod tests {
|
||||||
|
|
||||||
test_tokens!(
|
test_tokens!(
|
||||||
more_keywords,
|
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"),
|
(0, Fun, "fun"),
|
||||||
(4, If, "if"),
|
(4, If, "if"),
|
||||||
(7, Import, "import"),
|
(7, Import, "import"),
|
||||||
|
|
@ -589,8 +583,7 @@ 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!(
|
||||||
|
|
|
||||||
|
|
@ -39,12 +39,6 @@ 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,
|
||||||
|
|
@ -53,7 +47,6 @@ 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 {
|
||||||
|
|
@ -137,10 +130,6 @@ 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)
|
||||||
|
|
@ -395,21 +384,6 @@ 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)
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
fun sum(x: list<f64>) -> f64 {
|
||||||
let result = 0;
|
75 // lol
|
||||||
for v in x {
|
|
||||||
result = result + v;
|
|
||||||
}
|
|
||||||
result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun test() -> f64 {
|
fun test() {
|
||||||
let val = [1, 2, 3];
|
let val = [1, 2, 3];
|
||||||
sum(val)
|
sum(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
// @no-errors
|
// @no-errors
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue