Merge branch 'feature/smart-contract-vm' into review/smart-contract-021319

This commit is contained in:
Aaron Blankstein
2019-02-19 11:30:05 -06:00
21 changed files with 424 additions and 328 deletions

View File

@@ -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))
}

View File

@@ -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 }
}
}

View File

@@ -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 {

View File

@@ -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()))
}
}
}

View File

@@ -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>;

View File

@@ -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()))
}

View File

@@ -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 {

View File

@@ -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))
}

View File

@@ -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();

View File

@@ -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 {

View File

@@ -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

View File

@@ -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));
}
}

View File

@@ -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(&not_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");
}
}

View File

@@ -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(_, _)) => {},

View File

@@ -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]

View File

@@ -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));
}

View File

@@ -1,6 +1,5 @@
mod lists;
mod defines;
mod parser_lexer;
mod simple_apply_eval;
mod datamaps;
mod contracts;

View File

@@ -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(&not_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");
}

View File

@@ -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)));

View File

@@ -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)))
}
}

View File

@@ -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"];