mirror of
https://github.com/alexgo-io/stacks-puppet-node.git
synced 2026-05-24 08:09:52 +08:00
Merge branch 'feature/smart-contract-vm' into review/smart-contract-021319
This commit is contained in:
@@ -130,7 +130,7 @@ fn double_sha256_checksum(data: &[u8]) -> Vec<u8> {
|
||||
}
|
||||
|
||||
fn c32_check_encode(version: u8, data: &[u8]) -> Result<String, Error> {
|
||||
if (version >= 32) {
|
||||
if version >= 32 {
|
||||
return Err(Error::InvalidVersion(version))
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +1,29 @@
|
||||
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)]
|
||||
pub struct DefinedFunction {
|
||||
is_public: bool,
|
||||
types: Option<Vec<TypeSignature>>,
|
||||
pub enum DefinedFunction {
|
||||
Public(PublicFunction),
|
||||
Private(PrivateFunction)
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PublicFunction {
|
||||
types: Vec<TypeSignature>,
|
||||
pub arguments: Vec<String>,
|
||||
pub body: SymbolicExpression
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PrivateFunction {
|
||||
pub arguments: Vec<String>,
|
||||
pub body: SymbolicExpression
|
||||
}
|
||||
@@ -23,62 +34,75 @@ pub struct FunctionIdentifier {
|
||||
pub body: SymbolicExpression
|
||||
}
|
||||
|
||||
impl PublicFunction {
|
||||
pub fn new(mut arguments: Vec<(String, TypeSignature)>, body: SymbolicExpression) -> DefinedFunction {
|
||||
let (argument_names, types) = arguments.drain(..).unzip();
|
||||
|
||||
DefinedFunction::Public(PublicFunction {
|
||||
arguments: argument_names,
|
||||
body: body,
|
||||
types: types
|
||||
})
|
||||
}
|
||||
|
||||
fn apply(&self, args: &[Value], env: &mut Environment) -> Result<Value> {
|
||||
// since self is a malformed object.
|
||||
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) {
|
||||
return Err(Error::TypeError(format!("{:?}", type_sig), value.clone()))
|
||||
}
|
||||
if let Some(_) = context.variables.insert(arg.clone(), value.clone()) {
|
||||
return Err(Error::VariableDefinedMultipleTimes(arg.clone()))
|
||||
}
|
||||
}
|
||||
eval(&self.body, env, &context)
|
||||
}
|
||||
}
|
||||
|
||||
impl PrivateFunction {
|
||||
pub fn new(arguments: Vec<String>, body: SymbolicExpression) -> DefinedFunction {
|
||||
DefinedFunction::Private(PrivateFunction {
|
||||
arguments: arguments,
|
||||
body: body,
|
||||
})
|
||||
}
|
||||
|
||||
fn apply(&self, args: &[Value], env: &mut Environment) -> Result<Value> {
|
||||
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()) {
|
||||
return Err(Error::VariableDefinedMultipleTimes(arg.clone()))
|
||||
}
|
||||
}
|
||||
eval(&self.body, env, &context)
|
||||
}
|
||||
}
|
||||
|
||||
impl DefinedFunction {
|
||||
pub fn apply(&self, args: &[Value], env: &mut Environment) -> Result<Value> {
|
||||
let mut context = Context::new();
|
||||
|
||||
if !self.is_public {
|
||||
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()) {
|
||||
return Err(Error::MultiplyDefined(arg.clone()))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let types = self.types.as_ref().unwrap(); // if types is None, and is_public = true, we should panic.
|
||||
// since self is a malformed object.
|
||||
let arg_iterator = self.arguments.iter().zip(types.iter()).zip(args.iter());
|
||||
for ((arg, type_sig), value) in arg_iterator {
|
||||
if !type_sig.admits(value) {
|
||||
return Err(Error::TypeError(format!("{:?}", type_sig), value.clone()))
|
||||
}
|
||||
if let Some(_) = context.variables.insert(arg.clone(), value.clone()) {
|
||||
return Err(Error::MultiplyDefined(arg.clone()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
eval(&self.body, env, &context)
|
||||
}
|
||||
|
||||
pub fn new_private(arguments: Vec<String>, body: SymbolicExpression) -> DefinedFunction {
|
||||
DefinedFunction {
|
||||
arguments: arguments,
|
||||
body: body,
|
||||
is_public: false,
|
||||
types: None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_public(mut arguments: Vec<(String, TypeSignature)>, body: SymbolicExpression) -> DefinedFunction {
|
||||
let (argument_names, types) = arguments.drain(..).unzip();
|
||||
|
||||
DefinedFunction {
|
||||
arguments: argument_names,
|
||||
body: body,
|
||||
is_public: true,
|
||||
types: Some(types)
|
||||
match self {
|
||||
DefinedFunction::Private(f) => f.apply(args, env),
|
||||
DefinedFunction::Public(f) => f.apply(args, env),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_public(&self) -> bool {
|
||||
self.is_public
|
||||
match self {
|
||||
DefinedFunction::Public(_) => true,
|
||||
DefinedFunction::Private(_) => false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_identifier(&self) -> FunctionIdentifier {
|
||||
let (body, arguments) = match self {
|
||||
DefinedFunction::Private(f) => (f.body.clone(), f.arguments.clone()),
|
||||
DefinedFunction::Public(f) => (f.body.clone(), f.arguments.clone())
|
||||
};
|
||||
return FunctionIdentifier {
|
||||
body: self.body.clone(),
|
||||
arguments: self.arguments.clone() }
|
||||
body: body,
|
||||
arguments: arguments }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
@@ -28,34 +28,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 +85,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 +94,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,18 +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)?;
|
||||
@@ -26,7 +28,7 @@ impl <'a> Contract <'a> {
|
||||
}
|
||||
|
||||
// Todo: add principal value type, check for sender to be one.
|
||||
pub fn execute_transaction(&mut self, _sender: &Value, tx_name: &str,
|
||||
pub fn execute_transaction(&mut self, sender: &Value, tx_name: &str,
|
||||
args: &[SymbolicExpression]) -> Result<Value> {
|
||||
let func = self.global_context.lookup_function(tx_name)
|
||||
.ok_or(Error::Undefined(format!("No such function in contract: {}", tx_name)))?;
|
||||
@@ -34,16 +36,22 @@ impl <'a> Contract <'a> {
|
||||
return Err(Error::Undefined("Attempt to call non-public function".to_string()))
|
||||
}
|
||||
|
||||
let mut env = Environment::new(&self.global_context, &mut *self.db);
|
||||
if let Value::Principal(_, _) = sender {
|
||||
let mut env = Environment::new(&self.global_context, &mut *self.db);
|
||||
|
||||
for arg in args {
|
||||
match arg {
|
||||
SymbolicExpression::AtomValue(ref _v) => {},
|
||||
_ => return Err(Error::InterpreterError("Passed non-value expression to exec_tx!".to_string()))
|
||||
for arg in args {
|
||||
match arg {
|
||||
SymbolicExpression::AtomValue(ref _v) => {},
|
||||
_ => return Err(Error::InterpreterError("Passed non-value expression to exec_tx!".to_string()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let local_context = Context::new();
|
||||
apply(&CallableType::UserFunction(func), args, &mut env, &local_context)
|
||||
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)
|
||||
} else {
|
||||
Err(Error::BadSender(sender.clone()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,10 +22,11 @@ pub enum Error {
|
||||
ValueTooLarge,
|
||||
ExpectedListPairs,
|
||||
InvalidTypeDescription,
|
||||
BadSender(Value),
|
||||
BadSymbolicRepresentation(String),
|
||||
ReservedName(String),
|
||||
InterpreterError(String),
|
||||
MultiplyDefined(String)
|
||||
VariableDefinedMultipleTimes(String)
|
||||
}
|
||||
|
||||
pub type InterpreterResult <R> = Result<R, Error>;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
use vm::types::{Value, TupleTypeSignature, parse_name_type_pairs};
|
||||
use vm::callables::DefinedFunction;
|
||||
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,13 +13,13 @@ 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) {
|
||||
Err(Error::ReservedName(name.to_string()))
|
||||
} else if global_context.variables.contains_key(name) || global_context.functions.contains_key(name) {
|
||||
Err(Error::MultiplyDefined(name.to_string()))
|
||||
Err(Error::VariableDefinedMultipleTimes(name.to_string()))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
@@ -49,7 +50,7 @@ fn handle_define_private_function(signature: &[SymbolicExpression],
|
||||
.ok_or(Error::InvalidArguments("Must supply atleast a name argument to define a function".to_string()))?;
|
||||
|
||||
check_legal_define(&function_name, &env.global_context)?;
|
||||
let function = DefinedFunction::new_private(
|
||||
let function = PrivateFunction::new(
|
||||
arg_names.iter().map(|x| (*x).clone()).collect(),
|
||||
expression.clone());
|
||||
|
||||
@@ -69,10 +70,10 @@ fn handle_define_public_function(signature: &[SymbolicExpression],
|
||||
|
||||
check_legal_define(&function_name, &env.global_context)?;
|
||||
|
||||
let mut arguments = parse_name_type_pairs(arg_symbols)?;
|
||||
let arguments = parse_name_type_pairs(arg_symbols)?;
|
||||
|
||||
let function = DefinedFunction::new_public(arguments,
|
||||
expression.clone());
|
||||
let function = PublicFunction::new(arguments,
|
||||
expression.clone());
|
||||
|
||||
Ok(DefineResult::Function((*function_name).clone(), function))
|
||||
}
|
||||
|
||||
@@ -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())))
|
||||
}
|
||||
@@ -17,7 +17,7 @@ pub fn list_fold(args: &[SymbolicExpression], env: &mut Environment, context: &C
|
||||
let list = eval(&args[1], env, context)?;
|
||||
let initial = eval(&args[2], env, context)?;
|
||||
match list {
|
||||
Value::List(vector, _) => vector.iter().try_fold(
|
||||
Value::List(list_data) => list_data.data.iter().try_fold(
|
||||
initial,
|
||||
|acc, x| {
|
||||
let argument = [ AtomValue(x.clone()), AtomValue(acc) ];
|
||||
@@ -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())))
|
||||
}
|
||||
@@ -39,8 +39,8 @@ pub fn list_map(args: &[SymbolicExpression], env: &mut Environment, context: &Co
|
||||
|
||||
let list = eval(&args[1], env, context)?;
|
||||
match list {
|
||||
Value::List(vector, _) => {
|
||||
let result: Result<Vec<_>> = vector.iter().map(|x| {
|
||||
Value::List(list_data) => {
|
||||
let result: Result<Vec<_>> = list_data.data.iter().map(|x| {
|
||||
let argument = [ AtomValue(x.clone()) ];
|
||||
apply(&function, &argument, env, context)
|
||||
}).collect();
|
||||
|
||||
@@ -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
|
||||
@@ -78,7 +78,7 @@ fn special_let(args: &[SymbolicExpression], env: &mut Environment, context: &Con
|
||||
}
|
||||
let value = eval(&binding_exps[1], env, context)?;
|
||||
match inner_context.variables.insert((*var_name).clone(), value) {
|
||||
Some(_val) => return Err(Error::MultiplyDefined(var_name.to_string())),
|
||||
Some(_val) => return Err(Error::VariableDefinedMultipleTimes(var_name.to_string())),
|
||||
_ => continue
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -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,9 +165,49 @@ 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)?;
|
||||
eval_all(&parsed, &mut *db_instance, &mut global_context)
|
||||
}
|
||||
|
||||
|
||||
mod test {
|
||||
use vm::database::MemoryContractDatabase;
|
||||
use vm::errors::Error;
|
||||
use vm::{Value, LocalContext, GlobalContext, Environment, SymbolicExpression};
|
||||
use vm::callables::PrivateFunction;
|
||||
use vm::eval;
|
||||
|
||||
#[test]
|
||||
fn test_simple_user_function() {
|
||||
//
|
||||
// test program:
|
||||
// (define (do_work x) (+ 5 x))
|
||||
// (define a 59)
|
||||
// (do_work a)
|
||||
//
|
||||
let content = [ SymbolicExpression::List(
|
||||
Box::new([ SymbolicExpression::Atom("do_work".to_string()),
|
||||
SymbolicExpression::Atom("a".to_string()) ])) ];
|
||||
|
||||
let func_body = SymbolicExpression::List(
|
||||
Box::new([ SymbolicExpression::Atom("+".to_string()),
|
||||
SymbolicExpression::AtomValue(Value::Int(5)),
|
||||
SymbolicExpression::Atom("x".to_string())]));
|
||||
|
||||
let func_args = vec!["x".to_string()];
|
||||
let user_function = PrivateFunction::new(func_args, func_body);
|
||||
|
||||
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));
|
||||
global_context.functions.insert("do_work".to_string(), user_function);
|
||||
|
||||
let mut env = Environment::new(&global_context, &mut db);
|
||||
assert_eq!(Ok(Value::Int(64)), eval(&content[0], &mut env, &context));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,8 @@ pub fn lex(input: &str) -> Result<Vec<LexItem>> {
|
||||
// lazy_static (or just hand implementing that), and I'm not convinced
|
||||
// it's worth either (1) an extern macro, or (2) the complexity of hand implementing.
|
||||
let lex_matchers: &[LexMatcher] = &[
|
||||
LexMatcher::new(r##""(?P<value>((\\")|([[:ascii:]&&[^"\n\r\t]]))*)""##, TokenType::StringLiteral),
|
||||
LexMatcher::new(r##""(?P<value>((\\")|([[:print:]&&[^"\n\r\t]]))*)""##, TokenType::StringLiteral),
|
||||
LexMatcher::new(";;[[:print:]&&[^\n\r\t]]*", TokenType::Whitespace), // ;; comments.
|
||||
LexMatcher::new("[(]", TokenType::LParens),
|
||||
LexMatcher::new("[)]", TokenType::RParens),
|
||||
LexMatcher::new("[ \n\t\r]+", TokenType::Whitespace),
|
||||
@@ -111,7 +112,13 @@ pub fn lex(input: &str) -> Result<Vec<LexItem>> {
|
||||
let str_value = get_value_or_err(current_slice, captures)?;
|
||||
let (version, data) = c32_address_decode(&str_value)
|
||||
.map_err(|x| { Error::ParseError(format!("Invalid principal literal: {}", x)) })?;
|
||||
Ok(LexItem::LiteralValue(Value::Principal(version, data)))
|
||||
if data.len() != 20 {
|
||||
Err(Error::ParseError("Invalid principal literal: Expected 20 data bytes.".to_string()))
|
||||
} else {
|
||||
let mut fixed_data = [0; 20];
|
||||
fixed_data.copy_from_slice(&data[..20]);
|
||||
Ok(LexItem::LiteralValue(Value::Principal(version, fixed_data)))
|
||||
}
|
||||
},
|
||||
TokenType::HexStringLiteral => {
|
||||
panic!("Not implemented")
|
||||
@@ -206,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,4 +1,3 @@
|
||||
use vm::execute;
|
||||
use vm::errors::Error;
|
||||
use vm::types::{Value};
|
||||
use vm::representations::SymbolicExpression;
|
||||
@@ -63,14 +62,16 @@ fn test_factorial_contract() {
|
||||
Value::Int(120),
|
||||
];
|
||||
|
||||
let sender = Value::Principal(1, [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]);
|
||||
|
||||
arguments_to_test.iter().zip(expected.iter())
|
||||
.for_each(|(arguments, expectation)| assert_eq!(Ok(expectation.clone()),
|
||||
contract.execute_transaction(
|
||||
&Value::Void,
|
||||
&sender,
|
||||
&tx_name,
|
||||
arguments)));
|
||||
|
||||
let err_result = contract.execute_transaction(&Value::Void, &"init-factorial",
|
||||
let err_result = contract.execute_transaction(&sender, &"init-factorial",
|
||||
&symbols_from_values(vec![Value::Int(9000),
|
||||
Value::Int(15)]));
|
||||
match err_result {
|
||||
@@ -81,7 +82,7 @@ fn test_factorial_contract() {
|
||||
}
|
||||
}
|
||||
|
||||
let err_result = contract.execute_transaction(&Value::Void, &"compute",
|
||||
let err_result = contract.execute_transaction(&sender, &"compute",
|
||||
&symbols_from_values(vec![Value::Void]));
|
||||
match err_result {
|
||||
Err(Error::TypeError(_, _)) => {},
|
||||
|
||||
@@ -34,29 +34,22 @@ fn test_simple_tea_shop() {
|
||||
(consume 3))
|
||||
";
|
||||
|
||||
if let Ok(type_sig) = TypeSignature::new_list(AtomTypeIdentifier::BoolType, 12, 1) {
|
||||
let expected = Value::List(
|
||||
vec![
|
||||
Value::Bool(true),
|
||||
Value::Bool(true),
|
||||
Value::Bool(true),
|
||||
Value::Bool(true),
|
||||
Value::Bool(true),
|
||||
Value::Bool(true),
|
||||
Value::Bool(false),
|
||||
Value::Bool(true),
|
||||
Value::Bool(true),
|
||||
Value::Bool(false),
|
||||
Value::Bool(false),
|
||||
Value::Bool(false)],
|
||||
type_sig
|
||||
);
|
||||
|
||||
assert_eq!(Ok(expected), execute(test1));
|
||||
} else {
|
||||
panic!("Error in type construction")
|
||||
}
|
||||
let expected = Value::list_from(vec![
|
||||
Value::Bool(true),
|
||||
Value::Bool(true),
|
||||
Value::Bool(true),
|
||||
Value::Bool(true),
|
||||
Value::Bool(true),
|
||||
Value::Bool(true),
|
||||
Value::Bool(false),
|
||||
Value::Bool(true),
|
||||
Value::Bool(true),
|
||||
Value::Bool(false),
|
||||
Value::Bool(false),
|
||||
Value::Bool(false)],
|
||||
);
|
||||
|
||||
assert_eq!(expected, execute(test1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -39,7 +39,7 @@ fn test_bad_define_names() {
|
||||
assert_eq!(Err(Error::ReservedName("*".to_string())), execute(&test1));
|
||||
assert_eq!(Err(Error::InvalidArguments("Illegal operation: attempted to re-define a value type.".to_string())),
|
||||
execute(&test2));
|
||||
assert_eq!(Err(Error::MultiplyDefined("foo".to_string())),
|
||||
assert_eq!(Err(Error::VariableDefinedMultipleTimes("foo".to_string())),
|
||||
execute(&test3));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
mod lists;
|
||||
mod defines;
|
||||
mod parser_lexer;
|
||||
mod simple_apply_eval;
|
||||
mod datamaps;
|
||||
mod contracts;
|
||||
|
||||
@@ -1,71 +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
|
||||
(let ((x 3))
|
||||
(+ 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,43 +1,10 @@
|
||||
use vm::{eval, execute};
|
||||
use vm::database::MemoryContractDatabase;
|
||||
use vm::errors::Error;
|
||||
use vm::{Value, Context, Environment};
|
||||
use vm::callables::DefinedFunction;
|
||||
use vm::representations::SymbolicExpression;
|
||||
use vm::{Value, LocalContext, GlobalContext, Environment};
|
||||
use vm::callables::PrivateFunction;
|
||||
use vm::parser::parse;
|
||||
|
||||
#[test]
|
||||
fn test_simple_user_function() {
|
||||
//
|
||||
// test program:
|
||||
// (define (do_work x) (+ 5 x))
|
||||
// (define a 59)
|
||||
// (do_work a)
|
||||
//
|
||||
|
||||
let content = [ SymbolicExpression::List(
|
||||
Box::new([ SymbolicExpression::Atom("do_work".to_string()),
|
||||
SymbolicExpression::Atom("a".to_string()) ])) ];
|
||||
|
||||
let func_body = SymbolicExpression::List(
|
||||
Box::new([ SymbolicExpression::Atom("+".to_string()),
|
||||
SymbolicExpression::AtomValue(Value::Int(5)),
|
||||
SymbolicExpression::Atom("x".to_string())]));
|
||||
|
||||
let func_args = vec!["x".to_string()];
|
||||
let user_function = DefinedFunction::new_private(func_args, func_body);
|
||||
|
||||
let context = Context::new();
|
||||
let mut global_context = Context::new();
|
||||
let mut db = MemoryContractDatabase::new();
|
||||
|
||||
global_context.variables.insert("a".to_string(), Value::Int(59));
|
||||
global_context.functions.insert("do_work".to_string(), user_function);
|
||||
|
||||
let mut env = Environment::new(&global_context, &mut db);
|
||||
assert_eq!(Ok(Value::Int(64)), eval(&content[0], &mut env, &context));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simple_let() {
|
||||
/*
|
||||
@@ -56,8 +23,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);
|
||||
|
||||
@@ -120,11 +87,11 @@ fn test_simple_if_functions() {
|
||||
if let Ok(parsed_bodies) = function_bodies {
|
||||
let func_args1 = vec!["x".to_string()];
|
||||
let func_args2 = vec!["x".to_string()];
|
||||
let user_function1 = DefinedFunction::new_private(func_args1, parsed_bodies[0].clone());
|
||||
let user_function2 = DefinedFunction::new_private(func_args2, parsed_bodies[1].clone());
|
||||
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);
|
||||
@@ -262,7 +229,7 @@ fn test_bad_lets() {
|
||||
let expectations: &[Result<Value, Error>] = &[
|
||||
Err(Error::ReservedName("tx-sender".to_string())),
|
||||
Err(Error::ReservedName("*".to_string())),
|
||||
Err(Error::MultiplyDefined("a".to_string()))];
|
||||
Err(Error::VariableDefinedMultipleTimes("a".to_string()))];
|
||||
|
||||
tests.iter().zip(expectations.iter())
|
||||
.for_each(|(program, expectation)| assert_eq!(*expectation, execute(program)));
|
||||
|
||||
123
src/vm/types.rs
123
src/vm/types.rs
@@ -1,3 +1,4 @@
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::fmt;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
@@ -8,12 +9,12 @@ use util::hash;
|
||||
|
||||
const MAX_VALUE_SIZE: i128 = 1024 * 1024; // 1MB
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct TupleTypeSignature {
|
||||
type_map: BTreeMap<String, TypeSignature>
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum AtomTypeIdentifier {
|
||||
VoidType,
|
||||
IntType,
|
||||
@@ -23,13 +24,13 @@ pub enum AtomTypeIdentifier {
|
||||
TupleType(TupleTypeSignature)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
struct ListTypeData {
|
||||
max_len: u32,
|
||||
dimension: u8
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct TypeSignature {
|
||||
atomic_type: AtomTypeIdentifier,
|
||||
list_dimensions: Option<ListTypeData>,
|
||||
@@ -37,7 +38,7 @@ pub struct TypeSignature {
|
||||
// high dimensional lists are _expensive_ --- use lists of tuples!
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, Clone, Eq)]
|
||||
pub struct TupleData {
|
||||
type_signature: TupleTypeSignature,
|
||||
data_map: BTreeMap<String, Value>
|
||||
@@ -46,20 +47,49 @@ pub struct TupleData {
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct BuffData {
|
||||
data: Vec<u8>,
|
||||
length: u32
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, Clone, Eq)]
|
||||
pub struct ListData {
|
||||
pub data: Vec<Value>,
|
||||
type_signature: TypeSignature
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
pub enum Value {
|
||||
Void,
|
||||
Int(i128),
|
||||
Bool(bool),
|
||||
Buffer(BuffData),
|
||||
List(Vec<Value>, TypeSignature),
|
||||
Principal(u8, Vec<u8>), // a principal is a version byte + hash160 (20 bytes)
|
||||
List(ListData),
|
||||
Principal(u8, [u8; 20]), // a principal is a version byte + hash160 (20 bytes)
|
||||
Tuple(TupleData)
|
||||
}
|
||||
|
||||
impl PartialEq for ListData {
|
||||
fn eq(&self, other: &ListData) -> bool {
|
||||
self.data == other.data
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for ListData {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.data.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for TupleData {
|
||||
fn eq(&self, other: &TupleData) -> bool {
|
||||
self.data_map == other.data_map
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for TupleData {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.data_map.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl Value {
|
||||
pub fn new_list(list_data: &[Value]) -> Result<Value> {
|
||||
let vec_data = Vec::from(list_data);
|
||||
@@ -72,10 +102,10 @@ impl Value {
|
||||
// (e.g., from a (map...) call, or a (list...) call.
|
||||
// this is a problem _if_ the static analyzer cannot already prevent
|
||||
// this case. This applies to all the constructor size checks.
|
||||
if type_sig.size() > MAX_VALUE_SIZE {
|
||||
if type_sig.size()? > MAX_VALUE_SIZE {
|
||||
return Err(Error::ValueTooLarge)
|
||||
}
|
||||
Ok(Value::List(list_data, type_sig))
|
||||
Ok(Value::List(ListData { data: list_data, type_signature: type_sig }))
|
||||
}
|
||||
|
||||
pub fn buff_from(buff_data: Vec<u8>) -> Result<Value> {
|
||||
@@ -84,29 +114,27 @@ impl Value {
|
||||
} else if buff_data.len() as i128 > MAX_VALUE_SIZE {
|
||||
Err(Error::ValueTooLarge)
|
||||
} else {
|
||||
let length = buff_data.len() as u32;
|
||||
Ok(Value::Buffer(BuffData { data: buff_data,
|
||||
length: length }))
|
||||
Ok(Value::Buffer(BuffData { data: buff_data }))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tuple_from_data(paired_tuple_data: Vec<(String, Value)>) -> Result<Value> {
|
||||
let tuple_data = TupleData::from_data(paired_tuple_data)?;
|
||||
if tuple_data.size() > MAX_VALUE_SIZE {
|
||||
if tuple_data.size()? > MAX_VALUE_SIZE {
|
||||
return Err(Error::ValueTooLarge)
|
||||
}
|
||||
Ok(Value::Tuple(tuple_data))
|
||||
}
|
||||
|
||||
pub fn size(&self) -> i128 {
|
||||
pub fn size(&self) -> Result<i128> {
|
||||
match self {
|
||||
Value::Void => AtomTypeIdentifier::VoidType.size(),
|
||||
Value::Int(_i) => AtomTypeIdentifier::IntType.size(),
|
||||
Value::Bool(_i) => AtomTypeIdentifier::BoolType.size(),
|
||||
Value::Principal(_,_) => AtomTypeIdentifier::PrincipalType.size(),
|
||||
Value::Buffer(ref buff_data) => buff_data.length as i128,
|
||||
Value::Buffer(ref buff_data) => Ok(buff_data.data.len() as i128),
|
||||
Value::Tuple(ref tuple_data) => tuple_data.size(),
|
||||
Value::List(ref _v, ref type_signature) => type_signature.size()
|
||||
Value::List(ref list_data) => list_data.type_signature.size()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,15 +149,15 @@ impl fmt::Display for Value {
|
||||
Value::Buffer(vec_bytes) => write!(f, "0x{}", hash::to_hex(&vec_bytes.data)),
|
||||
Value::Tuple(data) => write!(f, "{}", data),
|
||||
Value::Principal(version, vec_bytes) => {
|
||||
let c32_str = match c32::c32_address(*version, &vec_bytes) {
|
||||
let c32_str = match c32::c32_address(*version, &vec_bytes[..]) {
|
||||
Ok(val) => val,
|
||||
Err(_) => "INVALID_C32_ADDR".to_string()
|
||||
};
|
||||
write!(f, "{}", c32_str)
|
||||
},
|
||||
Value::List(values, _type) => {
|
||||
Value::List(list_data) => {
|
||||
write!(f, "( ")?;
|
||||
for v in values.iter() {
|
||||
for v in list_data.data.iter() {
|
||||
write!(f, "{} ", v)?;
|
||||
}
|
||||
write!(f, ")")
|
||||
@@ -139,13 +167,13 @@ impl fmt::Display for Value {
|
||||
}
|
||||
|
||||
impl AtomTypeIdentifier {
|
||||
pub fn size(&self) -> i128 {
|
||||
pub fn size(&self) -> Result<i128> {
|
||||
match self {
|
||||
AtomTypeIdentifier::VoidType => 1,
|
||||
AtomTypeIdentifier::IntType => 16,
|
||||
AtomTypeIdentifier::BoolType => 1,
|
||||
AtomTypeIdentifier::PrincipalType => 21,
|
||||
AtomTypeIdentifier::BufferType(len) => *len as i128,
|
||||
AtomTypeIdentifier::VoidType => Ok(1),
|
||||
AtomTypeIdentifier::IntType => Ok(16),
|
||||
AtomTypeIdentifier::BoolType => Ok(1),
|
||||
AtomTypeIdentifier::PrincipalType => Ok(21),
|
||||
AtomTypeIdentifier::BufferType(len) => Ok(*len as i128),
|
||||
AtomTypeIdentifier::TupleType(tuple_sig) => tuple_sig.size()
|
||||
}
|
||||
}
|
||||
@@ -230,16 +258,20 @@ impl TupleTypeSignature {
|
||||
return true
|
||||
}
|
||||
|
||||
pub fn size(&self) -> i128 {
|
||||
pub fn size(&self) -> Result<i128> {
|
||||
let mut name_size: i128 = 0;
|
||||
let mut value_size: i128 = 0;
|
||||
for (name, type_signature) in self.type_map.iter() {
|
||||
// we only accept ascii names, so 1 char = 1 byte.
|
||||
name_size = name_size.checked_add(name.len() as i128).unwrap();
|
||||
value_size = value_size.checked_add(type_signature.size() as i128).unwrap();
|
||||
name_size = name_size.checked_add(name.len() as i128)
|
||||
.ok_or(Error::ValueTooLarge)?;
|
||||
value_size = value_size.checked_add(type_signature.size()? as i128)
|
||||
.ok_or(Error::ValueTooLarge)?;
|
||||
}
|
||||
let name_total_size = name_size.checked_mul(2).unwrap(); // counts the b-tree size...
|
||||
value_size.checked_add(name_total_size).unwrap()
|
||||
let name_total_size = name_size.checked_mul(2)
|
||||
.ok_or(Error::ValueTooLarge)?;
|
||||
value_size.checked_add(name_total_size)
|
||||
.ok_or(Error::ValueTooLarge)
|
||||
}
|
||||
|
||||
// NOTE: this function mutates self _even if it returns an error_.
|
||||
@@ -294,7 +326,7 @@ impl TupleData {
|
||||
|
||||
}
|
||||
|
||||
pub fn size(&self) -> i128 {
|
||||
pub fn size(&self) -> Result<i128> {
|
||||
self.type_signature.size()
|
||||
}
|
||||
}
|
||||
@@ -330,7 +362,7 @@ impl TypeSignature {
|
||||
dimension: dimension as u8 });
|
||||
let type_sig = TypeSignature { atomic_type: atomic_type,
|
||||
list_dimensions: list_dimensions };
|
||||
if type_sig.size() > MAX_VALUE_SIZE {
|
||||
if type_sig.size()? > MAX_VALUE_SIZE {
|
||||
Err(Error::ValueTooLarge)
|
||||
} else {
|
||||
Ok(type_sig)
|
||||
@@ -339,7 +371,7 @@ impl TypeSignature {
|
||||
}
|
||||
|
||||
fn new_atom_checked(atom_type: AtomTypeIdentifier) -> Result<TypeSignature> {
|
||||
if atom_type.size() > MAX_VALUE_SIZE {
|
||||
if atom_type.size()? > MAX_VALUE_SIZE {
|
||||
Err(Error::ValueTooLarge)
|
||||
} else {
|
||||
Ok(TypeSignature::new_atom(atom_type))
|
||||
@@ -365,27 +397,29 @@ impl TypeSignature {
|
||||
dimension: 1 })}
|
||||
}
|
||||
|
||||
pub fn size(&self) -> i128 {
|
||||
pub fn size(&self) -> Result<i128> {
|
||||
let list_multiplier = match self.list_dimensions {
|
||||
Some(ref list_data) => (list_data.max_len as i128).checked_mul(list_data.dimension as i128).unwrap(),
|
||||
None => 1
|
||||
};
|
||||
list_multiplier.checked_mul(self.atomic_type.size()).unwrap()
|
||||
Some(ref list_data) => (list_data.max_len as i128).checked_mul(list_data.dimension as i128)
|
||||
.ok_or(Error::ValueTooLarge),
|
||||
None => Ok(1)
|
||||
}?;
|
||||
list_multiplier.checked_mul(self.atomic_type.size()?)
|
||||
.ok_or(Error::ValueTooLarge)
|
||||
}
|
||||
|
||||
pub fn type_of(x: &Value) -> TypeSignature {
|
||||
if let Value::List(_, type_signature) = x {
|
||||
type_signature.clone()
|
||||
if let Value::List(list_data) = x {
|
||||
list_data.type_signature.clone()
|
||||
} else {
|
||||
let atom = match x {
|
||||
Value::Void => AtomTypeIdentifier::VoidType,
|
||||
Value::Principal(_,_) => AtomTypeIdentifier::PrincipalType,
|
||||
Value::Int(_v) => AtomTypeIdentifier::IntType,
|
||||
Value::Bool(_v) => AtomTypeIdentifier::BoolType,
|
||||
Value::Buffer(buff_data) => AtomTypeIdentifier::BufferType(buff_data.length),
|
||||
Value::Buffer(buff_data) => AtomTypeIdentifier::BufferType(buff_data.data.len() as u32),
|
||||
Value::Tuple(v) => AtomTypeIdentifier::TupleType(
|
||||
v.type_signature.clone()),
|
||||
Value::List(_,_) => panic!("Unreachable code")
|
||||
Value::List(_) => panic!("Unreachable code")
|
||||
};
|
||||
|
||||
TypeSignature::new_atom(atom)
|
||||
@@ -487,6 +521,7 @@ impl TypeSignature {
|
||||
"int" => Ok(AtomTypeIdentifier::IntType),
|
||||
"void" => Ok(AtomTypeIdentifier::VoidType),
|
||||
"bool" => Ok(AtomTypeIdentifier::BoolType),
|
||||
"principal" => Ok(AtomTypeIdentifier::PrincipalType),
|
||||
_ => Err(Error::ParseError(format!("Unknown type name: '{:?}'", typename)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
// This currently is just serving as a place-holder for reserved variable
|
||||
// names.
|
||||
|
||||
pub const TX_SENDER: &str = "tx-sender";
|
||||
|
||||
static RESERVED_VARIABLES: &[&str] =
|
||||
&["tx-sender",
|
||||
&[TX_SENDER,
|
||||
"block-height",
|
||||
"burn-block-height"];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user