mirror of
https://github.com/alexgo-io/stacks-puppet-node.git
synced 2026-05-26 03:06:53 +08:00
separate two kinds of contexts (local and global) into two types
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
use vm::errors::{InterpreterResult as Result, Error};
|
||||
use vm::representations::SymbolicExpression;
|
||||
use vm::types::TypeSignature;
|
||||
use vm::{eval, Value, Context, Environment};
|
||||
use vm::{eval, Value, LocalContext, Environment};
|
||||
|
||||
pub enum CallableType <'a> {
|
||||
UserFunction(DefinedFunction),
|
||||
NativeFunction(&'a Fn(&[Value]) -> Result<Value>),
|
||||
SpecialFunction(&'a Fn(&[SymbolicExpression], &mut Environment, &Context) -> Result<Value>)
|
||||
SpecialFunction(&'a Fn(&[SymbolicExpression], &mut Environment, &LocalContext) -> Result<Value>)
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -47,7 +47,7 @@ impl PublicFunction {
|
||||
|
||||
fn apply(&self, args: &[Value], env: &mut Environment) -> Result<Value> {
|
||||
// since self is a malformed object.
|
||||
let mut context = Context::new();
|
||||
let mut context = LocalContext::new();
|
||||
let arg_iterator = self.arguments.iter().zip(self.types.iter()).zip(args.iter());
|
||||
for ((arg, type_sig), value) in arg_iterator {
|
||||
if !type_sig.admits(value) {
|
||||
@@ -70,7 +70,7 @@ impl PrivateFunction {
|
||||
}
|
||||
|
||||
fn apply(&self, args: &[Value], env: &mut Environment) -> Result<Value> {
|
||||
let mut context = Context::new();
|
||||
let mut context = LocalContext::new();
|
||||
let arg_iterator = self.arguments.iter().zip(args.iter());
|
||||
for (arg, value) in arg_iterator {
|
||||
if let Some(_) = context.variables.insert(arg.clone(), value.clone()) {
|
||||
|
||||
@@ -9,7 +9,7 @@ use vm::database::ContractDatabase;
|
||||
const MAX_CONTEXT_DEPTH: u16 = 256;
|
||||
|
||||
pub struct Environment <'a> {
|
||||
pub global_context: &'a Context <'a>,
|
||||
pub global_context: &'a GlobalContext,
|
||||
pub call_stack: CallStack,
|
||||
pub database: &'a mut ContractDatabase
|
||||
}
|
||||
@@ -18,7 +18,7 @@ impl <'a> Environment <'a> {
|
||||
// Environments pack a reference to the global context, a mutable reference to a contract db,
|
||||
// together with a call stack. Generally, the environment structure is intended to be reconstructed
|
||||
// for every transaction.
|
||||
pub fn new(global_context: &'a Context<'a>,
|
||||
pub fn new(global_context: &'a GlobalContext,
|
||||
database: &'a mut ContractDatabase) -> Environment<'a> {
|
||||
Environment {
|
||||
global_context: global_context,
|
||||
@@ -31,31 +31,56 @@ impl <'a> Environment <'a> {
|
||||
// Aaron: note -- only the global context will ever have DefinedFunctions
|
||||
// so it is probably worthwhile to separate into 2 types.
|
||||
|
||||
pub struct Context <'a> {
|
||||
pub parent: Option< &'a Context<'a>>,
|
||||
pub struct GlobalContext {
|
||||
pub variables: HashMap<String, Value>,
|
||||
pub functions: HashMap<String, DefinedFunction>,
|
||||
}
|
||||
|
||||
pub struct LocalContext <'a> {
|
||||
pub parent: Option< &'a LocalContext<'a>>,
|
||||
pub variables: HashMap<String, Value>,
|
||||
depth: u16
|
||||
}
|
||||
|
||||
impl <'a> Context <'a> {
|
||||
pub fn new() -> Context<'a> {
|
||||
Context {
|
||||
depth: 0,
|
||||
parent: Option::None,
|
||||
impl GlobalContext {
|
||||
pub fn new() -> GlobalContext {
|
||||
GlobalContext {
|
||||
variables: HashMap::new(),
|
||||
functions: HashMap::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lookup_variable(&self, name: &str) -> Option<Value> {
|
||||
match self.variables.get(name) {
|
||||
Some(value) => Option::Some(value.clone()),
|
||||
None => Option::None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lookup_function(&self, name: &str) -> Option<DefinedFunction> {
|
||||
match self.functions.get(name) {
|
||||
Some(value) => Option::Some(value.clone()),
|
||||
None => Option::None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl <'a> LocalContext <'a> {
|
||||
pub fn new() -> LocalContext<'a> {
|
||||
LocalContext {
|
||||
depth: 0,
|
||||
parent: Option::None,
|
||||
variables: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extend(&'a self) -> Result<Context<'a>> {
|
||||
pub fn extend(&'a self) -> Result<LocalContext<'a>> {
|
||||
if self.depth >= MAX_CONTEXT_DEPTH {
|
||||
Err(Error::MaxContextDepthReached)
|
||||
} else {
|
||||
Ok(Context {
|
||||
Ok(LocalContext {
|
||||
parent: Some(self),
|
||||
variables: HashMap::new(),
|
||||
functions: HashMap::new(),
|
||||
depth: self.depth + 1
|
||||
})
|
||||
}
|
||||
@@ -63,7 +88,7 @@ impl <'a> Context <'a> {
|
||||
|
||||
pub fn lookup_variable(&self, name: &str) -> Option<Value> {
|
||||
match self.variables.get(name) {
|
||||
Some(value) => Option::Some((*value).clone()),
|
||||
Some(value) => Option::Some(value.clone()),
|
||||
None => {
|
||||
match self.parent {
|
||||
Some(parent) => parent.lookup_variable(name),
|
||||
@@ -72,20 +97,6 @@ impl <'a> Context <'a> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lookup_function(&self, name: &str) -> Option<DefinedFunction> {
|
||||
match self.functions.get(name) {
|
||||
Some(value) => {
|
||||
Option::Some(value.clone())
|
||||
},
|
||||
None => {
|
||||
match self.parent {
|
||||
Some(parent) => parent.lookup_function(name),
|
||||
None => Option::None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CallStack {
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
use vm::{SymbolicExpression, Value, apply, eval_all};
|
||||
use vm::errors::{Error, InterpreterResult as Result};
|
||||
use vm::callables::CallableType;
|
||||
use vm::{Context, SymbolicExpression, Value, Environment, apply, eval_all};
|
||||
use vm::contexts::{Environment, LocalContext, GlobalContext};
|
||||
use vm::database::{MemoryContractDatabase, ContractDatabase};
|
||||
use vm::parser;
|
||||
use vm::variables;
|
||||
|
||||
pub struct Contract <'a> {
|
||||
pub struct Contract {
|
||||
db: Box<ContractDatabase>,
|
||||
global_context: Context<'a>
|
||||
global_context: GlobalContext
|
||||
}
|
||||
|
||||
impl <'a> Contract <'a> {
|
||||
pub fn make_in_memory_contract(contract: &str) -> Result<Contract<'a>> {
|
||||
impl Contract {
|
||||
pub fn make_in_memory_contract(contract: &str) -> Result<Contract> {
|
||||
let parsed: Vec<_> = parser::parse(contract)?;
|
||||
let mut global_context = Context::new();
|
||||
let mut global_context = GlobalContext::new();
|
||||
let mut db_instance = Box::new(MemoryContractDatabase::new());
|
||||
|
||||
let result = eval_all(&parsed, &mut *db_instance, &mut global_context)?;
|
||||
@@ -45,7 +46,7 @@ impl <'a> Contract <'a> {
|
||||
}
|
||||
}
|
||||
|
||||
let mut local_context = Context::new();
|
||||
let mut local_context = LocalContext::new();
|
||||
local_context.variables.insert(variables::TX_SENDER.to_string(), sender.clone());
|
||||
|
||||
apply(&CallableType::UserFunction(func), args, &mut env, &local_context)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use vm::types::Value;
|
||||
use vm::errors::{Error, InterpreterResult as Result};
|
||||
use vm::representations::SymbolicExpression;
|
||||
use vm::{Context,Environment,eval};
|
||||
use vm::{LocalContext, Environment, eval};
|
||||
|
||||
fn type_force_bool(value: &Value) -> Result<bool> {
|
||||
match *value {
|
||||
@@ -10,7 +10,7 @@ fn type_force_bool(value: &Value) -> Result<bool> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn special_or(args: &[SymbolicExpression], env: &mut Environment, context: &Context) -> Result<Value> {
|
||||
pub fn special_or(args: &[SymbolicExpression], env: &mut Environment, context: &LocalContext) -> Result<Value> {
|
||||
if args.len() < 1 {
|
||||
return Err(Error::InvalidArguments("(or ...) requires at least 1 argument".to_string()))
|
||||
}
|
||||
@@ -26,7 +26,7 @@ pub fn special_or(args: &[SymbolicExpression], env: &mut Environment, context: &
|
||||
Ok(Value::Bool(false))
|
||||
}
|
||||
|
||||
pub fn special_and(args: &[SymbolicExpression], env: &mut Environment, context: &Context) -> Result<Value> {
|
||||
pub fn special_and(args: &[SymbolicExpression], env: &mut Environment, context: &LocalContext) -> Result<Value> {
|
||||
if args.len() < 1 {
|
||||
return Err(Error::InvalidArguments("(and ...) requires at least 1 argument".to_string()))
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use vm::types::{Value};
|
||||
use vm::representations::SymbolicExpression;
|
||||
use vm::errors::{Error, InterpreterResult as Result};
|
||||
use vm::database::DataMap;
|
||||
use vm::{eval,Context,Environment};
|
||||
use vm::{eval, LocalContext, Environment};
|
||||
|
||||
fn obtain_map <'a> (map_arg: &SymbolicExpression, env: &'a mut Environment) -> Result<&'a mut DataMap> {
|
||||
let map_name = match map_arg {
|
||||
@@ -17,7 +17,7 @@ fn obtain_map <'a> (map_arg: &SymbolicExpression, env: &'a mut Environment) -> R
|
||||
|
||||
pub fn special_fetch_entry(args: &[SymbolicExpression],
|
||||
env: &mut Environment,
|
||||
context: &Context) -> Result<Value> {
|
||||
context: &LocalContext) -> Result<Value> {
|
||||
// arg0 -> map name
|
||||
// arg1 -> key
|
||||
if args.len() != 2 {
|
||||
@@ -41,7 +41,7 @@ pub fn special_fetch_entry(args: &[SymbolicExpression],
|
||||
|
||||
pub fn special_set_entry(args: &[SymbolicExpression],
|
||||
env: &mut Environment,
|
||||
context: &Context) -> Result<Value> {
|
||||
context: &LocalContext) -> Result<Value> {
|
||||
// arg0 -> map name
|
||||
// arg1 -> key
|
||||
// arg2 -> value
|
||||
@@ -62,7 +62,7 @@ pub fn special_set_entry(args: &[SymbolicExpression],
|
||||
|
||||
pub fn special_insert_entry(args: &[SymbolicExpression],
|
||||
env: &mut Environment,
|
||||
context: &Context) -> Result<Value> {
|
||||
context: &LocalContext) -> Result<Value> {
|
||||
// arg0 -> map name
|
||||
// arg1 -> key
|
||||
// arg2 -> value
|
||||
@@ -80,7 +80,7 @@ pub fn special_insert_entry(args: &[SymbolicExpression],
|
||||
|
||||
pub fn special_delete_entry(args: &[SymbolicExpression],
|
||||
env: &mut Environment,
|
||||
context: &Context) -> Result<Value> {
|
||||
context: &LocalContext) -> Result<Value> {
|
||||
// arg0 -> map name
|
||||
// arg1 -> key
|
||||
if args.len() != 2 {
|
||||
|
||||
@@ -3,7 +3,8 @@ use vm::callables::{DefinedFunction, PublicFunction, PrivateFunction};
|
||||
use vm::representations::SymbolicExpression;
|
||||
use vm::representations::SymbolicExpression::{Atom, AtomValue, List, NamedParameter};
|
||||
use vm::errors::{Error, InterpreterResult as Result};
|
||||
use vm::{ Context, Environment, eval };
|
||||
use vm::contexts::{GlobalContext, LocalContext, Environment};
|
||||
use vm::eval;
|
||||
|
||||
pub enum DefineResult {
|
||||
Variable(String, Value),
|
||||
@@ -12,7 +13,7 @@ pub enum DefineResult {
|
||||
NoDefine
|
||||
}
|
||||
|
||||
fn check_legal_define(name: &str, global_context: &Context) -> Result<()> {
|
||||
fn check_legal_define(name: &str, global_context: &GlobalContext) -> Result<()> {
|
||||
use vm::is_reserved;
|
||||
|
||||
if is_reserved(name) {
|
||||
@@ -27,7 +28,7 @@ fn check_legal_define(name: &str, global_context: &Context) -> Result<()> {
|
||||
fn handle_define_variable(variable: &String, expression: &SymbolicExpression, env: &mut Environment) -> Result<DefineResult> {
|
||||
// is the variable name legal?
|
||||
check_legal_define(variable, &env.global_context)?;
|
||||
let context = Context::new();
|
||||
let context = LocalContext::new();
|
||||
let value = eval(expression, env, &context)?;
|
||||
Ok(DefineResult::Variable(variable.clone(), value))
|
||||
}
|
||||
|
||||
@@ -2,13 +2,13 @@ use vm::errors::{Error, InterpreterResult as Result};
|
||||
use vm::types::Value;
|
||||
use vm::representations::SymbolicExpression;
|
||||
use vm::representations::SymbolicExpression::{AtomValue};
|
||||
use vm::{Context,Environment,eval,apply,lookup_function};
|
||||
use vm::{LocalContext, Environment, eval, apply, lookup_function};
|
||||
|
||||
pub fn list_cons(args: &[Value]) -> Result<Value> {
|
||||
Value::new_list(args)
|
||||
}
|
||||
|
||||
pub fn list_fold(args: &[SymbolicExpression], env: &mut Environment, context: &Context) -> Result<Value> {
|
||||
pub fn list_fold(args: &[SymbolicExpression], env: &mut Environment, context: &LocalContext) -> Result<Value> {
|
||||
if args.len() != 3 {
|
||||
return Err(Error::InvalidArguments(format!("Wrong number of arguments ({}) to fold", args.len())))
|
||||
}
|
||||
@@ -30,7 +30,7 @@ pub fn list_fold(args: &[SymbolicExpression], env: &mut Environment, context: &C
|
||||
}
|
||||
}
|
||||
|
||||
pub fn list_map(args: &[SymbolicExpression], env: &mut Environment, context: &Context) -> Result<Value> {
|
||||
pub fn list_map(args: &[SymbolicExpression], env: &mut Environment, context: &LocalContext) -> Result<Value> {
|
||||
if args.len() != 2 {
|
||||
return Err(Error::InvalidArguments(format!("Wrong number of arguments ({}) to map", args.len())))
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ use vm::errors::{Error, InterpreterResult as Result};
|
||||
use vm::types::Value;
|
||||
use vm::callables::CallableType;
|
||||
use vm::representations::SymbolicExpression;
|
||||
use vm::{Context, Environment, eval};
|
||||
use vm::{LocalContext, Environment, eval};
|
||||
|
||||
|
||||
fn native_eq(args: &[Value]) -> Result<Value> {
|
||||
@@ -32,7 +32,7 @@ fn native_begin(args: &[Value]) -> Result<Value> {
|
||||
}
|
||||
}
|
||||
|
||||
fn special_if(args: &[SymbolicExpression], env: &mut Environment, context: &Context) -> Result<Value> {
|
||||
fn special_if(args: &[SymbolicExpression], env: &mut Environment, context: &LocalContext) -> Result<Value> {
|
||||
if !(args.len() == 2 || args.len() == 3) {
|
||||
return Err(Error::InvalidArguments("Wrong number of arguments to if (expect 2 or 3)".to_string()))
|
||||
}
|
||||
@@ -54,7 +54,7 @@ fn special_if(args: &[SymbolicExpression], env: &mut Environment, context: &Cont
|
||||
}
|
||||
}
|
||||
|
||||
fn special_let(args: &[SymbolicExpression], env: &mut Environment, context: &Context) -> Result<Value> {
|
||||
fn special_let(args: &[SymbolicExpression], env: &mut Environment, context: &LocalContext) -> Result<Value> {
|
||||
use vm::is_reserved;
|
||||
|
||||
// (let ((x 1) (y 2)) (+ x y)) -> 3
|
||||
|
||||
@@ -2,9 +2,9 @@ use vm::errors::{Error, InterpreterResult as Result};
|
||||
use vm::types::{Value};
|
||||
use vm::representations::SymbolicExpression;
|
||||
use vm::representations::SymbolicExpression::{NamedParameter};
|
||||
use vm::{Context, Environment, eval};
|
||||
use vm::{LocalContext, Environment, eval};
|
||||
|
||||
pub fn tuple_cons(args: &[SymbolicExpression], env: &mut Environment, context: &Context) -> Result<Value> {
|
||||
pub fn tuple_cons(args: &[SymbolicExpression], env: &mut Environment, context: &LocalContext) -> Result<Value> {
|
||||
// (tuple #arg-name value
|
||||
// #arg-name value ...)
|
||||
if args.len() % 2 != 0 {
|
||||
@@ -26,7 +26,7 @@ pub fn tuple_cons(args: &[SymbolicExpression], env: &mut Environment, context: &
|
||||
Value::tuple_from_data(evaled_pairs)
|
||||
}
|
||||
|
||||
pub fn tuple_get(args: &[SymbolicExpression], env: &mut Environment, context: &Context) -> Result<Value> {
|
||||
pub fn tuple_get(args: &[SymbolicExpression], env: &mut Environment, context: &LocalContext) -> Result<Value> {
|
||||
// (get arg-name (tuple ...))
|
||||
// if the tuple argument is 'null, then return 'null.
|
||||
// NOTE: a tuple field value itself may _never_ be 'null
|
||||
|
||||
@@ -18,14 +18,14 @@ mod tests;
|
||||
use vm::types::Value;
|
||||
use vm::callables::CallableType;
|
||||
use vm::representations::SymbolicExpression;
|
||||
use vm::contexts::{Context, Environment};
|
||||
use vm::contexts::{GlobalContext, LocalContext, Environment};
|
||||
use vm::database::ContractDatabase;
|
||||
use vm::functions::define::DefineResult;
|
||||
use vm::errors::{Error, InterpreterResult as Result};
|
||||
|
||||
const MAX_CALL_STACK_DEPTH: usize = 256;
|
||||
|
||||
fn lookup_variable(name: &str, context: &Context, env: &Environment) -> Result<Value> {
|
||||
fn lookup_variable(name: &str, context: &LocalContext, env: &Environment) -> Result<Value> {
|
||||
if name.starts_with(char::is_numeric) || name.starts_with('\'') {
|
||||
Err(Error::BadSymbolicRepresentation(format!("Unexpected variable name: {}", name)))
|
||||
} else {
|
||||
@@ -45,16 +45,14 @@ pub fn lookup_function<'a> (name: &str, env: &Environment)-> Result<CallableType
|
||||
if let Some(result) = functions::lookup_reserved_functions(name) {
|
||||
Ok(result)
|
||||
} else {
|
||||
if let Some(func) = env.global_context.lookup_function(name) {
|
||||
Ok(CallableType::UserFunction(func))
|
||||
} else {
|
||||
Err(Error::Undefined(format!("No such function found in context: {}", name)))
|
||||
}
|
||||
let user_function = env.global_context.lookup_function(name).ok_or(
|
||||
Error::Undefined(format!("No such function found in context: {}", name)))?;
|
||||
Ok(CallableType::UserFunction(user_function))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply(function: &CallableType, args: &[SymbolicExpression],
|
||||
env: &mut Environment, context: &Context) -> Result<Value> {
|
||||
env: &mut Environment, context: &LocalContext) -> Result<Value> {
|
||||
if let CallableType::SpecialFunction(function) = function {
|
||||
function(&args, env, context)
|
||||
} else {
|
||||
@@ -87,7 +85,7 @@ pub fn apply(function: &CallableType, args: &[SymbolicExpression],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eval <'a> (exp: &SymbolicExpression, env: &'a mut Environment, context: &Context) -> Result<Value> {
|
||||
pub fn eval <'a> (exp: &SymbolicExpression, env: &'a mut Environment, context: &LocalContext) -> Result<Value> {
|
||||
match exp {
|
||||
&SymbolicExpression::AtomValue(ref value) => Ok(value.clone()),
|
||||
&SymbolicExpression::Atom(ref value) => lookup_variable(&value, context, env),
|
||||
@@ -125,10 +123,10 @@ pub fn is_reserved(name: &str) -> bool {
|
||||
*/
|
||||
fn eval_all(expressions: &[SymbolicExpression],
|
||||
database: &mut ContractDatabase,
|
||||
global_context: &mut Context) -> Result<Value> {
|
||||
global_context: &mut GlobalContext) -> Result<Value> {
|
||||
|
||||
let mut last_executed = None;
|
||||
let context = Context::new();
|
||||
let context = LocalContext::new();
|
||||
|
||||
for exp in expressions {
|
||||
let try_define = {
|
||||
@@ -167,7 +165,7 @@ fn eval_all(expressions: &[SymbolicExpression],
|
||||
* database.
|
||||
*/
|
||||
pub fn execute(program: &str) -> Result<Value> {
|
||||
let mut global_context = Context::new();
|
||||
let mut global_context = GlobalContext::new();
|
||||
let mut db_instance = Box::new(database::MemoryContractDatabase::new());
|
||||
|
||||
let parsed = parser::parse(program)?;
|
||||
|
||||
@@ -213,3 +213,86 @@ pub fn parse(input: &str) -> Result<Vec<SymbolicExpression>> {
|
||||
let lexed = lex(input)?;
|
||||
parse_lexed(lexed)
|
||||
}
|
||||
|
||||
|
||||
mod test {
|
||||
use vm::representations::SymbolicExpression;
|
||||
use vm::errors::Error;
|
||||
use vm::types::Value;
|
||||
use vm::parser;
|
||||
|
||||
#[test]
|
||||
fn test_parse_let_expression() {
|
||||
let input = "z (let((x 1) (y 2))
|
||||
(+ x ;; \"comments section?\"
|
||||
;; this is also a comment!
|
||||
(let ((x 3)) ;; more commentary
|
||||
(+ x y))
|
||||
x)) x y";
|
||||
let program = vec![
|
||||
SymbolicExpression::Atom("z".to_string()),
|
||||
SymbolicExpression::List(Box::new([
|
||||
SymbolicExpression::Atom("let".to_string()),
|
||||
SymbolicExpression::List(Box::new([
|
||||
SymbolicExpression::List(Box::new([
|
||||
SymbolicExpression::Atom("x".to_string()),
|
||||
SymbolicExpression::AtomValue(Value::Int(1))])),
|
||||
SymbolicExpression::List(Box::new([
|
||||
SymbolicExpression::Atom("y".to_string()),
|
||||
SymbolicExpression::AtomValue(Value::Int(2))]))])),
|
||||
SymbolicExpression::List(Box::new([
|
||||
SymbolicExpression::Atom("+".to_string()),
|
||||
SymbolicExpression::Atom("x".to_string()),
|
||||
SymbolicExpression::List(Box::new([
|
||||
SymbolicExpression::Atom("let".to_string()),
|
||||
SymbolicExpression::List(Box::new([
|
||||
SymbolicExpression::List(Box::new([
|
||||
SymbolicExpression::Atom("x".to_string()),
|
||||
SymbolicExpression::AtomValue(Value::Int(3))]))])),
|
||||
SymbolicExpression::List(Box::new([
|
||||
SymbolicExpression::Atom("+".to_string()),
|
||||
SymbolicExpression::Atom("x".to_string()),
|
||||
SymbolicExpression::Atom("y".to_string())]))])),
|
||||
SymbolicExpression::Atom("x".to_string())]))])),
|
||||
SymbolicExpression::Atom("x".to_string()),
|
||||
SymbolicExpression::Atom("y".to_string()),
|
||||
];
|
||||
|
||||
let parsed = parser::parse(&input);
|
||||
assert_eq!(Ok(program), parsed, "Should match expected symbolic expression");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_failures() {
|
||||
let too_much_closure = "(let ((x 1) (y 2))))";
|
||||
let not_enough_closure = "(let ((x 1) (y 2))";
|
||||
let middle_hash = "(let ((x 1) (y#not 2)) x)";
|
||||
let unicode = "(let ((x🎶 1)) (eq x🎶 1))";
|
||||
|
||||
assert!(match parser::parse(&too_much_closure) {
|
||||
Err(Error::ParseError(_)) => true,
|
||||
_ => false
|
||||
}, "Should have failed to parse with too many right parens");
|
||||
|
||||
assert!(match parser::parse(¬_enough_closure) {
|
||||
Err(Error::ParseError(_)) => true,
|
||||
_ => false
|
||||
}, "Should have failed to parse with too few right parens");
|
||||
|
||||
let x = parser::parse(&middle_hash);
|
||||
assert!(match x {
|
||||
Err(Error::ParseError(_)) => true,
|
||||
_ => {
|
||||
println!("Expected parser error. Unexpected value is:\n {:?}", x);
|
||||
false
|
||||
}
|
||||
}, "Should have failed to parse with a middle hash");
|
||||
|
||||
assert!(match parser::parse(&unicode) {
|
||||
Err(Error::ParseError(_)) => true,
|
||||
_ => false
|
||||
}, "Should have failed to parse a unicode variable name");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
use vm::representations::SymbolicExpression;
|
||||
use vm::errors::Error;
|
||||
use vm::types::Value;
|
||||
use vm::parser;
|
||||
|
||||
#[test]
|
||||
fn test_parse_let_expression() {
|
||||
let input = "z (let((x 1) (y 2))
|
||||
(+ x ;; \"comments section?\"
|
||||
;; this is also a comment!
|
||||
(let ((x 3)) ;; more commentary
|
||||
(+ x y))
|
||||
x)) x y";
|
||||
let program = vec![
|
||||
SymbolicExpression::Atom("z".to_string()),
|
||||
SymbolicExpression::List(Box::new([
|
||||
SymbolicExpression::Atom("let".to_string()),
|
||||
SymbolicExpression::List(Box::new([
|
||||
SymbolicExpression::List(Box::new([
|
||||
SymbolicExpression::Atom("x".to_string()),
|
||||
SymbolicExpression::AtomValue(Value::Int(1))])),
|
||||
SymbolicExpression::List(Box::new([
|
||||
SymbolicExpression::Atom("y".to_string()),
|
||||
SymbolicExpression::AtomValue(Value::Int(2))]))])),
|
||||
SymbolicExpression::List(Box::new([
|
||||
SymbolicExpression::Atom("+".to_string()),
|
||||
SymbolicExpression::Atom("x".to_string()),
|
||||
SymbolicExpression::List(Box::new([
|
||||
SymbolicExpression::Atom("let".to_string()),
|
||||
SymbolicExpression::List(Box::new([
|
||||
SymbolicExpression::List(Box::new([
|
||||
SymbolicExpression::Atom("x".to_string()),
|
||||
SymbolicExpression::AtomValue(Value::Int(3))]))])),
|
||||
SymbolicExpression::List(Box::new([
|
||||
SymbolicExpression::Atom("+".to_string()),
|
||||
SymbolicExpression::Atom("x".to_string()),
|
||||
SymbolicExpression::Atom("y".to_string())]))])),
|
||||
SymbolicExpression::Atom("x".to_string())]))])),
|
||||
SymbolicExpression::Atom("x".to_string()),
|
||||
SymbolicExpression::Atom("y".to_string()),
|
||||
];
|
||||
|
||||
let parsed = parser::parse(&input);
|
||||
assert_eq!(Ok(program), parsed, "Should match expected symbolic expression");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_failures() {
|
||||
let too_much_closure = "(let ((x 1) (y 2))))";
|
||||
let not_enough_closure = "(let ((x 1) (y 2))";
|
||||
let middle_hash = "(let ((x 1) (y#not 2)) x)";
|
||||
|
||||
assert!(match parser::parse(&too_much_closure) {
|
||||
Err(Error::ParseError(_)) => true,
|
||||
_ => false
|
||||
}, "Should have failed to parse with too many right parens");
|
||||
|
||||
assert!(match parser::parse(¬_enough_closure) {
|
||||
Err(Error::ParseError(_)) => true,
|
||||
_ => false
|
||||
}, "Should have failed to parse with too few right parens");
|
||||
|
||||
let x = parser::parse(&middle_hash);
|
||||
assert!(match x {
|
||||
Err(Error::ParseError(_)) => true,
|
||||
_ => {
|
||||
println!("Expected parser error. Unexpected value is:\n {:?}", x);
|
||||
false
|
||||
}
|
||||
}, "Should have failed to parse with a middle hash");
|
||||
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
use vm::{eval, execute};
|
||||
use vm::database::MemoryContractDatabase;
|
||||
use vm::errors::Error;
|
||||
use vm::{Value, Context, Environment};
|
||||
use vm::{Value, LocalContext, GlobalContext, Environment};
|
||||
use vm::callables::PrivateFunction;
|
||||
use vm::representations::SymbolicExpression;
|
||||
use vm::parser::parse;
|
||||
@@ -27,8 +27,8 @@ fn test_simple_user_function() {
|
||||
let func_args = vec!["x".to_string()];
|
||||
let user_function = PrivateFunction::new(func_args, func_body);
|
||||
|
||||
let context = Context::new();
|
||||
let mut global_context = Context::new();
|
||||
let context = LocalContext::new();
|
||||
let mut global_context = GlobalContext::new();
|
||||
let mut db = MemoryContractDatabase::new();
|
||||
|
||||
global_context.variables.insert("a".to_string(), Value::Int(59));
|
||||
@@ -56,8 +56,8 @@ fn test_simple_let() {
|
||||
x))";
|
||||
|
||||
if let Ok(parsed_program) = parse(&program) {
|
||||
let context = Context::new();
|
||||
let global_context = Context::new();
|
||||
let context = LocalContext::new();
|
||||
let global_context = GlobalContext::new();
|
||||
let mut db = MemoryContractDatabase::new();
|
||||
let mut env = Environment::new(&global_context, &mut db);
|
||||
|
||||
@@ -123,8 +123,8 @@ fn test_simple_if_functions() {
|
||||
let user_function1 = PrivateFunction::new(func_args1, parsed_bodies[0].clone());
|
||||
let user_function2 = PrivateFunction::new(func_args2, parsed_bodies[1].clone());
|
||||
|
||||
let context = Context::new();
|
||||
let mut global_context = Context::new();
|
||||
let context = LocalContext::new();
|
||||
let mut global_context = GlobalContext::new();
|
||||
let mut db = MemoryContractDatabase::new();
|
||||
|
||||
global_context.functions.insert("with_else".to_string(), user_function1);
|
||||
|
||||
Reference in New Issue
Block a user