[fine] List iteration... works?
A little hacky but it works!
This commit is contained in:
parent
13aaca36c8
commit
d5059dd450
4 changed files with 190 additions and 15 deletions
|
|
@ -7,6 +7,12 @@ use crate::{
|
|||
tokens::TokenKind,
|
||||
};
|
||||
|
||||
pub const EXTERN_BUILTIN_NOOP: usize = 0;
|
||||
pub const EXTERN_BUILTIN_LIST_GET_ITERATOR: usize = 1;
|
||||
pub const EXTERN_BUILTIN_LIST_ITERATOR_NEXT: usize = 2;
|
||||
|
||||
pub const EXTERN_USER_FIRST: usize = 100000;
|
||||
|
||||
// TODO: If I were cool this would by actual bytecode.
|
||||
// But I'm not cool.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
|
|
@ -55,6 +61,7 @@ pub enum Instruction {
|
|||
StoreModule(usize),
|
||||
StoreSlot(usize),
|
||||
StringAdd,
|
||||
NewList(usize),
|
||||
}
|
||||
|
||||
pub enum Export {
|
||||
|
|
@ -153,10 +160,6 @@ struct Compiler<'a> {
|
|||
}
|
||||
|
||||
impl<'a> Compiler<'a> {
|
||||
pub fn type_of(&self, t: TreeRef) -> Type {
|
||||
self.semantics.type_of(t)
|
||||
}
|
||||
|
||||
fn add_string(&mut self, result: String) -> usize {
|
||||
let index = self.function.strings.len();
|
||||
self.function.strings.push(result.into());
|
||||
|
|
@ -307,6 +310,8 @@ fn compile_expression(c: &mut Compiler, t: TreeRef) {
|
|||
TreeKind::GroupingExpression => compile_grouping(c, tree),
|
||||
TreeKind::Identifier => compile_identifier_expression(c, t, tree),
|
||||
TreeKind::IsExpression => compile_is_expression(c, tree),
|
||||
TreeKind::ListConstructor => compile_list_constructor(c, tree),
|
||||
TreeKind::ListConstructorElement => compile_list_constructor_element(c, tree),
|
||||
TreeKind::LiteralExpression => compile_literal(c, t, tree),
|
||||
TreeKind::MemberAccess => compile_member_access(c, t, tree),
|
||||
TreeKind::NewObjectExpression => compile_new_object_expression(c, t, tree),
|
||||
|
|
@ -322,7 +327,7 @@ fn compile_expression(c: &mut Compiler, t: TreeRef) {
|
|||
|
||||
fn compile_literal(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR {
|
||||
let tok = tr.nth_token(0)?;
|
||||
match c.type_of(t) {
|
||||
match c.semantics.type_of(t) {
|
||||
Type::F64 => c.push(Instruction::PushFloat(tok.as_str().parse().unwrap())),
|
||||
Type::Bool => c.push(if tok.kind == TokenKind::True {
|
||||
Instruction::PushTrue
|
||||
|
|
@ -969,12 +974,31 @@ fn compile_self_reference(c: &mut Compiler) -> CR {
|
|||
OK
|
||||
}
|
||||
|
||||
fn compile_list_constructor(c: &mut Compiler, tree: &Tree) -> CR {
|
||||
let mut children: Vec<_> = tree
|
||||
.children_of_kind(c.syntax, TreeKind::ListConstructorElement)
|
||||
.collect();
|
||||
children.reverse();
|
||||
let count = children.len();
|
||||
for child in children {
|
||||
compile_expression(c, child);
|
||||
}
|
||||
c.push(Instruction::NewList(count));
|
||||
OK
|
||||
}
|
||||
|
||||
fn compile_list_constructor_element(c: &mut Compiler, tree: &Tree) -> CR {
|
||||
compile_expression(c, tree.nth_tree(0)?);
|
||||
OK
|
||||
}
|
||||
|
||||
fn compile_statement(c: &mut Compiler, t: TreeRef, gen_value: bool) {
|
||||
let tree = &c.semantics.tree()[t];
|
||||
let cr = match tree.kind {
|
||||
TreeKind::Block => compile_block_statement(c, t, gen_value),
|
||||
TreeKind::ClassDecl => compile_class_declaration(c, t, tree, gen_value),
|
||||
TreeKind::ExpressionStatement => compile_expression_statement(c, tree, gen_value),
|
||||
TreeKind::ForStatement => compile_for_statement(c, tree, gen_value),
|
||||
TreeKind::FunctionDecl => compile_function_declaration(c, t, tree, gen_value),
|
||||
TreeKind::IfStatement => compile_if_statement(c, tree, gen_value),
|
||||
TreeKind::LetStatement => compile_let_statement(c, t, tree, gen_value),
|
||||
|
|
@ -1180,3 +1204,86 @@ fn compile_return_statement(c: &mut Compiler, tree: &Tree) -> CR {
|
|||
c.push(Instruction::Return);
|
||||
OK
|
||||
}
|
||||
|
||||
fn compile_for_statement(c: &mut Compiler, tree: &Tree, gen_value: bool) -> CR {
|
||||
// Figure out the variable.
|
||||
let vt = tree.nth_tree(1)?;
|
||||
let var = &c.syntax[vt];
|
||||
let id = var.nth_token(0)?;
|
||||
|
||||
let body = tree.nth_tree(4)?;
|
||||
let env = c.semantics.environment_of(body);
|
||||
let Some(variable_decl) = env.bind(id) else {
|
||||
ice!(c, body, "Unable to bind {id} in loop body");
|
||||
};
|
||||
let variable_slot = match variable_decl {
|
||||
Declaration::Variable {
|
||||
location, index, ..
|
||||
} => {
|
||||
compiler_assert_eq!(
|
||||
c,
|
||||
vt,
|
||||
*location,
|
||||
Location::Local,
|
||||
"expected loop variable to be local"
|
||||
);
|
||||
|
||||
*index
|
||||
}
|
||||
_ => ice!(c, vt, "loop variable was not a variable"),
|
||||
};
|
||||
|
||||
// Figure out the generator.
|
||||
let iterable = tree.nth_tree(3)?;
|
||||
compile_expression(c, iterable);
|
||||
|
||||
// call 'get_iterator'
|
||||
let iterable_typ = c.semantics.type_of(iterable);
|
||||
match iterable_typ {
|
||||
Type::List(_) => {
|
||||
c.push(Instruction::LoadExternFunction(
|
||||
EXTERN_BUILTIN_LIST_GET_ITERATOR,
|
||||
));
|
||||
c.push(Instruction::Call(1));
|
||||
}
|
||||
_ => return None, // TODO: Bind and call get_iterator() on type of iterable
|
||||
}
|
||||
|
||||
// iterate
|
||||
// (Stack is clear except for the iterator.)
|
||||
|
||||
let loop_top = c.push(Instruction::Dup); // Save the iterator
|
||||
match iterable_typ {
|
||||
Type::List(_) => {
|
||||
c.push(Instruction::LoadExternFunction(
|
||||
EXTERN_BUILTIN_LIST_ITERATOR_NEXT,
|
||||
));
|
||||
}
|
||||
_ => return None, // TODO: Bind and call next() on type of iterator
|
||||
}
|
||||
c.push(Instruction::Call(1)); // Call 'next'
|
||||
|
||||
c.push(Instruction::Dup); // Save the result
|
||||
c.push(Instruction::IsNothing); // Check to see if iteration is done
|
||||
let jump_to_end = c.push(Instruction::JumpTrue(0));
|
||||
c.push(Instruction::StoreLocal(variable_slot)); // Store the value
|
||||
|
||||
// (Stack is clear except for the iterator.)
|
||||
|
||||
compile_statement(c, body, false); // Run the body
|
||||
|
||||
// (Stack is clear except for the iterator.)
|
||||
|
||||
c.push(Instruction::Jump(loop_top));
|
||||
|
||||
// Clean up the loop.
|
||||
c.patch(jump_to_end, |i| Instruction::JumpTrue(i));
|
||||
c.push(Instruction::Discard); // Drop the unused value
|
||||
c.push(Instruction::Discard); // Drop the iterator
|
||||
|
||||
if gen_value {
|
||||
c.push(Instruction::PushNothing);
|
||||
}
|
||||
|
||||
OK
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue