mirror of
https://github.com/zaphar/ucg.git
synced 2025-07-22 18:19:54 -04:00
DEV: A first pass at the runtime.
Minus the actual compiling of a file in imports.
This commit is contained in:
parent
a7aab10723
commit
9f31a16b93
@ -111,7 +111,7 @@ impl AssertCollector {
|
|||||||
failures: String::new(),
|
failures: String::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn record_assert_result(&mut self, msg: &str, is_success: bool) {
|
fn record_assert_result(&mut self, msg: &str, is_success: bool) {
|
||||||
if !is_success {
|
if !is_success {
|
||||||
let msg = format!("{} - NOT OK: {}\n", self.counter, msg);
|
let msg = format!("{} - NOT OK: {}\n", self.counter, msg);
|
||||||
@ -124,7 +124,6 @@ impl AssertCollector {
|
|||||||
}
|
}
|
||||||
self.counter += 1;
|
self.counter += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Builder handles building ucg code for a single file.
|
/// Builder handles building ucg code for a single file.
|
||||||
|
27
src/build/opcode/error.rs
Normal file
27
src/build/opcode/error.rs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
// 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::convert::From;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Error {}
|
||||||
|
|
||||||
|
impl<E> From<E> for Error
|
||||||
|
where
|
||||||
|
E: std::error::Error + Sized,
|
||||||
|
{
|
||||||
|
fn from(_e: E) -> Error {
|
||||||
|
// FIXME(jwall): This should really have more information for debugging
|
||||||
|
Error {}
|
||||||
|
}
|
||||||
|
}
|
@ -11,14 +11,17 @@
|
|||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
use std::convert::{TryFrom, TryInto};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
mod cache;
|
mod cache;
|
||||||
|
mod error;
|
||||||
pub mod pointer;
|
pub mod pointer;
|
||||||
mod runtime;
|
mod runtime;
|
||||||
pub mod scope;
|
pub mod scope;
|
||||||
mod vm;
|
mod vm;
|
||||||
|
|
||||||
|
pub use error::Error;
|
||||||
pub use vm::VM;
|
pub use vm::VM;
|
||||||
|
|
||||||
use pointer::OpPointer;
|
use pointer::OpPointer;
|
||||||
@ -34,12 +37,16 @@ pub enum Primitive {
|
|||||||
Empty,
|
Empty,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use Primitive::{Bool, Empty, Float, Int, Str};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub enum Composite {
|
pub enum Composite {
|
||||||
List(Vec<Rc<Value>>),
|
List(Vec<Rc<Value>>),
|
||||||
Tuple(Vec<(String, Rc<Value>)>),
|
Tuple(Vec<(String, Rc<Value>)>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use Composite::{List, Tuple};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub struct Func {
|
pub struct Func {
|
||||||
ptr: OpPointer,
|
ptr: OpPointer,
|
||||||
@ -70,6 +77,8 @@ pub enum Value {
|
|||||||
M(Module),
|
M(Module),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use Value::{C, F, M, P, S, T};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub enum Hook {
|
pub enum Hook {
|
||||||
Map,
|
Map,
|
||||||
@ -132,10 +141,101 @@ pub enum Op {
|
|||||||
FCall,
|
FCall,
|
||||||
// Runtime hooks
|
// Runtime hooks
|
||||||
Runtime(Hook),
|
Runtime(Hook),
|
||||||
|
// TODO(jwall): TRACE instruction
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
use super::ir::Val;
|
||||||
pub struct Error {}
|
|
||||||
|
impl TryFrom<Rc<Value>> for Val {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn try_from(val: Rc<Value>) -> Result<Val, Self::Error> {
|
||||||
|
val.as_ref().try_into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&Value> for Val {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn try_from(val: &Value) -> Result<Val, Self::Error> {
|
||||||
|
Ok(match val {
|
||||||
|
P(Int(i)) => Val::Int(*i),
|
||||||
|
P(Float(f)) => Val::Float(*f),
|
||||||
|
P(Str(s)) => Val::Str(s.clone()),
|
||||||
|
P(Bool(b)) => Val::Boolean(*b),
|
||||||
|
P(Empty) => Val::Empty,
|
||||||
|
C(Tuple(fs)) => {
|
||||||
|
let mut flds = Vec::new();
|
||||||
|
for &(ref k, ref v) in fs.iter() {
|
||||||
|
let v = v.clone();
|
||||||
|
// TODO(jwall): The RC for a Val should no longer be required.
|
||||||
|
flds.push((k.clone(), Rc::new(v.try_into()?)));
|
||||||
|
}
|
||||||
|
Val::Tuple(flds)
|
||||||
|
}
|
||||||
|
C(List(elems)) => {
|
||||||
|
let mut els = Vec::new();
|
||||||
|
for e in elems.iter() {
|
||||||
|
let e = e.clone();
|
||||||
|
// TODO
|
||||||
|
els.push(Rc::new(e.try_into()?));
|
||||||
|
}
|
||||||
|
Val::List(els)
|
||||||
|
}
|
||||||
|
S(_) | F(_) | M(_) | T(_) => {
|
||||||
|
return Err(dbg!(Error {}));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<Rc<Val>> for Value {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn try_from(val: Rc<Val>) -> Result<Self, Self::Error> {
|
||||||
|
val.as_ref().try_into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&Val> for Value {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn try_from(val: &Val) -> Result<Self, Self::Error> {
|
||||||
|
Ok(match val {
|
||||||
|
Val::Int(i) => P(Int(*i)),
|
||||||
|
Val::Float(f) => P(Float(*f)),
|
||||||
|
Val::Boolean(b) => P(Bool(*b)),
|
||||||
|
Val::Str(s) => P(Str(s.clone())),
|
||||||
|
Val::Empty => P(Empty),
|
||||||
|
Val::List(els) => {
|
||||||
|
let mut lst = Vec::new();
|
||||||
|
for e in els.iter() {
|
||||||
|
let e = e.clone();
|
||||||
|
lst.push(Rc::new(e.try_into()?));
|
||||||
|
}
|
||||||
|
C(List(lst))
|
||||||
|
}
|
||||||
|
Val::Tuple(flds) => {
|
||||||
|
let mut field_list = Vec::new();
|
||||||
|
for &(ref key, ref val) in flds.iter() {
|
||||||
|
let val = val.clone();
|
||||||
|
field_list.push((key.clone(), Rc::new(val.try_into()?)));
|
||||||
|
}
|
||||||
|
C(Tuple(field_list))
|
||||||
|
}
|
||||||
|
Val::Env(flds) => {
|
||||||
|
let mut field_list = Vec::new();
|
||||||
|
for &(ref key, ref val) in flds.iter() {
|
||||||
|
field_list.push((key.clone(), Rc::new(P(Str(val.clone())))));
|
||||||
|
}
|
||||||
|
C(Tuple(field_list))
|
||||||
|
}
|
||||||
|
// TODO(jwall): These can go away eventually when we replace the tree
|
||||||
|
// walking interpreter.
|
||||||
|
Val::Module(_) | Val::Func(_) => return Err(dbg!(Error {})),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test;
|
mod test;
|
||||||
|
@ -12,45 +12,116 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
use std::convert::TryInto;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Read;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use super::cache;
|
use super::cache;
|
||||||
|
use super::Value::{P, C, F};
|
||||||
use super::VM;
|
use super::VM;
|
||||||
use super::{Composite, Error, Hook, Primitive, Value};
|
use super::{Composite, Error, Hook, Primitive, Value};
|
||||||
use crate::build::AssertCollector;
|
use crate::build::AssertCollector;
|
||||||
use Composite::Tuple;
|
use crate::convert::{ConverterRegistry, ImporterRegistry};
|
||||||
use Primitive::{Bool, Str};
|
use Composite::{List, Tuple};
|
||||||
|
use Primitive::{Bool, Empty, Str};
|
||||||
|
|
||||||
pub struct Builtins {
|
pub struct Builtins {
|
||||||
op_cache: cache::Ops,
|
op_cache: cache::Ops,
|
||||||
val_cache: BTreeMap<String, Rc<Value>>,
|
val_cache: BTreeMap<String, Rc<Value>>,
|
||||||
assert_results: AssertCollector,
|
assert_results: AssertCollector,
|
||||||
|
converter_registry: ConverterRegistry,
|
||||||
|
importer_registry: ImporterRegistry,
|
||||||
|
working_dir: PathBuf,
|
||||||
|
import_path: Vec<PathBuf>,
|
||||||
// TODO(jwall): IO sink for stderr
|
// TODO(jwall): IO sink for stderr
|
||||||
// TODO(jwall): IO sink for stdout
|
// TODO(jwall): IO sink for stdout
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Builtins {
|
impl Builtins {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
|
Self::with_working_dir(std::env::current_dir().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_working_dir<P: Into<PathBuf>>(path: P) -> Self {
|
||||||
Self {
|
Self {
|
||||||
op_cache: cache::Ops::new(),
|
op_cache: cache::Ops::new(),
|
||||||
val_cache: BTreeMap::new(),
|
val_cache: BTreeMap::new(),
|
||||||
assert_results: AssertCollector::new(),
|
assert_results: AssertCollector::new(),
|
||||||
|
converter_registry: ConverterRegistry::make_registry(),
|
||||||
|
importer_registry: ImporterRegistry::make_registry(),
|
||||||
|
// TODO(jwall): This should move into the VM and not in the Runtime.
|
||||||
|
working_dir: path.into(),
|
||||||
|
import_path: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle(&mut self, h: Hook, stack: &mut Vec<Rc<Value>>) -> Result<(), Error> {
|
pub fn handle<P: AsRef<Path>>(
|
||||||
|
&mut self,
|
||||||
|
path: P,
|
||||||
|
h: Hook,
|
||||||
|
stack: &mut Vec<Rc<Value>>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
match h {
|
match h {
|
||||||
Hook::Import => self.import(stack),
|
Hook::Import => self.import(stack),
|
||||||
Hook::Include => self.include(stack),
|
Hook::Include => self.include(stack),
|
||||||
Hook::Assert => self.assert(stack),
|
Hook::Assert => self.assert(stack),
|
||||||
Hook::Convert => self.convert(stack),
|
Hook::Convert => self.convert(stack),
|
||||||
Hook::Out => self.out(stack),
|
Hook::Out => self.out(path, stack),
|
||||||
Hook::Map => self.map(stack),
|
Hook::Map => self.map(path, stack),
|
||||||
Hook::Filter => self.filter(stack),
|
Hook::Filter => self.filter(path, stack),
|
||||||
Hook::Reduce => self.reduce(stack),
|
Hook::Reduce => self.reduce(path, stack),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn find_file<P: Into<PathBuf>>(
|
||||||
|
&self,
|
||||||
|
path: P,
|
||||||
|
use_import_path: bool,
|
||||||
|
) -> Result<PathBuf, Error> {
|
||||||
|
// Try a relative path first.
|
||||||
|
let path = path.into();
|
||||||
|
let mut normalized = self.working_dir.clone();
|
||||||
|
if path.is_relative() {
|
||||||
|
normalized.push(&path);
|
||||||
|
// First see if the normalized file exists or not.
|
||||||
|
if !normalized.exists() && use_import_path {
|
||||||
|
// TODO(jwall): Support importing from a zip file in this
|
||||||
|
// import_path?
|
||||||
|
// If it does not then look for it in the list of import_paths
|
||||||
|
for mut p in self.import_path.iter().cloned() {
|
||||||
|
p.push(&path);
|
||||||
|
if p.exists() {
|
||||||
|
normalized = p;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
normalized = path;
|
||||||
|
}
|
||||||
|
match normalized.canonicalize() {
|
||||||
|
Ok(p) => Ok(p),
|
||||||
|
Err(_e) => Err(dbg!(Error {})),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_file_as_string(&self, path: &str) -> Result<String, Error> {
|
||||||
|
let sep = format!("{}", std::path::MAIN_SEPARATOR);
|
||||||
|
let raw_path = path.replace("/", &sep);
|
||||||
|
let normalized = match self.find_file(raw_path, false) {
|
||||||
|
Ok(p) => p,
|
||||||
|
Err(_e) => {
|
||||||
|
return Err(dbg!(Error {}));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let mut f = File::open(normalized).unwrap();
|
||||||
|
let mut contents = String::new();
|
||||||
|
f.read_to_string(&mut contents).unwrap();
|
||||||
|
Ok(contents)
|
||||||
|
}
|
||||||
|
|
||||||
fn import(&mut self, stack: &mut Vec<Rc<Value>>) -> Result<(), Error> {
|
fn import(&mut self, stack: &mut Vec<Rc<Value>>) -> Result<(), Error> {
|
||||||
let path = stack.pop();
|
let path = stack.pop();
|
||||||
if let Some(val) = path {
|
if let Some(val) = path {
|
||||||
@ -59,10 +130,10 @@ impl Builtins {
|
|||||||
stack.push(self.val_cache[path].clone());
|
stack.push(self.val_cache[path].clone());
|
||||||
} else {
|
} else {
|
||||||
let op_pointer = self.op_cache.entry(path).get_pointer_or_else(|| {
|
let op_pointer = self.op_cache.entry(path).get_pointer_or_else(|| {
|
||||||
// TODO(jwall): import
|
// FIXME(jwall): import
|
||||||
unimplemented!("Compiling paths are not implemented yet");
|
unimplemented!("Compiling paths are not implemented yet");
|
||||||
});
|
});
|
||||||
let mut vm = VM::with_pointer(op_pointer);
|
let mut vm = VM::with_pointer(path, op_pointer);
|
||||||
vm.run()?;
|
vm.run()?;
|
||||||
let result = Rc::new(vm.symbols_to_tuple(true));
|
let result = Rc::new(vm.symbols_to_tuple(true));
|
||||||
self.val_cache.insert(path.clone(), result.clone());
|
self.val_cache.insert(path.clone(), result.clone());
|
||||||
@ -77,15 +148,48 @@ impl Builtins {
|
|||||||
fn include(&self, stack: &mut Vec<Rc<Value>>) -> Result<(), Error> {
|
fn include(&self, stack: &mut Vec<Rc<Value>>) -> Result<(), Error> {
|
||||||
// TODO(jwall): include
|
// TODO(jwall): include
|
||||||
let path = stack.pop();
|
let path = stack.pop();
|
||||||
if let Some(val) = path {
|
let typ = stack.pop();
|
||||||
if let &Value::P(Str(ref path)) = val.as_ref() {}
|
let path = if let Some(val) = path {
|
||||||
unimplemented!("TODO(jwall): Includes are not implemented yet")
|
if let &Value::P(Str(ref path)) = val.as_ref() {
|
||||||
|
path.clone()
|
||||||
|
} else {
|
||||||
|
return dbg!(Err(Error {}));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return dbg!(Err(Error {}));
|
||||||
|
};
|
||||||
|
let typ = if let Some(val) = typ.as_ref() {
|
||||||
|
if let &Value::P(Str(ref typ)) = val.as_ref() {
|
||||||
|
typ.clone()
|
||||||
|
} else {
|
||||||
|
return dbg!(Err(Error {}));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return dbg!(Err(Error {}));
|
||||||
|
};
|
||||||
|
if typ == "str" {
|
||||||
|
stack.push(Rc::new(P(Str(self.get_file_as_string(&path)?))));
|
||||||
|
} else {
|
||||||
|
stack.push(Rc::new(match self.importer_registry.get_importer(&typ) {
|
||||||
|
Some(importer) => {
|
||||||
|
let contents = self.get_file_as_string(&path)?;
|
||||||
|
if contents.len() == 0 {
|
||||||
|
eprintln!("including an empty file. Use NULL as the result");
|
||||||
|
P(Empty)
|
||||||
|
} else {
|
||||||
|
match importer.import(contents.as_bytes()) {
|
||||||
|
Ok(v) => v.try_into()?,
|
||||||
|
Err(_e) => return dbg!(Err(Error {})),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => return dbg!(Err(Error {})),
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
return Err(Error {});
|
return Err(Error {});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assert(&mut self, stack: &mut Vec<Rc<Value>>) -> Result<(), Error> {
|
fn assert(&mut self, stack: &mut Vec<Rc<Value>>) -> Result<(), Error> {
|
||||||
// TODO(jwall): assert
|
|
||||||
let tuple = stack.pop();
|
let tuple = stack.pop();
|
||||||
if let Some(val) = tuple.clone() {
|
if let Some(val) = tuple.clone() {
|
||||||
if let &Value::C(Tuple(ref tuple)) = val.as_ref() {
|
if let &Value::C(Tuple(ref tuple)) = val.as_ref() {
|
||||||
@ -119,38 +223,177 @@ impl Builtins {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn out<P: AsRef<Path>>(&self, path: P, stack: &mut Vec<Rc<Value>>) -> Result<(), Error> {
|
||||||
|
let val = stack.pop();
|
||||||
|
if let Some(val) = val {
|
||||||
|
let val = val.try_into()?;
|
||||||
|
if let Some(c_type_val) = stack.pop() {
|
||||||
|
if let &Value::S(ref c_type) = c_type_val.as_ref() {
|
||||||
|
if let Some(c) = self.converter_registry.get_converter(c_type) {
|
||||||
|
match c.convert(Rc::new(val), &mut File::create(path)?) {
|
||||||
|
Ok(_) => {
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
Err(_e) => return Err(Error {}),
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Err(Error {});
|
||||||
|
}
|
||||||
|
|
||||||
fn convert(&self, stack: &mut Vec<Rc<Value>>) -> Result<(), Error> {
|
fn convert(&self, stack: &mut Vec<Rc<Value>>) -> Result<(), Error> {
|
||||||
// TODO(jwall): convert
|
|
||||||
let val = stack.pop();
|
let val = stack.pop();
|
||||||
if let Some(val) = val {
|
if let Some(val) = val {
|
||||||
unimplemented!("TODO(jwall): Conversions are not implemented yet")
|
let val = val.try_into()?;
|
||||||
} else {
|
if let Some(c_type_val) = stack.pop() {
|
||||||
Err(Error {})
|
if let &Value::S(ref c_type) = c_type_val.as_ref() {
|
||||||
|
if let Some(c) = self.converter_registry.get_converter(c_type) {
|
||||||
|
let mut buf: Vec<u8> = Vec::new();
|
||||||
|
match c.convert(Rc::new(val), &mut buf) {
|
||||||
|
Ok(_) => {
|
||||||
|
stack
|
||||||
|
.push(Rc::new(P(Str(
|
||||||
|
String::from_utf8_lossy(buf.as_slice()).to_string()
|
||||||
|
))));
|
||||||
|
}
|
||||||
|
Err(_e) => return Err(Error {}),
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return Err(Error {});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn out(&self, stack: &mut Vec<Rc<Value>>) -> Result<(), Error> {
|
fn map<P: AsRef<Path>>(&self, path: P, stack: &mut Vec<Rc<Value>>) -> Result<(), Error> {
|
||||||
// TODO(jwall): out
|
// get the list from the stack
|
||||||
let val = stack.pop();
|
let list = if let Some(list) = stack.pop() {
|
||||||
if let Some(val) = val {
|
list
|
||||||
unimplemented!("TODO(jwall): Out expressions are not implemented yet")
|
|
||||||
} else {
|
} else {
|
||||||
Err(Error {})
|
return dbg!(Err(Error {}));
|
||||||
|
};
|
||||||
|
let elems = if let &C(List(ref elems)) = list.as_ref() {
|
||||||
|
elems
|
||||||
|
} else {
|
||||||
|
return dbg!(Err(Error {}));
|
||||||
|
};
|
||||||
|
|
||||||
|
// get the func ptr from the stack
|
||||||
|
let fptr = if let Some(ptr) = stack.pop() {
|
||||||
|
ptr
|
||||||
|
} else {
|
||||||
|
return dbg!(Err(Error {}));
|
||||||
|
};
|
||||||
|
|
||||||
|
let f = if let &F(ref f) = fptr.as_ref() {
|
||||||
|
f
|
||||||
|
} else {
|
||||||
|
return dbg!(Err(Error {}));
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut result_elems = Vec::new();
|
||||||
|
for e in elems.iter() {
|
||||||
|
// push function argument on the stack.
|
||||||
|
stack.push(e.clone());
|
||||||
|
// call function and push it's result on the stack.
|
||||||
|
result_elems.push(VM::fcall_impl(path.as_ref().to_owned(), f, stack)?);
|
||||||
}
|
}
|
||||||
|
stack.push(Rc::new(C(List(result_elems))));
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn map(&self, stack: &mut Vec<Rc<Value>>) -> Result<(), Error> {
|
fn filter<P: AsRef<Path>>(&self, path: P, stack: &mut Vec<Rc<Value>>) -> Result<(), Error> {
|
||||||
// TODO(jwall): map (combine these into one?)
|
// get the list from the stack
|
||||||
unimplemented!("TODO(jwall): Map expressions are not implemented yet")
|
let list = if let Some(list) = stack.pop() {
|
||||||
|
list
|
||||||
|
} else {
|
||||||
|
return dbg!(Err(Error {}));
|
||||||
|
};
|
||||||
|
let elems = if let &C(List(ref elems)) = list.as_ref() {
|
||||||
|
elems
|
||||||
|
} else {
|
||||||
|
return dbg!(Err(Error {}));
|
||||||
|
};
|
||||||
|
|
||||||
|
// get the func ptr from the stack
|
||||||
|
let fptr = if let Some(ptr) = stack.pop() {
|
||||||
|
ptr
|
||||||
|
} else {
|
||||||
|
return dbg!(Err(Error {}));
|
||||||
|
};
|
||||||
|
|
||||||
|
let f = if let &F(ref f) = fptr.as_ref() {
|
||||||
|
f
|
||||||
|
} else {
|
||||||
|
return dbg!(Err(Error {}));
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut result_elems = Vec::new();
|
||||||
|
for e in elems.iter() {
|
||||||
|
// push function argument on the stack.
|
||||||
|
stack.push(e.clone());
|
||||||
|
// call function and push it's result on the stack.
|
||||||
|
let condition = VM::fcall_impl(path.as_ref().to_owned(), f, stack)?;
|
||||||
|
// Check for empty or boolean results and only push e back in
|
||||||
|
// if they are non empty and true
|
||||||
|
match condition.as_ref() {
|
||||||
|
&P(Empty) | &P(Bool(false)) => {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
_ => result_elems.push(e.clone()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stack.push(Rc::new(C(List(result_elems))));
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn filter(&self, stack: &mut Vec<Rc<Value>>) -> Result<(), Error> {
|
fn reduce<P: AsRef<Path>>(&self, path: P, stack: &mut Vec<Rc<Value>>) -> Result<(), Error> {
|
||||||
// TODO(jwall): filter
|
// get the list from the stack
|
||||||
unimplemented!("TODO(jwall): Filter expressions are not implemented yet")
|
let list = if let Some(list) = stack.pop() {
|
||||||
}
|
list
|
||||||
|
} else {
|
||||||
|
return dbg!(Err(Error {}));
|
||||||
|
};
|
||||||
|
let elems = if let &C(List(ref elems)) = list.as_ref() {
|
||||||
|
elems
|
||||||
|
} else {
|
||||||
|
return dbg!(Err(Error {}));
|
||||||
|
};
|
||||||
|
|
||||||
fn reduce(&self, stack: &mut Vec<Rc<Value>>) -> Result<(), Error> {
|
// Get the accumulator from the stack
|
||||||
// TODO(jwall): reduce
|
let mut acc = if let Some(acc) = stack.pop() {
|
||||||
unimplemented!("TODO(jwall): Reduce expressions are not implemented yet")
|
acc
|
||||||
|
} else {
|
||||||
|
return dbg!(Err(Error {}));
|
||||||
|
};
|
||||||
|
// get the func ptr from the stack
|
||||||
|
let fptr = if let Some(ptr) = stack.pop() {
|
||||||
|
ptr
|
||||||
|
} else {
|
||||||
|
return dbg!(Err(Error {}));
|
||||||
|
};
|
||||||
|
|
||||||
|
let f = if let &F(ref f) = fptr.as_ref() {
|
||||||
|
f
|
||||||
|
} else {
|
||||||
|
return dbg!(Err(Error {}));
|
||||||
|
};
|
||||||
|
|
||||||
|
for e in elems.iter() {
|
||||||
|
// push function arguments on the stack.
|
||||||
|
stack.push(e.clone());
|
||||||
|
stack.push(acc.clone());
|
||||||
|
// call function and push it's result on the stack.
|
||||||
|
acc = VM::fcall_impl(path.as_ref().to_owned(), f, stack)?;
|
||||||
|
// Check for empty or boolean results and only push e back in
|
||||||
|
// if they are non empty and true
|
||||||
|
}
|
||||||
|
// push the acc on the stack as our result
|
||||||
|
stack.push(acc);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ use super::VM;
|
|||||||
macro_rules! assert_cases {
|
macro_rules! assert_cases {
|
||||||
(__impl__ $cases:expr) => {
|
(__impl__ $cases:expr) => {
|
||||||
for case in $cases.drain(0..) {
|
for case in $cases.drain(0..) {
|
||||||
let mut vm = VM::new(Rc::new(case.0));
|
let mut vm = VM::new("foo.ucg", Rc::new(case.0));
|
||||||
vm.run().unwrap();
|
vm.run().unwrap();
|
||||||
assert_eq!(dbg!(vm.pop()).unwrap(), Rc::new(case.1));
|
assert_eq!(dbg!(vm.pop()).unwrap(), Rc::new(case.1));
|
||||||
}
|
}
|
||||||
@ -88,7 +88,7 @@ fn test_bind_op() {
|
|||||||
)];
|
)];
|
||||||
|
|
||||||
for case in cases.drain(0..) {
|
for case in cases.drain(0..) {
|
||||||
let mut vm = VM::new(Rc::new(case.0));
|
let mut vm = VM::new("bar.ucg", Rc::new(case.0));
|
||||||
vm.run().unwrap();
|
vm.run().unwrap();
|
||||||
let (name, result) = case.1;
|
let (name, result) = case.1;
|
||||||
let v = vm.get_binding(name).unwrap();
|
let v = vm.get_binding(name).unwrap();
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
use std::path::PathBuf;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use super::pointer::OpPointer;
|
use super::pointer::OpPointer;
|
||||||
@ -30,19 +31,21 @@ pub struct VM {
|
|||||||
symbols: Stack,
|
symbols: Stack,
|
||||||
runtime: Rc<RefCell<runtime::Builtins>>,
|
runtime: Rc<RefCell<runtime::Builtins>>,
|
||||||
ops: OpPointer,
|
ops: OpPointer,
|
||||||
|
path: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> VM {
|
impl<'a> VM {
|
||||||
pub fn new(ops: Rc<Vec<Op>>) -> Self {
|
pub fn new<P: Into<PathBuf>>(path: P, ops: Rc<Vec<Op>>) -> Self {
|
||||||
Self::with_pointer(OpPointer::new(ops))
|
Self::with_pointer(path, OpPointer::new(ops))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_pointer(ops: OpPointer) -> Self {
|
pub fn with_pointer<P: Into<PathBuf>>(path: P, ops: OpPointer) -> Self {
|
||||||
Self {
|
Self {
|
||||||
stack: Vec::new(),
|
stack: Vec::new(),
|
||||||
symbols: Stack::new(),
|
symbols: Stack::new(),
|
||||||
runtime: Rc::new(RefCell::new(runtime::Builtins::new())),
|
runtime: Rc::new(RefCell::new(runtime::Builtins::new())),
|
||||||
ops: ops,
|
ops: ops,
|
||||||
|
path: path.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,6 +55,7 @@ impl<'a> VM {
|
|||||||
symbols: symbols,
|
symbols: symbols,
|
||||||
runtime: self.runtime.clone(),
|
runtime: self.runtime.clone(),
|
||||||
ops: self.ops.clone(),
|
ops: self.ops.clone(),
|
||||||
|
path: self.path.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,26 +230,35 @@ impl<'a> VM {
|
|||||||
self.ops.jump(jptr)
|
self.ops.jump(jptr)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_fcall(&mut self) -> Result<(), Error> {
|
pub fn fcall_impl<P: Into<PathBuf>>(
|
||||||
let f = self.pop()?;
|
path: P,
|
||||||
if let &F(Func {
|
f: &Func,
|
||||||
|
stack: &mut Vec<Rc<Value>>,
|
||||||
|
) -> Result<Rc<Value>, Error> {
|
||||||
|
let Func {
|
||||||
ref ptr,
|
ref ptr,
|
||||||
ref bindings,
|
ref bindings,
|
||||||
ref snapshot,
|
ref snapshot,
|
||||||
}) = f.as_ref()
|
} = f;
|
||||||
{
|
// use the captured scope snapshot for the function.
|
||||||
// use the captured scope snapshot for the function.
|
let mut vm = Self::with_pointer(path, ptr.clone()).to_scoped(snapshot.clone());
|
||||||
let mut vm = Self::with_pointer(ptr.clone()).to_scoped(snapshot.clone());
|
for nm in bindings.iter() {
|
||||||
for nm in bindings.iter() {
|
// now put each argument on our scope stack as a binding.
|
||||||
// now put each argument on our scope stack as a binding.
|
// TODO(jwall): This should do a better error if there is
|
||||||
let val = self.pop()?;
|
// nothing on the stack.
|
||||||
vm.binding_push(nm.clone(), val)?;
|
let val = stack.pop().unwrap();
|
||||||
}
|
vm.binding_push(nm.clone(), val)?;
|
||||||
// proceed to the function body
|
}
|
||||||
vm.run()?;
|
// proceed to the function body
|
||||||
self.push(vm.pop()?)?;
|
vm.run()?;
|
||||||
} else {
|
return vm.pop();
|
||||||
return dbg!(Err(Error {}));
|
}
|
||||||
|
|
||||||
|
fn op_fcall(&mut self) -> Result<(), Error> {
|
||||||
|
let f = self.pop()?;
|
||||||
|
if let &F(ref f) = f.as_ref() {
|
||||||
|
let val = Self::fcall_impl(&self.path, f, &mut self.stack)?;
|
||||||
|
self.push(val)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -428,7 +441,11 @@ impl<'a> VM {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_in_flds(&self, index: &Value, flds: &Vec<(String, Rc<Value>)>) -> Result<Rc<Value>, Error> {
|
fn find_in_flds(
|
||||||
|
&self,
|
||||||
|
index: &Value,
|
||||||
|
flds: &Vec<(String, Rc<Value>)>,
|
||||||
|
) -> Result<Rc<Value>, Error> {
|
||||||
let idx = match index {
|
let idx = match index {
|
||||||
S(p) => p,
|
S(p) => p,
|
||||||
P(Str(p)) => p,
|
P(Str(p)) => p,
|
||||||
@ -508,7 +525,7 @@ impl<'a> VM {
|
|||||||
}
|
}
|
||||||
// FIXME(jwall): We need to populate the pkg key for modules.
|
// FIXME(jwall): We need to populate the pkg key for modules.
|
||||||
//self.merge_field_into_tuple(&mut flds, "this".to_owned(), this)?;
|
//self.merge_field_into_tuple(&mut flds, "this".to_owned(), this)?;
|
||||||
let mut vm = Self::with_pointer(ptr.clone());
|
let mut vm = Self::with_pointer(self.path.clone(), ptr.clone());
|
||||||
vm.push(Rc::new(S("mod".to_owned())))?;
|
vm.push(Rc::new(S("mod".to_owned())))?;
|
||||||
vm.push(Rc::new(C(Tuple(dbg!(flds)))))?;
|
vm.push(Rc::new(C(Tuple(dbg!(flds)))))?;
|
||||||
vm.run()?;
|
vm.run()?;
|
||||||
@ -548,7 +565,7 @@ impl<'a> VM {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn binding_push(&mut self, name: String, val: Rc<Value>) -> Result<(), Error> {
|
pub fn binding_push(&mut self, name: String, val: Rc<Value>) -> Result<(), Error> {
|
||||||
if self.symbols.is_bound(&name) {
|
if self.symbols.is_bound(&name) {
|
||||||
return Err(Error {});
|
return Err(Error {});
|
||||||
}
|
}
|
||||||
@ -609,6 +626,8 @@ impl<'a> VM {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn op_runtime(&mut self, h: Hook) -> Result<(), Error> {
|
fn op_runtime(&mut self, h: Hook) -> Result<(), Error> {
|
||||||
self.runtime.borrow_mut().handle(h, &mut self.stack)
|
self.runtime
|
||||||
|
.borrow_mut()
|
||||||
|
.handle(&self.path, h, &mut self.stack)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user