[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,
|
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.
|
// TODO: If I were cool this would by actual bytecode.
|
||||||
// But I'm not cool.
|
// But I'm not cool.
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
|
@ -55,6 +61,7 @@ pub enum Instruction {
|
||||||
StoreModule(usize),
|
StoreModule(usize),
|
||||||
StoreSlot(usize),
|
StoreSlot(usize),
|
||||||
StringAdd,
|
StringAdd,
|
||||||
|
NewList(usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Export {
|
pub enum Export {
|
||||||
|
|
@ -153,10 +160,6 @@ struct Compiler<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> 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 {
|
fn add_string(&mut self, result: String) -> usize {
|
||||||
let index = self.function.strings.len();
|
let index = self.function.strings.len();
|
||||||
self.function.strings.push(result.into());
|
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::GroupingExpression => compile_grouping(c, tree),
|
||||||
TreeKind::Identifier => compile_identifier_expression(c, t, tree),
|
TreeKind::Identifier => compile_identifier_expression(c, t, tree),
|
||||||
TreeKind::IsExpression => compile_is_expression(c, 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::LiteralExpression => compile_literal(c, t, tree),
|
||||||
TreeKind::MemberAccess => compile_member_access(c, t, tree),
|
TreeKind::MemberAccess => compile_member_access(c, t, tree),
|
||||||
TreeKind::NewObjectExpression => compile_new_object_expression(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 {
|
fn compile_literal(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR {
|
||||||
let tok = tr.nth_token(0)?;
|
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::F64 => c.push(Instruction::PushFloat(tok.as_str().parse().unwrap())),
|
||||||
Type::Bool => c.push(if tok.kind == TokenKind::True {
|
Type::Bool => c.push(if tok.kind == TokenKind::True {
|
||||||
Instruction::PushTrue
|
Instruction::PushTrue
|
||||||
|
|
@ -969,12 +974,31 @@ fn compile_self_reference(c: &mut Compiler) -> CR {
|
||||||
OK
|
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) {
|
fn compile_statement(c: &mut Compiler, t: TreeRef, gen_value: bool) {
|
||||||
let tree = &c.semantics.tree()[t];
|
let tree = &c.semantics.tree()[t];
|
||||||
let cr = match tree.kind {
|
let cr = match tree.kind {
|
||||||
TreeKind::Block => compile_block_statement(c, t, gen_value),
|
TreeKind::Block => compile_block_statement(c, t, gen_value),
|
||||||
TreeKind::ClassDecl => compile_class_declaration(c, t, tree, gen_value),
|
TreeKind::ClassDecl => compile_class_declaration(c, t, tree, gen_value),
|
||||||
TreeKind::ExpressionStatement => compile_expression_statement(c, 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::FunctionDecl => compile_function_declaration(c, t, tree, gen_value),
|
||||||
TreeKind::IfStatement => compile_if_statement(c, tree, gen_value),
|
TreeKind::IfStatement => compile_if_statement(c, tree, gen_value),
|
||||||
TreeKind::LetStatement => compile_let_statement(c, t, 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);
|
c.push(Instruction::Return);
|
||||||
OK
|
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
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -318,6 +318,7 @@ impl ExternalFunctionId {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
pub enum Declaration {
|
pub enum Declaration {
|
||||||
Variable {
|
Variable {
|
||||||
declaration: TreeRef,
|
declaration: TreeRef,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
use std::cell::RefCell;
|
use std::cell::{Cell, RefCell};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use crate::compiler::{Export, Function, Instruction, Module};
|
use crate::compiler::{
|
||||||
|
Export, Function, Instruction, Module, EXTERN_BUILTIN_LIST_GET_ITERATOR,
|
||||||
|
EXTERN_BUILTIN_LIST_ITERATOR_NEXT, EXTERN_BUILTIN_NOOP,
|
||||||
|
};
|
||||||
use crate::semantics::Type;
|
use crate::semantics::Type;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
|
@ -33,8 +36,14 @@ pub enum VMErrorCode {
|
||||||
StackExpectedFunction(StackValue),
|
StackExpectedFunction(StackValue),
|
||||||
#[error("internal error: stack type mismatch ({0:?} is not object)")]
|
#[error("internal error: stack type mismatch ({0:?} is not object)")]
|
||||||
StackExpectedObject(StackValue),
|
StackExpectedObject(StackValue),
|
||||||
|
#[error("internal error: stack type mismatch ({0:?} is not list)")]
|
||||||
|
StackExpectedList(StackValue),
|
||||||
|
#[error("internal error: stack type mismatch ({0:?} is not list iterator)")]
|
||||||
|
StackExpectedListIterator(StackValue),
|
||||||
#[error("internal error: slot {0} was out of range for object (type {1} with {2} slots)")]
|
#[error("internal error: slot {0} was out of range for object (type {1} with {2} slots)")]
|
||||||
SlotOutOfRange(usize, Rc<str>, usize),
|
SlotOutOfRange(usize, Rc<str>, usize),
|
||||||
|
#[error("internal error: the extern function with ID {0} was not registered")]
|
||||||
|
UnregisteredExternFunction(usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -65,6 +74,12 @@ impl Object {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct ListIterator {
|
||||||
|
list: Rc<Vec<StackValue>>,
|
||||||
|
index: Cell<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum StackValue {
|
pub enum StackValue {
|
||||||
Nothing,
|
Nothing,
|
||||||
|
|
@ -75,6 +90,8 @@ pub enum StackValue {
|
||||||
Function(Rc<Function>),
|
Function(Rc<Function>),
|
||||||
ExternFunction(usize),
|
ExternFunction(usize),
|
||||||
Object(Rc<Object>),
|
Object(Rc<Object>),
|
||||||
|
List(Rc<Vec<StackValue>>),
|
||||||
|
ListIterator(Rc<ListIterator>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StackValue {
|
impl StackValue {
|
||||||
|
|
@ -229,6 +246,10 @@ impl Frame {
|
||||||
self.push_value(StackValue::Int(v));
|
self.push_value(StackValue::Int(v));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn push_list(&mut self, value: Rc<Vec<StackValue>>) {
|
||||||
|
self.push_value(StackValue::List(value));
|
||||||
|
}
|
||||||
|
|
||||||
fn get_argument(&self, i: usize) -> Result<StackValue> {
|
fn get_argument(&self, i: usize) -> Result<StackValue> {
|
||||||
self.args
|
self.args
|
||||||
.get(i)
|
.get(i)
|
||||||
|
|
@ -320,6 +341,43 @@ impl Context {
|
||||||
.map(|v| v.clone())
|
.map(|v| v.clone())
|
||||||
.ok_or_else(|| VMErrorCode::FunctionOutOfRange(i)) // TODO: Test
|
.ok_or_else(|| VMErrorCode::FunctionOutOfRange(i)) // TODO: Test
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn call_extern_function(&self, index: usize, args: &[StackValue]) -> Result<StackValue> {
|
||||||
|
match index {
|
||||||
|
EXTERN_BUILTIN_NOOP => Ok(StackValue::Nothing),
|
||||||
|
EXTERN_BUILTIN_LIST_GET_ITERATOR => {
|
||||||
|
let Some(list_value) = args.get(0) else {
|
||||||
|
return Err(VMErrorCode::ArgumentOutOfRange(0));
|
||||||
|
};
|
||||||
|
let StackValue::List(list) = list_value else {
|
||||||
|
return Err(VMErrorCode::StackExpectedList(list_value.clone()));
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(StackValue::ListIterator(Rc::new(ListIterator {
|
||||||
|
list: list.clone(),
|
||||||
|
index: Cell::new(0),
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
EXTERN_BUILTIN_LIST_ITERATOR_NEXT => {
|
||||||
|
let Some(iter_value) = args.get(0) else {
|
||||||
|
return Err(VMErrorCode::ArgumentOutOfRange(0));
|
||||||
|
};
|
||||||
|
let StackValue::ListIterator(li) = iter_value else {
|
||||||
|
return Err(VMErrorCode::StackExpectedListIterator(iter_value.clone()));
|
||||||
|
};
|
||||||
|
|
||||||
|
let index = li.index.get();
|
||||||
|
if index >= li.list.len() {
|
||||||
|
Ok(StackValue::Nothing)
|
||||||
|
} else {
|
||||||
|
let result = li.list[index].clone();
|
||||||
|
li.index.set(index + 1);
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => Err(VMErrorCode::UnregisteredExternFunction(index)),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Flow {
|
enum Flow {
|
||||||
|
|
@ -451,7 +509,9 @@ fn eval_one(
|
||||||
stack.push(frame);
|
stack.push(frame);
|
||||||
*index = 0;
|
*index = 0;
|
||||||
}
|
}
|
||||||
FuncValue::ExternFunction(_) => todo!(),
|
FuncValue::ExternFunction(i) => {
|
||||||
|
f.push_value(c.call_extern_function(i, &args)?);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Instruction::Return => match stack.pop() {
|
Instruction::Return => match stack.pop() {
|
||||||
|
|
@ -552,6 +612,13 @@ fn eval_one(
|
||||||
let v = f.pop_value()?;
|
let v = f.pop_value()?;
|
||||||
f.push_bool(v.is_nothing());
|
f.push_bool(v.is_nothing());
|
||||||
}
|
}
|
||||||
|
Instruction::NewList(c) => {
|
||||||
|
let mut v = Vec::with_capacity(c);
|
||||||
|
for _ in 0..c {
|
||||||
|
v.push(f.pop_value()?);
|
||||||
|
}
|
||||||
|
f.push_list(Rc::new(v));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Flow::Continue)
|
Ok(Flow::Continue)
|
||||||
|
|
@ -571,14 +638,14 @@ pub fn eval(
|
||||||
let instruction = instructions[index];
|
let instruction = instructions[index];
|
||||||
|
|
||||||
// {
|
// {
|
||||||
// eprint!("{index}: {instruction:?} [");
|
// eprint!("{index}: [");
|
||||||
// for val in f.stack.iter().rev().take(3) {
|
|
||||||
// eprint!("{val:?} ");
|
|
||||||
// }
|
|
||||||
// if f.stack.len() > 3 {
|
// if f.stack.len() > 3 {
|
||||||
// eprint!("...");
|
// eprint!("...");
|
||||||
// }
|
// }
|
||||||
// eprintln!("]");
|
// for val in f.stack.iter().take(3) {
|
||||||
|
// eprint!("{val:?} ");
|
||||||
|
// }
|
||||||
|
// eprintln!("] => {instruction:?}");
|
||||||
// }
|
// }
|
||||||
|
|
||||||
index += 1;
|
index += 1;
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,6 @@ fun test() -> f64 {
|
||||||
sum(val)
|
sum(val)
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ignore WIP
|
|
||||||
// @no-errors
|
// @no-errors
|
||||||
// @type: 155 list<f64>
|
// @type: 155 list<f64>
|
||||||
|
// @eval: Float(6.0)
|
||||||
Loading…
Add table
Add a link
Reference in a new issue