mirror of
https://github.com/zaphar/ucg.git
synced 2025-07-22 18:19:54 -04:00
DEV: Function and Module operations
This commit is contained in:
parent
7242a68ff9
commit
da3e235942
@ -11,10 +11,11 @@
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
pub mod pointer;
|
||||
pub mod scope;
|
||||
|
||||
use pointer::OpPointer;
|
||||
use scope::Stack;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum Primitive {
|
||||
@ -36,6 +37,20 @@ pub enum Composite {
|
||||
|
||||
use Composite::{List, Tuple};
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct Func {
|
||||
ptr: usize,
|
||||
bindings: Vec<String>,
|
||||
snapshot: Stack,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct Module {
|
||||
ptr: usize,
|
||||
result_ptr: Option<usize>,
|
||||
flds: Vec<(String, Value)>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum Value {
|
||||
// Binding names.
|
||||
@ -46,8 +61,12 @@ pub enum Value {
|
||||
C(Composite),
|
||||
// Program Pointer
|
||||
T(usize),
|
||||
// Function
|
||||
F(Func),
|
||||
// Module
|
||||
M(Module),
|
||||
}
|
||||
use Value::{C, P, S, T};
|
||||
use Value::{C, F, M, P, S, T};
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum Op {
|
||||
@ -69,57 +88,74 @@ pub enum Op {
|
||||
Val(Primitive),
|
||||
// A bareword for use in bindings or lookups
|
||||
Sym(String),
|
||||
// Reference a binding on the heap
|
||||
DeRef(String),
|
||||
// Complex Type ops
|
||||
InitTuple,
|
||||
Field,
|
||||
InitList,
|
||||
Element,
|
||||
// Operations
|
||||
// Copy Operation
|
||||
Cp,
|
||||
// Control Flow
|
||||
Bang,
|
||||
Jump(usize),
|
||||
JumpIfTrue(usize),
|
||||
// TODO(jwall): Short circuiting operations
|
||||
// - And(usize)
|
||||
// - Or(usize)
|
||||
// Spacer operation, Does nothing.
|
||||
Noop,
|
||||
// Pending Computation
|
||||
InitThunk(usize),
|
||||
Module(usize),
|
||||
Func(usize),
|
||||
Return,
|
||||
// - Call
|
||||
FCall,
|
||||
// Runtime hooks
|
||||
// - Map
|
||||
// - Filter
|
||||
// - Reduce
|
||||
// - Import
|
||||
// - Out
|
||||
// - Assert
|
||||
// - Print
|
||||
// - Map,
|
||||
// - Filter,
|
||||
// - Reduce,
|
||||
// - Import,
|
||||
// - Out,
|
||||
// - Assert,
|
||||
// - Print,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Error {}
|
||||
|
||||
pub struct VM {
|
||||
pub struct VM<'a> {
|
||||
stack: Vec<Value>,
|
||||
// TODO(jwall): We may want to preserve order on these.
|
||||
symbols: BTreeMap<String, Value>,
|
||||
ops: OpPointer,
|
||||
symbols: Stack,
|
||||
ops: OpPointer<'a>,
|
||||
}
|
||||
|
||||
impl VM {
|
||||
pub fn new(ops: Vec<Op>) -> Self {
|
||||
impl<'a> VM<'a> {
|
||||
pub fn new(ops: &'a Vec<Op>) -> Self {
|
||||
Self {
|
||||
stack: Vec::new(),
|
||||
symbols: BTreeMap::new(),
|
||||
symbols: Stack::new(),
|
||||
ops: OpPointer::new(ops),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_scoped(&self, symbols: Stack) -> Self {
|
||||
Self {
|
||||
stack: Vec::new(),
|
||||
symbols: symbols,
|
||||
ops: self.ops.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(&mut self) -> Result<(), Error> {
|
||||
while self.ops.next().is_some() {
|
||||
let idx = self.ops.ptr.unwrap();
|
||||
match self.ops.op().unwrap() {
|
||||
Op::Val(p) => self.push(P(p.clone()))?,
|
||||
Op::Sym(s) => self.push(S(s.clone()))?,
|
||||
Op::DeRef(s) => self.op_deref(s.clone())?,
|
||||
Op::Add => self.op_add()?,
|
||||
Op::Sub => self.op_sub()?,
|
||||
Op::Mul => self.op_mul()?,
|
||||
@ -137,16 +173,18 @@ impl VM {
|
||||
Op::Field => self.op_field()?,
|
||||
Op::Element => self.op_element()?,
|
||||
Op::Cp => self.op_copy()?,
|
||||
// TODO(jwall): Should this whould take a user provided message?
|
||||
Op::Bang => return Err(Error {}),
|
||||
Op::InitThunk(jp) => self.op_thunk(idx, *jp)?,
|
||||
Op::Noop => {
|
||||
// Do nothing
|
||||
}
|
||||
Op::Return => {
|
||||
// TODO(jwall): This means we return back to the start of the frame.
|
||||
}
|
||||
Op::Jump(jp) => self.op_jump(*jp)?,
|
||||
Op::JumpIfTrue(jp) => self.op_jump_if_true(*jp)?,
|
||||
Op::Module(mptr) => self.op_module(idx, *mptr)?,
|
||||
Op::Func(jptr) => self.op_func(idx, *jptr)?,
|
||||
Op::FCall => self.op_fcall()?,
|
||||
Op::Return => return Ok(()),
|
||||
Op::Pop => {
|
||||
self.pop()?;
|
||||
}
|
||||
@ -155,6 +193,11 @@ impl VM {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn op_deref(&mut self, name: String) -> Result<(), Error> {
|
||||
let val = dbg!(self.get_binding(&name)?.clone());
|
||||
self.push(val)
|
||||
}
|
||||
|
||||
fn op_jump(&mut self, jp: usize) -> Result<(), Error> {
|
||||
self.ops.jump(self.ops.ptr.map(|v| v + jp).unwrap_or(jp))?;
|
||||
Ok(())
|
||||
@ -169,8 +212,85 @@ impl VM {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn op_module(&mut self, idx: usize, jptr: usize) -> Result<(), Error> {
|
||||
let (result_ptr, flds) = match self.pop()? {
|
||||
C(Tuple(flds)) => (None, flds),
|
||||
T(ptr) => {
|
||||
if let C(Tuple(flds)) = self.pop()? {
|
||||
(Some(ptr), flds)
|
||||
} else {
|
||||
return dbg!(Err(Error {}));
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
return dbg!(Err(Error {}));
|
||||
}
|
||||
};
|
||||
self.push(M(Module {
|
||||
ptr: dbg!(idx),
|
||||
result_ptr: result_ptr,
|
||||
flds: dbg!(flds),
|
||||
}))?;
|
||||
self.ops.jump(dbg!(jptr))
|
||||
}
|
||||
|
||||
fn op_func(&mut self, idx: usize, jptr: usize) -> Result<(), Error> {
|
||||
// get arity from stack
|
||||
let mut scope_snapshot = self.symbols.snapshot();
|
||||
scope_snapshot.push();
|
||||
scope_snapshot.to_open();
|
||||
eprintln!("Defining a new function");
|
||||
let mut bindings = Vec::new();
|
||||
// get imported symbols from stack
|
||||
if let C(List(elems)) = self.pop()? {
|
||||
for e in elems {
|
||||
if let S(sym) = e {
|
||||
bindings.push(sym);
|
||||
} else {
|
||||
return dbg!(Err(Error {}));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return dbg!(Err(Error {}));
|
||||
}
|
||||
eprintln!("Pushing function definition on stack");
|
||||
self.push(dbg!(F(Func {
|
||||
ptr: idx, // where the function starts.
|
||||
bindings: bindings,
|
||||
snapshot: scope_snapshot,
|
||||
})))?;
|
||||
eprintln!("Jumping to {} past the function body", jptr);
|
||||
self.ops.jump(jptr)
|
||||
}
|
||||
|
||||
fn op_fcall(&mut self) -> Result<(), Error> {
|
||||
let f = self.pop()?;
|
||||
if let F(Func {
|
||||
ptr,
|
||||
bindings,
|
||||
snapshot,
|
||||
}) = f
|
||||
{
|
||||
// TODO(jwall): This is wasteful. We can do better.
|
||||
let mut vm = self.to_scoped(snapshot);
|
||||
// use the captured scope snapshot for the function.
|
||||
for nm in bindings {
|
||||
// now put each argument on our scope stack as a binding.
|
||||
let val = self.pop()?;
|
||||
vm.binding_push(nm, val)?;
|
||||
}
|
||||
// proceed to the function body
|
||||
vm.ops.jump(ptr)?;
|
||||
vm.run()?;
|
||||
self.push(vm.pop()?)?;
|
||||
} else {
|
||||
return dbg!(Err(Error {}));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn op_thunk(&mut self, idx: usize, jp: usize) -> Result<(), Error> {
|
||||
self.push(T(idx))?;
|
||||
self.push(dbg!(T(idx)))?;
|
||||
self.op_jump(jp)
|
||||
}
|
||||
|
||||
@ -279,9 +399,9 @@ impl VM {
|
||||
|
||||
fn op_bind(&mut self) -> Result<(), Error> {
|
||||
// pop val off stack.
|
||||
let val = self.pop()?;
|
||||
let val = dbg!(self.pop())?;
|
||||
// pop name off stack.
|
||||
let name = self.pop()?;
|
||||
let name = dbg!(self.pop())?;
|
||||
if let S(name) = name {
|
||||
self.binding_push(name, val)?;
|
||||
} else {
|
||||
@ -331,17 +451,57 @@ impl VM {
|
||||
|
||||
fn op_copy(&mut self) -> Result<(), Error> {
|
||||
// TODO Use Cow pointers for this?
|
||||
// get next value. It should be a Composite Tuple.
|
||||
if let C(Tuple(flds)) = self.pop()? {
|
||||
// Make a copy of the original
|
||||
let original = Tuple(flds.clone());
|
||||
let copy = Tuple(flds);
|
||||
// Put the original on the Stack as well as the copy
|
||||
self.push(C(original))?;
|
||||
self.push(C(copy))?;
|
||||
// get next value. It should be a Module.
|
||||
let tgt = self.pop()?;
|
||||
match tgt {
|
||||
C(Tuple(mut flds)) => {
|
||||
let overrides = self.pop()?;
|
||||
if let C(Tuple(oflds)) = overrides {
|
||||
for (name, val) in oflds {
|
||||
self.merge_field_into_tuple(&mut flds, name, val)?;
|
||||
}
|
||||
} else {
|
||||
return dbg!(Err(Error {}));
|
||||
}
|
||||
// Put the copy on the Stack
|
||||
self.push(C(Tuple(flds)))?;
|
||||
}
|
||||
M(Module {
|
||||
ptr,
|
||||
result_ptr,
|
||||
mut flds,
|
||||
}) => {
|
||||
let overrides = dbg!(self.pop()?);
|
||||
if let C(Tuple(oflds)) = overrides {
|
||||
for (name, val) in oflds {
|
||||
self.merge_field_into_tuple(&mut flds, name, val)?;
|
||||
}
|
||||
} else {
|
||||
return dbg!(Err(Error {}));
|
||||
}
|
||||
let mut vm = VM::new(self.ops.ops);
|
||||
vm.push(S("mod".to_owned()))?;
|
||||
vm.push(C(Tuple(flds)))?;
|
||||
vm.ops.jump(ptr)?;
|
||||
vm.run()?;
|
||||
let mut flds = Vec::new();
|
||||
if let Some(ptr) = dbg!(result_ptr) {
|
||||
vm.ops.jump(ptr)?;
|
||||
vm.run()?;
|
||||
self.push(vm.pop()?)?;
|
||||
} else {
|
||||
for sym in vm.symbols.symbol_list() {
|
||||
if sym != "mod" {
|
||||
flds.push((sym.clone(), vm.symbols.get(sym).unwrap().clone()));
|
||||
}
|
||||
}
|
||||
self.push(dbg!(C(Tuple(flds))))?;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
return Err(Error {});
|
||||
};
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -368,7 +528,7 @@ impl VM {
|
||||
|
||||
fn binding_push(&mut self, name: String, val: Value) -> Result<(), Error> {
|
||||
// FIXME(jwall): Error if the symbol already exists.
|
||||
self.symbols.insert(name, val);
|
||||
self.symbols.add(name, val);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -14,13 +14,14 @@
|
||||
|
||||
use super::{Error, Op};
|
||||
|
||||
pub struct OpPointer {
|
||||
ops: Vec<Op>,
|
||||
#[derive(Clone)]
|
||||
pub struct OpPointer<'a> {
|
||||
pub ops: &'a Vec<Op>,
|
||||
pub ptr: Option<usize>,
|
||||
}
|
||||
|
||||
impl OpPointer {
|
||||
pub fn new(ops: Vec<Op>) -> Self {
|
||||
impl<'a> OpPointer<'a> {
|
||||
pub fn new(ops: &'a Vec<Op>) -> Self {
|
||||
// If we load an empty program what happens?
|
||||
Self {
|
||||
ops: ops,
|
||||
@ -56,4 +57,11 @@ impl OpPointer {
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn snapshot(&self) -> Self {
|
||||
Self {
|
||||
ops: self.ops,
|
||||
ptr: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
179
src/build/opcode/scope.rs
Normal file
179
src/build/opcode/scope.rs
Normal file
@ -0,0 +1,179 @@
|
||||
// Copyright 2019 Jeremy Wall
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::iter::FromIterator;
|
||||
|
||||
use super::{Error, Value};
|
||||
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub enum Bindings {
|
||||
Sealed(BTreeMap<String, Value>),
|
||||
Open(BTreeMap<String, Value>),
|
||||
}
|
||||
use Bindings::{Open, Sealed};
|
||||
|
||||
impl Bindings {
|
||||
pub fn new() -> Self {
|
||||
Sealed(BTreeMap::new())
|
||||
}
|
||||
|
||||
pub fn into_open(self) -> Self {
|
||||
match self {
|
||||
Open(flds) => Open(flds),
|
||||
Sealed(flds) => Open(flds),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_sealed(self) -> Self {
|
||||
match self {
|
||||
Open(flds) => Sealed(flds),
|
||||
Sealed(flds) => Sealed(flds),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self, name: &str) -> Option<&Value> {
|
||||
match self {
|
||||
Open(flds) => flds.get(name),
|
||||
Sealed(flds) => flds.get(name),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add(&mut self, name: String, val: Value) {
|
||||
match self {
|
||||
Sealed(flds) => flds.insert(name, val),
|
||||
Open(flds) => flds.insert(name, val),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn symbol_list(&self) -> Vec<&String> {
|
||||
match self {
|
||||
Sealed(flds) => flds.keys().collect(),
|
||||
Open(flds) => flds.keys().collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub struct Stack {
|
||||
curr: Bindings,
|
||||
prev: Vec<Bindings>,
|
||||
}
|
||||
|
||||
impl Stack {
|
||||
pub fn new() -> Self {
|
||||
Stack {
|
||||
curr: Bindings::new(),
|
||||
prev: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self, name: &str) -> Option<&Value> {
|
||||
match &self.curr {
|
||||
Sealed(flds) => flds.get(name),
|
||||
Open(flds) => {
|
||||
if let Some(v) = flds.get(name) {
|
||||
return Some(v);
|
||||
} else {
|
||||
for b in self.prev.iter() {
|
||||
match b {
|
||||
Sealed(bflds) => {
|
||||
if let Some(v) = bflds.get(name) {
|
||||
return Some(v);
|
||||
}
|
||||
return None;
|
||||
}
|
||||
Open(bflds) => {
|
||||
if let Some(v) = bflds.get(name) {
|
||||
return Some(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push(&mut self) {
|
||||
let mut nb = Bindings::new();
|
||||
std::mem::swap(&mut nb, &mut self.curr);
|
||||
self.prev.push(nb);
|
||||
}
|
||||
|
||||
pub fn to_open(&mut self) {
|
||||
let mut tmp = Bindings::new();
|
||||
std::mem::swap(&mut tmp, &mut self.curr);
|
||||
tmp = tmp.into_open();
|
||||
std::mem::swap(&mut tmp, &mut self.curr);
|
||||
}
|
||||
|
||||
pub fn pop(&mut self) -> Result<(), Error> {
|
||||
if let Some(parent) = self.prev.pop() {
|
||||
self.curr = parent;
|
||||
Ok(())
|
||||
} else {
|
||||
dbg!(Err(Error {}))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add(&mut self, name: String, val: Value) {
|
||||
self.curr.add(name, val);
|
||||
}
|
||||
|
||||
pub fn symbol_list(&self) -> Vec<&String> {
|
||||
match &self.curr {
|
||||
Sealed(flds) => flds.keys().collect(),
|
||||
Open(flds) => {
|
||||
let mut keys = BTreeSet::new();
|
||||
keys.extend(flds.keys());
|
||||
for b in self.prev.iter() {
|
||||
match b {
|
||||
Sealed(bflds) => {
|
||||
keys.extend(bflds.keys());
|
||||
return Vec::from_iter(keys.iter().cloned());
|
||||
}
|
||||
Open(bflds) => {
|
||||
keys.extend(bflds.keys());
|
||||
}
|
||||
}
|
||||
}
|
||||
return Vec::from_iter(keys.iter().cloned());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn snapshot(&self) -> Self {
|
||||
let curr = self.curr.clone();
|
||||
match curr {
|
||||
Sealed(_) => Self {
|
||||
curr: curr,
|
||||
prev: Vec::new(),
|
||||
},
|
||||
Open(_) => {
|
||||
let mut prev = Vec::new();
|
||||
for b in self.prev.iter() {
|
||||
match b {
|
||||
Sealed(_) => {
|
||||
prev.push(b.clone());
|
||||
break;
|
||||
}
|
||||
Open(_) => prev.push(b.clone()),
|
||||
}
|
||||
}
|
||||
Self { curr, prev }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -12,10 +12,11 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use super::scope::Stack;
|
||||
use super::Composite::{List, Tuple};
|
||||
use super::Op::{
|
||||
Add, Bind, Cp, Div, Element, Equal, Field, InitList, InitThunk, InitTuple, Jump, JumpIfTrue,
|
||||
Mul, Noop, Sub, Sym, Val,
|
||||
Add, Bind, Cp, DeRef, Div, Element, Equal, FCall, Field, Func, InitList, InitThunk, InitTuple,
|
||||
Jump, JumpIfTrue, Module, Mul, Noop, Return, Sub, Sym, Val,
|
||||
};
|
||||
use super::Primitive::{Bool, Float, Int, Str};
|
||||
use super::Value::{C, P, T};
|
||||
@ -60,7 +61,7 @@ fn test_math_ops() {
|
||||
),
|
||||
];
|
||||
for case in cases.drain(0..) {
|
||||
let mut vm = VM::new(case.0);
|
||||
let mut vm = VM::new(&case.0);
|
||||
vm.run().unwrap();
|
||||
assert_eq!(vm.pop().unwrap(), case.1);
|
||||
}
|
||||
@ -76,7 +77,7 @@ fn test_bind_op() {
|
||||
)];
|
||||
|
||||
for case in cases.drain(0..) {
|
||||
let mut vm = VM::new(case.0);
|
||||
let mut vm = VM::new(&case.0);
|
||||
vm.run().unwrap();
|
||||
let (name, result) = case.1;
|
||||
let v = vm.get_binding(name).unwrap();
|
||||
@ -110,7 +111,7 @@ fn test_list_ops() {
|
||||
),
|
||||
];
|
||||
for case in cases.drain(0..) {
|
||||
let mut vm = VM::new(case.0);
|
||||
let mut vm = VM::new(&case.0);
|
||||
vm.run().unwrap();
|
||||
assert_eq!(vm.pop().unwrap(), case.1);
|
||||
}
|
||||
@ -148,7 +149,6 @@ fn test_tuple_ops() {
|
||||
Val(Str("foo".to_owned())),
|
||||
Val(Int(1)),
|
||||
Field,
|
||||
Cp,
|
||||
Val(Str("foo".to_owned())),
|
||||
Val(Int(2)),
|
||||
Field,
|
||||
@ -169,7 +169,6 @@ fn test_tuple_ops() {
|
||||
Val(Str("foo".to_owned())),
|
||||
Val(Int(1)),
|
||||
Field,
|
||||
Cp,
|
||||
Val(Str("foo".to_owned())),
|
||||
Val(Int(2)),
|
||||
Field,
|
||||
@ -179,9 +178,31 @@ fn test_tuple_ops() {
|
||||
("foo".to_owned(), P(Int(2))),
|
||||
])),
|
||||
),
|
||||
(
|
||||
vec![
|
||||
InitTuple, // Override tuple
|
||||
Val(Str("foo".to_owned())),
|
||||
Val(Int(2)),
|
||||
Field,
|
||||
InitTuple, // Target tuple
|
||||
Sym("bar".to_owned()),
|
||||
Val(Str("ux".to_owned())),
|
||||
Val(Str("qu".to_owned())),
|
||||
Add,
|
||||
Field,
|
||||
Val(Str("foo".to_owned())),
|
||||
Val(Int(1)),
|
||||
Field,
|
||||
Cp, // Do the tuple copy operation
|
||||
],
|
||||
C(Tuple(vec![
|
||||
("bar".to_owned(), P(Str("quux".to_owned()))),
|
||||
("foo".to_owned(), P(Int(2))),
|
||||
])),
|
||||
),
|
||||
];
|
||||
for case in cases.drain(0..) {
|
||||
let mut vm = VM::new(case.0);
|
||||
let mut vm = VM::new(&case.0);
|
||||
vm.run().unwrap();
|
||||
assert_eq!(vm.pop().unwrap(), case.1);
|
||||
}
|
||||
@ -194,7 +215,7 @@ fn test_jump_ops() {
|
||||
(vec![Jump(1), Val(Int(1)), Noop, Val(Int(1))], P(Int(1))),
|
||||
];
|
||||
for case in cases.drain(0..) {
|
||||
let mut vm = VM::new(case.0);
|
||||
let mut vm = VM::new(&case.0);
|
||||
vm.run().unwrap();
|
||||
assert_eq!(vm.pop().unwrap(), case.1);
|
||||
}
|
||||
@ -284,7 +305,7 @@ fn test_equality_ops() {
|
||||
),
|
||||
];
|
||||
for case in cases.drain(0..) {
|
||||
let mut vm = VM::new(case.0);
|
||||
let mut vm = VM::new(&case.0);
|
||||
vm.run().unwrap();
|
||||
assert_eq!(vm.pop().unwrap(), case.1);
|
||||
}
|
||||
@ -336,8 +357,141 @@ fn test_conditional_jump_ops() {
|
||||
),
|
||||
];
|
||||
for case in cases.drain(0..) {
|
||||
let mut vm = VM::new(case.0);
|
||||
let mut vm = VM::new(&case.0);
|
||||
vm.run().unwrap();
|
||||
assert_eq!(vm.pop().unwrap(), case.1);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_function_definition_and_call() {
|
||||
let mut cases = vec![
|
||||
(
|
||||
vec![
|
||||
Sym("f".to_owned()), // 0
|
||||
InitList, // 1
|
||||
Sym("arg".to_owned()), // 2
|
||||
Element, // 3
|
||||
Func(6), // 4
|
||||
DeRef("arg".to_owned()), // 5
|
||||
Return, // 6
|
||||
Bind, // 7
|
||||
Val(Int(1)), // 8
|
||||
DeRef("f".to_owned()), // 9
|
||||
FCall, // 10
|
||||
],
|
||||
P(Int(1)),
|
||||
),
|
||||
(
|
||||
vec![
|
||||
Sym("closed".to_owned()), // 0
|
||||
Val(Int(1)), // 1
|
||||
Bind, // 2
|
||||
Sym("f".to_owned()), // 3
|
||||
InitList, // 4
|
||||
Sym("arg".to_owned()), // 5
|
||||
Element, // 6
|
||||
Func(11), // 7
|
||||
DeRef("arg".to_owned()), // 8
|
||||
DeRef("closed".to_owned()), // 9
|
||||
Add, // 10
|
||||
Return, // 11
|
||||
Bind, // 12
|
||||
Val(Int(1)), // 13
|
||||
DeRef("f".to_owned()), // 14
|
||||
FCall, // 16
|
||||
],
|
||||
P(Int(2)),
|
||||
),
|
||||
];
|
||||
for case in cases.drain(0..) {
|
||||
let mut vm = VM::new(&case.0);
|
||||
vm.run().unwrap();
|
||||
assert_eq!(vm.pop().unwrap(), case.1);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_module_call() {
|
||||
let mut cases = vec![
|
||||
(
|
||||
vec![
|
||||
InitTuple, // 0 // override tuple
|
||||
Sym("one".to_owned()), // 1
|
||||
Val(Int(11)), // 2
|
||||
Field, // 3
|
||||
Sym("m".to_owned()), // 4 // binding name for module
|
||||
InitTuple, // 5 // Module tuple bindings
|
||||
Sym("one".to_owned()), // 6
|
||||
Val(Int(1)), // 7
|
||||
Field, // 8
|
||||
Sym("two".to_owned()), // 9
|
||||
Val(Int(2)), // 10
|
||||
Field, // 11
|
||||
Module(17), // 12 // Module definition
|
||||
Bind, // 13
|
||||
Sym("foo".to_owned()), // 14
|
||||
DeRef("mod".to_owned()), // 15
|
||||
Bind, // 16 // bind mod tuple to foo
|
||||
Return, // 17 // end the module
|
||||
Bind, // 18 // bind module to the binding name
|
||||
DeRef("m".to_owned()), // 19
|
||||
Cp, // 20
|
||||
],
|
||||
C(Tuple(vec![(
|
||||
"foo".to_owned(),
|
||||
C(Tuple(vec![
|
||||
("one".to_owned(), P(Int(11))),
|
||||
("two".to_owned(), P(Int(2))),
|
||||
])),
|
||||
)])),
|
||||
),
|
||||
(
|
||||
vec![
|
||||
InitTuple, // 0 // override tuple
|
||||
Sym("one".to_owned()), // 1
|
||||
Val(Int(11)), // 2
|
||||
Field, // 3
|
||||
Sym("m".to_owned()), // 4 // binding name for module
|
||||
InitTuple, // 5 // Module tuple bindings
|
||||
Sym("one".to_owned()), // 6
|
||||
Val(Int(1)), // 7
|
||||
Field, // 8
|
||||
Sym("two".to_owned()), // 9
|
||||
Val(Int(2)), // 10
|
||||
Field, // 11
|
||||
InitThunk(2), // 12 // Module Return expression
|
||||
Val(Int(1)), // 13
|
||||
Return, // 14
|
||||
Module(20), // 15 // Module definition
|
||||
Bind, // 16
|
||||
Sym("foo".to_owned()), // 17
|
||||
DeRef("mod".to_owned()), // 18
|
||||
Bind, // 19 // bind mod tuple to foo
|
||||
Return, // 20 // end the module
|
||||
Bind, // 21 // bind module to the binding name
|
||||
DeRef("m".to_owned()), // 22
|
||||
Cp, // 23
|
||||
],
|
||||
P(Int(1)),
|
||||
),
|
||||
];
|
||||
for case in cases.drain(0..) {
|
||||
let mut vm = VM::new(&case.0);
|
||||
vm.run().unwrap();
|
||||
assert_eq!(vm.pop().unwrap(), case.1);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scope_stacks() {
|
||||
let mut stack = Stack::new();
|
||||
stack.add("one".to_owned(), P(Int(1)));
|
||||
let mut val = stack.get("one").unwrap();
|
||||
assert_eq!(val, &P(Int(1)));
|
||||
stack.push();
|
||||
assert!(stack.get("one").is_none());
|
||||
stack.to_open();
|
||||
val = stack.get("one").unwrap();
|
||||
assert_eq!(val, &P(Int(1)));
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user