From d062cd98596715e4605be18e2fca0ba284274999 Mon Sep 17 00:00:00 2001 From: Aaron Blankstein Date: Mon, 18 Feb 2019 09:15:02 -0600 Subject: [PATCH 1/9] return error from size() instead of panics on overflow. --- src/vm/types.rs | 53 ++++++++++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/src/vm/types.rs b/src/vm/types.rs index 3bc9f89c7..77a4b3ce6 100644 --- a/src/vm/types.rs +++ b/src/vm/types.rs @@ -72,7 +72,7 @@ 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)) @@ -92,19 +92,19 @@ impl Value { pub fn tuple_from_data(paired_tuple_data: Vec<(String, Value)>) -> Result { 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 { 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.length as i128), Value::Tuple(ref tuple_data) => tuple_data.size(), Value::List(ref _v, ref type_signature) => type_signature.size() } @@ -139,13 +139,13 @@ impl fmt::Display for Value { } impl AtomTypeIdentifier { - pub fn size(&self) -> i128 { + pub fn size(&self) -> Result { 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 +230,20 @@ impl TupleTypeSignature { return true } - pub fn size(&self) -> i128 { + pub fn size(&self) -> Result { 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 +298,7 @@ impl TupleData { } - pub fn size(&self) -> i128 { + pub fn size(&self) -> Result { self.type_signature.size() } } @@ -330,7 +334,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 +343,7 @@ impl TypeSignature { } fn new_atom_checked(atom_type: AtomTypeIdentifier) -> Result { - 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,12 +369,14 @@ impl TypeSignature { dimension: 1 })} } - pub fn size(&self) -> i128 { + pub fn size(&self) -> Result { 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 { @@ -487,6 +493,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))) } } From c62297dde7824fc2ad0a7de00e66cb46ab48cb25 Mon Sep 17 00:00:00 2001 From: Aaron Blankstein Date: Mon, 18 Feb 2019 09:18:24 -0600 Subject: [PATCH 2/9] handle the sender argument to contract execution --- src/vm/contracts.rs | 25 ++++++++++++++++--------- src/vm/errors.rs | 1 + src/vm/functions/define.rs | 2 +- src/vm/parser.rs | 3 ++- src/vm/tests/contracts.rs | 9 +++++---- src/vm/tests/parser_lexer.rs | 5 +++-- src/vm/variables.rs | 4 +++- 7 files changed, 31 insertions(+), 18 deletions(-) diff --git a/src/vm/contracts.rs b/src/vm/contracts.rs index d8880f089..4882106ab 100644 --- a/src/vm/contracts.rs +++ b/src/vm/contracts.rs @@ -3,6 +3,7 @@ use vm::callables::CallableType; use vm::{Context, SymbolicExpression, Value, Environment, apply, eval_all}; use vm::database::{MemoryContractDatabase, ContractDatabase}; use vm::parser; +use vm::variables; pub struct Contract <'a> { db: Box, @@ -26,7 +27,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 { let func = self.global_context.lookup_function(tx_name) .ok_or(Error::Undefined(format!("No such function in contract: {}", tx_name)))?; @@ -34,16 +35,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 = Context::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())) + } } } diff --git a/src/vm/errors.rs b/src/vm/errors.rs index 23446318a..607250ea7 100644 --- a/src/vm/errors.rs +++ b/src/vm/errors.rs @@ -22,6 +22,7 @@ pub enum Error { ValueTooLarge, ExpectedListPairs, InvalidTypeDescription, + BadSender(Value), BadSymbolicRepresentation(String), ReservedName(String), InterpreterError(String), diff --git a/src/vm/functions/define.rs b/src/vm/functions/define.rs index 94931ea0a..33e000d4f 100644 --- a/src/vm/functions/define.rs +++ b/src/vm/functions/define.rs @@ -69,7 +69,7 @@ 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()); diff --git a/src/vm/parser.rs b/src/vm/parser.rs index 5296b2733..197bc9e2b 100644 --- a/src/vm/parser.rs +++ b/src/vm/parser.rs @@ -47,7 +47,8 @@ pub fn lex(input: &str) -> Result> { // 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((\\")|([[:ascii:]&&[^"\n\r\t]]))*)""##, TokenType::StringLiteral), + LexMatcher::new(r##""(?P((\\")|([[: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), diff --git a/src/vm/tests/contracts.rs b/src/vm/tests/contracts.rs index 67e834a4c..9b017ae87 100644 --- a/src/vm/tests/contracts.rs +++ b/src/vm/tests/contracts.rs @@ -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, vec![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(_, _)) => {}, diff --git a/src/vm/tests/parser_lexer.rs b/src/vm/tests/parser_lexer.rs index 2906f2699..0d4a0226e 100644 --- a/src/vm/tests/parser_lexer.rs +++ b/src/vm/tests/parser_lexer.rs @@ -6,8 +6,9 @@ use vm::parser; #[test] fn test_parse_let_expression() { let input = "z (let((x 1) (y 2)) - (+ x - (let ((x 3)) + (+ x ;; \"comments section?\" + ;; this is also a comment! + (let ((x 3)) ;; more commentary (+ x y)) x)) x y"; let program = vec![ diff --git a/src/vm/variables.rs b/src/vm/variables.rs index 06e57d5d7..bd2e4617f 100644 --- a/src/vm/variables.rs +++ b/src/vm/variables.rs @@ -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"]; From 476cd7f1a0d361a441041e7a6342f99e5ad9edcd Mon Sep 17 00:00:00 2001 From: Aaron Blankstein Date: Mon, 18 Feb 2019 09:28:48 -0600 Subject: [PATCH 3/9] use [u8;20] array instead of vec for principal type --- src/vm/parser.rs | 8 +++++++- src/vm/tests/contracts.rs | 2 +- src/vm/types.rs | 4 ++-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/vm/parser.rs b/src/vm/parser.rs index 197bc9e2b..3d022ef8c 100644 --- a/src/vm/parser.rs +++ b/src/vm/parser.rs @@ -112,7 +112,13 @@ pub fn lex(input: &str) -> Result> { 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") diff --git a/src/vm/tests/contracts.rs b/src/vm/tests/contracts.rs index 9b017ae87..3878a93da 100644 --- a/src/vm/tests/contracts.rs +++ b/src/vm/tests/contracts.rs @@ -62,7 +62,7 @@ fn test_factorial_contract() { Value::Int(120), ]; - let sender = Value::Principal(1, vec![1,1,1,1,1]); + 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()), diff --git a/src/vm/types.rs b/src/vm/types.rs index 77a4b3ce6..b61f73ca1 100644 --- a/src/vm/types.rs +++ b/src/vm/types.rs @@ -56,7 +56,7 @@ pub enum Value { Bool(bool), Buffer(BuffData), List(Vec, TypeSignature), - Principal(u8, Vec), // a principal is a version byte + hash160 (20 bytes) + Principal(u8, [u8; 20]), // a principal is a version byte + hash160 (20 bytes) Tuple(TupleData) } @@ -121,7 +121,7 @@ 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() }; From 8791851bbf31c0aa86149ffdf87de47a30a6d558 Mon Sep 17 00:00:00 2001 From: Aaron Blankstein Date: Mon, 18 Feb 2019 09:46:53 -0600 Subject: [PATCH 4/9] use two types for public and private functions --- src/vm/callables.rs | 122 ++++++++++++++++++------------ src/vm/functions/define.rs | 8 +- src/vm/tests/simple_apply_eval.rs | 8 +- 3 files changed, 81 insertions(+), 57 deletions(-) diff --git a/src/vm/callables.rs b/src/vm/callables.rs index f40bf1b7c..bff3bc4c0 100644 --- a/src/vm/callables.rs +++ b/src/vm/callables.rs @@ -10,9 +10,20 @@ pub enum CallableType <'a> { } #[derive(Clone)] -pub struct DefinedFunction { - is_public: bool, - types: Option>, +pub enum DefinedFunction { + Public(PublicFunction), + Private(PrivateFunction) +} + +#[derive(Clone)] +pub struct PublicFunction { + types: Vec, + pub arguments: Vec, + pub body: SymbolicExpression +} + +#[derive(Clone)] +pub struct PrivateFunction { pub arguments: Vec, 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 { + // since self is a malformed object. + let mut context = Context::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::MultiplyDefined(arg.clone())) + } + } + eval(&self.body, env, &context) + } +} + +impl PrivateFunction { + pub fn new(arguments: Vec, body: SymbolicExpression) -> DefinedFunction { + DefinedFunction::Private(PrivateFunction { + arguments: arguments, + body: body, + }) + } + + fn apply(&self, args: &[Value], env: &mut Environment) -> Result { + let mut context = Context::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::MultiplyDefined(arg.clone())) + } + } + eval(&self.body, env, &context) + } +} impl DefinedFunction { pub fn apply(&self, args: &[Value], env: &mut Environment) -> Result { - 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, 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 } } } diff --git a/src/vm/functions/define.rs b/src/vm/functions/define.rs index 33e000d4f..7d0d661d8 100644 --- a/src/vm/functions/define.rs +++ b/src/vm/functions/define.rs @@ -1,5 +1,5 @@ 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}; @@ -49,7 +49,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()); @@ -71,8 +71,8 @@ fn handle_define_public_function(signature: &[SymbolicExpression], 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)) } diff --git a/src/vm/tests/simple_apply_eval.rs b/src/vm/tests/simple_apply_eval.rs index cfd3b2933..4c71e9b87 100644 --- a/src/vm/tests/simple_apply_eval.rs +++ b/src/vm/tests/simple_apply_eval.rs @@ -2,7 +2,7 @@ use vm::{eval, execute}; use vm::database::MemoryContractDatabase; use vm::errors::Error; use vm::{Value, Context, Environment}; -use vm::callables::DefinedFunction; +use vm::callables::PrivateFunction; use vm::representations::SymbolicExpression; use vm::parser::parse; @@ -25,7 +25,7 @@ fn test_simple_user_function() { SymbolicExpression::Atom("x".to_string())])); let func_args = vec!["x".to_string()]; - let user_function = DefinedFunction::new_private(func_args, func_body); + let user_function = PrivateFunction::new(func_args, func_body); let context = Context::new(); let mut global_context = Context::new(); @@ -120,8 +120,8 @@ 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(); From 241b3cc49fb04e28b92b4727062e24da6b347475 Mon Sep 17 00:00:00 2001 From: Aaron Blankstein Date: Mon, 18 Feb 2019 12:29:17 -0600 Subject: [PATCH 5/9] separate two kinds of contexts (local and global) into two types --- src/vm/callables.rs | 8 +-- src/vm/contexts.rs | 65 ++++++++++++++---------- src/vm/contracts.rs | 15 +++--- src/vm/functions/boolean.rs | 6 +-- src/vm/functions/database.rs | 10 ++-- src/vm/functions/define.rs | 7 +-- src/vm/functions/lists.rs | 6 +-- src/vm/functions/mod.rs | 6 +-- src/vm/functions/tuples.rs | 6 +-- src/vm/mod.rs | 22 ++++---- src/vm/parser.rs | 83 +++++++++++++++++++++++++++++++ src/vm/tests/parser_lexer.rs | 72 --------------------------- src/vm/tests/simple_apply_eval.rs | 14 +++--- 13 files changed, 171 insertions(+), 149 deletions(-) delete mode 100644 src/vm/tests/parser_lexer.rs diff --git a/src/vm/callables.rs b/src/vm/callables.rs index bff3bc4c0..bbb7f995b 100644 --- a/src/vm/callables.rs +++ b/src/vm/callables.rs @@ -1,12 +1,12 @@ use vm::errors::{InterpreterResult as Result, Error}; use vm::representations::SymbolicExpression; use vm::types::TypeSignature; -use vm::{eval, Value, Context, Environment}; +use vm::{eval, Value, LocalContext, Environment}; pub enum CallableType <'a> { UserFunction(DefinedFunction), NativeFunction(&'a Fn(&[Value]) -> Result), - SpecialFunction(&'a Fn(&[SymbolicExpression], &mut Environment, &Context) -> Result) + SpecialFunction(&'a Fn(&[SymbolicExpression], &mut Environment, &LocalContext) -> Result) } #[derive(Clone)] @@ -47,7 +47,7 @@ impl PublicFunction { fn apply(&self, args: &[Value], env: &mut Environment) -> Result { // since self is a malformed object. - let mut context = Context::new(); + let mut context = LocalContext::new(); let arg_iterator = self.arguments.iter().zip(self.types.iter()).zip(args.iter()); for ((arg, type_sig), value) in arg_iterator { if !type_sig.admits(value) { @@ -70,7 +70,7 @@ impl PrivateFunction { } fn apply(&self, args: &[Value], env: &mut Environment) -> Result { - let mut context = Context::new(); + let mut context = LocalContext::new(); let arg_iterator = self.arguments.iter().zip(args.iter()); for (arg, value) in arg_iterator { if let Some(_) = context.variables.insert(arg.clone(), value.clone()) { diff --git a/src/vm/contexts.rs b/src/vm/contexts.rs index 6c3ac6cf0..632ff59ad 100644 --- a/src/vm/contexts.rs +++ b/src/vm/contexts.rs @@ -9,7 +9,7 @@ use vm::database::ContractDatabase; const MAX_CONTEXT_DEPTH: u16 = 256; pub struct Environment <'a> { - pub global_context: &'a Context <'a>, + pub global_context: &'a GlobalContext, pub call_stack: CallStack, pub database: &'a mut ContractDatabase } @@ -18,7 +18,7 @@ impl <'a> Environment <'a> { // Environments pack a reference to the global context, a mutable reference to a contract db, // together with a call stack. Generally, the environment structure is intended to be reconstructed // for every transaction. - pub fn new(global_context: &'a Context<'a>, + pub fn new(global_context: &'a GlobalContext, database: &'a mut ContractDatabase) -> Environment<'a> { Environment { global_context: global_context, @@ -31,31 +31,56 @@ impl <'a> Environment <'a> { // Aaron: note -- only the global context will ever have DefinedFunctions // so it is probably worthwhile to separate into 2 types. -pub struct Context <'a> { - pub parent: Option< &'a Context<'a>>, +pub struct GlobalContext { pub variables: HashMap, pub functions: HashMap, +} + +pub struct LocalContext <'a> { + pub parent: Option< &'a LocalContext<'a>>, + pub variables: HashMap, 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 { + match self.variables.get(name) { + Some(value) => Option::Some(value.clone()), + None => Option::None + } + } + + pub fn lookup_function(&self, name: &str) -> Option { + 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> { + pub fn extend(&'a self) -> Result> { if self.depth >= MAX_CONTEXT_DEPTH { Err(Error::MaxContextDepthReached) } else { - Ok(Context { + Ok(LocalContext { parent: Some(self), variables: HashMap::new(), - functions: HashMap::new(), depth: self.depth + 1 }) } @@ -63,7 +88,7 @@ impl <'a> Context <'a> { pub fn lookup_variable(&self, name: &str) -> Option { match self.variables.get(name) { - Some(value) => Option::Some((*value).clone()), + Some(value) => Option::Some(value.clone()), None => { match self.parent { Some(parent) => parent.lookup_variable(name), @@ -72,20 +97,6 @@ impl <'a> Context <'a> { } } } - - pub fn lookup_function(&self, name: &str) -> Option { - 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 { diff --git a/src/vm/contracts.rs b/src/vm/contracts.rs index 4882106ab..a23f4a254 100644 --- a/src/vm/contracts.rs +++ b/src/vm/contracts.rs @@ -1,19 +1,20 @@ +use vm::{SymbolicExpression, Value, apply, eval_all}; use vm::errors::{Error, InterpreterResult as Result}; use vm::callables::CallableType; -use vm::{Context, SymbolicExpression, Value, Environment, apply, eval_all}; +use vm::contexts::{Environment, LocalContext, GlobalContext}; use vm::database::{MemoryContractDatabase, ContractDatabase}; use vm::parser; use vm::variables; -pub struct Contract <'a> { +pub struct Contract { db: Box, - global_context: Context<'a> + global_context: GlobalContext } -impl <'a> Contract <'a> { - pub fn make_in_memory_contract(contract: &str) -> Result> { +impl Contract { + pub fn make_in_memory_contract(contract: &str) -> Result { let parsed: Vec<_> = parser::parse(contract)?; - let mut global_context = Context::new(); + let mut global_context = GlobalContext::new(); let mut db_instance = Box::new(MemoryContractDatabase::new()); let result = eval_all(&parsed, &mut *db_instance, &mut global_context)?; @@ -45,7 +46,7 @@ impl <'a> Contract <'a> { } } - let mut local_context = Context::new(); + let mut local_context = LocalContext::new(); local_context.variables.insert(variables::TX_SENDER.to_string(), sender.clone()); apply(&CallableType::UserFunction(func), args, &mut env, &local_context) diff --git a/src/vm/functions/boolean.rs b/src/vm/functions/boolean.rs index 3ede4cec1..3d4fcc1b9 100644 --- a/src/vm/functions/boolean.rs +++ b/src/vm/functions/boolean.rs @@ -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 { match *value { @@ -10,7 +10,7 @@ fn type_force_bool(value: &Value) -> Result { } } -pub fn special_or(args: &[SymbolicExpression], env: &mut Environment, context: &Context) -> Result { +pub fn special_or(args: &[SymbolicExpression], env: &mut Environment, context: &LocalContext) -> Result { 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 { +pub fn special_and(args: &[SymbolicExpression], env: &mut Environment, context: &LocalContext) -> Result { if args.len() < 1 { return Err(Error::InvalidArguments("(and ...) requires at least 1 argument".to_string())) } diff --git a/src/vm/functions/database.rs b/src/vm/functions/database.rs index c87fc7539..2cbed4f211 100644 --- a/src/vm/functions/database.rs +++ b/src/vm/functions/database.rs @@ -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 { + context: &LocalContext) -> Result { // 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 { + context: &LocalContext) -> Result { // 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 { + context: &LocalContext) -> Result { // 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 { + context: &LocalContext) -> Result { // arg0 -> map name // arg1 -> key if args.len() != 2 { diff --git a/src/vm/functions/define.rs b/src/vm/functions/define.rs index 7d0d661d8..ed4dd5d60 100644 --- a/src/vm/functions/define.rs +++ b/src/vm/functions/define.rs @@ -3,7 +3,8 @@ use vm::callables::{DefinedFunction, PublicFunction, PrivateFunction}; use vm::representations::SymbolicExpression; use vm::representations::SymbolicExpression::{Atom, AtomValue, List, NamedParameter}; use vm::errors::{Error, InterpreterResult as Result}; -use vm::{ Context, Environment, eval }; +use vm::contexts::{GlobalContext, LocalContext, Environment}; +use vm::eval; pub enum DefineResult { Variable(String, Value), @@ -12,7 +13,7 @@ pub enum DefineResult { NoDefine } -fn check_legal_define(name: &str, global_context: &Context) -> Result<()> { +fn check_legal_define(name: &str, global_context: &GlobalContext) -> Result<()> { use vm::is_reserved; if is_reserved(name) { @@ -27,7 +28,7 @@ fn check_legal_define(name: &str, global_context: &Context) -> Result<()> { fn handle_define_variable(variable: &String, expression: &SymbolicExpression, env: &mut Environment) -> Result { // 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)) } diff --git a/src/vm/functions/lists.rs b/src/vm/functions/lists.rs index e4890a0bd..6397737c6 100644 --- a/src/vm/functions/lists.rs +++ b/src/vm/functions/lists.rs @@ -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::new_list(args) } -pub fn list_fold(args: &[SymbolicExpression], env: &mut Environment, context: &Context) -> Result { +pub fn list_fold(args: &[SymbolicExpression], env: &mut Environment, context: &LocalContext) -> Result { if args.len() != 3 { return Err(Error::InvalidArguments(format!("Wrong number of arguments ({}) to fold", args.len()))) } @@ -30,7 +30,7 @@ pub fn list_fold(args: &[SymbolicExpression], env: &mut Environment, context: &C } } -pub fn list_map(args: &[SymbolicExpression], env: &mut Environment, context: &Context) -> Result { +pub fn list_map(args: &[SymbolicExpression], env: &mut Environment, context: &LocalContext) -> Result { if args.len() != 2 { return Err(Error::InvalidArguments(format!("Wrong number of arguments ({}) to map", args.len()))) } diff --git a/src/vm/functions/mod.rs b/src/vm/functions/mod.rs index dddd63011..0e2fd1d02 100644 --- a/src/vm/functions/mod.rs +++ b/src/vm/functions/mod.rs @@ -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 { @@ -32,7 +32,7 @@ fn native_begin(args: &[Value]) -> Result { } } -fn special_if(args: &[SymbolicExpression], env: &mut Environment, context: &Context) -> Result { +fn special_if(args: &[SymbolicExpression], env: &mut Environment, context: &LocalContext) -> Result { 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 { +fn special_let(args: &[SymbolicExpression], env: &mut Environment, context: &LocalContext) -> Result { use vm::is_reserved; // (let ((x 1) (y 2)) (+ x y)) -> 3 diff --git a/src/vm/functions/tuples.rs b/src/vm/functions/tuples.rs index b9f559136..65ef45ade 100644 --- a/src/vm/functions/tuples.rs +++ b/src/vm/functions/tuples.rs @@ -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 { +pub fn tuple_cons(args: &[SymbolicExpression], env: &mut Environment, context: &LocalContext) -> Result { // (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 { +pub fn tuple_get(args: &[SymbolicExpression], env: &mut Environment, context: &LocalContext) -> Result { // (get arg-name (tuple ...)) // if the tuple argument is 'null, then return 'null. // NOTE: a tuple field value itself may _never_ be 'null diff --git a/src/vm/mod.rs b/src/vm/mod.rs index bc55ffdd6..09ede1d22 100644 --- a/src/vm/mod.rs +++ b/src/vm/mod.rs @@ -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 { +fn lookup_variable(name: &str, context: &LocalContext, env: &Environment) -> Result { 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 Result { + env: &mut Environment, context: &LocalContext) -> Result { 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 { +pub fn eval <'a> (exp: &SymbolicExpression, env: &'a mut Environment, context: &LocalContext) -> Result { 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 { + global_context: &mut GlobalContext) -> Result { let mut last_executed = None; - let context = Context::new(); + let context = LocalContext::new(); for exp in expressions { let try_define = { @@ -167,7 +165,7 @@ fn eval_all(expressions: &[SymbolicExpression], * database. */ pub fn execute(program: &str) -> Result { - 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)?; diff --git a/src/vm/parser.rs b/src/vm/parser.rs index 3d022ef8c..98b80bb1d 100644 --- a/src/vm/parser.rs +++ b/src/vm/parser.rs @@ -213,3 +213,86 @@ pub fn parse(input: &str) -> Result> { 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"); + + } + +} diff --git a/src/vm/tests/parser_lexer.rs b/src/vm/tests/parser_lexer.rs deleted file mode 100644 index 0d4a0226e..000000000 --- a/src/vm/tests/parser_lexer.rs +++ /dev/null @@ -1,72 +0,0 @@ -use vm::representations::SymbolicExpression; -use vm::errors::Error; -use vm::types::Value; -use vm::parser; - -#[test] -fn test_parse_let_expression() { - let input = "z (let((x 1) (y 2)) - (+ x ;; \"comments section?\" - ;; this is also a comment! - (let ((x 3)) ;; more commentary - (+ x y)) - x)) x y"; - let program = vec![ - SymbolicExpression::Atom("z".to_string()), - SymbolicExpression::List(Box::new([ - SymbolicExpression::Atom("let".to_string()), - SymbolicExpression::List(Box::new([ - SymbolicExpression::List(Box::new([ - SymbolicExpression::Atom("x".to_string()), - SymbolicExpression::AtomValue(Value::Int(1))])), - SymbolicExpression::List(Box::new([ - SymbolicExpression::Atom("y".to_string()), - SymbolicExpression::AtomValue(Value::Int(2))]))])), - SymbolicExpression::List(Box::new([ - SymbolicExpression::Atom("+".to_string()), - SymbolicExpression::Atom("x".to_string()), - SymbolicExpression::List(Box::new([ - SymbolicExpression::Atom("let".to_string()), - SymbolicExpression::List(Box::new([ - SymbolicExpression::List(Box::new([ - SymbolicExpression::Atom("x".to_string()), - SymbolicExpression::AtomValue(Value::Int(3))]))])), - SymbolicExpression::List(Box::new([ - SymbolicExpression::Atom("+".to_string()), - SymbolicExpression::Atom("x".to_string()), - SymbolicExpression::Atom("y".to_string())]))])), - SymbolicExpression::Atom("x".to_string())]))])), - SymbolicExpression::Atom("x".to_string()), - SymbolicExpression::Atom("y".to_string()), - ]; - - let parsed = parser::parse(&input); - assert_eq!(Ok(program), parsed, "Should match expected symbolic expression"); -} - -#[test] -fn test_parse_failures() { - let too_much_closure = "(let ((x 1) (y 2))))"; - let not_enough_closure = "(let ((x 1) (y 2))"; - let middle_hash = "(let ((x 1) (y#not 2)) x)"; - - assert!(match parser::parse(&too_much_closure) { - Err(Error::ParseError(_)) => true, - _ => false - }, "Should have failed to parse with too many right parens"); - - assert!(match parser::parse(¬_enough_closure) { - Err(Error::ParseError(_)) => true, - _ => false - }, "Should have failed to parse with too few right parens"); - - let x = parser::parse(&middle_hash); - assert!(match x { - Err(Error::ParseError(_)) => true, - _ => { - println!("Expected parser error. Unexpected value is:\n {:?}", x); - false - } - }, "Should have failed to parse with a middle hash"); - -} diff --git a/src/vm/tests/simple_apply_eval.rs b/src/vm/tests/simple_apply_eval.rs index 4c71e9b87..f98eb62c6 100644 --- a/src/vm/tests/simple_apply_eval.rs +++ b/src/vm/tests/simple_apply_eval.rs @@ -1,7 +1,7 @@ use vm::{eval, execute}; use vm::database::MemoryContractDatabase; use vm::errors::Error; -use vm::{Value, Context, Environment}; +use vm::{Value, LocalContext, GlobalContext, Environment}; use vm::callables::PrivateFunction; use vm::representations::SymbolicExpression; use vm::parser::parse; @@ -27,8 +27,8 @@ fn test_simple_user_function() { let func_args = vec!["x".to_string()]; let user_function = PrivateFunction::new(func_args, func_body); - let context = Context::new(); - let mut global_context = Context::new(); + let context = LocalContext::new(); + let mut global_context = GlobalContext::new(); let mut db = MemoryContractDatabase::new(); global_context.variables.insert("a".to_string(), Value::Int(59)); @@ -56,8 +56,8 @@ fn test_simple_let() { x))"; if let Ok(parsed_program) = parse(&program) { - let context = Context::new(); - let global_context = Context::new(); + let context = LocalContext::new(); + let global_context = GlobalContext::new(); let mut db = MemoryContractDatabase::new(); let mut env = Environment::new(&global_context, &mut db); @@ -123,8 +123,8 @@ fn test_simple_if_functions() { let user_function1 = PrivateFunction::new(func_args1, parsed_bodies[0].clone()); let user_function2 = PrivateFunction::new(func_args2, parsed_bodies[1].clone()); - let context = Context::new(); - let mut global_context = Context::new(); + let context = LocalContext::new(); + let mut global_context = GlobalContext::new(); let mut db = MemoryContractDatabase::new(); global_context.functions.insert("with_else".to_string(), user_function1); From f3177eece8ca2e68b0b2d0ab4de8811f72138350 Mon Sep 17 00:00:00 2001 From: Aaron Blankstein Date: Tue, 19 Feb 2019 09:47:23 -0600 Subject: [PATCH 6/9] implement equality for Value enum --- src/vm/contexts.rs | 3 --- src/vm/tests/mod.rs | 1 - src/vm/types.rs | 32 +++++++++++++++++++++++++------- 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/src/vm/contexts.rs b/src/vm/contexts.rs index 632ff59ad..da46de672 100644 --- a/src/vm/contexts.rs +++ b/src/vm/contexts.rs @@ -28,9 +28,6 @@ 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 GlobalContext { pub variables: HashMap, pub functions: HashMap, diff --git a/src/vm/tests/mod.rs b/src/vm/tests/mod.rs index f42d2d502..edacf9007 100644 --- a/src/vm/tests/mod.rs +++ b/src/vm/tests/mod.rs @@ -1,6 +1,5 @@ mod lists; mod defines; -mod parser_lexer; mod simple_apply_eval; mod datamaps; mod contracts; diff --git a/src/vm/types.rs b/src/vm/types.rs index b61f73ca1..c000deedc 100644 --- a/src/vm/types.rs +++ b/src/vm/types.rs @@ -44,12 +44,11 @@ pub struct TupleData { } #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct BuffData { +struct BuffData { data: Vec, - length: u32 } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, Eq, Hash)] pub enum Value { Void, Int(i128), @@ -60,6 +59,26 @@ pub enum Value { Tuple(TupleData) } + +impl PartialEq for Value { + fn eq(&self, other: &Value) -> bool { + match (self, other) { + (Value::Void, Value::Void) => { true }, + (Value::Int(x), Value::Int(y)) => { x == y }, + (Value::Bool(x), Value::Bool(y)) => { x == y }, + (Value::Buffer(ref x), Value::Buffer(ref y)) => { x.data == y.data }, + (Value::List(ref x_data, _), Value::List(ref y_data, _)) => { x_data == y_data }, + (Value::Principal(x_version, ref x_data), Value::Principal(y_version, ref y_data)) => { + x_version == y_version && x_data == y_data + }, + (Value::Tuple(ref x_data), Value::Tuple(ref y_data)) => { + x_data.data_map == y_data.data_map + }, + _ => false + } + } +} + impl Value { pub fn new_list(list_data: &[Value]) -> Result { let vec_data = Vec::from(list_data); @@ -85,8 +104,7 @@ impl Value { 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 })) } } @@ -104,7 +122,7 @@ impl Value { Value::Int(_i) => AtomTypeIdentifier::IntType.size(), Value::Bool(_i) => AtomTypeIdentifier::BoolType.size(), Value::Principal(_,_) => AtomTypeIdentifier::PrincipalType.size(), - Value::Buffer(ref buff_data) => Ok(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() } @@ -388,7 +406,7 @@ impl TypeSignature { 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") From 74e45cbcb8697d5b3ad8b3a122e71e6a1264a420 Mon Sep 17 00:00:00 2001 From: Aaron Blankstein Date: Tue, 19 Feb 2019 10:19:16 -0600 Subject: [PATCH 7/9] implement partialeq, hash for ListData and TupleData manually: allows Value to derive. remove now uneccessary Hash traits. --- src/vm/functions/lists.rs | 6 ++-- src/vm/tests/datamaps.rs | 37 +++++++++----------- src/vm/types.rs | 71 ++++++++++++++++++++++----------------- 3 files changed, 59 insertions(+), 55 deletions(-) diff --git a/src/vm/functions/lists.rs b/src/vm/functions/lists.rs index 6397737c6..199654ea1 100644 --- a/src/vm/functions/lists.rs +++ b/src/vm/functions/lists.rs @@ -17,7 +17,7 @@ pub fn list_fold(args: &[SymbolicExpression], env: &mut Environment, context: &L 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) ]; @@ -39,8 +39,8 @@ pub fn list_map(args: &[SymbolicExpression], env: &mut Environment, context: &Lo let list = eval(&args[1], env, context)?; match list { - Value::List(vector, _) => { - let result: Result> = vector.iter().map(|x| { + Value::List(list_data) => { + let result: Result> = list_data.data.iter().map(|x| { let argument = [ AtomValue(x.clone()) ]; apply(&function, &argument, env, context) }).collect(); diff --git a/src/vm/tests/datamaps.rs b/src/vm/tests/datamaps.rs index 276593d60..b389a521a 100644 --- a/src/vm/tests/datamaps.rs +++ b/src/vm/tests/datamaps.rs @@ -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] diff --git a/src/vm/types.rs b/src/vm/types.rs index c000deedc..a71e01c88 100644 --- a/src/vm/types.rs +++ b/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 } -#[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, @@ -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 @@ -48,34 +49,44 @@ struct BuffData { data: Vec, } -#[derive(Debug, Clone, Eq, Hash)] +#[derive(Debug, Clone, Eq)] +pub struct ListData { + pub data: Vec, + type_signature: TypeSignature +} + +#[derive(Debug, Clone, Eq, PartialEq, Hash)] pub enum Value { Void, Int(i128), Bool(bool), Buffer(BuffData), - List(Vec, TypeSignature), + 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 PartialEq for Value { - fn eq(&self, other: &Value) -> bool { - match (self, other) { - (Value::Void, Value::Void) => { true }, - (Value::Int(x), Value::Int(y)) => { x == y }, - (Value::Bool(x), Value::Bool(y)) => { x == y }, - (Value::Buffer(ref x), Value::Buffer(ref y)) => { x.data == y.data }, - (Value::List(ref x_data, _), Value::List(ref y_data, _)) => { x_data == y_data }, - (Value::Principal(x_version, ref x_data), Value::Principal(y_version, ref y_data)) => { - x_version == y_version && x_data == y_data - }, - (Value::Tuple(ref x_data), Value::Tuple(ref y_data)) => { - x_data.data_map == y_data.data_map - }, - _ => false - } +impl Hash for ListData { + fn hash(&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(&self, state: &mut H) { + self.data_map.hash(state); } } @@ -94,7 +105,7 @@ impl Value { 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) -> Result { @@ -124,7 +135,7 @@ impl Value { Value::Principal(_,_) => AtomTypeIdentifier::PrincipalType.size(), 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() } } @@ -145,9 +156,9 @@ impl fmt::Display for Value { }; 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, ")") @@ -398,8 +409,8 @@ impl TypeSignature { } 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, @@ -409,7 +420,7 @@ impl TypeSignature { 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) From bb203956e7eec2dc612d87a5a2fde7454d61a7cd Mon Sep 17 00:00:00 2001 From: Aaron Blankstein Date: Tue, 19 Feb 2019 10:40:22 -0600 Subject: [PATCH 8/9] rename MultiplyDefined error to VariableDefinedMultipleTimes --- src/vm/callables.rs | 4 ++-- src/vm/errors.rs | 2 +- src/vm/functions/define.rs | 2 +- src/vm/functions/mod.rs | 2 +- src/vm/mod.rs | 35 +++++++++++++++++++++++++++++++ src/vm/tests/defines.rs | 2 +- src/vm/tests/simple_apply_eval.rs | 34 +----------------------------- 7 files changed, 42 insertions(+), 39 deletions(-) diff --git a/src/vm/callables.rs b/src/vm/callables.rs index bbb7f995b..55319929e 100644 --- a/src/vm/callables.rs +++ b/src/vm/callables.rs @@ -54,7 +54,7 @@ impl PublicFunction { 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())) + return Err(Error::VariableDefinedMultipleTimes(arg.clone())) } } eval(&self.body, env, &context) @@ -74,7 +74,7 @@ impl PrivateFunction { 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())) + return Err(Error::VariableDefinedMultipleTimes(arg.clone())) } } eval(&self.body, env, &context) diff --git a/src/vm/errors.rs b/src/vm/errors.rs index 607250ea7..3d2a06ca4 100644 --- a/src/vm/errors.rs +++ b/src/vm/errors.rs @@ -26,7 +26,7 @@ pub enum Error { BadSymbolicRepresentation(String), ReservedName(String), InterpreterError(String), - MultiplyDefined(String) + VariableDefinedMultipleTimes(String) } pub type InterpreterResult = Result; diff --git a/src/vm/functions/define.rs b/src/vm/functions/define.rs index ed4dd5d60..af37fca6c 100644 --- a/src/vm/functions/define.rs +++ b/src/vm/functions/define.rs @@ -19,7 +19,7 @@ fn check_legal_define(name: &str, global_context: &GlobalContext) -> Result<()> 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(()) } diff --git a/src/vm/functions/mod.rs b/src/vm/functions/mod.rs index 0e2fd1d02..8cf5c5c19 100644 --- a/src/vm/functions/mod.rs +++ b/src/vm/functions/mod.rs @@ -78,7 +78,7 @@ fn special_let(args: &[SymbolicExpression], env: &mut Environment, context: &Loc } 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 { diff --git a/src/vm/mod.rs b/src/vm/mod.rs index 09ede1d22..8f8e1aa56 100644 --- a/src/vm/mod.rs +++ b/src/vm/mod.rs @@ -171,3 +171,38 @@ pub fn execute(program: &str) -> Result { let parsed = parser::parse(program)?; eval_all(&parsed, &mut *db_instance, &mut global_context) } + + +mod test { + #[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)); + } +} diff --git a/src/vm/tests/defines.rs b/src/vm/tests/defines.rs index 10a8d83f3..9333f7a25 100644 --- a/src/vm/tests/defines.rs +++ b/src/vm/tests/defines.rs @@ -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)); } diff --git a/src/vm/tests/simple_apply_eval.rs b/src/vm/tests/simple_apply_eval.rs index f98eb62c6..11cfa5840 100644 --- a/src/vm/tests/simple_apply_eval.rs +++ b/src/vm/tests/simple_apply_eval.rs @@ -6,38 +6,6 @@ use vm::callables::PrivateFunction; use vm::representations::SymbolicExpression; 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 = 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)); -} - #[test] fn test_simple_let() { /* @@ -262,7 +230,7 @@ fn test_bad_lets() { let expectations: &[Result] = &[ 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))); From 29a02255314d06aa82e37f38fff4d8d586ff916d Mon Sep 17 00:00:00 2001 From: Aaron Blankstein Date: Tue, 19 Feb 2019 11:28:30 -0600 Subject: [PATCH 9/9] light cleaning up of test functions --- src/address/c32.rs | 2 +- src/vm/mod.rs | 7 ++++++- src/vm/tests/simple_apply_eval.rs | 1 - src/vm/types.rs | 3 +-- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/address/c32.rs b/src/address/c32.rs index 150aa4573..7885c6cb9 100644 --- a/src/address/c32.rs +++ b/src/address/c32.rs @@ -130,7 +130,7 @@ fn double_sha256_checksum(data: &[u8]) -> Vec { } fn c32_check_encode(version: u8, data: &[u8]) -> Result { - if (version >= 32) { + if version >= 32 { return Err(Error::InvalidVersion(version)) } diff --git a/src/vm/mod.rs b/src/vm/mod.rs index 8f8e1aa56..c06df09ea 100644 --- a/src/vm/mod.rs +++ b/src/vm/mod.rs @@ -174,6 +174,12 @@ pub fn execute(program: &str) -> Result { 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() { // @@ -182,7 +188,6 @@ mod test { // (define a 59) // (do_work a) // - let content = [ SymbolicExpression::List( Box::new([ SymbolicExpression::Atom("do_work".to_string()), SymbolicExpression::Atom("a".to_string()) ])) ]; diff --git a/src/vm/tests/simple_apply_eval.rs b/src/vm/tests/simple_apply_eval.rs index 11cfa5840..b14e9da5a 100644 --- a/src/vm/tests/simple_apply_eval.rs +++ b/src/vm/tests/simple_apply_eval.rs @@ -3,7 +3,6 @@ use vm::database::MemoryContractDatabase; use vm::errors::Error; use vm::{Value, LocalContext, GlobalContext, Environment}; use vm::callables::PrivateFunction; -use vm::representations::SymbolicExpression; use vm::parser::parse; #[test] diff --git a/src/vm/types.rs b/src/vm/types.rs index a71e01c88..796cf6e92 100644 --- a/src/vm/types.rs +++ b/src/vm/types.rs @@ -45,7 +45,7 @@ pub struct TupleData { } #[derive(Debug, Clone, PartialEq, Eq, Hash)] -struct BuffData { +pub struct BuffData { data: Vec, } @@ -114,7 +114,6 @@ 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 })) } }