mirror of
https://github.com/alexgo-io/stacks-puppet-node.git
synced 2026-05-25 00:33:20 +08:00
Merge pull request #921 from blockstack/review/smart-contract-021319
Code Review: Smart Contract VM 02/13/19
This commit is contained in:
@@ -20,6 +20,7 @@ serde_json = "1.0"
|
||||
rust-crypto = "0.2"
|
||||
sha2 = "0.8.0"
|
||||
dirs = "1.0.4"
|
||||
regex = "1"
|
||||
|
||||
[dependencies.secp256k1]
|
||||
version = "0.11.5"
|
||||
|
||||
@@ -16,3 +16,320 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Blockstack. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
use super::Error;
|
||||
use crypto::sha2::Sha256;
|
||||
use crypto::digest::Digest;
|
||||
|
||||
const C32_CHARACTERS: &str = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
|
||||
|
||||
fn c32_encode(input_bytes: &[u8]) -> String {
|
||||
let c32_chars: &[u8] = C32_CHARACTERS.as_bytes();
|
||||
|
||||
let mut result = vec![];
|
||||
let mut carry = 0;
|
||||
let mut carry_bits = 0;
|
||||
|
||||
for current_value in input_bytes.iter().rev() {
|
||||
let low_bits_to_take = 5 - carry_bits;
|
||||
let low_bits = current_value & ((1<<low_bits_to_take) - 1);
|
||||
let c32_value = (low_bits << carry_bits) + carry;
|
||||
result.push(c32_chars[c32_value as usize]);
|
||||
carry_bits = (8 + carry_bits) - 5;
|
||||
carry = current_value >> (8 - carry_bits);
|
||||
|
||||
if carry_bits >= 5 {
|
||||
let c32_value = carry & ((1<<5) - 1);
|
||||
result.push(c32_chars[c32_value as usize]);
|
||||
carry_bits = carry_bits - 5;
|
||||
carry = carry >> 5;
|
||||
}
|
||||
}
|
||||
|
||||
if carry_bits > 0 {
|
||||
result.push(c32_chars[carry as usize]);
|
||||
}
|
||||
|
||||
// remove leading zeros from c32 encoding
|
||||
|
||||
while let Some(v) = result.pop() {
|
||||
if v != c32_chars[0] {
|
||||
result.push(v);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// add leading zeros from input.
|
||||
|
||||
for current_value in input_bytes.iter() {
|
||||
if *current_value == 0 {
|
||||
result.push(c32_chars[0]);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let result: Vec<u8> = result.drain(..).rev().collect();
|
||||
String::from_utf8(result).unwrap()
|
||||
}
|
||||
|
||||
fn c32_decode(input_str: &str) -> Result<Vec<u8>, Error> {
|
||||
let mut result = vec![];
|
||||
let mut carry: u16 = 0;
|
||||
let mut carry_bits = 0; // can be up to 5
|
||||
|
||||
let iter_c32_digits = input_str.chars().rev()
|
||||
.map(|x| { C32_CHARACTERS.find(x) });
|
||||
|
||||
for current_result in iter_c32_digits {
|
||||
let current_5bit = current_result.ok_or(Error::InvalidCrockford32)?;
|
||||
carry += (current_5bit as u16) << carry_bits;
|
||||
carry_bits += 5;
|
||||
|
||||
if carry_bits >= 8 {
|
||||
result.push((carry & ((1<<8) - 1)) as u8);
|
||||
carry_bits -= 8;
|
||||
carry = carry >> 8;
|
||||
}
|
||||
}
|
||||
|
||||
if carry_bits > 0 {
|
||||
result.push(carry as u8);
|
||||
}
|
||||
|
||||
// remove leading zeros from Vec<u8> encoding
|
||||
while let Some(v) = result.pop() {
|
||||
if v != 0 {
|
||||
result.push(v);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// add leading zeros from input.
|
||||
for current_value in input_str.chars() {
|
||||
if current_value == '0' {
|
||||
result.push(0);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
result.reverse();
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn double_sha256_checksum(data: &[u8]) -> Vec<u8> {
|
||||
let mut sha2 = Sha256::new();
|
||||
let mut tmp = [0u8; 32];
|
||||
sha2.input(data);
|
||||
sha2.result(&mut tmp);
|
||||
let mut sha2 = Sha256::new();
|
||||
sha2.input(&tmp);
|
||||
sha2.result(&mut tmp);
|
||||
tmp[0..4].to_vec()
|
||||
}
|
||||
|
||||
fn c32_check_encode(version: u8, data: &[u8]) -> Result<String, Error> {
|
||||
if version >= 32 {
|
||||
return Err(Error::InvalidVersion(version))
|
||||
}
|
||||
|
||||
let mut check_data = vec![version];
|
||||
check_data.extend_from_slice(data);
|
||||
let checksum = double_sha256_checksum(&check_data);
|
||||
|
||||
let mut encoding_data = data.to_vec();
|
||||
encoding_data.extend_from_slice(&checksum);
|
||||
|
||||
// working with ascii strings is awful.
|
||||
let mut c32_string = c32_encode(&encoding_data).into_bytes();
|
||||
let version_char = C32_CHARACTERS.as_bytes()[version as usize];
|
||||
c32_string.insert(0, version_char);
|
||||
|
||||
Ok(String::from_utf8(c32_string).unwrap())
|
||||
}
|
||||
|
||||
fn c32_check_decode(check_data: &str) -> Result<(u8, Vec<u8>), Error> {
|
||||
if check_data.len() < 2 {
|
||||
return Err(Error::InvalidCrockford32)
|
||||
}
|
||||
let (version, data) = check_data.split_at(1);
|
||||
|
||||
let data_sum_bytes = c32_decode(data)?;
|
||||
if data_sum_bytes.len() < 5 {
|
||||
return Err(Error::InvalidCrockford32)
|
||||
}
|
||||
|
||||
let (data_bytes, expected_sum) = data_sum_bytes.split_at(data_sum_bytes.len() - 4);
|
||||
|
||||
let mut check_data = c32_decode(version)?;
|
||||
check_data.extend_from_slice(data_bytes);
|
||||
|
||||
let computed_sum = double_sha256_checksum(&check_data);
|
||||
if computed_sum != expected_sum {
|
||||
return Err(Error::InvalidChecksum(computed_sum, expected_sum.to_vec()))
|
||||
}
|
||||
|
||||
let version = check_data[0];
|
||||
let data = data_bytes.to_vec();
|
||||
Ok((version, data))
|
||||
}
|
||||
|
||||
pub fn c32_address_decode(c32_address_str: &str) -> Result<(u8, Vec<u8>), Error> {
|
||||
if c32_address_str.len() <= 5 {
|
||||
Err(Error::InvalidCrockford32)
|
||||
} else {
|
||||
c32_check_decode(&c32_address_str[1..])
|
||||
}
|
||||
}
|
||||
|
||||
pub fn c32_address(version: u8, data: &[u8]) -> Result<String, Error> {
|
||||
let c32_string = c32_check_encode(version, data)?;
|
||||
Ok(format!("S{}", c32_string))
|
||||
}
|
||||
|
||||
mod test {
|
||||
use util::hash::hex_bytes;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_addresses() {
|
||||
let hex_strs = [
|
||||
"a46ff88886c2ef9762d970b4d2c63678835bd39d",
|
||||
"0000000000000000000000000000000000000000",
|
||||
"0000000000000000000000000000000000000001",
|
||||
"1000000000000000000000000000000000000001",
|
||||
"1000000000000000000000000000000000000000"
|
||||
];
|
||||
|
||||
let versions = [
|
||||
22,
|
||||
0,
|
||||
31,
|
||||
20,
|
||||
26,
|
||||
21
|
||||
];
|
||||
|
||||
let c32_addrs = [
|
||||
[
|
||||
"SP2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKNRV9EJ7",
|
||||
"SP000000000000000000002Q6VF78",
|
||||
"SP00000000000000000005JA84HQ",
|
||||
"SP80000000000000000000000000000004R0CMNV",
|
||||
"SP800000000000000000000000000000033H8YKK"
|
||||
],
|
||||
[
|
||||
"S02J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKPVKG2CE",
|
||||
"S0000000000000000000002AA028H",
|
||||
"S000000000000000000006EKBDDS",
|
||||
"S080000000000000000000000000000007R1QC00",
|
||||
"S080000000000000000000000000000003ENTGCQ"
|
||||
],
|
||||
[
|
||||
"SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR",
|
||||
"SZ000000000000000000002ZE1VMN",
|
||||
"SZ00000000000000000005HZ3DVN",
|
||||
"SZ80000000000000000000000000000004XBV6MS",
|
||||
"SZ800000000000000000000000000000007VF5G0"
|
||||
],
|
||||
[
|
||||
"SM2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQVX8X0G",
|
||||
"SM0000000000000000000062QV6X",
|
||||
"SM00000000000000000005VR75B2",
|
||||
"SM80000000000000000000000000000004WBEWKC",
|
||||
"SM80000000000000000000000000000000JGSYGV"
|
||||
],
|
||||
[
|
||||
"ST2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQYAC0RQ",
|
||||
"ST000000000000000000002AMW42H",
|
||||
"ST000000000000000000042DB08Y",
|
||||
"ST80000000000000000000000000000006BYJ4R4",
|
||||
"ST80000000000000000000000000000002YBNPV3"
|
||||
],
|
||||
[
|
||||
"SN2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKP6D2ZK9",
|
||||
"SN000000000000000000003YDHWKJ",
|
||||
"SN00000000000000000005341MC8",
|
||||
"SN800000000000000000000000000000066KZWY0",
|
||||
"SN800000000000000000000000000000006H75AK"
|
||||
]
|
||||
];
|
||||
|
||||
for i in 0..hex_strs.len() {
|
||||
for j in 0..versions.len() {
|
||||
let h = hex_strs[i];
|
||||
let v = versions[j];
|
||||
let b = hex_bytes(h).unwrap();
|
||||
let z = c32_address(v, &b).unwrap();
|
||||
|
||||
assert_eq!(z, c32_addrs[j][i]);
|
||||
|
||||
let (decoded_version, decoded_bytes) = c32_address_decode(&z).unwrap();
|
||||
assert_eq!(decoded_version, v);
|
||||
assert_eq!(decoded_bytes, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simple() {
|
||||
let hex_strings = &[
|
||||
"a46ff88886c2ef9762d970b4d2c63678835bd39d",
|
||||
"",
|
||||
"0000000000000000000000000000000000000000",
|
||||
"0000000000000000000000000000000000000001",
|
||||
"1000000000000000000000000000000000000001",
|
||||
"1000000000000000000000000000000000000000",
|
||||
"01",
|
||||
"22",
|
||||
"0001",
|
||||
"000001",
|
||||
"00000001",
|
||||
"10",
|
||||
"0100",
|
||||
"1000",
|
||||
"010000",
|
||||
"100000",
|
||||
"01000000",
|
||||
"10000000",
|
||||
"0100000000"
|
||||
];
|
||||
let c32_strs = [
|
||||
"MHQZH246RBQSERPSE2TD5HHPF21NQMWX",
|
||||
"",
|
||||
"00000000000000000000",
|
||||
"00000000000000000001",
|
||||
"20000000000000000000000000000001",
|
||||
"20000000000000000000000000000000",
|
||||
"1",
|
||||
"12",
|
||||
"01",
|
||||
"001",
|
||||
"0001",
|
||||
"G",
|
||||
"80",
|
||||
"400",
|
||||
"2000",
|
||||
"10000",
|
||||
"G0000",
|
||||
"800000",
|
||||
"4000000"
|
||||
];
|
||||
|
||||
let results: Vec<_> = hex_strings.iter().zip(c32_strs.iter())
|
||||
.map(|(hex_str, expected)|
|
||||
{
|
||||
let bytes = hex_bytes(hex_str).unwrap();
|
||||
let c32_encoded = c32_encode(&bytes);
|
||||
let decoded_bytes = c32_decode(&c32_encoded).unwrap();
|
||||
let result = (bytes, c32_encoded, decoded_bytes, expected);
|
||||
println!("{:?}", result);
|
||||
result
|
||||
}).collect();
|
||||
for (bytes, c32_encoded, decoded_bytes, expected_c32) in results.iter() {
|
||||
assert_eq!(bytes, decoded_bytes);
|
||||
assert_eq!(c32_encoded, *expected_c32);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,4 +17,27 @@
|
||||
along with Blockstack. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use std::error;
|
||||
use std::fmt;
|
||||
|
||||
pub mod c32;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
InvalidCrockford32,
|
||||
InvalidVersion(u8),
|
||||
InvalidChecksum(Vec<u8>, Vec<u8>),
|
||||
EmptyData
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for Error {
|
||||
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
17
src/main.rs
17
src/main.rs
@@ -30,6 +30,7 @@ extern crate curve25519_dalek;
|
||||
extern crate ed25519_dalek;
|
||||
extern crate sha2;
|
||||
extern crate dirs;
|
||||
extern crate regex;
|
||||
|
||||
#[macro_use] extern crate serde_derive;
|
||||
|
||||
@@ -38,7 +39,9 @@ mod burnchains;
|
||||
mod chainstate;
|
||||
mod core;
|
||||
mod vm;
|
||||
mod address;
|
||||
|
||||
use std::fs;
|
||||
use std::env;
|
||||
use std::process;
|
||||
|
||||
@@ -82,6 +85,20 @@ fn main() {
|
||||
}
|
||||
}
|
||||
|
||||
if argv[1] == "exec_program" {
|
||||
if argv.len() < 3 {
|
||||
eprintln!("Usage: {} exec_program [program-file.scm]", argv[0]);
|
||||
process::exit(1);
|
||||
}
|
||||
let program: String = fs::read_to_string(&argv[2])
|
||||
.expect(&format!("Error reading file: {}", argv[2]));
|
||||
match vm::execute(&program) {
|
||||
Ok(result) => println!("{}", result),
|
||||
Err(error) => println!("Program Execution Error: \n {}", error)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if argv.len() < 4 {
|
||||
eprintln!("Usage: {} blockchain network working_dir", argv[0]);
|
||||
process::exit(1);
|
||||
|
||||
108
src/vm/callables.rs
Normal file
108
src/vm/callables.rs
Normal file
@@ -0,0 +1,108 @@
|
||||
use vm::errors::{InterpreterResult as Result, Error};
|
||||
use vm::representations::SymbolicExpression;
|
||||
use vm::types::TypeSignature;
|
||||
use vm::{eval, Value, LocalContext, Environment};
|
||||
|
||||
pub enum CallableType <'a> {
|
||||
UserFunction(DefinedFunction),
|
||||
NativeFunction(&'a Fn(&[Value]) -> Result<Value>),
|
||||
SpecialFunction(&'a Fn(&[SymbolicExpression], &mut Environment, &LocalContext) -> Result<Value>)
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
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
|
||||
}
|
||||
|
||||
#[derive(Clone,PartialEq,Eq,Hash)]
|
||||
pub struct FunctionIdentifier {
|
||||
pub arguments: Vec<String>,
|
||||
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> {
|
||||
match self {
|
||||
DefinedFunction::Private(f) => f.apply(args, env),
|
||||
DefinedFunction::Public(f) => f.apply(args, env),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_public(&self) -> bool {
|
||||
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: body,
|
||||
arguments: arguments }
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,25 @@
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
|
||||
use vm::types::{DefinedFunction, FunctionIdentifier, Value};
|
||||
use vm::errors::{Error, InterpreterResult as Result};
|
||||
use vm::types::Value;
|
||||
use vm::callables::{DefinedFunction, FunctionIdentifier};
|
||||
use vm::database::ContractDatabase;
|
||||
|
||||
const MAX_CONTEXT_DEPTH: u16 = 256;
|
||||
|
||||
pub struct Environment <'a> {
|
||||
pub global_context: Context <'a>,
|
||||
pub global_context: &'a GlobalContext,
|
||||
pub call_stack: CallStack,
|
||||
pub database: Box<ContractDatabase>
|
||||
pub database: &'a mut ContractDatabase
|
||||
}
|
||||
|
||||
impl <'a> Environment <'a> {
|
||||
pub fn new(database: Box<ContractDatabase>) -> Environment<'a> {
|
||||
let global_context = Context::new();
|
||||
// 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 GlobalContext,
|
||||
database: &'a mut ContractDatabase) -> Environment<'a> {
|
||||
Environment {
|
||||
global_context: global_context,
|
||||
call_stack: CallStack::new(),
|
||||
@@ -21,22 +28,20 @@ impl <'a> Environment <'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Context <'a> {
|
||||
pub parent: Option< &'a Context<'a>>,
|
||||
pub struct GlobalContext {
|
||||
pub variables: HashMap<String, Value>,
|
||||
pub functions: HashMap<String, Box<DefinedFunction>>,
|
||||
pub functions: HashMap<String, DefinedFunction>,
|
||||
}
|
||||
|
||||
impl <'a> Context <'a> {
|
||||
pub fn new() -> Context<'a> {
|
||||
Context { parent: Option::None,
|
||||
variables: HashMap::new(),
|
||||
functions: HashMap::new() }
|
||||
}
|
||||
|
||||
pub fn extend(&'a self) -> Context<'a> {
|
||||
Context {
|
||||
parent: Some(self),
|
||||
pub struct LocalContext <'a> {
|
||||
pub parent: Option< &'a LocalContext<'a>>,
|
||||
pub variables: HashMap<String, Value>,
|
||||
depth: u16
|
||||
}
|
||||
|
||||
impl GlobalContext {
|
||||
pub fn new() -> GlobalContext {
|
||||
GlobalContext {
|
||||
variables: HashMap::new(),
|
||||
functions: HashMap::new()
|
||||
}
|
||||
@@ -44,24 +49,46 @@ impl <'a> Context <'a> {
|
||||
|
||||
pub fn lookup_variable(&self, name: &str) -> Option<Value> {
|
||||
match self.variables.get(name) {
|
||||
Some(value) => Option::Some((*value).clone()),
|
||||
None => {
|
||||
match self.parent {
|
||||
Some(parent) => parent.lookup_variable(name),
|
||||
None => Option::None
|
||||
}
|
||||
}
|
||||
Some(value) => Option::Some(value.clone()),
|
||||
None => Option::None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lookup_function(&self, name: &str) -> Option<Box<DefinedFunction>> {
|
||||
pub fn lookup_function(&self, name: &str) -> Option<DefinedFunction> {
|
||||
match self.functions.get(name) {
|
||||
Some(value) => {
|
||||
Option::Some(Box::new(*value.clone()))
|
||||
},
|
||||
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<LocalContext<'a>> {
|
||||
if self.depth >= MAX_CONTEXT_DEPTH {
|
||||
Err(Error::MaxContextDepthReached)
|
||||
} else {
|
||||
Ok(LocalContext {
|
||||
parent: Some(self),
|
||||
variables: HashMap::new(),
|
||||
depth: self.depth + 1
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lookup_variable(&self, name: &str) -> Option<Value> {
|
||||
match self.variables.get(name) {
|
||||
Some(value) => Option::Some(value.clone()),
|
||||
None => {
|
||||
match self.parent {
|
||||
Some(parent) => parent.lookup_function(name),
|
||||
Some(parent) => parent.lookup_variable(name),
|
||||
None => Option::None
|
||||
}
|
||||
}
|
||||
@@ -81,6 +108,10 @@ impl CallStack {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn depth(&self) -> usize {
|
||||
self.stack.len()
|
||||
}
|
||||
|
||||
pub fn contains(&self, user_function: &FunctionIdentifier) -> bool {
|
||||
self.stack.contains(user_function)
|
||||
}
|
||||
|
||||
57
src/vm/contracts.rs
Normal file
57
src/vm/contracts.rs
Normal file
@@ -0,0 +1,57 @@
|
||||
use vm::{SymbolicExpression, Value, apply, eval_all};
|
||||
use vm::errors::{Error, InterpreterResult as Result};
|
||||
use vm::callables::CallableType;
|
||||
use vm::contexts::{Environment, LocalContext, GlobalContext};
|
||||
use vm::database::{MemoryContractDatabase, ContractDatabase};
|
||||
use vm::parser;
|
||||
use vm::variables;
|
||||
|
||||
pub struct Contract {
|
||||
db: Box<ContractDatabase>,
|
||||
global_context: GlobalContext
|
||||
}
|
||||
|
||||
impl Contract {
|
||||
pub fn make_in_memory_contract(contract: &str) -> Result<Contract> {
|
||||
let parsed: Vec<_> = parser::parse(contract)?;
|
||||
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)?;
|
||||
match result {
|
||||
Value::Void => {},
|
||||
_ => return Err(Error::Generic("Contract instantiation should return null.".to_string()))
|
||||
}
|
||||
|
||||
Ok(Contract { db: db_instance,
|
||||
global_context: global_context })
|
||||
}
|
||||
|
||||
// Todo: add principal value type, check for sender to be one.
|
||||
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)))?;
|
||||
if !func.is_public() {
|
||||
return Err(Error::Undefined("Attempt to call non-public function".to_string()))
|
||||
}
|
||||
|
||||
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()))
|
||||
}
|
||||
}
|
||||
|
||||
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()))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,18 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use vm::errors::Error;
|
||||
use vm::InterpreterResult;
|
||||
use vm::errors::{Error, InterpreterResult as Result};
|
||||
use vm::types::{Value, TypeSignature, TupleTypeSignature, AtomTypeIdentifier};
|
||||
|
||||
pub trait DataMap {
|
||||
fn fetch_entry(&self, key: &Value) -> InterpreterResult;
|
||||
fn set_entry(&mut self, key: Value, value: Value) -> Result<(), Error>;
|
||||
fn insert_entry(&mut self, key: Value, value: Value) -> InterpreterResult;
|
||||
fn delete_entry(&mut self, key: &Value) -> InterpreterResult;
|
||||
fn fetch_entry(&self, key: &Value) -> Result<Value>;
|
||||
fn set_entry(&mut self, key: Value, value: Value) -> Result<()>;
|
||||
fn insert_entry(&mut self, key: Value, value: Value) -> Result<Value>;
|
||||
fn delete_entry(&mut self, key: &Value) -> Result<Value>;
|
||||
}
|
||||
|
||||
pub trait ContractDatabase {
|
||||
fn get_data_map(&mut self, map_name: &str) -> Option<&mut DataMap>;
|
||||
fn get_data_map(&mut self, map_name: &str) -> Option<&DataMap>;
|
||||
fn get_mut_data_map(&mut self, map_name: &str) -> Option<&mut DataMap>;
|
||||
fn create_map(&mut self, map_name: &str, key_type: TupleTypeSignature, value_type: TupleTypeSignature);
|
||||
}
|
||||
|
||||
@@ -31,10 +31,8 @@ impl MemoryDataMap {
|
||||
value_type: TupleTypeSignature) -> MemoryDataMap {
|
||||
MemoryDataMap {
|
||||
map: HashMap::new(),
|
||||
key_type: TypeSignature::new(
|
||||
AtomTypeIdentifier::TupleType(key_type), 0),
|
||||
value_type: TypeSignature::new(
|
||||
AtomTypeIdentifier::TupleType(value_type), 0)
|
||||
key_type: TypeSignature::new_atom(AtomTypeIdentifier::TupleType(key_type)),
|
||||
value_type: TypeSignature::new_atom(AtomTypeIdentifier::TupleType(value_type))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -46,7 +44,7 @@ impl MemoryContractDatabase {
|
||||
}
|
||||
|
||||
impl ContractDatabase for MemoryContractDatabase {
|
||||
fn get_data_map(&mut self, map_name: &str) -> Option<&mut DataMap> {
|
||||
fn get_mut_data_map(&mut self, map_name: &str) -> Option<&mut DataMap> {
|
||||
if let Some(data_map) = self.maps.get_mut(map_name) {
|
||||
Some(data_map)
|
||||
} else {
|
||||
@@ -54,6 +52,14 @@ impl ContractDatabase for MemoryContractDatabase {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_data_map(&mut self, map_name: &str) -> Option<&DataMap> {
|
||||
if let Some(data_map) = self.maps.get(map_name) {
|
||||
Some(data_map)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn create_map(&mut self, map_name: &str, key_type: TupleTypeSignature, value_type: TupleTypeSignature) {
|
||||
let new_map = MemoryDataMap::new(key_type, value_type);
|
||||
self.maps.insert(map_name.to_string(), new_map);
|
||||
@@ -65,9 +71,8 @@ impl DataMap for MemoryDataMap {
|
||||
// however, they should really be specified in the functions/database.rs file, whereas
|
||||
// this file should really just be speccing out the database connection/requirement.
|
||||
|
||||
fn fetch_entry(&self, key: &Value) -> InterpreterResult {
|
||||
let key_type = TypeSignature::type_of(key);
|
||||
if self.key_type != key_type {
|
||||
fn fetch_entry(&self, key: &Value) -> Result<Value> {
|
||||
if !self.key_type.admits(key) {
|
||||
return Err(Error::TypeError(format!("{:?}", self.key_type), (*key).clone()))
|
||||
}
|
||||
if let Some(value) = self.map.get(key) {
|
||||
@@ -77,26 +82,22 @@ impl DataMap for MemoryDataMap {
|
||||
}
|
||||
}
|
||||
|
||||
fn set_entry(&mut self, key: Value, value: Value) -> Result<(), Error> {
|
||||
let key_type = TypeSignature::type_of(&key);
|
||||
if self.key_type != key_type {
|
||||
fn set_entry(&mut self, key: Value, value: Value) -> Result<()> {
|
||||
if !self.key_type.admits(&key) {
|
||||
return Err(Error::TypeError(format!("{:?}", self.key_type), key))
|
||||
}
|
||||
let value_type = TypeSignature::type_of(&value);
|
||||
if self.value_type != value_type {
|
||||
if !self.value_type.admits(&value) {
|
||||
return Err(Error::TypeError(format!("{:?}", self.value_type), value))
|
||||
}
|
||||
self.map.insert(key, value);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn insert_entry(&mut self, key: Value, value: Value) -> InterpreterResult {
|
||||
let key_type = TypeSignature::type_of(&key);
|
||||
if self.key_type != key_type {
|
||||
fn insert_entry(&mut self, key: Value, value: Value) -> Result<Value> {
|
||||
if !self.key_type.admits(&key) {
|
||||
return Err(Error::TypeError(format!("{:?}", self.key_type), key))
|
||||
}
|
||||
let value_type = TypeSignature::type_of(&value);
|
||||
if self.value_type != value_type {
|
||||
if !self.value_type.admits(&value) {
|
||||
return Err(Error::TypeError(format!("{:?}", self.value_type), value))
|
||||
}
|
||||
if self.map.contains_key(&key) {
|
||||
@@ -107,9 +108,8 @@ impl DataMap for MemoryDataMap {
|
||||
}
|
||||
}
|
||||
|
||||
fn delete_entry(&mut self, key: &Value) -> InterpreterResult {
|
||||
let key_type = TypeSignature::type_of(key);
|
||||
if self.key_type != key_type {
|
||||
fn delete_entry(&mut self, key: &Value) -> Result<Value> {
|
||||
if !self.key_type.admits(key) {
|
||||
return Err(Error::TypeError(format!("{:?}", self.key_type), (*key).clone()))
|
||||
}
|
||||
if let Some(_value) = self.map.remove(key) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::fmt;
|
||||
use std::error;
|
||||
use super::types::Value;
|
||||
use vm::types::Value;
|
||||
|
||||
#[derive(Debug,PartialEq)]
|
||||
pub enum Error {
|
||||
@@ -13,15 +13,30 @@ pub enum Error {
|
||||
Arithmetic(String),
|
||||
ParseError(String),
|
||||
RecursionDetected,
|
||||
ExpectedListPairs
|
||||
MaxStackDepthReached,
|
||||
MaxContextDepthReached,
|
||||
ListDimensionTooHigh,
|
||||
ListTooLarge,
|
||||
BadTypeConstruction,
|
||||
BufferTooLarge,
|
||||
ValueTooLarge,
|
||||
ExpectedListPairs,
|
||||
InvalidTypeDescription,
|
||||
BadSender(Value),
|
||||
BadSymbolicRepresentation(String),
|
||||
ReservedName(String),
|
||||
InterpreterError(String),
|
||||
VariableDefinedMultipleTimes(String)
|
||||
}
|
||||
|
||||
pub type InterpreterResult <R> = Result<R, Error>;
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Error::RecursionDetected => write!(f, "Illegal operation: attempted recursion detected."),
|
||||
Error::TryEvalToFunction => write!(f, "Illegal operation: attempt to evaluate to function."),
|
||||
Error::TypeError(ref expected, ref found) => write!(f, "TypeError: Expected {}, found {:?}.", expected, found),
|
||||
Error::TypeError(ref expected, ref found) => write!(f, "TypeError: Expected {}, found {}.", expected, found),
|
||||
_ => write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
@@ -29,8 +44,18 @@ impl fmt::Display for Error {
|
||||
|
||||
impl error::Error for Error {
|
||||
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
|
||||
match *self {
|
||||
_ => None
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_formats() {
|
||||
assert_eq!(format!("{}", Error::RecursionDetected),
|
||||
"Illegal operation: attempted recursion detected.");
|
||||
assert_eq!(format!("{}", Error::TryEvalToFunction),
|
||||
"Illegal operation: attempt to evaluate to function.");
|
||||
assert_eq!(format!("{}", Error::TypeError("Test".to_string(), Value::Void)),
|
||||
"TypeError: Expected Test, found null.");
|
||||
assert_eq!(format!("{}", Error::NotImplemented),
|
||||
"NotImplemented");
|
||||
}
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
use super::super::types::Value;
|
||||
use super::super::errors::Error;
|
||||
use super::super::InterpreterResult;
|
||||
use vm::types::Value;
|
||||
use vm::errors::{Error, InterpreterResult as Result};
|
||||
|
||||
fn type_force_integer(value: &Value) -> Result<i128, Error> {
|
||||
fn type_force_integer(value: &Value) -> Result<i128> {
|
||||
match *value {
|
||||
Value::Int(int) => Ok(int),
|
||||
_ => Err(Error::TypeError("IntType".to_string(), value.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
fn binary_comparison<F>(args: &[Value], function: &F) -> InterpreterResult
|
||||
fn binary_comparison<F>(args: &[Value], function: &F) -> Result<Value>
|
||||
where F: Fn(i128, i128) -> bool {
|
||||
if args.len() == 2 {
|
||||
let arg1 = type_force_integer(&args[0])?;
|
||||
@@ -20,21 +19,21 @@ where F: Fn(i128, i128) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn native_geq(args: &[Value]) -> InterpreterResult {
|
||||
pub fn native_geq(args: &[Value]) -> Result<Value> {
|
||||
binary_comparison(args, &|x, y| x >= y)
|
||||
}
|
||||
pub fn native_leq(args: &[Value]) -> InterpreterResult {
|
||||
pub fn native_leq(args: &[Value]) -> Result<Value> {
|
||||
binary_comparison(args, &|x, y| x <= y)
|
||||
}
|
||||
pub fn native_ge(args: &[Value]) -> InterpreterResult {
|
||||
pub fn native_ge(args: &[Value]) -> Result<Value> {
|
||||
binary_comparison(args, &|x, y| x > y)
|
||||
}
|
||||
pub fn native_le(args: &[Value]) -> InterpreterResult {
|
||||
pub fn native_le(args: &[Value]) -> Result<Value> {
|
||||
binary_comparison(args, &|x, y| x < y)
|
||||
}
|
||||
|
||||
pub fn native_add(args: &[Value]) -> InterpreterResult {
|
||||
let typed_args: Result<Vec<_>, Error> = args.iter().map(|x| type_force_integer(x)).collect();
|
||||
pub fn native_add(args: &[Value]) -> Result<Value> {
|
||||
let typed_args: Result<Vec<_>> = args.iter().map(|x| type_force_integer(x)).collect();
|
||||
let parsed_args = typed_args?;
|
||||
let checked_result = parsed_args.iter().fold(Some(0), |acc: Option<i128>, x| {
|
||||
match acc {
|
||||
@@ -48,8 +47,8 @@ pub fn native_add(args: &[Value]) -> InterpreterResult {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn native_sub(args: &[Value]) -> InterpreterResult {
|
||||
let typed_args: Result<Vec<_>, Error> = args.iter().map(|x| type_force_integer(x)).collect();
|
||||
pub fn native_sub(args: &[Value]) -> Result<Value> {
|
||||
let typed_args: Result<Vec<_>> = args.iter().map(|x| type_force_integer(x)).collect();
|
||||
let parsed_args = typed_args?;
|
||||
if let Some((first, rest)) = parsed_args.split_first() {
|
||||
if rest.len() == 0 { // return negation
|
||||
@@ -71,8 +70,8 @@ pub fn native_sub(args: &[Value]) -> InterpreterResult {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn native_mul(args: &[Value]) -> InterpreterResult {
|
||||
let typed_args: Result<Vec<_>, Error> = args.iter().map(|x| type_force_integer(x)).collect();
|
||||
pub fn native_mul(args: &[Value]) -> Result<Value> {
|
||||
let typed_args: Result<Vec<_>> = args.iter().map(|x| type_force_integer(x)).collect();
|
||||
let parsed_args = typed_args?;
|
||||
let checked_result = parsed_args.iter().fold(Some(1), |acc: Option<i128>, x| {
|
||||
match acc {
|
||||
@@ -86,8 +85,8 @@ pub fn native_mul(args: &[Value]) -> InterpreterResult {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn native_div(args: &[Value]) -> InterpreterResult {
|
||||
let typed_args: Result<Vec<_>, Error> = args.iter().map(|x| type_force_integer(x)).collect();
|
||||
pub fn native_div(args: &[Value]) -> Result<Value> {
|
||||
let typed_args: Result<Vec<_>> = args.iter().map(|x| type_force_integer(x)).collect();
|
||||
let parsed_args = typed_args?;
|
||||
if let Some((first, rest)) = parsed_args.split_first() {
|
||||
let checked_result = rest.iter().fold(Some(*first), |acc, x| {
|
||||
@@ -105,6 +104,8 @@ pub fn native_div(args: &[Value]) -> InterpreterResult {
|
||||
}
|
||||
}
|
||||
|
||||
// AARON: Note -- this was pulled straight for rustlang's nightly @ 1.34
|
||||
// -- this _should be_ deleted once 1.34 ships.
|
||||
fn checked_pow(mut base: i128, mut exp: u32) -> Option<i128> {
|
||||
let mut acc: i128 = 1;
|
||||
|
||||
@@ -126,7 +127,7 @@ fn checked_pow(mut base: i128, mut exp: u32) -> Option<i128> {
|
||||
Some(acc)
|
||||
}
|
||||
|
||||
pub fn native_pow(args: &[Value]) -> InterpreterResult {
|
||||
pub fn native_pow(args: &[Value]) -> Result<Value> {
|
||||
if args.len() == 2 {
|
||||
let base = type_force_integer(&args[0])?;
|
||||
let power_i128 = type_force_integer(&args[1])?;
|
||||
@@ -148,7 +149,7 @@ pub fn native_pow(args: &[Value]) -> InterpreterResult {
|
||||
}
|
||||
|
||||
|
||||
pub fn native_mod(args: &[Value]) -> InterpreterResult {
|
||||
pub fn native_mod(args: &[Value]) -> Result<Value> {
|
||||
if args.len() == 2 {
|
||||
let numerator = type_force_integer(&args[0])?;
|
||||
let denominator = type_force_integer(&args[1])?;
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
use super::super::types::Value;
|
||||
use super::super::errors::Error;
|
||||
use super::super::representations::SymbolicExpression;
|
||||
use super::super::{Context,Environment,eval,InterpreterResult};
|
||||
use vm::types::Value;
|
||||
use vm::errors::{Error, InterpreterResult as Result};
|
||||
use vm::representations::SymbolicExpression;
|
||||
use vm::{LocalContext, Environment, eval};
|
||||
|
||||
fn type_force_bool(value: &Value) -> Result<bool, Error> {
|
||||
fn type_force_bool(value: &Value) -> Result<bool> {
|
||||
match *value {
|
||||
Value::Bool(boolean) => Ok(boolean),
|
||||
_ => Err(Error::TypeError("BoolType".to_string(), value.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn special_or(args: &[SymbolicExpression], env: &mut Environment, context: &Context) -> InterpreterResult {
|
||||
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) -> InterpreterResult {
|
||||
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()))
|
||||
}
|
||||
@@ -42,7 +42,7 @@ pub fn special_and(args: &[SymbolicExpression], env: &mut Environment, context:
|
||||
Ok(Value::Bool(true))
|
||||
}
|
||||
|
||||
pub fn native_not(args: &[Value]) -> InterpreterResult {
|
||||
pub fn native_not(args: &[Value]) -> Result<Value> {
|
||||
if args.len() != 1 {
|
||||
return Err(Error::InvalidArguments("(not ...) requires exactly 1 argument".to_string()))
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
use super::super::types::{Value};
|
||||
use super::super::representations::SymbolicExpression;
|
||||
use super::super::{InterpreterResult,eval,Context,Environment};
|
||||
use super::super::errors::Error;
|
||||
use super::super::database::DataMap;
|
||||
use vm::types::{Value};
|
||||
use vm::representations::SymbolicExpression;
|
||||
use vm::errors::{Error, InterpreterResult as Result};
|
||||
use vm::database::DataMap;
|
||||
use vm::{eval, LocalContext, Environment};
|
||||
|
||||
fn obtain_map <'a> (map_arg: &SymbolicExpression, env: &'a mut Environment) -> Result<&'a mut DataMap, Error> {
|
||||
fn obtain_map <'a> (map_arg: &SymbolicExpression, env: &'a mut Environment) -> Result<&'a mut DataMap> {
|
||||
let map_name = match map_arg {
|
||||
SymbolicExpression::Atom(value) => Ok(value),
|
||||
_ => Err(Error::InvalidArguments("First argument in data functions must be the map name".to_string()))
|
||||
}?;
|
||||
match env.database.get_data_map(map_name) {
|
||||
match env.database.get_mut_data_map(map_name) {
|
||||
Some(map) => Ok(map),
|
||||
None => Err(Error::Undefined(format!("No such map named: {}", map_name)))
|
||||
}
|
||||
@@ -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) -> InterpreterResult {
|
||||
context: &LocalContext) -> Result<Value> {
|
||||
// arg0 -> map name
|
||||
// arg1 -> key
|
||||
if args.len() != 2 {
|
||||
@@ -26,14 +26,22 @@ pub fn special_fetch_entry(args: &[SymbolicExpression],
|
||||
|
||||
let key = eval(&args[1], env, context)?;
|
||||
|
||||
let map = obtain_map(&args[0], env)?;
|
||||
let map_name = match &args[0] {
|
||||
SymbolicExpression::Atom(value) => Ok(value),
|
||||
_ => Err(Error::InvalidArguments("First argument in data functions must be the map name".to_string()))
|
||||
}?;
|
||||
|
||||
let map = match env.database.get_data_map(&map_name) {
|
||||
Some(map) => Ok(map),
|
||||
None => Err(Error::Undefined(format!("No such map named: {}", map_name)))
|
||||
}?;
|
||||
|
||||
map.fetch_entry(&key)
|
||||
}
|
||||
|
||||
pub fn special_set_entry(args: &[SymbolicExpression],
|
||||
env: &mut Environment,
|
||||
context: &Context) -> InterpreterResult {
|
||||
context: &LocalContext) -> Result<Value> {
|
||||
// arg0 -> map name
|
||||
// arg1 -> key
|
||||
// arg2 -> value
|
||||
@@ -54,7 +62,7 @@ pub fn special_set_entry(args: &[SymbolicExpression],
|
||||
|
||||
pub fn special_insert_entry(args: &[SymbolicExpression],
|
||||
env: &mut Environment,
|
||||
context: &Context) -> InterpreterResult {
|
||||
context: &LocalContext) -> Result<Value> {
|
||||
// arg0 -> map name
|
||||
// arg1 -> key
|
||||
// arg2 -> value
|
||||
@@ -72,7 +80,7 @@ pub fn special_insert_entry(args: &[SymbolicExpression],
|
||||
|
||||
pub fn special_delete_entry(args: &[SymbolicExpression],
|
||||
env: &mut Environment,
|
||||
context: &Context) -> InterpreterResult {
|
||||
context: &LocalContext) -> Result<Value> {
|
||||
// arg0 -> map name
|
||||
// arg1 -> key
|
||||
if args.len() != 2 {
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
use super::super::types::{Value, DefinedFunction, TupleTypeSignature, TypeSignature};
|
||||
use super::super::representations::SymbolicExpression;
|
||||
use super::super::representations::SymbolicExpression::{Atom,AtomValue,List,NamedParameter};
|
||||
use super::super::{Context,Environment,eval};
|
||||
use super::super::errors::Error;
|
||||
use vm::types::{Value, TupleTypeSignature, parse_name_type_pairs};
|
||||
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::contexts::{GlobalContext, LocalContext, Environment};
|
||||
use vm::eval;
|
||||
|
||||
pub enum DefineResult {
|
||||
Variable(String, Value),
|
||||
@@ -11,14 +13,30 @@ pub enum DefineResult {
|
||||
NoDefine
|
||||
}
|
||||
|
||||
pub fn handle_define_variable(variable: &String, expression: &SymbolicExpression, env: &mut Environment) -> Result<DefineResult, Error> {
|
||||
let context = Context::new();
|
||||
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::VariableDefinedMultipleTimes(name.to_string()))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
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 = LocalContext::new();
|
||||
let value = eval(expression, env, &context)?;
|
||||
Ok(DefineResult::Variable(variable.clone(), value))
|
||||
}
|
||||
|
||||
pub fn handle_define_function(signature: &[SymbolicExpression], expression: &SymbolicExpression) -> Result<DefineResult, Error> {
|
||||
let coerced_atoms: Result<Vec<_>, _> = signature.iter().map(|x| {
|
||||
fn handle_define_private_function(signature: &[SymbolicExpression],
|
||||
expression: &SymbolicExpression,
|
||||
env: &Environment) -> Result<DefineResult> {
|
||||
let coerced_atoms: Result<Vec<_>> = signature.iter().map(|x| {
|
||||
if let Atom(name) = x {
|
||||
Ok(name)
|
||||
} else {
|
||||
@@ -27,78 +45,57 @@ pub fn handle_define_function(signature: &[SymbolicExpression], expression: &Sym
|
||||
}).collect();
|
||||
|
||||
let names = coerced_atoms?;
|
||||
if let Some((function_name, arg_names)) = names.split_first() {
|
||||
let function = DefinedFunction {
|
||||
arguments: arg_names.iter().map(|x| (*x).clone()).collect(),
|
||||
body: expression.clone()
|
||||
};
|
||||
Ok(DefineResult::Function((*function_name).clone(), function))
|
||||
} else {
|
||||
Err(Error::InvalidArguments("Must supply atleast a name argument to define a function".to_string()))
|
||||
}
|
||||
|
||||
let (function_name, arg_names) = names.split_first()
|
||||
.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 = PrivateFunction::new(
|
||||
arg_names.iter().map(|x| (*x).clone()).collect(),
|
||||
expression.clone());
|
||||
|
||||
Ok(DefineResult::Function((*function_name).clone(), function))
|
||||
}
|
||||
|
||||
fn parse_name_type_pair_list(type_def: &SymbolicExpression) -> Result<TupleTypeSignature, Error> {
|
||||
fn handle_define_public_function(signature: &[SymbolicExpression],
|
||||
expression: &SymbolicExpression,
|
||||
env: &Environment) -> Result<DefineResult> {
|
||||
let (function_symbol, arg_symbols) = signature.split_first()
|
||||
.ok_or(Error::InvalidArguments("Must supply atleast a name argument to define a function".to_string()))?;
|
||||
|
||||
// this is a pretty deep nesting here, but what we're trying to do is pick out the values of
|
||||
// the form:
|
||||
// ((name1 type1) (name2 type2) (name3 type3) ...)
|
||||
// which is a list of 2-length lists of atoms.
|
||||
|
||||
let mapped_key_types = match type_def {
|
||||
List(ref key_vec) => {
|
||||
// step 1: parse it into a vec of symbolicexpression pairs.
|
||||
let as_pairs: Result<Vec<_>, Error> =
|
||||
key_vec.iter().map(
|
||||
|key_type_pair| {
|
||||
if let List(ref as_vec) = key_type_pair {
|
||||
if as_vec.len() != 2 {
|
||||
Err(Error::ExpectedListPairs)
|
||||
} else {
|
||||
Ok((&as_vec[0], &as_vec[1]))
|
||||
}
|
||||
} else {
|
||||
Err(Error::ExpectedListPairs)
|
||||
}
|
||||
}).collect();
|
||||
|
||||
// step 2: turn into a vec of (name, typesignature) pairs.
|
||||
let key_types: Result<Vec<_>, Error> =
|
||||
(as_pairs?).iter().map(|(name_symbol, type_symbol)| {
|
||||
let name = match name_symbol {
|
||||
Atom(ref var) => Ok(var.clone()),
|
||||
_ => Err(Error::ExpectedListPairs)
|
||||
}?;
|
||||
let type_info = match type_symbol {
|
||||
Atom(ref type_description) => TypeSignature::parse_type_str(type_description),
|
||||
_ => Err(Error::ExpectedListPairs)
|
||||
}?;
|
||||
Ok((name, type_info))
|
||||
}).collect();
|
||||
|
||||
key_types
|
||||
},
|
||||
_ => Err(Error::ExpectedListPairs)
|
||||
let function_name = match function_symbol {
|
||||
Atom(name) => Ok(name),
|
||||
_ => Err(Error::InvalidArguments(format!("Invalid function name {:?}", function_symbol)))
|
||||
}?;
|
||||
|
||||
TupleTypeSignature::new(mapped_key_types)
|
||||
check_legal_define(&function_name, &env.global_context)?;
|
||||
|
||||
let arguments = parse_name_type_pairs(arg_symbols)?;
|
||||
|
||||
let function = PublicFunction::new(arguments,
|
||||
expression.clone());
|
||||
|
||||
Ok(DefineResult::Function((*function_name).clone(), function))
|
||||
}
|
||||
|
||||
fn handle_define_map(map_name: &SymbolicExpression,
|
||||
key_type: &SymbolicExpression,
|
||||
value_type: &SymbolicExpression) -> Result<DefineResult, Error> {
|
||||
value_type: &SymbolicExpression,
|
||||
env: &Environment) -> Result<DefineResult> {
|
||||
let map_str = match map_name {
|
||||
Atom(ref map_name) => Ok(map_name.clone()),
|
||||
_ => Err(Error::InvalidArguments("Non-name argument to define-map".to_string()))
|
||||
}?;
|
||||
|
||||
let key_type_signature = parse_name_type_pair_list(key_type)?;
|
||||
let value_type_signature = parse_name_type_pair_list(value_type)?;
|
||||
check_legal_define(&map_str, &env.global_context)?;
|
||||
|
||||
let key_type_signature = TupleTypeSignature::parse_name_type_pair_list(key_type)?;
|
||||
let value_type_signature = TupleTypeSignature::parse_name_type_pair_list(value_type)?;
|
||||
|
||||
Ok(DefineResult::Map(map_str, key_type_signature, value_type_signature))
|
||||
}
|
||||
|
||||
pub fn evaluate_define(expression: &SymbolicExpression, env: &mut Environment) -> Result<DefineResult, Error> {
|
||||
pub fn evaluate_define(expression: &SymbolicExpression, env: &mut Environment) -> Result<DefineResult> {
|
||||
if let SymbolicExpression::List(elements) = expression {
|
||||
if let Some(Atom(func_name)) = elements.get(0) {
|
||||
return match func_name.as_str() {
|
||||
@@ -112,7 +109,20 @@ pub fn evaluate_define(expression: &SymbolicExpression, env: &mut Environment) -
|
||||
"Illegal operation: attempted to re-define a value type.".to_string())),
|
||||
NamedParameter(ref _value) => Err(Error::InvalidArguments(
|
||||
"Illegal operation: attempted to re-define a named parameter.".to_string())),
|
||||
List(ref function_signature) => handle_define_function(&function_signature, &elements[2])
|
||||
List(ref function_signature) =>
|
||||
handle_define_private_function(&function_signature, &elements[2], env)
|
||||
}
|
||||
}
|
||||
},
|
||||
"define-public" => {
|
||||
if elements.len() != 3 {
|
||||
Err(Error::InvalidArguments("(define-public ...) requires 2 arguments".to_string()))
|
||||
} else {
|
||||
if let List(ref function_signature) = elements[1] {
|
||||
handle_define_public_function(&function_signature, &elements[2], env)
|
||||
} else {
|
||||
Err(Error::InvalidArguments(
|
||||
"Illegal operation: attempted to define-public a non-function.".to_string()))
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -120,7 +130,7 @@ pub fn evaluate_define(expression: &SymbolicExpression, env: &mut Environment) -
|
||||
if elements.len() != 4 {
|
||||
Err(Error::InvalidArguments("(define-map ...) requires 3 arguments".to_string()))
|
||||
} else {
|
||||
handle_define_map(&elements[1], &elements[2], &elements[3])
|
||||
handle_define_map(&elements[1], &elements[2], &elements[3], env)
|
||||
}
|
||||
}
|
||||
_ => Ok(DefineResult::NoDefine)
|
||||
|
||||
@@ -1,31 +1,14 @@
|
||||
use super::InterpreterResult;
|
||||
use super::super::errors::Error;
|
||||
use super::super::types::{Value, TypeSignature};
|
||||
use super::super::types::Value::{List};
|
||||
use super::super::representations::SymbolicExpression;
|
||||
use super::super::representations::SymbolicExpression::{AtomValue};
|
||||
use super::super::{Context,Environment,eval,apply,lookup_function};
|
||||
use vm::errors::{Error, InterpreterResult as Result};
|
||||
use vm::types::Value;
|
||||
use vm::representations::SymbolicExpression;
|
||||
use vm::representations::SymbolicExpression::{AtomValue};
|
||||
use vm::{LocalContext, Environment, eval, apply, lookup_function};
|
||||
|
||||
pub fn list_cons(args: &[Value]) -> InterpreterResult {
|
||||
if let Some((first, _rest)) = args.split_first() {
|
||||
let list_type = TypeSignature::get_list_type_for(first)?;
|
||||
let list_result: Result<Vec<_>, Error> = args.iter().map(|x| {
|
||||
let x_type = TypeSignature::get_list_type_for(x)?;
|
||||
if x_type == list_type {
|
||||
Ok(x.clone())
|
||||
} else {
|
||||
Err(Error::InvalidArguments("List must be composed of a single type".to_string()))
|
||||
}
|
||||
}).collect();
|
||||
let list_contents = list_result?;
|
||||
|
||||
Ok(List(list_contents, list_type))
|
||||
} else {
|
||||
Ok(List(Vec::new(), TypeSignature::get_empty_list_type()))
|
||||
}
|
||||
pub fn list_cons(args: &[Value]) -> Result<Value> {
|
||||
Value::new_list(args)
|
||||
}
|
||||
|
||||
pub fn list_fold(args: &[SymbolicExpression], env: &mut Environment, context: &Context) -> InterpreterResult {
|
||||
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())))
|
||||
}
|
||||
@@ -34,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 {
|
||||
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) ];
|
||||
@@ -47,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) -> InterpreterResult {
|
||||
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())))
|
||||
}
|
||||
@@ -56,28 +39,13 @@ pub fn list_map(args: &[SymbolicExpression], env: &mut Environment, context: &Co
|
||||
|
||||
let list = eval(&args[1], env, context)?;
|
||||
match list {
|
||||
List(vector, _) => {
|
||||
let mut result_value_type: Option<TypeSignature> = None;
|
||||
let result: Result<Vec<_>, Error> = vector.iter().map(|x| {
|
||||
let argument = [ SymbolicExpression::AtomValue(x.clone()) ];
|
||||
let value = apply(&function, &argument, env, context)?;
|
||||
let value_type = TypeSignature::get_list_type_for(&value)?;
|
||||
if let Some(ref all_type) = result_value_type {
|
||||
if *all_type == value_type {
|
||||
Ok(value)
|
||||
} else {
|
||||
Err(Error::InvalidArguments("Results of map must all be of a single type".to_string()))
|
||||
}
|
||||
} else {
|
||||
result_value_type = Some(value_type);
|
||||
Ok(value)
|
||||
}
|
||||
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();
|
||||
let vec = result?;
|
||||
match result_value_type {
|
||||
Some(value_type) => Ok(List(vec, value_type)),
|
||||
None => Ok(List(Vec::new(), TypeSignature::get_empty_list_type()))
|
||||
}
|
||||
let as_vec = result?;
|
||||
Value::list_from(as_vec)
|
||||
},
|
||||
_ => Err(Error::TypeError("List".to_string(), list))
|
||||
}
|
||||
|
||||
@@ -5,15 +5,14 @@ mod boolean;
|
||||
mod database;
|
||||
mod tuples;
|
||||
|
||||
use vm::types::{Value, CallableType};
|
||||
use vm::errors::{Error, InterpreterResult as Result};
|
||||
use vm::types::Value;
|
||||
use vm::callables::CallableType;
|
||||
use vm::representations::SymbolicExpression;
|
||||
use vm::{Context,Environment};
|
||||
use vm::InterpreterResult;
|
||||
use vm::errors::Error;
|
||||
use vm::eval;
|
||||
use vm::{LocalContext, Environment, eval};
|
||||
|
||||
|
||||
fn native_eq(args: &[Value]) -> InterpreterResult {
|
||||
fn native_eq(args: &[Value]) -> Result<Value> {
|
||||
// TODO: this currently uses the derived equality checks of Value,
|
||||
// however, that's probably not how we want to implement equality
|
||||
// checks on the ::ListTypes
|
||||
@@ -26,14 +25,14 @@ fn native_eq(args: &[Value]) -> InterpreterResult {
|
||||
}
|
||||
}
|
||||
|
||||
fn native_begin(args: &[Value]) -> InterpreterResult {
|
||||
fn native_begin(args: &[Value]) -> Result<Value> {
|
||||
match args.last() {
|
||||
Some(v) => Ok(v.clone()),
|
||||
None => Ok(Value::Void)
|
||||
}
|
||||
}
|
||||
|
||||
fn special_if(args: &[SymbolicExpression], env: &mut Environment, context: &Context) -> InterpreterResult {
|
||||
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()))
|
||||
}
|
||||
@@ -55,7 +54,9 @@ fn special_if(args: &[SymbolicExpression], env: &mut Environment, context: &Cont
|
||||
}
|
||||
}
|
||||
|
||||
fn special_let(args: &[SymbolicExpression], env: &mut Environment, context: &Context) -> InterpreterResult {
|
||||
fn special_let(args: &[SymbolicExpression], env: &mut Environment, context: &LocalContext) -> Result<Value> {
|
||||
use vm::is_reserved;
|
||||
|
||||
// (let ((x 1) (y 2)) (+ x y)) -> 3
|
||||
// arg0 => binding list
|
||||
// arg1 => body
|
||||
@@ -63,37 +64,35 @@ fn special_let(args: &[SymbolicExpression], env: &mut Environment, context: &Con
|
||||
return Err(Error::InvalidArguments("Wrong number of arguments to let (expect 2)".to_string()))
|
||||
}
|
||||
// create a new context.
|
||||
let mut inner_context = context.extend();
|
||||
let mut inner_context = context.extend()?;
|
||||
|
||||
if let SymbolicExpression::List(ref bindings) = args[0] {
|
||||
let bind_result = bindings.iter().try_for_each(|binding| {
|
||||
for binding in bindings.iter() {
|
||||
if let SymbolicExpression::List(ref binding_exps) = *binding {
|
||||
if binding_exps.len() != 2 {
|
||||
Err(Error::Generic("Passed non 2-length list as binding in let expression".to_string()))
|
||||
return Err(Error::Generic("Passed non 2-length list as binding in let expression".to_string()))
|
||||
} else {
|
||||
if let SymbolicExpression::Atom(ref var_name) = binding_exps[0] {
|
||||
if is_reserved(var_name) {
|
||||
return Err(Error::ReservedName(var_name.to_string()))
|
||||
}
|
||||
let value = eval(&binding_exps[1], env, context)?;
|
||||
match inner_context.variables.insert((*var_name).clone(), value) {
|
||||
Some(_val) => Err(Error::Generic("Multiply defined binding in let expression".to_string())),
|
||||
_ => Ok(())
|
||||
Some(_val) => return Err(Error::VariableDefinedMultipleTimes(var_name.to_string())),
|
||||
_ => continue
|
||||
}
|
||||
} else {
|
||||
Err(Error::Generic("Passed non-atomic variable name to let expression binding".to_string()))
|
||||
return Err(Error::InvalidArguments("Passed non-atomic variable name to let expression binding".to_string()))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Err(Error::Generic("Passed non-list as binding in let expression.".to_string()))
|
||||
return Err(Error::InvalidArguments("Passed non-list as binding in let expression.".to_string()))
|
||||
}
|
||||
});
|
||||
// if there was an error during binding, return error.
|
||||
if let Err(e) = bind_result {
|
||||
Err(e)
|
||||
} else {
|
||||
// otherwise, evaluate the let-body
|
||||
eval(&args[1], env, &inner_context)
|
||||
}
|
||||
// evaluate the let-body
|
||||
eval(&args[1], env, &inner_context)
|
||||
} else {
|
||||
Err(Error::Generic("Passed non-list as second argument to let expression.".to_string()))
|
||||
Err(Error::InvalidArguments("Passed non-list as second argument to let expression.".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
use super::InterpreterResult;
|
||||
use super::super::errors::Error;
|
||||
use super::super::types::{Value, TupleData};
|
||||
use super::super::representations::SymbolicExpression;
|
||||
use super::super::representations::SymbolicExpression::{NamedParameter};
|
||||
use super::super::{Context,Environment,eval};
|
||||
use vm::errors::{Error, InterpreterResult as Result};
|
||||
use vm::types::{Value};
|
||||
use vm::representations::SymbolicExpression;
|
||||
use vm::representations::SymbolicExpression::{NamedParameter};
|
||||
use vm::{LocalContext, Environment, eval};
|
||||
|
||||
pub fn tuple_cons(args: &[SymbolicExpression], env: &mut Environment, context: &Context) -> InterpreterResult {
|
||||
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 {
|
||||
@@ -13,9 +12,9 @@ pub fn tuple_cons(args: &[SymbolicExpression], env: &mut Environment, context: &
|
||||
}
|
||||
let num_pairs = args.len() / 2;
|
||||
// turn list into pairs.
|
||||
let eval_result: Result<Vec<_>, Error> = (0..num_pairs).map(|i| {
|
||||
let eval_result: Result<Vec<_>> = (0..num_pairs).map(|i| {
|
||||
let arg_name = match args[i * 2] {
|
||||
NamedParameter(ref name) => Ok(name.as_str()),
|
||||
NamedParameter(ref name) => Ok(name.clone()),
|
||||
_ => Err(Error::InvalidArguments("Named arguments must be supplied as #name-arg".to_string()))
|
||||
}?;
|
||||
let value = eval(&args[i * 2 + 1], env, context)?;
|
||||
@@ -24,11 +23,10 @@ pub fn tuple_cons(args: &[SymbolicExpression], env: &mut Environment, context: &
|
||||
|
||||
let evaled_pairs = eval_result?;
|
||||
|
||||
let tuple_data = TupleData::from_data(&evaled_pairs)?;
|
||||
Ok(Value::Tuple(tuple_data))
|
||||
Value::tuple_from_data(evaled_pairs)
|
||||
}
|
||||
|
||||
pub fn tuple_get(args: &[SymbolicExpression], env: &mut Environment, context: &Context) -> InterpreterResult {
|
||||
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
|
||||
|
||||
156
src/vm/mod.rs
156
src/vm/mod.rs
@@ -1,37 +1,33 @@
|
||||
pub mod types;
|
||||
pub mod representations;
|
||||
pub mod parser;
|
||||
pub mod contexts;
|
||||
extern crate regex;
|
||||
|
||||
pub mod errors;
|
||||
pub mod database;
|
||||
pub mod types;
|
||||
|
||||
pub mod contracts;
|
||||
|
||||
mod representations;
|
||||
mod parser;
|
||||
mod contexts;
|
||||
mod database;
|
||||
|
||||
mod functions;
|
||||
|
||||
mod variables;
|
||||
mod callables;
|
||||
mod tests;
|
||||
|
||||
use vm::types::{Value, CallableType};
|
||||
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;
|
||||
use vm::errors::{Error, InterpreterResult as Result};
|
||||
|
||||
type InterpreterResult = Result<Value, Error>;
|
||||
const MAX_CALL_STACK_DEPTH: usize = 256;
|
||||
|
||||
fn lookup_variable(name: &str, context: &Context, env: &Environment) -> InterpreterResult {
|
||||
// first off, are we talking about a constant?
|
||||
if name.starts_with(char::is_numeric) {
|
||||
match i128::from_str_radix(name, 10) {
|
||||
Ok(parsed) => Ok(Value::Int(parsed)),
|
||||
Err(_e) => Err(Error::Generic("Failed to parse native int!".to_string()))
|
||||
}
|
||||
} else if name.starts_with('\'') {
|
||||
// Quoted! true or false?
|
||||
match &name as &str {
|
||||
"'null" => Ok(Value::Void),
|
||||
"'true" => Ok(Value::Bool(true)),
|
||||
"'false" => Ok(Value::Bool(false)),
|
||||
_ => Err(Error::NotImplemented)
|
||||
}
|
||||
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 {
|
||||
if let Some(value) = context.lookup_variable(name) {
|
||||
Ok(value)
|
||||
@@ -43,24 +39,24 @@ fn lookup_variable(name: &str, context: &Context, env: &Environment) -> Interpre
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lookup_function<'a> (name: &str, env: &Environment)-> Result<CallableType<'a>, Error> {
|
||||
// Aaron:: todo -- now that global_context is an immutable reference when it's used here,
|
||||
// I am pretty sure we can return a reference with lifetime 'a here.
|
||||
pub fn lookup_function<'a> (name: &str, env: &Environment)-> Result<CallableType<'a>> {
|
||||
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) -> InterpreterResult {
|
||||
env: &mut Environment, context: &LocalContext) -> Result<Value> {
|
||||
if let CallableType::SpecialFunction(function) = function {
|
||||
function(&args, env, context)
|
||||
} else {
|
||||
let eval_tried: Result<Vec<Value>, errors::Error> =
|
||||
let eval_tried: Result<Vec<Value>> =
|
||||
args.iter().map(|x| eval(x, env, context)).collect();
|
||||
match eval_tried {
|
||||
Ok(evaluated_args) => {
|
||||
@@ -72,6 +68,8 @@ pub fn apply(function: &CallableType, args: &[SymbolicExpression],
|
||||
let identifier = function.get_identifier();
|
||||
if env.call_stack.contains(&identifier) {
|
||||
Err(Error::RecursionDetected)
|
||||
} else if env.call_stack.depth() >= MAX_CALL_STACK_DEPTH {
|
||||
Err(Error::MaxStackDepthReached)
|
||||
} else {
|
||||
env.call_stack.insert(&identifier);
|
||||
let resp = function.apply(&evaluated_args, env);
|
||||
@@ -87,7 +85,7 @@ pub fn apply(function: &CallableType, args: &[SymbolicExpression],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eval <'a> (exp: &SymbolicExpression, env: &'a mut Environment, context: &Context) -> InterpreterResult {
|
||||
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),
|
||||
@@ -110,33 +108,47 @@ pub fn eval <'a> (exp: &SymbolicExpression, env: &'a mut Environment, context: &
|
||||
}
|
||||
|
||||
|
||||
pub fn is_reserved(name: &str) -> bool {
|
||||
if let Some(_result) = functions::lookup_reserved_functions(name) {
|
||||
true
|
||||
} else if variables::is_reserved_variable(name) {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/* This function evaluates a list of expressions, sharing a global context.
|
||||
* It returns the final evaluated result.
|
||||
*/
|
||||
pub fn eval_all(expressions: &[SymbolicExpression],
|
||||
contract_db: Option<Box<database::ContractDatabase>>) -> InterpreterResult {
|
||||
let db_instance = match contract_db {
|
||||
Some(db) => db,
|
||||
None => Box::new(database::MemoryContractDatabase::new())
|
||||
};
|
||||
let mut env = Environment::new(db_instance);
|
||||
fn eval_all(expressions: &[SymbolicExpression],
|
||||
database: &mut ContractDatabase,
|
||||
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 = functions::define::evaluate_define(exp, &mut env)?;
|
||||
let try_define = {
|
||||
let mut env = Environment::new(
|
||||
global_context, database);
|
||||
|
||||
functions::define::evaluate_define(exp, &mut env)
|
||||
}?;
|
||||
match try_define {
|
||||
DefineResult::Variable(name, value) => {
|
||||
env.global_context.variables.insert(name, value);
|
||||
global_context.variables.insert(name, value);
|
||||
},
|
||||
DefineResult::Function(name, value) => {
|
||||
env.global_context.functions.insert(name, Box::new(value));
|
||||
global_context.functions.insert(name, value);
|
||||
},
|
||||
DefineResult::Map(name, key_type, value_type) => {
|
||||
env.database.create_map(&name, key_type, value_type);
|
||||
database.create_map(&name, key_type, value_type);
|
||||
},
|
||||
DefineResult::NoDefine => {
|
||||
// not a define function, evaluate normally.
|
||||
let mut env = Environment::new(
|
||||
global_context, database);
|
||||
last_executed = Some(eval(exp, &mut env, &context));
|
||||
}
|
||||
}
|
||||
@@ -145,11 +157,57 @@ pub fn eval_all(expressions: &[SymbolicExpression],
|
||||
if let Some(result) = last_executed {
|
||||
result
|
||||
} else {
|
||||
Err(Error::Generic("Failed to get response from eval()".to_string()))
|
||||
Ok(Value::Void)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn execute(program: &str) -> InterpreterResult {
|
||||
/* Run provided program in a brand new environment, with a transient, empty
|
||||
* database.
|
||||
*/
|
||||
pub fn execute(program: &str) -> Result<Value> {
|
||||
let mut global_context = GlobalContext::new();
|
||||
let mut db_instance = Box::new(database::MemoryContractDatabase::new());
|
||||
|
||||
let parsed = parser::parse(program)?;
|
||||
eval_all(&parsed, None)
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
311
src/vm/parser.rs
311
src/vm/parser.rs
@@ -1,103 +1,172 @@
|
||||
use vm::errors::Error;
|
||||
use regex::{Regex, Captures};
|
||||
use address::c32::c32_address_decode;
|
||||
use vm::errors::{Error, InterpreterResult as Result};
|
||||
use vm::representations::SymbolicExpression;
|
||||
use vm::types::Value;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum LexItem {
|
||||
LeftParen,
|
||||
RightParen,
|
||||
NameParameter(String),
|
||||
Atom(String)
|
||||
NamedParameter(String),
|
||||
LiteralValue(Value),
|
||||
Variable(String),
|
||||
Whitespace
|
||||
}
|
||||
|
||||
fn finish_atom(current: &mut Option<String>) -> Option<LexItem> {
|
||||
let resp = match current {
|
||||
&mut None => {
|
||||
None
|
||||
},
|
||||
&mut Some(ref value) => {
|
||||
if value.starts_with('#') {
|
||||
Some(LexItem::NameParameter(value[1..].to_string()))
|
||||
} else {
|
||||
Some(LexItem::Atom(value.clone()))
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
*current = None;
|
||||
resp
|
||||
#[derive(Debug)]
|
||||
enum TokenType {
|
||||
LParens, RParens, Whitespace,
|
||||
StringLiteral, HexStringLiteral,
|
||||
IntLiteral, QuoteLiteral,
|
||||
Variable, NamedParameter, PrincipalLiteral
|
||||
}
|
||||
|
||||
pub fn lex(input: &str) -> Result<Vec<LexItem>, Error> {
|
||||
struct LexMatcher {
|
||||
matcher: Regex,
|
||||
handler: TokenType
|
||||
}
|
||||
|
||||
impl LexMatcher {
|
||||
fn new(regex_str: &str, handles: TokenType) -> LexMatcher {
|
||||
LexMatcher {
|
||||
matcher: Regex::new(&format!("^{}", regex_str)).unwrap(),
|
||||
handler: handles
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_value_or_err(input: &str, captures: Captures) -> Result<String> {
|
||||
let matched = captures.name("value").ok_or(
|
||||
Error::ParseError("Failed to capture value from input".to_string()))?;
|
||||
Ok(input[matched.start()..matched.end()].to_string())
|
||||
}
|
||||
|
||||
pub fn lex(input: &str) -> Result<Vec<LexItem>> {
|
||||
// Aaron: I'd like these to be static, but that'd require using
|
||||
// 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>((\\")|([[: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),
|
||||
LexMatcher::new("(?P<value>[[:digit:]]+)", TokenType::IntLiteral),
|
||||
LexMatcher::new("'(?P<value>true|false|null)", TokenType::QuoteLiteral),
|
||||
LexMatcher::new("'(?P<value>[0123456789ABCDEFGHJKMNPQRSTVWXYZ]{40,41})", TokenType::PrincipalLiteral),
|
||||
LexMatcher::new("0x(?P<value>[[:xdigit:]])", TokenType::HexStringLiteral),
|
||||
LexMatcher::new("#(?P<value>([[:word:]]|[-#!?+<>=/*])+)", TokenType::NamedParameter),
|
||||
LexMatcher::new("(?P<value>([[:word:]]|[-#!?+<>=/*])+)", TokenType::Variable),
|
||||
];
|
||||
|
||||
|
||||
let mut result = Vec::new();
|
||||
let current = &mut None;
|
||||
for c in input.chars() {
|
||||
match c {
|
||||
'(' => {
|
||||
if let Some(value) = finish_atom(current) {
|
||||
result.push(value);
|
||||
}
|
||||
result.push(LexItem::LeftParen)
|
||||
},
|
||||
')' => {
|
||||
if let Some(value) = finish_atom(current) {
|
||||
result.push(value);
|
||||
}
|
||||
result.push(LexItem::RightParen)
|
||||
},
|
||||
'#' => {
|
||||
if let Some(ref _value) = *current {
|
||||
return Err(Error::ParseError("You may not use # in the middle of an atom.".to_string()))
|
||||
} else {
|
||||
*current = Some(c.to_string())
|
||||
}
|
||||
},
|
||||
' '|'\t'|'\n'|'\r' => {
|
||||
if let Some(value) = finish_atom(current) {
|
||||
result.push(value);
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
match *current {
|
||||
None => {
|
||||
*current = Some(c.to_string());
|
||||
let mut munch_index = 0;
|
||||
let mut did_match = true;
|
||||
while did_match && munch_index < input.len() {
|
||||
did_match = false;
|
||||
let current_slice = &input[munch_index..];
|
||||
for matcher in lex_matchers.iter() {
|
||||
if let Some(captures) = matcher.matcher.captures(current_slice) {
|
||||
let whole_match = captures.get(0).unwrap();
|
||||
assert_eq!(whole_match.start(), 0);
|
||||
munch_index += whole_match.end();
|
||||
let token = match matcher.handler {
|
||||
TokenType::LParens => Ok(LexItem::LeftParen),
|
||||
TokenType::RParens => Ok(LexItem::RightParen),
|
||||
TokenType::Whitespace => Ok(LexItem::Whitespace),
|
||||
TokenType::NamedParameter => {
|
||||
let value = get_value_or_err(current_slice, captures)?;
|
||||
if value.contains("#") {
|
||||
return Err(Error::ParseError(format!("Illegal variable name: '{}'", value)))
|
||||
}
|
||||
Ok(LexItem::NamedParameter(value))
|
||||
},
|
||||
Some(ref mut value) => {
|
||||
value.push(c);
|
||||
TokenType::Variable => {
|
||||
let value = get_value_or_err(current_slice, captures)?;
|
||||
if value.contains("#") {
|
||||
return Err(Error::ParseError(format!("Illegal variable name: '{}'", value)))
|
||||
}
|
||||
Ok(LexItem::Variable(value))
|
||||
},
|
||||
TokenType::QuoteLiteral => {
|
||||
let str_value = get_value_or_err(current_slice, captures)?;
|
||||
let value = match str_value.as_str() {
|
||||
"null" => Ok(Value::Void),
|
||||
"true" => Ok(Value::Bool(true)),
|
||||
"false" => Ok(Value::Bool(false)),
|
||||
_ => Err(Error::ParseError(format!("Unknown 'quoted value '{}'", str_value)))
|
||||
}?;
|
||||
Ok(LexItem::LiteralValue(value))
|
||||
},
|
||||
TokenType::IntLiteral => {
|
||||
let str_value = get_value_or_err(current_slice, captures)?;
|
||||
let value = match i128::from_str_radix(&str_value, 10) {
|
||||
Ok(parsed) => Ok(Value::Int(parsed)),
|
||||
Err(_e) => Err(Error::ParseError(format!("Failed to parse int literal '{}'", str_value)))
|
||||
}?;
|
||||
Ok(LexItem::LiteralValue(value))
|
||||
},
|
||||
TokenType::PrincipalLiteral => {
|
||||
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)) })?;
|
||||
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")
|
||||
},
|
||||
TokenType::StringLiteral => {
|
||||
let str_value = get_value_or_err(current_slice, captures)?;
|
||||
let quote_unescaped = str_value.replace("\\\"","\"");
|
||||
let slash_unescaped = quote_unescaped.replace("\\\\","\\");
|
||||
let byte_vec = slash_unescaped.as_bytes().to_vec();
|
||||
let value = Value::buff_from(byte_vec)?;
|
||||
Ok(LexItem::LiteralValue(value))
|
||||
}
|
||||
}
|
||||
}?;
|
||||
|
||||
result.push(token);
|
||||
did_match = true;
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(value) = finish_atom(current) {
|
||||
result.push(value);
|
||||
if munch_index == input.len() {
|
||||
Ok(result)
|
||||
} else {
|
||||
Err(Error::ParseError(format!("Failed to lex input remainder: {}", &input[munch_index..])))
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn parse_lexed(input: &Vec<LexItem>) -> Result<Vec<SymbolicExpression>, Error> {
|
||||
pub fn parse_lexed(mut input: Vec<LexItem>) -> Result<Vec<SymbolicExpression>> {
|
||||
let mut parse_stack = Vec::new();
|
||||
|
||||
let mut output_list = Vec::new();
|
||||
|
||||
// TODO: we don't need to be cloning here, we can just seize item ownership from the
|
||||
// input iterator by popping.
|
||||
let _result = input.iter().try_for_each(|item| {
|
||||
match *item {
|
||||
for item in input.drain(..) {
|
||||
match item {
|
||||
LexItem::LeftParen => {
|
||||
// start new list.
|
||||
let new_list = Vec::new();
|
||||
parse_stack.push(new_list);
|
||||
Ok(())
|
||||
},
|
||||
LexItem::NameParameter(ref value) => {
|
||||
let symbol_out = SymbolicExpression::NamedParameter(value.clone());
|
||||
LexItem::NamedParameter(value) => {
|
||||
let symbol_out = SymbolicExpression::NamedParameter(value);
|
||||
match parse_stack.last_mut() {
|
||||
None => output_list.push(symbol_out),
|
||||
Some(ref mut list) => list.push(symbol_out)
|
||||
};
|
||||
Ok(())
|
||||
},
|
||||
LexItem::RightParen => {
|
||||
// end current list.
|
||||
@@ -112,20 +181,25 @@ pub fn parse_lexed(input: &Vec<LexItem>) -> Result<Vec<SymbolicExpression>, Erro
|
||||
list.push(expression);
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::ParseError("Tried to close list which isn't open.".to_string()))
|
||||
return Err(Error::ParseError("Tried to close list which isn't open.".to_string()))
|
||||
}
|
||||
},
|
||||
LexItem::Atom(ref value) => {
|
||||
LexItem::Variable(value) => {
|
||||
match parse_stack.last_mut() {
|
||||
None => output_list.push(SymbolicExpression::Atom(value.clone())),
|
||||
Some(ref mut list) => list.push(SymbolicExpression::Atom(value.clone()))
|
||||
None => output_list.push(SymbolicExpression::Atom(value)),
|
||||
Some(ref mut list) => list.push(SymbolicExpression::Atom(value))
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
})?;
|
||||
},
|
||||
LexItem::LiteralValue(value) => {
|
||||
match parse_stack.last_mut() {
|
||||
None => output_list.push(SymbolicExpression::AtomValue(value)),
|
||||
Some(ref mut list) => list.push(SymbolicExpression::AtomValue(value))
|
||||
};
|
||||
},
|
||||
LexItem::Whitespace => ()
|
||||
};
|
||||
}
|
||||
|
||||
// check unfinished stack:
|
||||
if parse_stack.len() > 0 {
|
||||
@@ -135,7 +209,90 @@ pub fn parse_lexed(input: &Vec<LexItem>) -> Result<Vec<SymbolicExpression>, Erro
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse(input: &str) -> Result<Vec<SymbolicExpression>, Error> {
|
||||
pub fn parse(input: &str) -> Result<Vec<SymbolicExpression>> {
|
||||
let lexed = lex(input)?;
|
||||
parse_lexed(&lexed)
|
||||
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");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,7 +12,3 @@ pub enum SymbolicExpression {
|
||||
List(Box<[SymbolicExpression]>),
|
||||
NamedParameter(String)
|
||||
}
|
||||
|
||||
pub struct Contract {
|
||||
pub content: Box<[SymbolicExpression]>
|
||||
}
|
||||
|
||||
95
src/vm/tests/contracts.rs
Normal file
95
src/vm/tests/contracts.rs
Normal file
@@ -0,0 +1,95 @@
|
||||
use vm::errors::Error;
|
||||
use vm::types::{Value};
|
||||
use vm::representations::SymbolicExpression;
|
||||
use vm::contracts::Contract;
|
||||
|
||||
fn symbols_from_values(mut vec: Vec<Value>) -> Vec<SymbolicExpression> {
|
||||
vec.drain(..).map(|value| SymbolicExpression::AtomValue(value)).collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_factorial_contract() {
|
||||
let contract_defn =
|
||||
"(define-map factorials ((id int)) ((current int) (index int)))
|
||||
(define (init-factorial id factorial)
|
||||
(insert-entry! factorials (tuple #id id) (tuple #current 1 #index factorial)))
|
||||
(define-public (compute (id int))
|
||||
(let ((entry (fetch-entry factorials (tuple #id id))))
|
||||
(if (eq? entry 'null)
|
||||
0
|
||||
(let ((current (get current entry))
|
||||
(index (get index entry)))
|
||||
(if (<= index 1)
|
||||
current
|
||||
(begin
|
||||
(set-entry! factorials (tuple #id id)
|
||||
(tuple #current (* current index)
|
||||
#index (- index 1)))
|
||||
0))))))
|
||||
(begin (init-factorial 1337 3)
|
||||
(init-factorial 8008 5)
|
||||
'null)
|
||||
";
|
||||
|
||||
|
||||
let mut contract = Contract::make_in_memory_contract(contract_defn).unwrap();
|
||||
|
||||
let tx_name = "compute";
|
||||
let arguments_to_test = [symbols_from_values(vec![Value::Int(1337)]),
|
||||
symbols_from_values(vec![Value::Int(1337)]),
|
||||
symbols_from_values(vec![Value::Int(1337)]),
|
||||
symbols_from_values(vec![Value::Int(1337)]),
|
||||
symbols_from_values(vec![Value::Int(1337)]),
|
||||
symbols_from_values(vec![Value::Int(8008)]),
|
||||
symbols_from_values(vec![Value::Int(8008)]),
|
||||
symbols_from_values(vec![Value::Int(8008)]),
|
||||
symbols_from_values(vec![Value::Int(8008)]),
|
||||
symbols_from_values(vec![Value::Int(8008)]),
|
||||
symbols_from_values(vec![Value::Int(8008)])];
|
||||
|
||||
|
||||
let expected = vec![
|
||||
Value::Int(0),
|
||||
Value::Int(0),
|
||||
Value::Int(6),
|
||||
Value::Int(6),
|
||||
Value::Int(6),
|
||||
Value::Int(0),
|
||||
Value::Int(0),
|
||||
Value::Int(0),
|
||||
Value::Int(0),
|
||||
Value::Int(120),
|
||||
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(
|
||||
&sender,
|
||||
&tx_name,
|
||||
arguments)));
|
||||
|
||||
let err_result = contract.execute_transaction(&sender, &"init-factorial",
|
||||
&symbols_from_values(vec![Value::Int(9000),
|
||||
Value::Int(15)]));
|
||||
match err_result {
|
||||
Err(Error::Undefined(_)) => {},
|
||||
_ => {
|
||||
println!("{:?}", err_result);
|
||||
assert!(false, "Attempt to call init-factorial should fail!")
|
||||
}
|
||||
}
|
||||
|
||||
let err_result = contract.execute_transaction(&sender, &"compute",
|
||||
&symbols_from_values(vec![Value::Void]));
|
||||
match err_result {
|
||||
Err(Error::TypeError(_, _)) => {},
|
||||
_ => {
|
||||
println!("{:?}", err_result);
|
||||
assert!(false, "Attempt to call compute with void type should fail!")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
use vm::errors::Error;
|
||||
use vm::types::{Value, TypeSignature, AtomTypeIdentifier};
|
||||
|
||||
use vm::execute;
|
||||
|
||||
#[test]
|
||||
@@ -32,23 +34,73 @@ fn test_simple_tea_shop() {
|
||||
(consume 3))
|
||||
";
|
||||
|
||||
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)],
|
||||
TypeSignature::new(AtomTypeIdentifier::BoolType, 1));
|
||||
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!(Ok(expected), execute(test1));
|
||||
assert_eq!(expected, execute(test1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_factorial_contract() {
|
||||
let test1 =
|
||||
"(define-map factorials ((id int)) ((current int) (index int)))
|
||||
(define (init-factorial id factorial)
|
||||
(insert-entry! factorials (tuple #id id) (tuple #current 1 #index factorial)))
|
||||
(define (compute id)
|
||||
(let ((entry (fetch-entry factorials (tuple #id id))))
|
||||
(if (eq? entry 'null)
|
||||
0
|
||||
(let ((current (get current entry))
|
||||
(index (get index entry)))
|
||||
(if (<= index 1)
|
||||
current
|
||||
(begin
|
||||
(set-entry! factorials (tuple #id id)
|
||||
(tuple #current (* current index)
|
||||
#index (- index 1)))
|
||||
0))))))
|
||||
(init-factorial 1337 3)
|
||||
(init-factorial 8008 5)
|
||||
(list (compute 1337)
|
||||
(compute 1337)
|
||||
(compute 1337)
|
||||
(compute 1337)
|
||||
(compute 1337)
|
||||
(compute 8008)
|
||||
(compute 8008)
|
||||
(compute 8008)
|
||||
(compute 8008)
|
||||
(compute 8008)
|
||||
(compute 8008))
|
||||
";
|
||||
|
||||
let expected = Value::list_from(vec![
|
||||
Value::Int(0),
|
||||
Value::Int(0),
|
||||
Value::Int(6),
|
||||
Value::Int(6),
|
||||
Value::Int(6),
|
||||
Value::Int(0),
|
||||
Value::Int(0),
|
||||
Value::Int(0),
|
||||
Value::Int(0),
|
||||
Value::Int(120),
|
||||
Value::Int(120),
|
||||
]);
|
||||
|
||||
assert_eq!(expected, execute(test1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -78,20 +130,199 @@ fn silly_naming_system() {
|
||||
(who-owns? 1))
|
||||
";
|
||||
|
||||
let expected = Value::List(
|
||||
vec![
|
||||
Value::Int(1),
|
||||
Value::Int(0),
|
||||
Value::Int(1),
|
||||
Value::Int(0),
|
||||
Value::Int(0),
|
||||
Value::Int(1),
|
||||
Value::Int(0),
|
||||
Value::Int(1),
|
||||
Value::Int(0),
|
||||
Value::Int(-1),
|
||||
],
|
||||
TypeSignature::new(AtomTypeIdentifier::IntType, 1));
|
||||
|
||||
assert_eq!(Ok(expected), execute(test1));
|
||||
let expected = Value::list_from(vec![
|
||||
Value::Int(1),
|
||||
Value::Int(0),
|
||||
Value::Int(1),
|
||||
Value::Int(0),
|
||||
Value::Int(0),
|
||||
Value::Int(1),
|
||||
Value::Int(0),
|
||||
Value::Int(1),
|
||||
Value::Int(0),
|
||||
Value::Int(-1),
|
||||
]);
|
||||
assert_eq!(expected, execute(test1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn datamap_errors() {
|
||||
let tests = [
|
||||
"(fetch-entry non-existent (tuple #name 1))",
|
||||
"(delete-entry! non-existent (tuple #name 1))",
|
||||
];
|
||||
|
||||
let expected = [
|
||||
Err(Error::Undefined("No such map named: non-existent".to_string())),
|
||||
Err(Error::Undefined("No such map named: non-existent".to_string())),
|
||||
];
|
||||
|
||||
for (program, expectation) in tests.iter().zip(expected.iter()) {
|
||||
assert_eq!(*expectation, execute(program));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lists_system() {
|
||||
let test1 =
|
||||
"(define-map lists ((name int)) ((contents (list 5 1 int))))
|
||||
(define (add-list name content)
|
||||
(insert-entry! lists (tuple #name name)
|
||||
(tuple #contents content)))
|
||||
(define (get-list name)
|
||||
(get contents (fetch-entry lists (tuple #name name))))
|
||||
(add-list 0 (list 1 2 3 4 5))
|
||||
(add-list 1 (list 1 2 3))
|
||||
(list (get-list 0)
|
||||
(get-list 1))
|
||||
";
|
||||
|
||||
let mut test_list_too_big = test1.to_string();
|
||||
test_list_too_big.push_str("(add-list 2 (list 1 2 3 4 5 6))");
|
||||
|
||||
let mut test_bad_tuple_1 = test1.to_string();
|
||||
test_bad_tuple_1.push_str("(insert-entry! lists (tuple #name 1) (tuple #contentious (list 1 2 6)))");
|
||||
|
||||
let mut test_bad_tuple_2 = test1.to_string();
|
||||
test_bad_tuple_2.push_str("(insert-entry! lists (tuple #name 1) (tuple #contents (list 1 2 6) #discontents 1))");
|
||||
|
||||
let mut test_bad_tuple_3 = test1.to_string();
|
||||
test_bad_tuple_3.push_str("(insert-entry! lists (tuple #name 1) (tuple #contents (list 'false 'true 'false)))");
|
||||
|
||||
let mut test_bad_tuple_4 = test1.to_string();
|
||||
test_bad_tuple_4.push_str("(insert-entry! lists (tuple #name (list 1)) (tuple #contents (list 1 2 3)))");
|
||||
|
||||
let expected = || {
|
||||
let list1 = Value::list_from(vec![
|
||||
Value::Int(1),
|
||||
Value::Int(2),
|
||||
Value::Int(3),
|
||||
Value::Int(4),
|
||||
Value::Int(5)])?;
|
||||
let list2 = Value::list_from(vec![
|
||||
Value::Int(1),
|
||||
Value::Int(2),
|
||||
Value::Int(3)])?;
|
||||
Value::list_from(vec![list1, list2])
|
||||
};
|
||||
|
||||
assert_eq!(expected(), execute(test1));
|
||||
|
||||
for test in [test_list_too_big, test_bad_tuple_1, test_bad_tuple_2,
|
||||
test_bad_tuple_3, test_bad_tuple_4].iter() {
|
||||
let expected_type_error = match execute(test) {
|
||||
Err(Error::TypeError(_,_)) => true,
|
||||
_ => {
|
||||
println!("{:?}", execute(test));
|
||||
false
|
||||
}
|
||||
};
|
||||
|
||||
assert!(expected_type_error);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tuples_system() {
|
||||
let test1 =
|
||||
"(define-map tuples ((name int))
|
||||
((contents (tuple ((name (buff 5))
|
||||
(owner (buff 5)))))))
|
||||
|
||||
(define (add-tuple name content)
|
||||
(insert-entry! tuples (tuple #name name)
|
||||
(tuple #contents
|
||||
(tuple #name content
|
||||
#owner content))))
|
||||
(define (get-tuple name)
|
||||
(get name (get contents (fetch-entry tuples (tuple #name name)))))
|
||||
|
||||
|
||||
(add-tuple 0 \"abcde\")
|
||||
(add-tuple 1 \"abcd\")
|
||||
(list (get-tuple 0)
|
||||
(get-tuple 1))
|
||||
";
|
||||
|
||||
let mut test_list_too_big = test1.to_string();
|
||||
test_list_too_big.push_str("(add-tuple 2 \"abcdef\")");
|
||||
|
||||
let mut test_bad_tuple_1 = test1.to_string();
|
||||
test_bad_tuple_1.push_str("(insert-entry! tuples (tuple #name 1) (tuple #contents (tuple #name \"abcde\" #owner \"abcdef\")))");
|
||||
|
||||
let expected = || {
|
||||
let buff1 = Value::buff_from("abcde".to_string().into_bytes())?;
|
||||
let buff2 = Value::buff_from("abcd".to_string().into_bytes())?;
|
||||
Value::list_from(vec![buff1, buff2])
|
||||
};
|
||||
|
||||
assert_eq!(expected(), execute(test1));
|
||||
|
||||
for test in [test_list_too_big, test_bad_tuple_1].iter() {
|
||||
let expected_type_error = match execute(test) {
|
||||
Err(Error::TypeError(_,_)) => true,
|
||||
_ => {
|
||||
println!("{:?}", execute(test));
|
||||
false
|
||||
}
|
||||
};
|
||||
|
||||
assert!(expected_type_error);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bad_define_maps() {
|
||||
let test_list_pairs = [
|
||||
"(define-map lists ((name int)) ((contents int bool)))",
|
||||
"(define-map lists ((name int)) (contents bool))",
|
||||
"(define-map lists ((#name int)) (contents bool))",
|
||||
"(define-map lists ((name int)) contents)"];
|
||||
let test_define_args = [
|
||||
"(define-map (lists) ((name #int)) contents)",
|
||||
"(define-map lists ((name #int)) contents 5)"];
|
||||
|
||||
let test_bad_type = [
|
||||
"(define-map lists ((name int)) ((contents (list 5 0 int))))",
|
||||
"(define-map lists ((name #int)) (contents bool))"];
|
||||
|
||||
for test in test_list_pairs.iter() {
|
||||
println!("Test: {:?}", test);
|
||||
assert_eq!(Err(Error::ExpectedListPairs), execute(test));
|
||||
}
|
||||
|
||||
for test in test_define_args.iter() {
|
||||
assert!(match execute(test) {
|
||||
Err(Error::InvalidArguments(_)) => true,
|
||||
_ => false
|
||||
})
|
||||
}
|
||||
|
||||
for test in test_bad_type.iter() {
|
||||
assert_eq!(Err(Error::InvalidTypeDescription), execute(test));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bad_tuples() {
|
||||
let tests = ["(tuple #name 1 #name 3)",
|
||||
"(tuple #name 'null)",
|
||||
"(tuple name 1)",
|
||||
"(tuple #name 1 #blame)",
|
||||
"(get value (tuple #name 1))",
|
||||
"(get name five (tuple #name 1))",
|
||||
"(get 1234 (tuple #name 1))"];
|
||||
|
||||
for test in tests.iter() {
|
||||
let outcome = execute(test);
|
||||
match outcome {
|
||||
Err(Error::InvalidArguments(_)) => continue,
|
||||
_ => {
|
||||
println!("Expected InvalidArguments Error, but found {:?}", outcome);
|
||||
assert!(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,56 @@ fn test_defines() {
|
||||
(f 3 1)";
|
||||
|
||||
assert_eq!(Ok(Value::Int(29)), execute(&tests));
|
||||
|
||||
let tests =
|
||||
"1";
|
||||
|
||||
assert_eq!(Ok(Value::Int(1)), execute(&tests));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bad_define_names() {
|
||||
let test0 =
|
||||
"(define tx-sender 1)
|
||||
(+ tx-sender tx-sender)";
|
||||
let test1 =
|
||||
"(define * 1)
|
||||
(+ * *)";
|
||||
let test2 =
|
||||
"(define 1 1)
|
||||
(+ 1 1)";
|
||||
let test3 =
|
||||
"(define foo 1)
|
||||
(define foo 2)
|
||||
(+ foo foo)";
|
||||
|
||||
assert_eq!(Err(Error::ReservedName("tx-sender".to_string())), execute(&test0));
|
||||
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::VariableDefinedMultipleTimes("foo".to_string())),
|
||||
execute(&test3));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_stack_depth() {
|
||||
let mut function_defines = Vec::new();
|
||||
function_defines.push("(define (foo-0 x) (+ 1 x))".to_string());
|
||||
for i in 1..257 {
|
||||
function_defines.push(
|
||||
format!("(define (foo-{} x) (foo-{} (+ 1 x)))",
|
||||
i, i-1));
|
||||
}
|
||||
function_defines.push(
|
||||
format!("(foo-255 1)"));
|
||||
|
||||
let test0 = function_defines.join("\n");
|
||||
function_defines.push(
|
||||
format!("(foo-256 2)"));
|
||||
let test1 = function_defines.join("\n");
|
||||
|
||||
assert_eq!(Ok(Value::Int(257)), execute(&test0));
|
||||
assert_eq!(Err(Error::MaxStackDepthReached), execute(&test1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -26,10 +76,34 @@ fn test_recursive_panic() {
|
||||
assert_eq!(Err(Error::RecursionDetected), execute(&tests));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bad_variables() {
|
||||
let test0 = "(+ a 1)";
|
||||
let expected = Err(Error::Undefined("No such variable found in context: a".to_string()));
|
||||
assert_eq!(expected, execute(&test0));
|
||||
|
||||
|
||||
let test1 = "(foo 2 1)";
|
||||
let expected = Err(Error::Undefined("No such function found in context: foo".to_string()));
|
||||
assert_eq!(expected, execute(&test1));
|
||||
|
||||
|
||||
let test2 = "((lambda (x y) 1) 2 1)";
|
||||
let expected = Err(Error::TryEvalToFunction);
|
||||
assert_eq!(expected, execute(&test2));
|
||||
|
||||
let test3 = "#foo";
|
||||
let expected = Err(Error::InvalidArguments("Cannot eval a named parameter".to_string()));
|
||||
assert_eq!(expected, execute(&test3));
|
||||
|
||||
let test4 = "()";
|
||||
let expected = Ok(Value::Void);
|
||||
assert_eq!(expected, execute(&test4));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_define_parse_panic() {
|
||||
let tests = "(define () 1)";
|
||||
|
||||
let expected = Err(Error::InvalidArguments("Must supply atleast a name argument to define a function".to_string()));
|
||||
assert_eq!(expected, execute(&tests));
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use vm::types::{Value, TypeSignature, AtomTypeIdentifier};
|
||||
use vm::types::{Value, TypeSignature};
|
||||
|
||||
use vm::execute;
|
||||
use vm::errors::Error;
|
||||
@@ -9,22 +9,60 @@ fn test_simple_map() {
|
||||
"(define (square x) (* x x))
|
||||
(map square (list 1 2 3 4))";
|
||||
|
||||
let expected = Value::List(
|
||||
vec![
|
||||
Value::Int(1),
|
||||
Value::Int(4),
|
||||
Value::Int(9),
|
||||
Value::Int(16)],
|
||||
TypeSignature::new(AtomTypeIdentifier::IntType, 1));
|
||||
let expected = Value::list_from(vec![
|
||||
Value::Int(1),
|
||||
Value::Int(4),
|
||||
Value::Int(9),
|
||||
Value::Int(16)]);
|
||||
|
||||
assert_eq!(Ok(expected.clone()), execute(test1));
|
||||
assert_eq!(expected, execute(test1));
|
||||
|
||||
// let's test lists of lists.
|
||||
let test2 = "(define (multiply x acc) (* x acc))
|
||||
(define (multiply-all x) (fold multiply x 1))
|
||||
(map multiply-all (list (list 1 1 1) (list 2 2 1) (list 3 3) (list 2 2 2 2)))";
|
||||
assert_eq!(Ok(expected), execute(test2));
|
||||
|
||||
assert_eq!(expected, execute(test2));
|
||||
|
||||
// let's test empty lists.
|
||||
let test2 = "(define (double x) (* x 2))
|
||||
(map double (list))";
|
||||
assert_eq!(Value::list_from(vec![]), execute(test2));
|
||||
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_list_tuple_admission() {
|
||||
let test =
|
||||
"(define (bufferize x) (if (eq? x 1) \"abc\" \"ab\"))
|
||||
(define (tuplize x)
|
||||
(tuple #value (bufferize x)))
|
||||
(map tuplize (list 0 1 0 1 0 1))";
|
||||
|
||||
let expected_type =
|
||||
"(list (tuple #value \"012\")
|
||||
(tuple #value \"012\")
|
||||
(tuple #value \"012\")
|
||||
(tuple #value \"012\")
|
||||
(tuple #value \"012\")
|
||||
(tuple #value \"012\"))";
|
||||
|
||||
let not_expected_type =
|
||||
"(list (tuple #value \"01\")
|
||||
(tuple #value \"02\")
|
||||
(tuple #value \"12\")
|
||||
(tuple #value \"12\")
|
||||
(tuple #value \"01\")
|
||||
(tuple #value \"02\"))";
|
||||
|
||||
|
||||
let result_type = TypeSignature::type_of(&execute(test).unwrap());
|
||||
let expected_type = TypeSignature::type_of(&execute(expected_type).unwrap());
|
||||
let testing_value = &execute(not_expected_type).unwrap();
|
||||
let not_expected_type = TypeSignature::type_of(testing_value);
|
||||
|
||||
assert_eq!(expected_type, result_type);
|
||||
assert!(not_expected_type != result_type);
|
||||
assert!(result_type.admits(&testing_value));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -41,13 +79,36 @@ fn test_simple_folds() {
|
||||
#[test]
|
||||
fn test_construct_bad_list() {
|
||||
let test1 = "(list 1 2 3 'true)";
|
||||
assert_eq!(Err(Error::InvalidArguments("List must be composed of a single type".to_string())),
|
||||
execute(test1));
|
||||
assert!(
|
||||
match execute(test1) {
|
||||
Err(Error::BadTypeConstruction) => true,
|
||||
_ => false
|
||||
});
|
||||
|
||||
let test2 = "(define (bad-function x) (if (eq? x 1) 'true x))
|
||||
(map bad-function (list 0 1 2 3))";
|
||||
assert_eq!(Err(Error::InvalidArguments("Results of map must all be of a single type".to_string())),
|
||||
execute(test2));
|
||||
assert!(
|
||||
match execute(test2) {
|
||||
Err(Error::BadTypeConstruction) => true,
|
||||
_ => false
|
||||
});
|
||||
|
||||
let bad_2d_list = "(list (list 1 2 3) (list 'true 'false 'true))";
|
||||
let bad_high_order_list = "(list (list 1 2 3) (list (list 1 2 3)))";
|
||||
|
||||
let expected_err_1 = match execute(bad_2d_list) {
|
||||
Err(Error::BadTypeConstruction) => true,
|
||||
_ => false
|
||||
};
|
||||
|
||||
assert!(expected_err_1);
|
||||
|
||||
let expected_err_2 = match execute(bad_high_order_list) {
|
||||
Err(Error::BadTypeConstruction) => true,
|
||||
_ => false
|
||||
};
|
||||
|
||||
assert!(expected_err_2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
mod lists;
|
||||
mod defines;
|
||||
mod parser_lexer;
|
||||
mod simple_apply_eval;
|
||||
mod datamaps;
|
||||
mod contracts;
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
use vm::representations::SymbolicExpression;
|
||||
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::Atom("1".to_string())])),
|
||||
SymbolicExpression::List(Box::new([
|
||||
SymbolicExpression::Atom("y".to_string()),
|
||||
SymbolicExpression::Atom("2".to_string())]))])),
|
||||
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::Atom("3".to_string())]))])),
|
||||
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()),
|
||||
];
|
||||
|
||||
if let Ok(parsed) = parser::parse(&input) {
|
||||
assert_eq!(program, parsed, "Should match expected symbolic expression");
|
||||
} else {
|
||||
assert!(false, "Failed to lex and parse input");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_failures() {
|
||||
let too_much_closure = "(let ((x 1) (y 2))))";
|
||||
let not_enough_closure = "(let ((x 1) (y 2))";
|
||||
|
||||
match parser::parse(&too_much_closure) {
|
||||
Ok(_parsed) => assert!(false, "Should have failed to parse with too many right parens"),
|
||||
Err(_s) => assert!(true, "Should have failed to parse with too many right parens")
|
||||
}
|
||||
match parser::parse(¬_enough_closure) {
|
||||
Ok(_parsed) => assert!(false, "Should have failed to parse with too few right parens"),
|
||||
Err(_s) => assert!(true, "Should have failed to parse with too few right parens")
|
||||
}
|
||||
}
|
||||
@@ -1,41 +1,10 @@
|
||||
use vm::eval;
|
||||
use vm::{eval, execute};
|
||||
use vm::database::MemoryContractDatabase;
|
||||
use vm::errors::Error;
|
||||
use vm::contexts::{Context, Environment};
|
||||
use vm::types::{Value, 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::Atom("5".to_string()),
|
||||
SymbolicExpression::Atom("x".to_string())]));
|
||||
|
||||
let func_args = vec!["x".to_string()];
|
||||
let user_function = Box::new(DefinedFunction::new(func_body, func_args));
|
||||
|
||||
let context = Context::new();
|
||||
let mut env = Environment::new(Box::new(MemoryContractDatabase::new()));
|
||||
|
||||
env.global_context.variables.insert("a".to_string(), Value::Int(59));
|
||||
env.global_context.functions.insert("do_work".to_string(), user_function);
|
||||
|
||||
assert_eq!(Ok(Value::Int(64)), eval(&content[0], &mut env, &context));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simple_let() {
|
||||
/*
|
||||
@@ -54,8 +23,10 @@ fn test_simple_let() {
|
||||
x))";
|
||||
|
||||
if let Ok(parsed_program) = parse(&program) {
|
||||
let context = Context::new();
|
||||
let mut env = Environment::new(Box::new(MemoryContractDatabase::new()));
|
||||
let context = LocalContext::new();
|
||||
let global_context = GlobalContext::new();
|
||||
let mut db = MemoryContractDatabase::new();
|
||||
let mut env = Environment::new(&global_context, &mut db);
|
||||
|
||||
assert_eq!(Ok(Value::Int(7)), eval(&parsed_program[0], &mut env, &context));
|
||||
} else {
|
||||
@@ -64,6 +35,37 @@ fn test_simple_let() {
|
||||
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_buffer_equality() {
|
||||
let tests = [
|
||||
"(eq? \"a b c\" \"a b c\")",
|
||||
"(eq? \"\\\" a b d\"
|
||||
\"\\\" a b d\")",
|
||||
"(not (eq? \"\\\" a b d\"
|
||||
\" a b d\"))"];
|
||||
let expectations = [
|
||||
Value::Bool(true),
|
||||
Value::Bool(true),
|
||||
Value::Bool(true)];
|
||||
|
||||
tests.iter().zip(expectations.iter())
|
||||
.for_each(|(program, expectation)| assert_eq!(Ok(expectation.clone()), execute(program)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_principal_equality() {
|
||||
let tests = [
|
||||
"(eq? 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR)",
|
||||
"(not (eq? 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR
|
||||
'SM2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQVX8X0G))"];
|
||||
let expectations = [
|
||||
Value::Bool(true),
|
||||
Value::Bool(true)];
|
||||
|
||||
tests.iter().zip(expectations.iter())
|
||||
.for_each(|(program, expectation)| assert_eq!(Ok(expectation.clone()), execute(program)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simple_if_functions() {
|
||||
//
|
||||
@@ -85,15 +87,17 @@ 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 = Box::new(DefinedFunction::new(parsed_bodies[0].clone(),
|
||||
func_args1));
|
||||
let user_function2 = Box::new(DefinedFunction::new(parsed_bodies[1].clone(),
|
||||
func_args2));
|
||||
let mut context = Context::new();
|
||||
let mut env = Environment::new(Box::new(MemoryContractDatabase::new()));
|
||||
let user_function1 = PrivateFunction::new(func_args1, parsed_bodies[0].clone());
|
||||
let user_function2 = PrivateFunction::new(func_args2, parsed_bodies[1].clone());
|
||||
|
||||
env.global_context.functions.insert("with_else".to_string(), user_function1);
|
||||
env.global_context.functions.insert("without_else".to_string(), user_function2);
|
||||
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);
|
||||
global_context.functions.insert("without_else".to_string(), user_function2);
|
||||
|
||||
let mut env = Environment::new(&global_context, &mut db);
|
||||
|
||||
if let Ok(tests) = evals {
|
||||
assert_eq!(Ok(Value::Int(1)), eval(&tests[0], &mut env, &context));
|
||||
@@ -109,25 +113,24 @@ fn test_simple_if_functions() {
|
||||
|
||||
#[test]
|
||||
fn test_simple_arithmetic_functions() {
|
||||
let tests = parse(&
|
||||
"(* 52314 414)
|
||||
(/ 52314 414)
|
||||
(* 2 3 4 5)
|
||||
(/ 10 13)
|
||||
(mod 51 2)
|
||||
(- 5 4 1)
|
||||
(+ 5 4 1)
|
||||
(eq? (* 2 3)
|
||||
(+ 2 2 2))
|
||||
(> 1 2)
|
||||
(< 1 2)
|
||||
(<= 1 1)
|
||||
(>= 2 1)
|
||||
(>= 1 1)
|
||||
(pow 2 16)
|
||||
(pow 2 32)
|
||||
(- (pow 2 32))
|
||||
");
|
||||
let tests = [
|
||||
"(* 52314 414)",
|
||||
"(/ 52314 414)",
|
||||
"(* 2 3 4 5)",
|
||||
"(/ 10 13)",
|
||||
"(mod 51 2)",
|
||||
"(- 5 4 1)",
|
||||
"(+ 5 4 1)",
|
||||
"(eq? (* 2 3)
|
||||
(+ 2 2 2))",
|
||||
"(> 1 2)",
|
||||
"(< 1 2)",
|
||||
"(<= 1 1)",
|
||||
"(>= 2 1)",
|
||||
"(>= 1 1)",
|
||||
"(pow 2 16)",
|
||||
"(pow 2 32)",
|
||||
"(- (pow 2 32))"];
|
||||
|
||||
let expectations = [
|
||||
Value::Int(21657996),
|
||||
@@ -146,33 +149,29 @@ fn test_simple_arithmetic_functions() {
|
||||
Value::Int(65536),
|
||||
Value::Int(u32::max_value() as i128 + 1),
|
||||
Value::Int(-1 * (u32::max_value() as i128 + 1)),
|
||||
];
|
||||
];
|
||||
|
||||
if let Ok(to_eval) = tests {
|
||||
let context = Context::new();
|
||||
let mut env = Environment::new(Box::new(MemoryContractDatabase::new()));
|
||||
to_eval.iter().zip(expectations.iter())
|
||||
.for_each(|(program, expectation)| assert_eq!(Ok(expectation.clone()), eval(program, &mut env, &context)));
|
||||
} else {
|
||||
assert!(false, "Failed to parse function bodies.");
|
||||
}
|
||||
tests.iter().zip(expectations.iter())
|
||||
.for_each(|(program, expectation)| assert_eq!(Ok(expectation.clone()), execute(program)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_arithmetic_errors() {
|
||||
let tests = parse(&
|
||||
"(>= 1)
|
||||
(+ 1 'true)
|
||||
(/ 10 0)
|
||||
(mod 10 0)
|
||||
(pow 2 128)
|
||||
(* 10 (pow 2 126))
|
||||
(+ (pow 2 126) (pow 2 126))
|
||||
(- 0 (pow 2 126) (pow 2 126) 1)
|
||||
(-) (/) (mod 1) (pow 1)
|
||||
(pow 2 (pow 2 32))
|
||||
(pow 2 (- 1))
|
||||
");
|
||||
let tests = [
|
||||
"(>= 1)",
|
||||
"(+ 1 'true)",
|
||||
"(/ 10 0)",
|
||||
"(mod 10 0)",
|
||||
"(pow 2 128)",
|
||||
"(* 10 (pow 2 126))",
|
||||
"(+ (pow 2 126) (pow 2 126))",
|
||||
"(- 0 (pow 2 126) (pow 2 126) 1)",
|
||||
"(-)",
|
||||
"(/)",
|
||||
"(mod 1)",
|
||||
"(pow 1)",
|
||||
"(pow 2 (pow 2 32))",
|
||||
"(pow 2 (- 1))"];
|
||||
|
||||
let expectations = [
|
||||
Err(Error::InvalidArguments("Binary comparison must be called with exactly 2 arguments".to_string())),
|
||||
@@ -191,28 +190,24 @@ fn test_arithmetic_errors() {
|
||||
Err(Error::Arithmetic("Power argument to (pow ...) must be a u32 integer".to_string()))
|
||||
];
|
||||
|
||||
if let Ok(to_eval) = tests {
|
||||
let context = Context::new();
|
||||
let mut env = Environment::new(Box::new(MemoryContractDatabase::new()));
|
||||
for (program, expectation) in to_eval.iter().zip(expectations.iter()) {
|
||||
assert_eq!(*expectation, eval(program, &mut env, &context));
|
||||
}
|
||||
} else {
|
||||
assert!(false, "Failed to parse function bodies.");
|
||||
for (program, expectation) in tests.iter().zip(expectations.iter()) {
|
||||
assert_eq!(*expectation, execute(program));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bool_functions() {
|
||||
let tests = parse(&
|
||||
"(and 'true 'true 'true)
|
||||
(and 'false 'true 'true)
|
||||
(and 'false (> 1 (/ 10 0)))
|
||||
(or 'true (> 1 (/ 10 0)))
|
||||
(or 'false 'false 'false)
|
||||
(not 'true)");
|
||||
let tests = [
|
||||
"'true",
|
||||
"(and 'true 'true 'true)",
|
||||
"(and 'false 'true 'true)",
|
||||
"(and 'false (> 1 (/ 10 0)))",
|
||||
"(or 'true (> 1 (/ 10 0)))",
|
||||
"(or 'false 'false 'false)",
|
||||
"(not 'true)"];
|
||||
|
||||
let expectations = [
|
||||
Value::Bool(true),
|
||||
Value::Bool(true),
|
||||
Value::Bool(false),
|
||||
Value::Bool(false),
|
||||
@@ -220,12 +215,22 @@ fn test_bool_functions() {
|
||||
Value::Bool(false),
|
||||
Value::Bool(false)];
|
||||
|
||||
if let Ok(to_eval) = tests {
|
||||
let context = Context::new();
|
||||
let mut env = Environment::new(Box::new(MemoryContractDatabase::new()));
|
||||
to_eval.iter().zip(expectations.iter())
|
||||
.for_each(|(program, expectation)| assert_eq!(Ok(expectation.clone()), eval(program, &mut env, &context)));
|
||||
} else {
|
||||
assert!(false, "Failed to parse function bodies.");
|
||||
}
|
||||
tests.iter().zip(expectations.iter())
|
||||
.for_each(|(program, expectation)| assert_eq!(Ok(expectation.clone()), execute(program)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bad_lets() {
|
||||
let tests = [
|
||||
"(let ((tx-sender 1)) (+ tx-sender tx-sender))",
|
||||
"(let ((* 1)) (+ * *))",
|
||||
"(let ((a 1) (a 2)) (+ a a))"];
|
||||
|
||||
let expectations: &[Result<Value, Error>] = &[
|
||||
Err(Error::ReservedName("tx-sender".to_string())),
|
||||
Err(Error::ReservedName("*".to_string())),
|
||||
Err(Error::VariableDefinedMultipleTimes("a".to_string()))];
|
||||
|
||||
tests.iter().zip(expectations.iter())
|
||||
.for_each(|(program, expectation)| assert_eq!(*expectation, execute(program)));
|
||||
}
|
||||
|
||||
681
src/vm/types.rs
681
src/vm/types.rs
@@ -1,66 +1,236 @@
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::fmt;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use vm::InterpreterResult;
|
||||
use vm::errors::Error;
|
||||
use address::c32;
|
||||
use vm::representations::SymbolicExpression;
|
||||
use vm::{eval, Context, Environment};
|
||||
use vm::errors::{Error, InterpreterResult as Result};
|
||||
use util::hash;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum AtomTypeIdentifier {
|
||||
VoidType,
|
||||
IntType,
|
||||
BoolType,
|
||||
BufferType,
|
||||
TupleType(TupleTypeSignature)
|
||||
}
|
||||
const MAX_VALUE_SIZE: i128 = 1024 * 1024; // 1MB
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct TypeSignature {
|
||||
atomic_type: AtomTypeIdentifier,
|
||||
dimension: u8
|
||||
}
|
||||
|
||||
#[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,
|
||||
BoolType,
|
||||
BufferType(u32),
|
||||
PrincipalType,
|
||||
TupleType(TupleTypeSignature)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
struct ListTypeData {
|
||||
max_len: u32,
|
||||
dimension: u8
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct TypeSignature {
|
||||
atomic_type: AtomTypeIdentifier,
|
||||
list_dimensions: Option<ListTypeData>,
|
||||
// NOTE: for the purposes of type-checks and cost computations, list size = dimension * max_length!
|
||||
// high dimensional lists are _expensive_ --- use lists of tuples!
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq)]
|
||||
pub struct TupleData {
|
||||
pub type_signature: TupleTypeSignature,
|
||||
type_signature: TupleTypeSignature,
|
||||
data_map: BTreeMap<String, Value>
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct BuffData {
|
||||
data: Vec<u8>,
|
||||
}
|
||||
|
||||
#[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(Box<[char]>),
|
||||
List(Vec<Value>, TypeSignature),
|
||||
Buffer(BuffData),
|
||||
List(ListData),
|
||||
Principal(u8, [u8; 20]), // a principal is a version byte + hash160 (20 bytes)
|
||||
Tuple(TupleData)
|
||||
}
|
||||
|
||||
pub enum CallableType <'a> {
|
||||
UserFunction(Box<DefinedFunction>),
|
||||
NativeFunction(&'a Fn(&[Value]) -> InterpreterResult),
|
||||
SpecialFunction(&'a Fn(&[SymbolicExpression], &mut Environment, &Context) -> InterpreterResult)
|
||||
impl PartialEq for ListData {
|
||||
fn eq(&self, other: &ListData) -> bool {
|
||||
self.data == other.data
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DefinedFunction {
|
||||
pub arguments: Vec<String>,
|
||||
pub body: SymbolicExpression
|
||||
impl Hash for ListData {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.data.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone,PartialEq,Eq,Hash)]
|
||||
pub struct FunctionIdentifier {
|
||||
pub arguments: Vec<String>,
|
||||
pub body: SymbolicExpression
|
||||
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);
|
||||
Value::list_from(vec_data)
|
||||
}
|
||||
|
||||
pub fn list_from(list_data: Vec<Value>) -> Result<Value> {
|
||||
let type_sig = TypeSignature::construct_parent_list_type(&list_data)?;
|
||||
// Aaron: at this point, we've _already_ allocated memory for this type.
|
||||
// (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 {
|
||||
return Err(Error::ValueTooLarge)
|
||||
}
|
||||
Ok(Value::List(ListData { data: list_data, type_signature: type_sig }))
|
||||
}
|
||||
|
||||
pub fn buff_from(buff_data: Vec<u8>) -> Result<Value> {
|
||||
if buff_data.len() > u32::max_value() as usize {
|
||||
Err(Error::BufferTooLarge)
|
||||
} else if buff_data.len() as i128 > MAX_VALUE_SIZE {
|
||||
Err(Error::ValueTooLarge)
|
||||
} else {
|
||||
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 {
|
||||
return Err(Error::ValueTooLarge)
|
||||
}
|
||||
Ok(Value::Tuple(tuple_data))
|
||||
}
|
||||
|
||||
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) => Ok(buff_data.data.len() as i128),
|
||||
Value::Tuple(ref tuple_data) => tuple_data.size(),
|
||||
Value::List(ref list_data) => list_data.type_signature.size()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl fmt::Display for Value {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Value::Void => write!(f, "null"),
|
||||
Value::Int(int) => write!(f, "{}", int),
|
||||
Value::Bool(boolean) => write!(f, "{}", boolean),
|
||||
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[..]) {
|
||||
Ok(val) => val,
|
||||
Err(_) => "INVALID_C32_ADDR".to_string()
|
||||
};
|
||||
write!(f, "{}", c32_str)
|
||||
},
|
||||
Value::List(list_data) => {
|
||||
write!(f, "( ")?;
|
||||
for v in list_data.data.iter() {
|
||||
write!(f, "{} ", v)?;
|
||||
}
|
||||
write!(f, ")")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AtomTypeIdentifier {
|
||||
pub fn size(&self) -> Result<i128> {
|
||||
match self {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
fn expand_to_admit(&mut self, other: &AtomTypeIdentifier) -> Result<()> {
|
||||
match self {
|
||||
AtomTypeIdentifier::BufferType(ref mut my_len) => {
|
||||
if let AtomTypeIdentifier::BufferType(ref other_len) = other {
|
||||
if other_len > my_len {
|
||||
*my_len = *other_len
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::BadTypeConstruction)
|
||||
}
|
||||
},
|
||||
AtomTypeIdentifier::TupleType(ref mut tuple_sig) => {
|
||||
if let AtomTypeIdentifier::TupleType(ref other_tuple_sig) = other {
|
||||
tuple_sig.expand_to_admit(other_tuple_sig)
|
||||
} else {
|
||||
Err(Error::BadTypeConstruction)
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
if other == self {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::BadTypeConstruction)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn admits(&self, other: &AtomTypeIdentifier) -> bool {
|
||||
match self {
|
||||
AtomTypeIdentifier::BufferType(ref my_len) => {
|
||||
if let AtomTypeIdentifier::BufferType(ref other_len) = other {
|
||||
my_len >= other_len
|
||||
} else {
|
||||
false
|
||||
}
|
||||
},
|
||||
AtomTypeIdentifier::TupleType(ref tuple_sig) => {
|
||||
if let AtomTypeIdentifier::TupleType(ref other_tuple_sig) = other {
|
||||
tuple_sig.admits(other_tuple_sig)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
},
|
||||
_ => other == self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl TupleTypeSignature {
|
||||
pub fn new(type_data: Vec<(String, TypeSignature)>) -> Result<TupleTypeSignature, Error> {
|
||||
pub fn new(type_data: Vec<(String, TypeSignature)>) -> Result<TupleTypeSignature> {
|
||||
let mut type_map = BTreeMap::new();
|
||||
for (name, type_info) in type_data {
|
||||
if let Some(_v) = type_map.insert(name, type_info) {
|
||||
@@ -70,35 +240,84 @@ impl TupleTypeSignature {
|
||||
Ok(TupleTypeSignature { type_map: type_map })
|
||||
}
|
||||
|
||||
pub fn check_valid(&self, name: &str, value: &Value) -> bool {
|
||||
if let Some(expected_type) = self.type_map.get(name) {
|
||||
*expected_type == TypeSignature::type_of(value)
|
||||
pub fn admits(&self, other: &TupleTypeSignature) -> bool {
|
||||
if self.type_map.len() != other.type_map.len() {
|
||||
return false
|
||||
}
|
||||
|
||||
for (name, my_type_sig) in self.type_map.iter() {
|
||||
if let Some(other_type_sig) = other.type_map.get(name) {
|
||||
if !my_type_sig.admits_type(other_type_sig) {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
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)
|
||||
.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)
|
||||
.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_.
|
||||
fn expand_to_admit(&mut self, other: &TupleTypeSignature) -> Result<()> {
|
||||
if self.type_map.len() != other.type_map.len() {
|
||||
return Err(Error::BadTypeConstruction)
|
||||
}
|
||||
|
||||
for (name, my_type_sig) in self.type_map.iter_mut() {
|
||||
let other_type_sig = other.type_map.get(name).ok_or(Error::BadTypeConstruction)?;
|
||||
my_type_sig.expand_to_admit(other_type_sig)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn parse_name_type_pair_list(type_def: &SymbolicExpression) -> Result<TupleTypeSignature> {
|
||||
if let SymbolicExpression::List(ref name_type_pairs) = type_def {
|
||||
let mapped_key_types = parse_name_type_pairs(name_type_pairs)?;
|
||||
TupleTypeSignature::new(mapped_key_types)
|
||||
} else {
|
||||
false
|
||||
Err(Error::ExpectedListPairs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TupleData {
|
||||
pub fn from_data(data: &[(&str, Value)]) -> Result<TupleData, Error> {
|
||||
fn from_data(mut data: Vec<(String, Value)>) -> Result<TupleData> {
|
||||
let mut type_map = BTreeMap::new();
|
||||
let mut data_map = BTreeMap::new();
|
||||
for (name, value) in data {
|
||||
let type_info = TypeSignature::type_of(value);
|
||||
for (name, value) in data.drain(..) {
|
||||
let type_info = TypeSignature::type_of(&value);
|
||||
if type_info.atomic_type == AtomTypeIdentifier::VoidType {
|
||||
return Err(Error::InvalidArguments("Cannot use VoidTypes in tuples.".to_string()))
|
||||
}
|
||||
if let Some(_v) = type_map.insert(name.to_string(), type_info) {
|
||||
return Err(Error::InvalidArguments("Cannot use named argument twice in tuple construction.".to_string()))
|
||||
}
|
||||
data_map.insert(name.to_string(), (*value).clone());
|
||||
data_map.insert(name.to_string(), value);
|
||||
}
|
||||
Ok(TupleData { type_signature: TupleTypeSignature { type_map: type_map },
|
||||
data_map: data_map })
|
||||
|
||||
}
|
||||
|
||||
pub fn get(&self, name: &str) -> InterpreterResult {
|
||||
pub fn get(&self, name: &str) -> Result<Value> {
|
||||
if let Some(value) = self.data_map.get(name) {
|
||||
Ok(value.clone())
|
||||
} else {
|
||||
@@ -106,107 +325,321 @@ impl TupleData {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub fn size(&self) -> Result<i128> {
|
||||
self.type_signature.size()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for TupleData {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let mut first = true;
|
||||
write!(f, "(")?;
|
||||
for (name, value) in self.data_map.iter() {
|
||||
if !first {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
first = false;
|
||||
write!(f, "{}: {}", name, value)?;
|
||||
}
|
||||
write!(f, ")")
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeSignature {
|
||||
pub fn new(atomic_type: AtomTypeIdentifier, dimension: u8) -> TypeSignature {
|
||||
pub fn new_atom(atomic_type: AtomTypeIdentifier) -> TypeSignature {
|
||||
TypeSignature { atomic_type: atomic_type,
|
||||
dimension: dimension }
|
||||
list_dimensions: None }
|
||||
}
|
||||
|
||||
pub fn type_of(x: &Value) -> TypeSignature {
|
||||
match x {
|
||||
Value::Void => TypeSignature::new(AtomTypeIdentifier::VoidType, 0),
|
||||
Value::Int(_v) => TypeSignature::new(AtomTypeIdentifier::IntType, 0),
|
||||
Value::Bool(_v) => TypeSignature::new(AtomTypeIdentifier::BoolType, 0),
|
||||
Value::Buffer(_v) => TypeSignature::new(AtomTypeIdentifier::BufferType, 0),
|
||||
Value::List(_v, type_signature) => type_signature.clone(),
|
||||
Value::Tuple(v) => TypeSignature::new(AtomTypeIdentifier::TupleType(
|
||||
v.type_signature.clone()), 0)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_list_type_for(x: &Value) -> Result<TypeSignature, Error> {
|
||||
match x {
|
||||
Value::Void => Err(Error::InvalidArguments("Cannot construct list of void types".to_string())),
|
||||
Value::Tuple(_a) => Err(Error::InvalidArguments("Cannot construct list of tuple types".to_string())),
|
||||
_ => {
|
||||
let mut base_type = TypeSignature::type_of(x);
|
||||
base_type.dimension += 1;
|
||||
Ok(base_type)
|
||||
pub fn new_list(atomic_type: AtomTypeIdentifier, max_len: i128, dimension: i128) -> Result<TypeSignature> {
|
||||
if dimension == 0 {
|
||||
Err(Error::InvalidTypeDescription)
|
||||
} else if max_len > u32::max_value() as i128 || dimension > u8::max_value() as i128 {
|
||||
Err(Error::ListTooLarge)
|
||||
} else {
|
||||
let list_dimensions = Some(ListTypeData { max_len: max_len as u32,
|
||||
dimension: dimension as u8 });
|
||||
let type_sig = TypeSignature { atomic_type: atomic_type,
|
||||
list_dimensions: list_dimensions };
|
||||
if type_sig.size()? > MAX_VALUE_SIZE {
|
||||
Err(Error::ValueTooLarge)
|
||||
} else {
|
||||
Ok(type_sig)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_atom_type(typename: &str) -> Result<AtomTypeIdentifier, Error> {
|
||||
fn new_atom_checked(atom_type: AtomTypeIdentifier) -> Result<TypeSignature> {
|
||||
if atom_type.size()? > MAX_VALUE_SIZE {
|
||||
Err(Error::ValueTooLarge)
|
||||
} else {
|
||||
Ok(TypeSignature::new_atom(atom_type))
|
||||
}
|
||||
}
|
||||
|
||||
fn new_tuple(tuple_type_sig: TupleTypeSignature) -> Result<TypeSignature> {
|
||||
TypeSignature::new_atom_checked(AtomTypeIdentifier::TupleType(tuple_type_sig))
|
||||
}
|
||||
|
||||
fn new_buffer(buff_len: i128) -> Result<TypeSignature> {
|
||||
if buff_len > u32::max_value() as i128 {
|
||||
Err(Error::BufferTooLarge)
|
||||
} else {
|
||||
let atom_type = AtomTypeIdentifier::BufferType(buff_len as u32);
|
||||
TypeSignature::new_atom_checked(atom_type)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_empty_list_type() -> TypeSignature {
|
||||
TypeSignature { atomic_type: AtomTypeIdentifier::IntType,
|
||||
list_dimensions: Some(ListTypeData { max_len: 0,
|
||||
dimension: 1 })}
|
||||
}
|
||||
|
||||
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)
|
||||
.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(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.data.len() as u32),
|
||||
Value::Tuple(v) => AtomTypeIdentifier::TupleType(
|
||||
v.type_signature.clone()),
|
||||
Value::List(_) => panic!("Unreachable code")
|
||||
};
|
||||
|
||||
TypeSignature::new_atom(atom)
|
||||
}
|
||||
}
|
||||
|
||||
fn expand_to_admit(&mut self, x_type: &TypeSignature) -> Result<()> {
|
||||
if let Some(ref mut my_list_dimensions) = self.list_dimensions {
|
||||
if let Some(ref x_list_dimensions) = x_type.list_dimensions {
|
||||
if my_list_dimensions.dimension != x_list_dimensions.dimension {
|
||||
return Err(Error::BadTypeConstruction)
|
||||
}
|
||||
if my_list_dimensions.max_len < x_list_dimensions.max_len {
|
||||
my_list_dimensions.max_len = x_list_dimensions.max_len;
|
||||
}
|
||||
} else {
|
||||
return Err(Error::BadTypeConstruction)
|
||||
}
|
||||
} else {
|
||||
if let Some(_) = x_type.list_dimensions {
|
||||
return Err(Error::BadTypeConstruction)
|
||||
}
|
||||
}
|
||||
|
||||
self.atomic_type.expand_to_admit(& x_type.atomic_type)
|
||||
}
|
||||
|
||||
// Checks if resulting type signature is of valid size.
|
||||
// Aaron:
|
||||
// currently, this does "loose admission" for higher-order lists --
|
||||
// but should it do the same for buffers and tuples or is it better
|
||||
// like it is now, where it requires an exact type match on those?
|
||||
// e.g.: (list "abcd" "abc") will currently error because one etry is
|
||||
// if type (buffer 4) and the other is of type (buffer 3)
|
||||
// my feeling is that this should probably be allowed, and the resulting
|
||||
// type should be (list 2 (buffer 4))
|
||||
fn construct_parent_list_type(args: &[Value]) -> Result<TypeSignature> {
|
||||
if let Some((first, rest)) = args.split_first() {
|
||||
// children must be all of identical types, though we're a little more permissive about
|
||||
// children which are _lists_: we don't care about their max_len, we just take the max()
|
||||
let mut child_type = TypeSignature::type_of(first);
|
||||
for x in rest {
|
||||
let x_type_signature = TypeSignature::type_of(x);
|
||||
child_type.expand_to_admit(&x_type_signature)?;
|
||||
}
|
||||
|
||||
let mut parent_max_len = {
|
||||
let args_len = args.len();
|
||||
if args_len > (u32::max_value() as usize) {
|
||||
Err(Error::ListTooLarge)
|
||||
} else {
|
||||
Ok(args_len as u32)
|
||||
}
|
||||
}?;
|
||||
|
||||
let parent_dimension = match child_type.list_dimensions {
|
||||
Some(ref type_data) => {
|
||||
if type_data.max_len > parent_max_len {
|
||||
parent_max_len = type_data.max_len
|
||||
}
|
||||
type_data.dimension.checked_add(1)
|
||||
.ok_or(Error::ListDimensionTooHigh)
|
||||
},
|
||||
None => {
|
||||
Ok(1)
|
||||
}
|
||||
}?;
|
||||
|
||||
TypeSignature::new_list(child_type.atomic_type,
|
||||
parent_max_len as i128, parent_dimension as i128)
|
||||
} else {
|
||||
Ok(TypeSignature::get_empty_list_type())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn admits(&self, x: &Value) -> bool {
|
||||
let x_type = TypeSignature::type_of(x);
|
||||
self.admits_type(&x_type)
|
||||
}
|
||||
|
||||
pub fn admits_type(&self, x_type: &TypeSignature) -> bool {
|
||||
if let Some(ref x_type_data) = x_type.list_dimensions {
|
||||
if let Some(ref my_type_data) = self.list_dimensions {
|
||||
if my_type_data.dimension == x_type_data.dimension && my_type_data.max_len >= x_type_data.max_len {
|
||||
self.atomic_type.admits(&x_type.atomic_type)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
self.atomic_type.admits(&x_type.atomic_type)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_atom_type(typename: &str) -> Result<AtomTypeIdentifier> {
|
||||
match typename {
|
||||
"int" => Ok(AtomTypeIdentifier::IntType),
|
||||
"void" => Ok(AtomTypeIdentifier::VoidType),
|
||||
"bool" => Ok(AtomTypeIdentifier::BoolType),
|
||||
"buff" => Ok(AtomTypeIdentifier::BufferType),
|
||||
"principal" => Ok(AtomTypeIdentifier::PrincipalType),
|
||||
_ => Err(Error::ParseError(format!("Unknown type name: '{:?}'", typename)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn get_list_type(prefix: &str, typename: &str, dimension: &str) -> Result<TypeSignature, Error> {
|
||||
if prefix != "list" {
|
||||
let message = format!("Unknown type name: '{}-{}-{}'", prefix, typename, dimension);
|
||||
return Err(Error::ParseError(message))
|
||||
// Parses list type signatures ->
|
||||
// (list maximum-length dimension atomic-type) or
|
||||
// (list maximum-length atomic-type) -> denotes list of dimension 1
|
||||
fn parse_list_type_repr(type_args: &[SymbolicExpression]) -> Result<TypeSignature> {
|
||||
if type_args.len() != 2 && type_args.len() != 3 {
|
||||
return Err(Error::InvalidTypeDescription);
|
||||
}
|
||||
let atom_type = TypeSignature::get_atom_type(typename)?;
|
||||
let dimension = match u8::from_str_radix(dimension, 10) {
|
||||
Ok(parsed) => Ok(parsed),
|
||||
Err(_e) => Err(Error::ParseError(
|
||||
format!("Failed to parse dimension of type: '{}-{}-{}'",
|
||||
prefix, typename, dimension)))
|
||||
}?;
|
||||
Ok(TypeSignature::new(atom_type, dimension))
|
||||
}
|
||||
|
||||
pub fn parse_type_str(x: &str) -> Result<TypeSignature, Error> {
|
||||
let components: Vec<_> = x.split('-').collect();
|
||||
match components.len() {
|
||||
1 => {
|
||||
let atom_type = TypeSignature::get_atom_type(components[0])?;
|
||||
Ok(TypeSignature::new(atom_type, 0))
|
||||
},
|
||||
3 => TypeSignature::get_list_type(components[0], components[1], components[2]),
|
||||
_ => Err(Error::ParseError(
|
||||
format!("Unknown type name: '{}'", x)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn get_empty_list_type() -> TypeSignature {
|
||||
TypeSignature::new(AtomTypeIdentifier::IntType, 0)
|
||||
}
|
||||
}
|
||||
|
||||
impl DefinedFunction {
|
||||
pub fn new(body: SymbolicExpression, arguments: Vec<String>) -> DefinedFunction {
|
||||
DefinedFunction {
|
||||
body: body,
|
||||
arguments: arguments,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply(&self, args: &[Value], env: &mut Environment) -> InterpreterResult {
|
||||
let mut context = Context::new();
|
||||
|
||||
let mut arg_iterator = self.arguments.iter().zip(args.iter());
|
||||
let _result = arg_iterator.try_for_each(|(arg, value)| {
|
||||
match context.variables.insert((*arg).clone(), (*value).clone()) {
|
||||
Some(_val) => Err(Error::InvalidArguments("Multiply defined function argument".to_string())),
|
||||
_ => Ok(())
|
||||
let dimension = {
|
||||
if type_args.len() == 2 {
|
||||
Ok(1)
|
||||
} else {
|
||||
if let SymbolicExpression::AtomValue(Value::Int(dimension)) = &type_args[1] {
|
||||
Ok(*dimension)
|
||||
} else {
|
||||
Err(Error::InvalidTypeDescription)
|
||||
}
|
||||
}
|
||||
})?;
|
||||
eval(&self.body, env, &context)
|
||||
}?;
|
||||
|
||||
if let SymbolicExpression::AtomValue(Value::Int(max_len)) = &type_args[0] {
|
||||
let atomic_type_arg = &type_args[type_args.len()-1];
|
||||
let atomic_type = TypeSignature::parse_type_repr(atomic_type_arg, false)?;
|
||||
TypeSignature::new_list(atomic_type.atomic_type, *max_len, dimension)
|
||||
} else {
|
||||
Err(Error::InvalidTypeDescription)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_identifier(&self) -> FunctionIdentifier {
|
||||
return FunctionIdentifier {
|
||||
body: self.body.clone(),
|
||||
arguments: self.arguments.clone() }
|
||||
// Parses type signatures of the following form:
|
||||
// (tuple ((key-name-0 value-type-0) (key-name-1 value-type-1)))
|
||||
fn parse_tuple_type_repr(type_args: &[SymbolicExpression]) -> Result<TypeSignature> {
|
||||
if type_args.len() != 1 {
|
||||
return Err(Error::InvalidTypeDescription)
|
||||
}
|
||||
let tuple_type_signature = TupleTypeSignature::parse_name_type_pair_list(&type_args[0])?;
|
||||
TypeSignature::new_tuple(tuple_type_signature)
|
||||
}
|
||||
|
||||
// Parses type signatures of the form:
|
||||
// (buff 10)
|
||||
fn parse_buff_type_repr(type_args: &[SymbolicExpression]) -> Result<TypeSignature> {
|
||||
if type_args.len() != 1 {
|
||||
return Err(Error::InvalidTypeDescription)
|
||||
}
|
||||
if let SymbolicExpression::AtomValue(Value::Int(buff_len)) = &type_args[0] {
|
||||
TypeSignature::new_buffer(*buff_len)
|
||||
} else {
|
||||
Err(Error::InvalidTypeDescription)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_type_repr(x: &SymbolicExpression, allow_list: bool) -> Result<TypeSignature> {
|
||||
match x {
|
||||
SymbolicExpression::Atom(atom_type_str) => {
|
||||
let atomic_type = TypeSignature::parse_atom_type(atom_type_str)?;
|
||||
Ok(TypeSignature::new_atom(atomic_type))
|
||||
},
|
||||
SymbolicExpression::List(list_contents) => {
|
||||
let (compound_type, rest) = list_contents.split_first()
|
||||
.ok_or(Error::InvalidTypeDescription)?;
|
||||
if let SymbolicExpression::Atom(compound_type) = compound_type {
|
||||
match compound_type.as_str() {
|
||||
"list" =>
|
||||
if !allow_list {
|
||||
Err(Error::InvalidTypeDescription)
|
||||
} else {
|
||||
TypeSignature::parse_list_type_repr(rest)
|
||||
},
|
||||
"buff" => TypeSignature::parse_buff_type_repr(rest),
|
||||
"tuple" => TypeSignature::parse_tuple_type_repr(rest),
|
||||
_ => Err(Error::InvalidTypeDescription)
|
||||
}
|
||||
} else {
|
||||
Err(Error::InvalidTypeDescription)
|
||||
}
|
||||
},
|
||||
_ => Err(Error::InvalidTypeDescription)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn parse_name_type_pairs(name_type_pairs: &[SymbolicExpression]) -> Result<Vec<(String, TypeSignature)>> {
|
||||
// this is a pretty deep nesting here, but what we're trying to do is pick out the values of
|
||||
// the form:
|
||||
// ((name1 type1) (name2 type2) (name3 type3) ...)
|
||||
// which is a list of 2-length lists of atoms.
|
||||
use vm::representations::SymbolicExpression::{List, Atom};
|
||||
|
||||
// step 1: parse it into a vec of symbolicexpression pairs.
|
||||
let as_pairs: Result<Vec<_>> =
|
||||
name_type_pairs.iter().map(
|
||||
|key_type_pair| {
|
||||
if let List(ref as_vec) = key_type_pair {
|
||||
if as_vec.len() != 2 {
|
||||
Err(Error::ExpectedListPairs)
|
||||
} else {
|
||||
Ok((&as_vec[0], &as_vec[1]))
|
||||
}
|
||||
} else {
|
||||
Err(Error::ExpectedListPairs)
|
||||
}
|
||||
}).collect();
|
||||
|
||||
// step 2: turn into a vec of (name, typesignature) pairs.
|
||||
let key_types: Result<Vec<_>> =
|
||||
(as_pairs?).iter().map(|(name_symbol, type_symbol)| {
|
||||
let name = match name_symbol {
|
||||
Atom(ref var) => Ok(var.clone()),
|
||||
_ => Err(Error::ExpectedListPairs)
|
||||
}?;
|
||||
let type_info = TypeSignature::parse_type_repr(type_symbol, true)?;
|
||||
Ok((name, type_info))
|
||||
}).collect();
|
||||
|
||||
key_types
|
||||
}
|
||||
|
||||
13
src/vm/variables.rs
Normal file
13
src/vm/variables.rs
Normal file
@@ -0,0 +1,13 @@
|
||||
// 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,
|
||||
"block-height",
|
||||
"burn-block-height"];
|
||||
|
||||
pub fn is_reserved_variable(name: &str) -> bool {
|
||||
RESERVED_VARIABLES.contains(&name)
|
||||
}
|
||||
Reference in New Issue
Block a user