Add array example, ArrayObj implementation
* VM creates arrays using the BeginArray and EndArray instructions * ArrayObj is implemented with a __str__ function (annoying) * Simple example of array creation is added Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
15
examples/array.sy
Normal file
15
examples/array.sy
Normal file
@@ -0,0 +1,15 @@
|
||||
# Create an array with just some numbers
|
||||
{ 1 2 3 }
|
||||
println!
|
||||
|
||||
[ :x x x ] :dup
|
||||
# Call some functions
|
||||
{ 1 dup! dup! }
|
||||
println!
|
||||
|
||||
# Call your own functions, why not?
|
||||
{
|
||||
[ 2 *! ] :double
|
||||
1 dup! double! dup! double!
|
||||
}
|
||||
println!
|
||||
140
src/obj/array.rs
140
src/obj/array.rs
@@ -0,0 +1,140 @@
|
||||
use crate::obj::prelude::*;
|
||||
use crate::syn::span::Span;
|
||||
use crate::vm::{error::*, machine::Machine};
|
||||
use gc::{Finalize, Gc, Trace};
|
||||
|
||||
pub type Array = Vec<ObjPtr>;
|
||||
|
||||
#[derive(Debug, Trace, Finalize)]
|
||||
pub struct ArrayObj {
|
||||
value: Array,
|
||||
vtable: VTable,
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
static VTABLE: VTable = VTableBuilder::default()
|
||||
.with_builtin("__str__", |machine, reentry| {
|
||||
// stack (top to bottom):
|
||||
// - array[i].__str__
|
||||
// - array[i]
|
||||
// - array
|
||||
// - int i
|
||||
// - working string
|
||||
|
||||
// TODO - re-implement this in the language itself ffs
|
||||
// TODO - catch array recursion
|
||||
const INNER_BEGIN: usize = 1;
|
||||
const INNER_END: usize = 2;
|
||||
match reentry {
|
||||
0 => {
|
||||
let array = machine.stack_pop()?;
|
||||
let index = IntObj::new(0);
|
||||
let working = StrObj::new(String::new());
|
||||
machine.stack_push(working)?;
|
||||
machine.stack_push(index)?;
|
||||
machine.stack_push(array)?;
|
||||
// this will basically just dump us down below, except
|
||||
// slower and kludgier and just worse overall
|
||||
Ok(BuiltinExit::Resume(INNER_BEGIN))
|
||||
}
|
||||
INNER_BEGIN => {
|
||||
machine.stack();
|
||||
let array_obj = machine.stack_pop()?;
|
||||
let index_obj = machine.stack_pop()?;
|
||||
// cast the array and index to their appropriate types
|
||||
let array = array_obj.as_any().downcast_ref::<ArrayObj>()
|
||||
.ok_or_else(|| RuntimeError::WrongValue("array".to_string()))?;
|
||||
let index_int = index_obj.as_any().downcast_ref::<IntObj>()
|
||||
.ok_or_else(|| RuntimeError::WrongValue("int".to_string()))?;
|
||||
let index = index_int.value() as usize;
|
||||
if array.value().len() <= index {
|
||||
// done iterating, exit
|
||||
return Ok(BuiltinExit::Return);
|
||||
}
|
||||
// get the current item from the array, push the index and
|
||||
// array back onto the stack
|
||||
let item = Gc::clone(array.value().get(index).unwrap());
|
||||
machine.stack_push(index_obj)?;
|
||||
machine.stack_push(array_obj)?;
|
||||
|
||||
// push the array item at the current index to the stack
|
||||
let str_fun = item.get("__str__")
|
||||
.ok_or_else(|| RuntimeError::UnsetWord("__str__".to_string()))?;
|
||||
machine.stack_push(item)?;
|
||||
// call the __str__ item
|
||||
str_fun.call(None, machine)?;
|
||||
Ok(BuiltinExit::Resume(INNER_END))
|
||||
}
|
||||
INNER_END => {
|
||||
machine.stack();
|
||||
// get the returned string, add it to the accumulator
|
||||
|
||||
let returned_obj = machine.stack_pop()?;
|
||||
let returned = returned_obj.as_any().downcast_ref::<StrObj>()
|
||||
.ok_or_else(|| RuntimeError::WrongValue("str".to_string()))?;
|
||||
|
||||
let array_obj = machine.stack_pop()?;
|
||||
let index_obj = machine.stack_pop()?;
|
||||
let index = index_obj.as_any().downcast_ref::<IntObj>()
|
||||
.ok_or_else(|| RuntimeError::WrongValue("int".to_string()))?;
|
||||
|
||||
let working_obj = machine.stack_pop()?;
|
||||
let working = working_obj.as_any().downcast_ref::<StrObj>()
|
||||
.ok_or_else(|| RuntimeError::WrongValue("str".to_string()))?;
|
||||
|
||||
let new_str = StrObj::new(format!("{}{}", working.value(), returned.value()));
|
||||
// index gets incremented here
|
||||
let new_index = IntObj::new(index.value() + 1);
|
||||
|
||||
machine.stack_push(new_str)?;
|
||||
machine.stack_push(new_index)?;
|
||||
machine.stack_push(array_obj)?;
|
||||
Ok(BuiltinExit::Resume(INNER_BEGIN))
|
||||
}
|
||||
_ => unreachable!()
|
||||
}
|
||||
/*
|
||||
let obj_ptr: ObjPtr = machine.stack_pop()?;
|
||||
let obj: &(dyn Obj + 'static) = &*obj_ptr;
|
||||
if let Some(int) = obj.as_any().downcast_ref::<IntObj>() {
|
||||
let string = StrObj::new(int.value.to_string());
|
||||
machine.stack_push(string)?;
|
||||
Ok(BuiltinExit::Return)
|
||||
} else {
|
||||
Err(RuntimeError::WrongValue("int".to_string()))
|
||||
}
|
||||
*/
|
||||
})
|
||||
.finish();
|
||||
}
|
||||
|
||||
impl ArrayObj {
|
||||
pub fn new(value: Array) -> Gc<Self> {
|
||||
Gc::new(Self {
|
||||
value,
|
||||
vtable: VTABLE.with(Clone::clone),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn value(&self) -> &Array {
|
||||
&self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl Obj for ArrayObj {
|
||||
fn vtable(&self) -> &VTable {
|
||||
&self.vtable
|
||||
}
|
||||
|
||||
fn vtable_mut(&mut self) -> &mut VTable {
|
||||
&mut self.vtable
|
||||
}
|
||||
|
||||
fn call(&self, call_site: Option<Span>, _machine: &mut Machine) -> Result<()> {
|
||||
Err(RuntimeError::CannotCall("array value".to_string()).with_location(call_site))
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &(dyn std::any::Any + 'static) {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,5 +79,8 @@ pub trait Obj: Trace + Finalize + Debug {
|
||||
self.vtable_mut().insert(key, value)
|
||||
}
|
||||
|
||||
/// Gets this object as an `Any`.
|
||||
///
|
||||
/// This is useful for downcasting to expected values.
|
||||
fn as_any(&self) -> &(dyn Any + 'static);
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ assign = { ":" ~ word }
|
||||
atom = { float | int | assign | word | str | apply }
|
||||
quote = { "[" ~ stmt* ~ "]" }
|
||||
array = { "{" ~ stmt* ~ "}" }
|
||||
expr = { atom | quote }
|
||||
expr = { atom | quote | array }
|
||||
include = { "%include" ~ str }
|
||||
stmt = { include | expr }
|
||||
|
||||
|
||||
@@ -18,6 +18,9 @@ pub enum RuntimeError {
|
||||
#[error("expected {0}")]
|
||||
WrongValue(String),
|
||||
|
||||
#[error("underflow when trying to finalize a new array (stack was smaller than when array creation began)")]
|
||||
ArrayUnderflow,
|
||||
|
||||
#[error("at {0}")]
|
||||
Span(Span, Box<RuntimeError>),
|
||||
}
|
||||
|
||||
@@ -69,6 +69,7 @@ pub struct Machine {
|
||||
quote_table: QuoteTable,
|
||||
scope_stack: ScopeStack,
|
||||
call_stack: Vec<Frame>,
|
||||
array_marker_stack: Vec<usize>,
|
||||
}
|
||||
|
||||
impl Machine {
|
||||
@@ -84,6 +85,7 @@ impl Machine {
|
||||
quote_table: Default::default(),
|
||||
scope_stack,
|
||||
call_stack: Default::default(),
|
||||
array_marker_stack: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,10 +298,16 @@ impl Machine {
|
||||
value.call(Some(inst.span().clone()), self)?;
|
||||
}
|
||||
Inst::BeginArray => {
|
||||
todo!()
|
||||
self.array_marker_stack.push(self.stack().len());
|
||||
}
|
||||
Inst::EndArray => {
|
||||
todo!()
|
||||
let array_start = self.array_marker_stack.pop().expect("expected array end");
|
||||
if self.stack().len() <= array_start + 1 {
|
||||
return Err(RuntimeError::ArrayUnderflow);
|
||||
}
|
||||
let array = self.stack_mut().split_off(array_start);
|
||||
let array_obj = ArrayObj::new(array);
|
||||
self.stack_push(array_obj)?;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user