├── .gitignore ├── .travis.yml ├── .travis └── publish.sh ├── Cargo.toml ├── README.md ├── src ├── bytecode │ ├── mod.rs │ └── tests.rs ├── cell.rs ├── lib.rs ├── slist.rs └── tests.rs └── tests └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io 2 | 3 | ### Rust ### 4 | # Compiled files 5 | *.o 6 | *.so 7 | *.rlib 8 | *.dll 9 | 10 | # Executables 11 | *.exe 12 | 13 | # Generated by Cargo 14 | **/target/** 15 | Cargo.lock 16 | 17 | # Generated by RustBook 18 | **/_book/** 19 | 20 | 21 | ### OSX ### 22 | .DS_Store 23 | .AppleDouble 24 | .LSOverride 25 | 26 | # Icon must end with two \r 27 | Icon 28 | 29 | 30 | # Thumbnails 31 | ._* 32 | 33 | # Files that might appear on external disk 34 | .Spotlight-V100 35 | .Trashes 36 | 37 | # Directories potentially created on remote AFP share 38 | .AppleDB 39 | .AppleDesktop 40 | Network Trash Folder 41 | Temporary Items 42 | .apdisk 43 | 44 | 45 | ### SublimeText ### 46 | # cache files for sublime text 47 | *.tmlanguage.cache 48 | *.tmPreferences.cache 49 | *.stTheme.cache 50 | 51 | # workspace files are user-specific 52 | *.sublime-workspace 53 | 54 | # project files should be checked into the repository, unless a significant 55 | # proportion of contributors will probably not be using SublimeText 56 | # *.sublime-project 57 | 58 | # sftp configuration file 59 | sftp-config.json 60 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: rust 3 | rust: nightly 4 | addons: 5 | apt: 6 | packages: 7 | - libcurl4-openssl-dev 8 | - libelf-dev 9 | - libdw-dev 10 | before_script: 11 | - | 12 | pip install 'travis-cargo<0.2' --user && 13 | export PATH=$HOME/.local/bin:$PATH 14 | script: 15 | - travis-cargo build 16 | - travis-cargo test 17 | - travis-cargo bench 18 | env: 19 | global: 20 | - TRAVIS_CARGO_NIGHTLY_FEATURE="nightly" 21 | - secure: xHUGJn89lFSrNutHity1M+97tZjQ5ff0tjNbjDwchujL5BgP9aLVY3O7Wg3LoO/sWN8TYtO5sDVBy/8nV6HvVU28k/1ZLOQzDc2WeXIVBGdqOxB9QtLAkIa4aRHA6yhC65vI9zeTzGnOo166++4HvtU5DN5Bkq+Zm+AZxXVEZM+3AeiqaOLly6AzgUWCQf2TF8/aqU9K2sb6yBYsrBi8m4yMVYoC3pKlVlk6NbevhG059V/46zNQqDwbryLa+MPiRsnMuDVU2xAaKd4OAIxVNOfYmEETHnpZ1UNXMCc85XHOeu2+AVxJJzdb3U3jb7ixZ8z14ZwUBHzVgMv2p2lHvSL7TPQ/y+N4NOg7FEptxVzlzYT9h/5Nrx2sqVkgezzFf3L9vJ7W9CNHTBuW29gKpnNxuiFw0ZOU5c7St/tgccjzphOJaUvT1n7KuZinRktz7lIawShIuC8z1BChC2nDJoe650nvtUEETU++nj+rEdFmJwwGg+bz+vjsfFiXRxC6Fswe3Q7xrVrq+PDO51ZqZbOj1vj6Kxq1CdnHvvjNCb+wF4ihM4qf48Hnor80f+ciLwK5vENF6cMsSsTcEXHgXd1HwDGOkaBwkhQjwqg3fStuARmCQYmTnxuNHSNXnnazZq4MpRn4cqcr/ZeZsXOvJQ4BEA9HLacSD5Ii03D4u7A= 22 | - secure: hOaCd97R1ixJZzYZolcMBlqEtm/WAnIjNfUdIAne6GecWrpIrpUL6K9kXCv/LAOp0T2JzcbT/a9AlwSgzse5ZTqsf39letwtmCP4iMnoX6cqBGL39jTXMCQKYRhOKQiINhpu1pBnTyxHJAzKry+dglbaYHqapu/v7VR18Zqn0IpbawKw/C0xZzmnNZ8HUco30JVtRC8vNOUvoJNtlz1I7Q9H8GLBJ3E6ACfnz+pQomtuTcw28bkXxFC2WtaQAXWHbkJujVs+9RrD4EfbHJL1fkqQeJn/aoxfN0JBb3pTPWSi/ydPJUDXKrZkRqfAmb3YcTqOSO4jg+EpstNhHLf8nnz9pjMbxJEsLP/yTVSPefcTehwDnSK/0+1mmgWNBrXMzrapJUxVWSlc4oAvwk+mbju/LmVLZrP7uWUuPjRHCMOzpvZu62bUqq5j7QewTzQop6w56UbhRmlGhWqLD/oZbtZh/FwQ5TslsQtERAAPRV3lwIsE9ePC4rgNJi1petECCAkPufr+SmL8oReB8aa0NC5ojnc9lGxA8DIqW/9IwILBwRMCR+9iUO5ukclc7KxmayeCBZZJxCX5JRylfUhGarl+l8kgd7AE61YMO8dZ08U5ERlTgmoixtTcZB7wJY7JpBN7kg/O9wFp9VWw0FcQVwbyoqBlv7C9CkaMc2E/QpI= 23 | - secure: E+00NmwXknVUGsmmcDsEkuLPJCTM6/GXUspvWAhN7i58eSY/zn4pG0Kl0bCy8/uMpdo8TsJZ1844qgrufqlxnrieIURb9xi/+niB5Eld5PR4G9kQqKcSAyIXy4RUKmWeq4V/DUiYHs1wEQm+gWmVyAa6uDVuCfXOqJ9l792kl6ITOMYOLgXpCR9S5SUDICHBBRCedF/Rd6ok2igDu6Sn90PO+/VO5JGLe9jfSg3dl9KNUc8waLT/ra8a0Xd5v1r9dlMR+FuBIKgtk8bkVhGlEjYoeO7IT3MITH7XMCVQCVrS0/1RjIcLIQClDFtcOFIIbtgWS7LndYeHzHmKIsuO+QZTC0LmLq3HjfPhey1SZ9y0IC/Fbq1ph3uUnBKiXEom9LMsj0pl8GlKwbbbW0G6IsMzNl17M3yk/wj8FPftNXQP9uCdYSucKWPJ29rq2XfYXPDn0wr0foVJWSknJJ375Q3vBpUn9jVsldESx6AC7nUKnZWrqMhVbfTOR6q1GMMUbanVtGHgQLPOaKMD3fyBI/XgwiJ/VcWP3A7IpwmZjAFVkGCGCxN5AESRH7hy/YbP+UhxEluE0rnnzcrA69wMR7IQYwrjuK7WroMIMPfPaJhUB+q0d+PZEw9B8YReIujqXYEAeJoUEFWGWJuBbGL+3NSk/SusDxASTj3l8kTIVo0= 24 | after_success: 25 | - travis-cargo coveralls --only nightly --no-sudo 26 | - bash <(curl https://codecov.io/bash) 27 | before_deploy: 28 | - travis-cargo doc 29 | - travis-cargo doc-upload 30 | - travis-cargo build -- --release 31 | - travis-cargo package 32 | - travis-cargo publish -- --token $CRATES_TOKEN 33 | deploy: 34 | provider: releases 35 | api_key: 36 | secure: R63t7UD46RuxkKTJRcLuK0tIezdZhd6BsLKbQ1L4b4H+7P+xfwuhDf+rC0HVw6qbJ3fCcGasYYxPUVkwYfqs9tIWORd+7JmBuQo6g1dsj+z/D6OSjn5XQ9YnmXHglGkADEneU/9SfDt+8xv171mJKgKZ6ZlIKg/mHDfbnM2DmiSwMigA/UPpVVVBcw1Bmk96umPeuLUSVOb67cy1F/56MQpv1Vzz0dfevjJxsA/BFbcBoDKFGwRaP8HqgDJYkbyCpN1EgM3lj99LmbCE0N7Jvl8x2VdAa0eIYXKAkc/oYCZsMhSYL5zFhcuRG/wOM875VqiEgK+aZMMhHYrWQWEkvycpM6ZvIahzj8JqO+NDqkN4xOGMKiUXLXjQHZpsDWh4le83V1xClc04R/QxSiJGVibw2O2H49L5bSW2idxBTOjSCH5tfxWdBvLL7JwJSlTPENGPPm3LZmVy5bRlSR2vE1vABwB5UmbhhT2NG9Ar6siXEzfxyG+Fp7a5GSJbDC3R5xRymUJIJQBI++A+3Am/Mgk4ZSzrIqMqfwof1xYM+Hd02xseWy2OqU89N+ufAaKe8SM+lQyr/lIQjUNBXMjlKYRd3BkmhBVleUv/0i5HabBG5q2tohyze59vdSA3FlxPjia1ON6gsmGS75EFVfJA2bQxVFu0wL9YbRBjfwCtPgQ= 37 | file: target/release/libseax_svm.rlib 38 | on: 39 | repo: hawkw/seax_svm 40 | rust: nightly 41 | tags: true 42 | -------------------------------------------------------------------------------- /.travis/publish.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo -e "Publishing RustDoc...\n" 3 | 4 | cp -R target/doc $HOME/api/ 5 | 6 | cd $HOME 7 | git config --global user.email "travis@travis-ci.org" 8 | git config --global user.name "travis-ci" 9 | git clone --quiet --branch=gh-pages https://${GH_TOKEN}@github.com/hawkw/seax_svm gh-pages > /dev/null 10 | 11 | cd gh-pages 12 | git rm -rf ./api 13 | cp -Rf $HOME/api . 14 | git add -f . 15 | git commit -m "Lastest RustDoc for version $TRAVIS_TAG on successful travis build $TRAVIS_BUILD_NUMBER auto-pushed to gh-pages" 16 | git push -fq origin gh-pages > /dev/null 17 | 18 | echo -e "Published RustDoc to gh-pages.\n" 19 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | 3 | name = "seax_svm" 4 | version = "0.2.8" 5 | authors = ["Hawk Weisman "] 6 | description = "SECD virtual machine for interpreting programs in FP languages" 7 | license = "MIT" 8 | homepage = "http://hawkweisman.me/seax" 9 | repository = "https://github.com/hawkw/seax_svm" 10 | readme = "README.md" 11 | keywords = ["vm","lisp","languages"] 12 | 13 | [dependencies] 14 | log = "0.3.1" 15 | byteorder = "*" 16 | 17 | [dev-dependencies] 18 | quickcheck = "*" 19 | 20 | [features] 21 | nightly = [] 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Seax Virtual Machine (SVM) 2 | ========================== 3 | 4 | [![Join the chat at https://gitter.im/hawkw/seax](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/hawkw/seax?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 5 | [![Build Status](https://img.shields.io/travis/hawkw/seax_svm/master.svg?style=flat)](https://travis-ci.org/hawkw/seax_svm) 6 | [![Coverage](https://img.shields.io/codecov/c/github/hawkw/seax_svm/master.svg?style=flat)](http://codecov.io/github/hawkw/seax_svm?branch=master) 7 | [![Latest RustDoc](https://img.shields.io/badge/rustdoc-latest-green.svg?style=flat)](http://hawkweisman.me/seax/api/seax_svm/) 8 | [![Latest SVM release](https://img.shields.io/crates/v/seax_svm.svg?style=flat)](https://crates.io/crates/seax_svm) 9 | [![License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat)](https://github.com/hawkw/seax/LICENSE) 10 | 11 | The [Seax](http://hawkweisman.me/seax/) virtual machine, a runtime environment for functional programming languages. 12 | 13 | Seax is a virtual machine based on the [SECD machine](http://en.wikipedia.org/wiki/SECD_machine) described by Peter Landin in 1963. It is intended as a portible, robust run-time environment to be used as an implementation target for programming languages, with an emphasis on functional programming. 14 | 15 | SVM is distributed as a library so that a Seax runtime may be embedded in other software systems. That means that the code in this repository is not sufficient to compile and run Scheme programs for Seax on its own. The [hawkw/seax](https://github.com/hawkw/seax) repository contains the Seax command-line application, which you will probably want if you intend to develop programs targeting Seax. Currently, a [Scheme interpreter/compiler]((https://github.com/hawkw/seax_scheme)) is under active development, with other programming languages targeting Seax coming eventually. 16 | 17 | Please report all issues and feature requests to the main repository ([hawkw/seax](https://github.com/hawkw/seax)). 18 | 19 | Contributing 20 | ------------ 21 | 22 | Seax is an open-source project and contributions are happily welcomed. For more information on how to contribute to Seax, please see the [CONTRIBUTING](https://github.com/hawkw/seax/blob/master/CONTRIBUTING.md) document on the main Seax repository. 23 | -------------------------------------------------------------------------------- /src/bytecode/mod.rs: -------------------------------------------------------------------------------- 1 | //! Bytecode 2 | //! ======== 3 | //! 4 | //! This module contains functions for encoding and decoding Seax bytecode. 5 | //! 6 | //! Seax Bytecode Format 7 | //! ==================== 8 | //! 9 | //! Seax Bytecode Standard Revision 0, June 11th, 2015 10 | //! 11 | //! I: The preamble 12 | //! --------------- 13 | //! 14 | //! All Seax Bytecode files begin with a preamble. This preamble consists of the following: 15 | //! 16 | //! 1. The identifying bytes 0x5ECD. These bytes, chosen based on a poorly-advised attempt to 17 | //! spell out the abbreviation SECD in hexadecimal, identify the file as a Seax bytecode file. 18 | //! 2. A 16-bit unsigned integer that represents the version of the Seax bytecode format that the 19 | //! file was encoded with. This number is used to determine how the remainder of the file should 20 | //! be decoded. This document is Revision 0 of the Seax Bytecode format, so the version 21 | //! should be 0x0000. 22 | //! 23 | //! Future revisions of this standard will allow additional metadata, such as the author's 24 | //! cryptographic signature, checksums for ensuring the executable's integrity, and directives to 25 | //! the virtual machine, to be placed in the preamble as well. 26 | //! 27 | //! II: Instructions 28 | //! ---------------- 29 | //! 30 | //! All Seax VM instructions are encoded using single byes. The Seax opcodes occupy the 31 | //! space 0x00 to 0x30, with the bytes 0x1D through 0x3F being reserved for future use. 32 | //! 33 | //! The following table shows all of the currently available SVM opcodes. 34 | //! 35 | //! | Value | Name | Description 36 | //! +-------+---------------+-------------------------------------------------------------------- 37 | //! 0x00 | NIL | Pushes an empty list (nil) onto `$s`. 38 | //! 0x01 | LD (a . b) | Pushes the variable at `$e[a][b]` onto the stack. 39 | //! 0x02 | LDF f | Constructs a closure from the list `f` and the current environment, 40 | //! and pushes it to `$s`. 41 | //! 0x03 | AP c | Applies the function closure `c`. 42 | //! 0x04 | APCC c | Applies the function closure `c` and pushes a continuation on `$d`. 43 | //! 0x05 | JOIN | Returns control to the calling scope at the end of a `SEL`. 44 | //! 0x06 | RAP c | Applies the recursive closure `c`. 45 | //! 0x07 | RET | Returns control from a function to the calling function. 46 | //! 0x08 | DUM | Pushes a dummy environment to `$e` for applying a recursive function. 47 | //! 0x09 | SEL a | Applies the first list of instructions on `$c` if `a` is non-nil, 48 | //! or the second list if it is nil. 49 | //! 0x0A | ADD a b | 50 | //! 0x0B | SUB a b | 51 | //! 0x0C | MUL a b | 52 | //! 0x0D | DIV a b | 53 | //! 0x0E | MOD a b | 54 | //! 0x0F | FDIV a b | 55 | //! 0x10 | EQ a b | 56 | //! 0x11 | GT a b | 57 | //! 0x12 | GTE a b | 58 | //! 0x13 | LT a b | 59 | //! 0x14 | LTE a b | 60 | //! 0x15 | ATOM a | 61 | //! 0x16 | NULL a | 62 | //! 0x17 | READC | 63 | //! 0x18 | WRITEC | 64 | //! 0x19 | CONS a b | 65 | //! 0x1A | CAR (a . b) | 66 | //! 0x1B | CDR (a . b) | 67 | //! 0x1C | LDC | 68 | //! 0x1D | STOP | 69 | //! 0x1E | reserved | 70 | //! | ... | 71 | //! 0x30 | reserved | 72 | //! 73 | //! III: Constants 74 | //! -------------- 75 | //! 76 | //! Constants are identified by a preceeding constant-identification byte. Constant-identification 77 | //! bytes comprise the range of bytes between 0xC0 and 0xCF, inclusive, and the NIL byte, 0x00. 78 | //! 79 | //! Constants are expected in two places: following either an LDC (LoaD Constant) instruction, 80 | //! or following an instruction that expects a list on $c, such as a SEL instruction. 81 | //! 82 | //! 1. CONS Cells (0xC0) 83 | //! 84 | //! 0xC0 identifies the beginning of a CONS cell constant. It may then be followed by the 85 | //! identification byte denoting any other constant, which will be interpreted as the CAR part 86 | //! of the CONS cell. This is followed by the actual bytecode data for the CAR part of the CONS 87 | //! cell, which is of the length specified by the type identified by the identification byte. 88 | //! 89 | //! After the constant for the CAR part, there must be an additional identification byte, which 90 | //! identifies the type of the CONS cell's CDR part. Unlike the CAR part, this may be any 91 | //! constant type, including another CONS cell. This identification byte is again followed by 92 | //! the bytecode for the data stored in the CONS cell's CDR part, whose length is determined 93 | //! by the type identified by the identification byte. 94 | //! 95 | //! Alternatively, the CAR or CDR parts of a CONS cell may also contain a Seax instruction. In 96 | //! such a case, the identification byte is replaced by that instruction's opcode. The opcode 97 | //! comprises the entirity of the CAR or CDR part, and any further data is interpreted as 98 | //! appropriate (i.e., if the opcode is the CAR part, another opcode or identifying byte will 99 | //! be expected, while if the opcode is in the CDR part, a new instruction or constant will 100 | //! be expected.) 101 | //! 102 | //! 2. Atom constants (0xC1 ... 0xCF) 103 | //! 104 | //! Any constants that are not CONS cells are atom constants. Atom constants are identified by 105 | //! bytes in the range between 0xC1 and 0xCF, inclusive. Currently, 0xC1, 0xC2, 0xC3, and 0xC4 106 | //! identify extant atom types, while 0xC5 ... 0xCE are reserved for future use. 107 | //! 108 | //! Once an atom constant identifying byte is read, the bytes that follow it will be read as 109 | //! that type of atom. The number of bytes read depends on the length of the atom type, which is 110 | //! determined using the identifying bytes. The following identifying bytes correspond to the 111 | //! following atom types: 112 | //! 113 | //! + 0xC1: uint atom (64-bit unsigned integer) 114 | //! + 0xC2: sint atom (64-bit signed integer) 115 | //! + 0xC3: char atom (32-bit Unicode scalar value) 116 | //! + 0xC4: float atom (64-bit double-precision floating point number 117 | //! 118 | //! If additional primitive data types are added to the Seax VM, the bytes 0xC5 to 0xCF will 119 | //! be used to identify those types. 120 | //! 121 | //! Note that the type tag identifying a constant may be extracted by byte-masking the 122 | //! identifying byte with the number 0x0F. 123 | //! 124 | 125 | extern crate byteorder; 126 | 127 | use self::byteorder::{ByteOrder, BigEndian, ReadBytesExt, WriteBytesExt}; 128 | 129 | use std::error::Error; 130 | use std::io::Read; 131 | use std::fmt; 132 | use std::char; 133 | 134 | use super::slist::List; 135 | use super::slist::List::*; 136 | use super::{SVMCell,Atom,Inst}; 137 | use super::SVMCell::*; 138 | use super::Atom::*; 139 | use super::Inst::*; 140 | 141 | #[cfg(test)] 142 | mod tests; 143 | 144 | #[cfg(not(feature = "nightly"))] 145 | macro_rules! push_all { 146 | ( $vec:ident, $other:expr ) => { 147 | for item in $other { 148 | $vec.push(*item); 149 | } 150 | } 151 | } 152 | #[cfg(feature = "nightly")] 153 | macro_rules! push_all { 154 | ( $vec:ident, $other:expr ) => { $vec.push_all($other); } 155 | } 156 | 157 | /// exported constants 158 | #[cfg_attr(feature = "nightly", stable(feature="decode", since="0.3.0"))] 159 | pub const IDENT_BYTES: u16 = 0x5ECD; 160 | #[cfg_attr(feature = "nightly", stable(feature="decode", since="0.3.0"))] 161 | pub const VERSION: u16 = 0x0000; 162 | 163 | /// block reserved for future opcodes 164 | const RESERVED_START: u8 = 0x1E; 165 | const RESERVED_LEN: u8 = 0x12; 166 | /// block reserved for typetags 167 | const CONST_START: u8 = 0xC1; 168 | const CONST_LEN: u8 = 0x0E; 169 | /// important bytecodes 170 | const BYTE_CONS: u8 = 0xC0; 171 | const BYTE_NIL: u8 = 0x00; 172 | 173 | #[cfg_attr(feature = "nightly", unstable(feature = "decode"))] 174 | pub fn decode_program(source: &mut R) -> Result, String> 175 | where R: Read 176 | { 177 | let mut decoder = Decoder::new(source); 178 | decoder 179 | .check_ident_bytes() 180 | .map(|_| decoder.check_version() 181 | .map_err(|why| warn!("{}", why) ) 182 | ) 183 | .map(|_| decoder.collect::>() ) 184 | } 185 | 186 | #[cfg_attr(feature = "nightly", stable(feature="decode", since="0.2.6"))] 187 | pub struct Decoder<'a, R: 'a> { 188 | source: &'a mut R, 189 | num_read: usize 190 | } 191 | 192 | #[cfg_attr(feature = "nightly", stable(feature="decode", since="0.2.6"))] 193 | fn decode_inst(byte: &u8) -> Result { 194 | match *byte { 195 | BYTE_NIL => Ok(NIL), 196 | 0x01 => Ok(LD), 197 | 0x02 => Ok(LDF), 198 | 0x03 => Ok(AP), 199 | 0x04 => Ok(APCC), 200 | 0x05 => Ok(JOIN), 201 | 0x06 => Ok(RAP), 202 | 0x07 => Ok(RET), 203 | 0x08 => Ok(DUM), 204 | 0x09 => Ok(SEL), 205 | 0x0A => Ok(ADD), 206 | 0x0B => Ok(SUB), 207 | 0x0C => Ok(MUL), 208 | 0x0D => Ok(DIV), 209 | 0x0E => Ok(MOD), 210 | 0x0F => Ok(FDIV), 211 | 0x10 => Ok(EQ), 212 | 0x11 => Ok(GT), 213 | 0x12 => Ok(GTE), 214 | 0x13 => Ok(LT), 215 | 0x14 => Ok(LTE), 216 | 0x15 => Ok(ATOM), 217 | 0x16 => Ok(NULL), 218 | 0x17 => Ok(READC), 219 | 0x18 => Ok(WRITEC), 220 | 0x19 => Ok(CONS), 221 | 0x1A => Ok(CAR), 222 | 0x1B => Ok(CDR), 223 | 0x1C => Ok(LDC), 224 | 0x1D => Ok(STOP), 225 | b if b >= RESERVED_START && 226 | b <= (RESERVED_START + RESERVED_LEN) => 227 | Err(format!("Unimplemented: reserved byte {:#X}", b)), 228 | b if b > (RESERVED_START + RESERVED_LEN) => 229 | Err(String::from("byte too high")), 230 | _ => unreachable!() // Should require an act of God. 231 | } 232 | } 233 | 234 | 235 | #[cfg_attr(feature = "nightly", stable(feature="decode", since="0.2.6"))] 236 | impl<'a, R> Decoder<'a, R> 237 | where R: Read 238 | { 239 | #[cfg_attr(feature = "nightly", stable(feature="decode", since="0.3.0"))] 240 | pub fn check_ident_bytes(&mut self) -> Result<(), String> { 241 | self.source 242 | .read_u16::() 243 | .map_err(|why| String::from(why.description())) 244 | .and_then(|ident| { 245 | self.num_read += 2; 246 | match ident { 247 | IDENT_BYTES => Ok(()), 248 | other_bytes => Err( 249 | format!("invalid identifying bytes {:#06x}", other_bytes) 250 | ) 251 | } 252 | }) 253 | } 254 | 255 | #[cfg_attr(feature = "nightly", stable(feature="decode", since="0.3.0"))] 256 | pub fn check_version(&mut self) -> Result<(), String> { 257 | self.source 258 | .read_u16::() 259 | .map_err(|why| String::from(why.description())) 260 | .and_then(|version| { 261 | self.num_read += 2; 262 | match version { 263 | VERSION => Ok(()), 264 | bytes => Err( // I expect this will generate a warning 265 | // at the call site... 266 | format!("mismatched version {}, expected {}", 267 | bytes, version) 268 | ) 269 | } 270 | }) 271 | } 272 | 273 | #[cfg_attr(feature = "nightly", stable(feature="decode", since="0.2.6"))] 274 | pub fn new(src: &'a mut R) -> Decoder<'a, R> { 275 | Decoder { 276 | source: src, 277 | num_read: 0 278 | } 279 | } 280 | 281 | /// Returns the number of bytes read by the decoder 282 | #[cfg_attr(feature = "nightly", stable(feature="decode", since="0.2.6"))] 283 | pub fn num_read(&self) -> usize { 284 | self.num_read 285 | } 286 | 287 | #[cfg_attr(feature = "nightly", stable(feature="decode", since="0.2.6"))] 288 | fn decode_const(&mut self, byte: &u8) -> Result { 289 | match *byte & 0x0F { // extract the type tag 290 | 1 => { 291 | self.num_read += 8; // this should be more FP i guess 292 | self.source 293 | .read_u64::() 294 | .map(Atom::UInt) 295 | .map_err(|why| String::from(why.description())) 296 | }, 297 | 2 => { 298 | self.num_read += 8; 299 | self.source 300 | .read_i64::() 301 | .map(Atom::SInt) 302 | .map_err(|why| String::from(why.description())) 303 | }, 304 | 3 => { 305 | self.num_read += 4; 306 | self.source 307 | .read_u32::() 308 | .map_err( |why | String::from(why.description())) 309 | .and_then(|byte| 310 | char::from_u32(byte) 311 | .ok_or(String::from("Could not read character.")) 312 | ) 313 | .map(Atom::Char) 314 | }, 315 | 4 => { 316 | self.num_read += 8; 317 | self.source 318 | .read_f64::() 319 | .map(Atom::Float) 320 | .map_err(|why| String::from(why.description())) 321 | }, 322 | _ => unimplemented!() 323 | } 324 | } 325 | // Decodes a CONS cell 326 | #[cfg_attr(feature = "nightly", stable(feature="decode", since="0.2.6"))] 327 | fn decode_cons(&mut self) -> Result>>, String> { 328 | self.next_cell() 329 | .and_then(|car| 330 | car.ok_or(String::from("EOF while decoding CONS cell")) 331 | ) 332 | .map(|car| { 333 | debug!("Decoded {:?}, {} bytes read", car, self.num_read); 334 | car 335 | }) 336 | .and_then(|car| { 337 | let mut buf = [0;1]; 338 | try!(self.source.read(&mut buf) // try to get next byte 339 | .map_err(|why| String::from(why.description()))); 340 | self.num_read += 1; 341 | match buf[0] { 342 | BYTE_CONS => 343 | self.decode_cons() 344 | .and_then(|cdr| cdr.ok_or( 345 | String::from("EOF while decoding CONS")) ) 346 | .map( |cdr| (car, cdr) ), 347 | BYTE_NIL => Ok((car, Box::new(Nil))), 348 | b => Err( 349 | format!("Unexpected byte {:#02x} while decoding CONS", b) 350 | ) 351 | } 352 | }) 353 | .map(|(car, cdr)| Some(Box::new( Cons(car, cdr)) )) 354 | } 355 | 356 | /// Decodes the next cell in the source 357 | #[cfg_attr(feature = "nightly", stable(feature="decode", since="0.2.6"))] 358 | pub fn next_cell(&mut self) -> Result,String> { 359 | let mut buf = [0;1]; 360 | match self.source.read(&mut buf) { 361 | Ok(1) => { // a byte was read 362 | self.num_read += 1; 363 | debug!("Read {:#X}, {} bytes read", buf[0], self.num_read); 364 | match buf[0] { 365 | b if b < 0x30 => decode_inst(&b) 366 | .map(SVMCell::InstCell) 367 | .map(Some), 368 | b if b >= CONST_START && 369 | b < (CONST_START + CONST_LEN) => 370 | self.decode_const(&b) 371 | .map(SVMCell::AtomCell) 372 | .map(Some), 373 | BYTE_CONS => self.decode_cons() 374 | .map(|cell| 375 | cell.map(SVMCell::ListCell) 376 | ), 377 | b => Err(format!("Unsupported byte {:#02x}", b)) 378 | } 379 | }, 380 | Ok(0) => Ok(None), // we're out of bytes - EOF 381 | Ok(_) => unreachable!(), // 382 | Err(why) => Err(String::from(why.description())) 383 | } 384 | } 385 | 386 | } 387 | 388 | #[cfg_attr(feature = "nightly", stable(feature="decode", since="0.2.6"))] 389 | impl<'a, R> Iterator for Decoder<'a, R> where R: Read { 390 | #[cfg_attr(feature = "nightly", stable(feature="decode", since="0.2.6"))] 391 | type Item = SVMCell; 392 | 393 | #[cfg_attr(feature = "nightly", stable(feature="decode", since="0.2.6"))] 394 | fn next(&mut self) -> Option { 395 | self.next_cell() 396 | .unwrap() 397 | } 398 | } 399 | #[cfg_attr(feature = "nightly", stable(feature="decode", since="0.2.6"))] 400 | impl<'a, R> fmt::Debug for Decoder<'a, R> where R: fmt::Debug { 401 | #[cfg_attr(feature = "nightly", stable(feature="decode", since="0.2.6"))] 402 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 403 | write!(f, "Decoding from: {:?}, {} bytes read", 404 | self.source, 405 | self.num_read 406 | ) 407 | } 408 | 409 | } 410 | 411 | #[cfg_attr(feature = "nightly", stable(feature="encode", since="0.2.6"))] 412 | pub trait Encode { 413 | #[cfg_attr(feature = "nightly", stable(feature="encode", since="0.2.6"))] 414 | fn emit(&self) -> Vec; 415 | } 416 | 417 | #[cfg_attr(feature = "nightly", stable(feature="encode", since="0.2.6"))] 418 | impl Encode for SVMCell { 419 | #[cfg_attr(feature = "nightly", stable(feature="encode", since="0.2.6"))] 420 | fn emit(&self) -> Vec { 421 | match *self { 422 | AtomCell(ref atom) => atom.emit(), 423 | InstCell(ref inst) => inst.emit(), 424 | ListCell(ref list) => (*list).emit() 425 | } 426 | } 427 | } 428 | 429 | #[cfg_attr(feature = "nightly", stable(feature="encode", since="0.2.6"))] 430 | impl Encode for Atom { 431 | #[cfg_attr(feature = "nightly", stable(feature="encode", since="0.2.6"))] 432 | fn emit(&self) -> Vec { 433 | match *self { 434 | UInt(value) => { 435 | let mut buf = vec![0xC1]; 436 | buf.write_u64::(value) 437 | .unwrap(); 438 | buf 439 | }, 440 | SInt(value) => { 441 | let mut buf = vec![0xC2]; 442 | buf.write_i64::(value) 443 | .unwrap(); 444 | buf 445 | }, 446 | Char(value) => { 447 | let mut buf = vec![0xC3]; 448 | buf.write_u32::(value as u32) 449 | .unwrap(); 450 | buf 451 | }, 452 | Float(value) => { 453 | let mut buf = vec![0xC4]; 454 | buf.write_f64::(value) 455 | .unwrap(); 456 | buf 457 | } 458 | } 459 | } 460 | } 461 | 462 | #[cfg_attr(feature = "nightly", stable(feature="encode", since="0.2.6"))] 463 | impl Encode for Inst { 464 | #[cfg_attr(feature = "nightly", stable(feature="encode", since="0.2.6"))] 465 | fn emit(&self) -> Vec { 466 | match *self { 467 | NIL => vec![BYTE_NIL], 468 | LD => vec![0x01], 469 | LDF => vec![0x02], 470 | AP => vec![0x03], 471 | APCC => vec![0x04], 472 | JOIN => vec![0x05], 473 | RAP => vec![0x06], 474 | RET => vec![0x07], 475 | DUM => vec![0x08], 476 | SEL => vec![0x09], 477 | ADD => vec![0x0A], 478 | SUB => vec![0x0B], 479 | MUL => vec![0x0C], 480 | DIV => vec![0x0D], 481 | MOD => vec![0x0E], 482 | FDIV => vec![0x0F], 483 | EQ => vec![0x10], 484 | GT => vec![0x11], 485 | GTE => vec![0x12], 486 | LT => vec![0x13], 487 | LTE => vec![0x14], 488 | ATOM => vec![0x15], 489 | NULL => vec![0x16], 490 | READC => vec![0x17], 491 | WRITEC => vec![0x18], 492 | CONS => vec![0x19], 493 | CAR => vec![0x1A], 494 | CDR => vec![0x1B], 495 | LDC => vec![0x1C], 496 | STOP => vec![0x1D] 497 | } 498 | } 499 | } 500 | 501 | #[cfg_attr(feature = "nightly", stable(feature="encode", since="0.2.6"))] 502 | impl Encode for List where T: Encode { 503 | #[cfg_attr(feature = "nightly", stable(feature="encode", since="0.2.6"))] 504 | fn emit(&self) -> Vec { 505 | match *self { 506 | Cons(ref it, ref tail) => { 507 | let mut result = vec![BYTE_CONS]; 508 | push_all!(result, &it.emit()); 509 | push_all!(result, &(*tail.emit())); 510 | result 511 | }, 512 | Nil => vec![BYTE_NIL] 513 | } 514 | } 515 | } 516 | -------------------------------------------------------------------------------- /src/bytecode/tests.rs: -------------------------------------------------------------------------------- 1 | use super::{Encode,Decoder}; 2 | use ::cell::{Atom,Inst,SVMCell}; 3 | use ::cell::Atom::*; 4 | use ::cell::SVMCell::*; 5 | use ::Inst::*; 6 | use ::slist::List::{Cons,Nil}; 7 | 8 | use std::io::Cursor; 9 | 10 | use quickcheck::quickcheck; 11 | 12 | #[cfg(not(feature = "nightly"))] 13 | macro_rules! push_all { 14 | ( $vec:ident, $other:expr ) => { 15 | for item in $other { 16 | $vec.push(*item); 17 | } 18 | } 19 | } 20 | #[cfg(feature = "nightly")] 21 | macro_rules! push_all { 22 | ( $vec:ident, $other:expr ) => { $vec.push_all($other); } 23 | } 24 | 25 | macro_rules! impl_encode_test { 26 | ($name:ident, $it:expr) => { 27 | #[test] 28 | fn $name () { 29 | let cell = $it; 30 | let encoded = cell.emit(); 31 | let decoded = Decoder::new(&mut Cursor::new(encoded)).next_cell(); 32 | assert_eq!(Ok(Some(cell)), decoded) 33 | } 34 | 35 | } 36 | } 37 | 38 | #[test] 39 | fn prop_encode_float () { 40 | fn prop (x: f64) -> bool { 41 | let cell = SVMCell::AtomCell(Atom::Float(x)); 42 | let encoded = cell.emit(); 43 | let decoded = Decoder::new(&mut Cursor::new(encoded)).next_cell(); 44 | decoded == Ok(Some(cell)) 45 | } 46 | quickcheck(prop as fn(f64) -> bool); 47 | } 48 | 49 | #[test] 50 | fn prop_encode_uint () { 51 | fn prop (x: u64) -> bool { 52 | let cell = SVMCell::AtomCell(Atom::UInt(x)); 53 | let encoded = cell.emit(); 54 | let decoded = Decoder::new(&mut Cursor::new(encoded)).next_cell(); 55 | decoded == Ok(Some(cell)) 56 | } 57 | quickcheck(prop as fn(u64) -> bool); 58 | } 59 | 60 | #[test] 61 | fn prop_encode_sint () { 62 | fn prop (x: i64) -> bool { 63 | let cell = SVMCell::AtomCell(Atom::SInt(x)); 64 | let encoded = cell.emit(); 65 | let decoded = Decoder::new(&mut Cursor::new(encoded)).next_cell(); 66 | decoded == Ok(Some(cell)) 67 | } 68 | quickcheck(prop as fn(i64) -> bool); 69 | } 70 | 71 | #[test] 72 | fn prop_encode_char () { 73 | fn prop (x: char) -> bool { 74 | let cell = SVMCell::AtomCell(Atom::Char(x)); 75 | let encoded = cell.emit(); 76 | let decoded = Decoder::new(&mut Cursor::new(encoded)).next_cell(); 77 | decoded == Ok(Some(cell)) 78 | } 79 | quickcheck(prop as fn(char) -> bool); 80 | } 81 | #[test] 82 | fn test_decode_program () { 83 | let cell = list_cell![ 84 | InstCell(LDC), AtomCell(SInt(1)), InstCell(LDC), AtomCell(SInt(1)), 85 | InstCell(SUB), 86 | InstCell(LDC), AtomCell(SInt(0)), 87 | InstCell(EQ), 88 | InstCell(SEL), 89 | list_cell![ InstCell(LDC), AtomCell(SInt(1)), InstCell(JOIN) ], 90 | list_cell![ InstCell(NIL), InstCell(JOIN) ] 91 | ]; 92 | let mut encoded = vec![0x5e, 0xcd, 0x00, 0x00]; 93 | push_all!(encoded,&cell.emit()); 94 | assert_eq!( 95 | Ok(list!(cell)), 96 | super::decode_program(&mut Cursor::new(encoded)) 97 | ) 98 | } 99 | 100 | #[test] 101 | fn test_decode_program_no_ident () { 102 | let cell = list_cell![ 103 | InstCell(LDC), AtomCell(SInt(1)), InstCell(LDC), AtomCell(SInt(1)), 104 | InstCell(SUB), 105 | InstCell(LDC), AtomCell(SInt(0)), 106 | InstCell(EQ), 107 | InstCell(SEL), 108 | list_cell![ InstCell(LDC), AtomCell(SInt(1)), InstCell(JOIN) ], 109 | list_cell![ InstCell(NIL), InstCell(JOIN) ] 110 | ]; 111 | let mut encoded = vec![0x00, 0x00]; 112 | push_all!(encoded,&cell.emit()); 113 | assert_eq!( 114 | Err(String::from("invalid identifying bytes 0x0000")), 115 | super::decode_program(&mut Cursor::new(encoded)) 116 | ) 117 | } 118 | 119 | #[test] 120 | fn test_decode_program_wrong_version () { 121 | let cell = list_cell![ 122 | InstCell(LDC), AtomCell(SInt(1)), InstCell(LDC), AtomCell(SInt(1)), 123 | InstCell(SUB), 124 | InstCell(LDC), AtomCell(SInt(0)), 125 | InstCell(EQ), 126 | InstCell(SEL), 127 | list_cell![ InstCell(LDC), AtomCell(SInt(1)), InstCell(JOIN) ], 128 | list_cell![ InstCell(NIL), InstCell(JOIN) ] 129 | ]; 130 | let mut encoded = vec![0x5e, 0xcd, 0x10, 0x00]; 131 | push_all!(encoded,&cell.emit()); 132 | assert_eq!( 133 | Ok(list!(cell)), 134 | super::decode_program(&mut Cursor::new(encoded)) 135 | ) 136 | } 137 | 138 | impl_encode_test!( 139 | test_encode_uint_zero, 140 | SVMCell::AtomCell(Atom::UInt(0)) 141 | ); 142 | impl_encode_test!( 143 | test_encode_inst_nil, 144 | SVMCell::InstCell(Inst::NIL) 145 | ); 146 | impl_encode_test!( 147 | test_encode_inst_ld, 148 | SVMCell::InstCell(Inst::LD) 149 | ); 150 | impl_encode_test!( 151 | test_encode_inst_ldf, 152 | SVMCell::InstCell(Inst::LDF) 153 | ); 154 | impl_encode_test!( 155 | test_encode_inst_ap, 156 | SVMCell::InstCell(Inst::AP) 157 | ); 158 | impl_encode_test!( 159 | test_encode_inst_apcc, 160 | SVMCell::InstCell(Inst::APCC) 161 | ); 162 | impl_encode_test!( 163 | test_encode_inst_join, 164 | SVMCell::InstCell(Inst::JOIN) 165 | ); 166 | impl_encode_test!( 167 | test_encode_inst_rap, 168 | SVMCell::InstCell(Inst::RAP) 169 | ); 170 | impl_encode_test!( 171 | test_encode_inst_ret, 172 | SVMCell::InstCell(Inst::RET) 173 | ); 174 | impl_encode_test!( 175 | test_encode_inst_dum, 176 | SVMCell::InstCell(Inst::DUM) 177 | ); 178 | impl_encode_test!( 179 | test_encode_inst_sel, 180 | SVMCell::InstCell(Inst::SEL) 181 | ); 182 | impl_encode_test!( 183 | test_encode_inst_add, 184 | SVMCell::InstCell(Inst::ADD) 185 | ); 186 | impl_encode_test!( 187 | test_encode_inst_sub, 188 | SVMCell::InstCell(Inst::SUB) 189 | ); 190 | impl_encode_test!( 191 | test_encode_inst_mul, 192 | SVMCell::InstCell(Inst::MUL) 193 | ); 194 | impl_encode_test!( 195 | test_encode_inst_div, 196 | SVMCell::InstCell(Inst::DIV) 197 | ); 198 | impl_encode_test!( 199 | test_encode_inst_mod, 200 | SVMCell::InstCell(Inst::MOD) 201 | ); 202 | impl_encode_test!( 203 | test_encode_inst_fdiv, 204 | SVMCell::InstCell(Inst::FDIV) 205 | ); 206 | impl_encode_test!( 207 | test_encode_inst_eq, 208 | SVMCell::InstCell(Inst::EQ) 209 | ); 210 | impl_encode_test!( 211 | test_encode_inst_gt, 212 | SVMCell::InstCell(Inst::GT) 213 | ); 214 | impl_encode_test!( 215 | test_encode_inst_gte, 216 | SVMCell::InstCell(Inst::GTE) 217 | ); 218 | impl_encode_test!( 219 | test_encode_inst_lt, 220 | SVMCell::InstCell(Inst::LT) 221 | ); 222 | impl_encode_test!( 223 | test_encode_inst_lte, 224 | SVMCell::InstCell(Inst::LTE) 225 | ); 226 | impl_encode_test!( 227 | test_encode_inst_atom, 228 | SVMCell::InstCell(Inst::ATOM) 229 | ); 230 | impl_encode_test!( 231 | test_encode_inst_null, 232 | SVMCell::InstCell(Inst::NULL) 233 | ); 234 | impl_encode_test!( 235 | test_encode_inst_cons, 236 | SVMCell::InstCell(Inst::CONS) 237 | ); 238 | impl_encode_test!( 239 | test_encode_inst_car, 240 | SVMCell::InstCell(Inst::CAR) 241 | ); 242 | impl_encode_test!( 243 | test_encode_inst_cdr, 244 | SVMCell::InstCell(Inst::CDR) 245 | ); 246 | impl_encode_test!( 247 | test_encode_inst_ldc, 248 | SVMCell::InstCell(Inst::LDC) 249 | ); 250 | impl_encode_test!( 251 | test_encode_inst_stop, 252 | SVMCell::InstCell(Inst::STOP) 253 | ); 254 | impl_encode_test!( 255 | test_encode_simple_program, 256 | list_cell![ 257 | InstCell(LDC), 258 | AtomCell(SInt(1)) 259 | ] 260 | ); 261 | 262 | impl_encode_test!( 263 | // test for encode & decode of the list creation 264 | // program from the integration tests 265 | test_encode_program_list_creation, 266 | list_cell![ 267 | InstCell(NIL), 268 | InstCell(LDC), AtomCell(SInt(20)), InstCell(CONS), 269 | InstCell(LDC), AtomCell(SInt(10)), InstCell(CONS) 270 | ] 271 | ); 272 | 273 | impl_encode_test!( 274 | // test for encode & decode of the list CAR 275 | // program from the integration tests 276 | test_encode_program_car, 277 | list_cell![ 278 | InstCell(NIL), 279 | InstCell(LDC), AtomCell(SInt(10)), InstCell(CONS), 280 | InstCell(LDC), AtomCell(SInt(20)), InstCell(CONS), 281 | InstCell(CAR) 282 | ] 283 | ); 284 | 285 | impl_encode_test!( 286 | // test for encode & decode of the list CDR 287 | // program from the integration tests 288 | test_encode_program_cdr, 289 | list_cell![ 290 | InstCell(NIL), 291 | InstCell(LDC), AtomCell(SInt(10)), InstCell(CONS), 292 | InstCell(LDC), AtomCell(SInt(20)), InstCell(CONS), 293 | InstCell(CDR) 294 | ] 295 | ); 296 | 297 | impl_encode_test!( 298 | // test for encode & decode of the simple add 299 | // program from the integration tests 300 | test_encode_program_add, 301 | list_cell![ 302 | InstCell(LDC), AtomCell(SInt(10)), 303 | InstCell(LDC), AtomCell(SInt(10)), 304 | InstCell(ADD) 305 | ] 306 | ); 307 | 308 | impl_encode_test!( 309 | // test for encode & decode of the nested arithmetic 310 | // program from the integration tests 311 | test_encode_program_nested_arith, 312 | list_cell![ 313 | InstCell(LDC), AtomCell(SInt(5)), 314 | InstCell(LDC), AtomCell(SInt(5)), 315 | InstCell(ADD), 316 | InstCell(LDC), AtomCell(SInt(20)), 317 | InstCell(SUB) 318 | ] 319 | ); 320 | 321 | impl_encode_test!( 322 | // test for encode & decode of the first basic branching 323 | // program from the integration tests 324 | test_encode_program_basic_branching_1, 325 | list_cell![ 326 | InstCell(LDC), AtomCell(SInt(1)), InstCell(LDC), AtomCell(SInt(1)), 327 | InstCell(SUB), 328 | InstCell(LDC), AtomCell(SInt(0)), 329 | InstCell(EQ), 330 | InstCell(SEL), 331 | list_cell![ InstCell(LDC), AtomCell(SInt(1)), InstCell(JOIN) ], 332 | list_cell![ InstCell(NIL), InstCell(JOIN) ] 333 | ] 334 | ); 335 | 336 | impl_encode_test!( 337 | // test for encode & decode of the second basic branching 338 | // program from the integration tests 339 | test_encode_program_basic_branching_2, 340 | list_cell![ 341 | InstCell(NIL), InstCell(NULL), 342 | InstCell(SEL), 343 | list_cell![ InstCell(LDC), AtomCell(SInt(10)), InstCell(JOIN) ], 344 | list_cell![ InstCell(LDC), AtomCell(SInt(20)), InstCell(JOIN) ], 345 | InstCell(LDC), AtomCell(SInt(10)), 346 | InstCell(ADD) 347 | ] 348 | ); 349 | -------------------------------------------------------------------------------- /src/cell.rs: -------------------------------------------------------------------------------- 1 | pub use self::SVMCell::*; 2 | pub use self::Atom::*; 3 | 4 | use ::slist::List; 5 | 6 | use std::{fmt,ops}; 7 | 8 | #[macro_export] 9 | #[cfg_attr(feature = "nightly", unstable(feature = "list"))] 10 | macro_rules! list_cell { 11 | [ $first:expr, $($rest:expr),+ ] => { 12 | ListCell(Box::new( list!( $first, $( $rest),+ ) )) 13 | }; 14 | [ $e:expr ] => { 15 | ListCell(Box::new( list!($e) )) 16 | }; 17 | [] => { ListCell(Box::new(Nil)) }; 18 | 19 | } 20 | 21 | #[derive(PartialEq,Clone)] 22 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.1.0"))] 23 | pub enum SVMCell { 24 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.1.0"))] 25 | AtomCell(Atom), 26 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.1.0"))] 27 | ListCell(Box>), 28 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.1.0"))] 29 | InstCell(Inst) 30 | } 31 | 32 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.1.0"))] 33 | impl fmt::Display for SVMCell { 34 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 35 | write!(f, "{}", self) 36 | } 37 | } 38 | 39 | #[cfg_attr(feature = "nightly", stable(feature="debug", since="0.2.1"))] 40 | impl fmt::Debug for SVMCell { 41 | #[cfg_attr(feature = "nightly", stable(feature="debug", since="0.2.1"))] 42 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 43 | match self { 44 | &AtomCell(atom) => write!(f, "{:?}", atom), 45 | &ListCell(ref list) => write!(f, "{:?}", list), 46 | &InstCell(inst) => write!(f, "{:?}", inst) 47 | } 48 | } 49 | } 50 | 51 | /// SVM atom types. 52 | /// 53 | /// A VM atom can be either an unsigned int, signed int, float, or char. 54 | #[derive(PartialEq,PartialOrd,Copy,Clone)] 55 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.1.0"))] 56 | pub enum Atom { 57 | /// Unsigned integer atom (machine 64) 58 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.3.0"))] 59 | UInt(u64), 60 | /// Signed integer atom (machine 64) 61 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.3.0"))] 62 | SInt(i64), 63 | /// Floating point number atom (64-bits) 64 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.1.0"))] 65 | Float(f64), 66 | /// UTF-8 character atom 67 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.1.0"))] 68 | Char(char) 69 | } 70 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.1.0"))] 71 | impl fmt::Display for Atom { 72 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.1.0"))] 73 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 74 | match self { 75 | &Atom::UInt(value) => write!(f, "{}", value), 76 | &Atom::SInt(value) => write!(f, "{}", value), 77 | &Atom::Float(value) => write!(f, "{}", value), 78 | &Atom::Char(value) => write!(f, "'{}'", value), 79 | } 80 | } 81 | } 82 | 83 | #[cfg_attr(feature = "nightly", stable(feature="debug", since="0.2.1"))] 84 | impl fmt::Debug for Atom { 85 | #[cfg_attr(feature = "nightly", stable(feature="debug", since="0.2.1"))] 86 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 87 | match self { 88 | &Atom::UInt(value) => write!(f, "{:?}u", value), 89 | &Atom::SInt(value) => write!(f, "{:?}", value), 90 | &Atom::Float(value) => write!(f, "{:?}f", value), 91 | &Atom::Char(value) => write!(f, "'{}'", value), 92 | } 93 | } 94 | } 95 | 96 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.3.0"))] 97 | impl ops::Add for Atom { 98 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.3.0"))] 99 | type Output = Atom; 100 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.3.0"))] 101 | fn add(self, other: Atom) -> Atom { 102 | match (self, other) { 103 | // same type: no coercion 104 | (SInt(a), SInt(b)) => SInt(a + b), 105 | (UInt(a), UInt(b)) => UInt(a + b), 106 | (Float(a), Float(b)) => Float(a + b), 107 | (Char(a), Char(b)) => Char((a as u8 + b as u8) as char), 108 | // float + int: coerce to float 109 | (Float(a), SInt(b)) => Float(a + b as f64), 110 | (Float(a), UInt(b)) => Float(a + b as f64), 111 | (SInt(a), Float(b)) => Float(a as f64 + b), 112 | (UInt(a), Float(b)) => Float(a as f64 + b), 113 | // uint + sint: coerce to sint 114 | (UInt(a), SInt(b)) => SInt(a as i64 + b), 115 | (SInt(a), UInt(b)) => SInt(a + b as i64), 116 | // char + any: coerce to char 117 | // because of the supported operations on Ru64t chars, 118 | // everything has to be cast to u8 (byte) to allow 119 | // arithmetic ops and then cast back to char. 120 | (Char(a), UInt(b)) => Char((a as u8 + b as u8) as char), 121 | (Char(a), SInt(b)) => Char((a as u8 + b as u8) as char), 122 | (Char(a), Float(b)) => Char((a as u8 + b as u8) as char), 123 | (UInt(a), Char(b)) => Char((a as u8 + b as u8) as char), 124 | (SInt(a), Char(b)) => Char((a as u8 + b as u8) as char), 125 | (Float(a), Char(b)) => Char((a as u8 + b as u8) as char) 126 | } 127 | } 128 | 129 | } 130 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.3.0"))] 131 | impl ops::Sub for Atom { 132 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.3.0"))] 133 | type Output = Atom; 134 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.3.0"))] 135 | fn sub(self, other: Atom) -> Atom { 136 | match (self, other) { 137 | // same type: no coercion 138 | (SInt(a), SInt(b)) => SInt(a - b), 139 | (UInt(a), UInt(b)) => UInt(a - b), 140 | (Float(a), Float(b)) => Float(a - b), 141 | (Char(a), Char(b)) => Char((a as u8 - b as u8) as char), 142 | // float + int: coerce to float 143 | (Float(a), SInt(b)) => Float(a - b as f64), 144 | (Float(a), UInt(b)) => Float(a - b as f64), 145 | (SInt(a), Float(b)) => Float(a as f64 - b), 146 | (UInt(a), Float(b)) => Float(a as f64 - b), 147 | // uint + sint: coerce to sint 148 | (UInt(a), SInt(b)) => SInt(a as i64 - b), 149 | (SInt(a), UInt(b)) => SInt(a - b as i64), 150 | // char + any: coerce to char 151 | (Char(a), UInt(b)) => Char((a as u8 - b as u8) as char), 152 | (Char(a), SInt(b)) => Char((a as u8 - b as u8) as char), 153 | (Char(a), Float(b)) => Char((a as u8 - b as u8) as char), 154 | (UInt(a), Char(b)) => Char((a as u8 - b as u8) as char), 155 | (SInt(a), Char(b)) => Char((a as u8 - b as u8) as char), 156 | (Float(a), Char(b)) => Char((a as u8 - b as u8) as char) 157 | } 158 | } 159 | 160 | } 161 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.3.0"))] 162 | impl ops::Div for Atom { 163 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.3.0"))] 164 | type Output = Atom; 165 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.3.0"))] 166 | fn div(self, other: Atom) -> Atom { 167 | match (self, other) { 168 | // same type: no coercion 169 | (SInt(a), SInt(b)) => SInt(a / b), 170 | (UInt(a), UInt(b)) => UInt(a / b), 171 | (Float(a), Float(b)) => Float(a / b), 172 | (Char(a), Char(b)) => Char((a as u8 / b as u8) as char), 173 | // float + int: coerce to float 174 | (Float(a), SInt(b)) => Float(a / b as f64), 175 | (Float(a), UInt(b)) => Float(a / b as f64), 176 | (SInt(a), Float(b)) => Float(a as f64 / b), 177 | (UInt(a), Float(b)) => Float(a as f64 / b), 178 | // uint + sint: coerce to sint 179 | (UInt(a), SInt(b)) => SInt(a as i64 / b), 180 | (SInt(a), UInt(b)) => SInt(a / b as i64), 181 | // char + any: coerce to char 182 | (Char(a), UInt(b)) => Char((a as u8 / b as u8) as char), 183 | (Char(a), SInt(b)) => Char((a as u8 / b as u8) as char), 184 | (Char(a), Float(b)) => Char((a as u8 / b as u8) as char), 185 | (UInt(a), Char(b)) => Char((a as u8 / b as u8) as char), 186 | (SInt(a), Char(b)) => Char((a as u8 / b as u8) as char), 187 | (Float(a), Char(b)) => Char((a as u8 / b as u8) as char) 188 | } 189 | } 190 | 191 | } 192 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.3.0"))] 193 | impl ops::Mul for Atom { 194 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.3.0"))] 195 | type Output = Atom; 196 | 197 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.3.0"))] 198 | fn mul(self, other: Atom) -> Atom { 199 | match (self, other) { 200 | // same type: no coercion 201 | (SInt(a), SInt(b)) => SInt(a * b), 202 | (UInt(a), UInt(b)) => UInt(a * b), 203 | (Float(a), Float(b)) => Float(a * b), 204 | (Char(a), Char(b)) => Char((a as u8 * b as u8) as char), 205 | // float + int: coerce to float 206 | (Float(a), SInt(b)) => Float(a * b as f64), 207 | (Float(a), UInt(b)) => Float(a * b as f64), 208 | (SInt(a), Float(b)) => Float(a as f64* b), 209 | (UInt(a), Float(b)) => Float(a as f64* b), 210 | // uint + sint: coerce to sint 211 | (UInt(a), SInt(b)) => SInt(a as i64 * b), 212 | (SInt(a), UInt(b)) => SInt(a * b as i64), 213 | // char + any: coerce to char 214 | (Char(a), UInt(b)) => Char((a as u8 * b as u8) as char), 215 | (Char(a), SInt(b)) => Char((a as u8 * b as u8) as char), 216 | (Char(a), Float(b)) => Char((a as u8 * b as u8) as char), 217 | (UInt(a), Char(b)) => Char((a as u8 * b as u8) as char), 218 | (SInt(a), Char(b)) => Char((a as u8 * b as u8) as char), 219 | (Float(a), Char(b)) => Char((a as u8 * b as u8) as char) 220 | } 221 | } 222 | 223 | } 224 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.3.0"))] 225 | impl ops::Rem for Atom { 226 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.3.0"))] 227 | type Output = Atom; 228 | 229 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.3.0"))] 230 | fn rem(self, other: Atom) -> Atom { 231 | match (self, other) { 232 | // same type: no coercion 233 | (SInt(a), SInt(b)) => SInt(a % b), 234 | (UInt(a), UInt(b)) => UInt(a % b), 235 | (Float(a), Float(b)) => Float(a % b), 236 | (Char(a), Char(b)) => Char((a as u8 % b as u8) as char), 237 | // float + int: coerce to float 238 | (Float(a), SInt(b)) => Float(a % b as f64), 239 | (Float(a), UInt(b)) => Float(a % b as f64), 240 | (SInt(a), Float(b)) => Float(a as f64 % b), 241 | (UInt(a), Float(b)) => Float(a as f64 % b), 242 | // uint + sint: coerce to sint 243 | (UInt(a), SInt(b)) => SInt(a as i64 % b), 244 | (SInt(a), UInt(b)) => SInt(a % b as i64), 245 | // char + any: coerce to char 246 | (Char(a), UInt(b)) => Char((a as u8 % b as u8) as char), 247 | (Char(a), SInt(b)) => Char((a as u8 % b as u8) as char), 248 | (Char(a), Float(b)) => Char((a as u8 % b as u8) as char), 249 | (UInt(a), Char(b)) => Char((a as u8 % b as u8) as char), 250 | (SInt(a), Char(b)) => Char((a as u8 % b as u8) as char), 251 | (Float(a), Char(b)) => Char((a as u8 % b as u8) as char) 252 | } 253 | } 254 | 255 | } 256 | 257 | /// SVM instruction types. 258 | /// 259 | /// Each SVM instruction will be described using operational 260 | /// semantics through the use of the following notation: 261 | /// 262 | /// + a state is written `(s, e, c, d)` 263 | /// + `(x.y)` is `Cons(x, y)`. The empty list is `nil`. 264 | /// + each instruction is described as a state transition `(s, e, c, d) → (s´, e´, c´, d´)` 265 | #[derive(Debug,Copy,Clone,PartialEq)] 266 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.1.0"))] 267 | pub enum Inst { 268 | /// `nil` 269 | /// 270 | /// Pushes an empty list (nil) onto the stack. 271 | /// 272 | /// __Operational semantics__: `(s, e, (NIL.c), d) → ( (nil.s), e, c, d )` 273 | /// 274 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.1.0"))] 275 | NIL, 276 | /// `ldc`: `L`oa`d` `C`onstant. Loads a constant (atom) 277 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.1.0"))] 278 | LDC, 279 | /// `ld`: `L`oa`d`. Pushes a variable onto the stack. 280 | /// 281 | /// The variable is indicated by the argument, a pair. 282 | /// The pair's `car` specifies the level, the `cdr` the position. 283 | /// So `(1 . 3)` gives the current function's (level 1) third 284 | /// parameter. 285 | /// 286 | /// __Operational semantics__: `(s, e, LDC.v.c, d) → (v.s, e, c, d)` 287 | /// 288 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.1.0"))] 289 | LD, 290 | /// `ldf`: `L`oa`d` `F`unction. 291 | /// 292 | /// Takes one list argument representing a function and constructs 293 | /// a closure (a pair containing the function and the current 294 | /// environment) and pushes that onto the stack. 295 | /// 296 | /// _Operational semantics_: `(s, e, (LDF f.c), d) → ( ([f e].s), e, c, d)` 297 | /// 298 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.2.4"))] 299 | LDF, 300 | /// `join` 301 | /// 302 | /// Pops a list reference from the dump and makes thi64 the new value 303 | /// of `C`. This instruction occurs at the end of both alternatives of 304 | /// a `sel`. 305 | /// 306 | /// __Operational semantics__: `(s, e, JOIN.c, c´.d) → (s, e, c´, d)` 307 | /// 308 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.1.0"))] 309 | JOIN, 310 | /// `ap`: `Ap`ply. 311 | /// 312 | /// Pops a closure and a list of parameter values from the stack. 313 | /// The closure is applied to the parameters by installing its 314 | /// environment as the current one, pushing the parameter list 315 | /// in front of that, clearing the stack, and setting `c` to the 316 | /// closure's function pointer. The previous values of `s`, `e`, 317 | /// and the next value of `c` are saved on the dump. 318 | /// 319 | /// __Operational semantics__: `(([f e´] v.s), e, (AP.c), d) → (nil, (v.e&prime), f, (s e c.d))` 320 | /// 321 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.1.0"))] 322 | AP, 323 | /// `ret`: `Ret`urn. 324 | /// 325 | /// Pops one return value from the stack, restores 326 | /// `$s`, `$e`, and `$c` from the dump, and pushes 327 | /// the return value onto the now-current stack. 328 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.1.0"))] 329 | RET, 330 | /// `dum`: `Dum`my. 331 | /// 332 | /// Pops a dummy environment (an empty list) onto the `$e` stack. 333 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.1.0"))] 334 | DUM, 335 | /// `rap`: `R`ecursive `Ap`ply. 336 | /// Works like `ap`, only that it replaces an occurrence of a 337 | /// dummy environment with the current one, thus making recursive 338 | /// functions possible. 339 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.1.0"))] 340 | RAP, 341 | /// `sel`: `Sel`ect branch 342 | /// 343 | /// Expects two list arguments on the control stack, and pops a value 344 | /// from the stack. The first list is executed if the popped value 345 | /// was non-nil, the second list otherwise. Before one of these list 346 | /// pointers is made the new `$c`, a pointer to the instruction 347 | /// following `sel` is saved on the dump. 348 | /// 349 | /// __Operational semantics__: `(v.s, e, SEL.true.false.c, d) → (s, e, (if v then true else false), c.d)` 350 | /// 351 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.1.0"))] 352 | SEL, 353 | /// `add` 354 | /// 355 | /// Pops two numbers off of the stack and adds them, pu64hing the 356 | /// result onto the stack. This will up-convert integers to floating 357 | /// point if necessary. 358 | /// 359 | /// TODO: figure out what happens when you try to add things that aren't 360 | /// numbers (maybe the compiler won't let this happen?). 361 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.1.0"))] 362 | ADD, 363 | /// `sub`: `Sub`tract 364 | /// 365 | /// Pops two numbers off of the stack and subtracts the first from the 366 | /// second, pu64hing the result onto the stack. This will up-convert 367 | /// integers to floating point if necessary. 368 | /// 369 | /// TODO: figure out what happens when you try to subtract things that 370 | /// aren't numbers (maybe the compiler won't let thi64 happen?). 371 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.1.0"))] 372 | SUB, 373 | /// `mul`: `Mul`tiply 374 | /// 375 | /// Pops two numbers off of the stack and multiplies them, pu64hing the 376 | /// result onto the stack. This will up-convert integers to floating 377 | /// point if necessary. 378 | /// 379 | /// TODO: figure out what happens when you try to multiply things that 380 | /// aren't numbers (maybe the compiler won't let thi64 happen?). 381 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.1.0"))] 382 | MUL, 383 | /// `div`: `Div`ide 384 | /// 385 | /// Pops two numbers off of the stack and divides the first by the second, 386 | /// pushing the result onto the stack. This performs integer divi64ion. 387 | /// 388 | /// TODO: figure out what happens when you try to divide things that 389 | /// aren't numbers (maybe the compiler won't let thi64 happen?). 390 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.1.0"))] 391 | DIV, 392 | /// `fdiv`: `F`loating-point `div`ide 393 | /// 394 | /// Pops two numbers off of the stack and divides the first by the second, 395 | /// pu64hing the result onto the stack. This performs float divi64ion. 396 | /// 397 | /// TODO: figure out what happens when you try to divide things that 398 | /// aren't numbers (maybe the compiler won't let this happen?). 399 | /// 400 | /// TODO: Not sure if there should be separate float and int divide words 401 | /// I guess the compiler can figure this out 402 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.1.0"))] 403 | FDIV, 404 | /// `mod`: `Mod`ulo 405 | /// 406 | /// Pops two numbers off of the stack and divides the first by the second, 407 | /// pushing the remainder onto the stack. 408 | /// 409 | /// TODO: figure out what happens when you try to modulo things that 410 | /// aren't numbers (maybe the compiler won't let this happen?). 411 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.1.0"))] 412 | MOD, 413 | /// `eq`: `Eq`uality of atoms 414 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.1.0"))] 415 | EQ, 416 | /// `gt`: `G`reater `t`han 417 | /// 418 | /// Pops two numbers on the stack and puts a 'true' on the stack 419 | /// if the first atom i64 greater than the other atom, false otherwi64e. 420 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.1.0"))] 421 | GT, 422 | /// `gte`: `G`reater `t`han or `e`qual 423 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.1.0"))] 424 | GTE, 425 | /// `lt`: `L`ess `t`han 426 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.1.0"))] 427 | LT, 428 | /// `lte`: `L`ess `t`han or `e`qual 429 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.1.0"))] 430 | LTE, 431 | /// `atom`: test if `atom` 432 | /// 433 | /// Pops an item from the stack and returns true if it's an atom, false 434 | /// otherwise. 435 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.1.0"))] 436 | ATOM, 437 | /// `car`: `C`ontents of `A`ddress `R`egister 438 | /// 439 | /// Pops a list from the stack and returns the list's `car` (head) 440 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.1.0"))] 441 | CAR, 442 | /// `cdr`: `C`ontents of `D`ecrement `R`egister 443 | /// 444 | /// Pops a list from the stack and returns the list's `cdr` (tail) 445 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.1.0"))] 446 | CDR, 447 | /// `cons`: `Cons`truct 448 | /// 449 | /// Pops an item and a list from the stack and returns the list, with 450 | /// the item prepended. 451 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.1.0"))] 452 | CONS, 453 | /// `null`: test if `null` 454 | /// 455 | /// Pops an item from the stack and returns true if it is `nil`, false 456 | /// otherwise. 457 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.1.0"))] 458 | NULL, 459 | /// `stop`: `stop` execution 460 | /// 461 | /// Terminates program execution. The `eval_program()` function will return 462 | /// the last state of the VM. 463 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.1.2"))] 464 | STOP, 465 | /// `readc`: `read` `c`haracter 466 | /// 467 | /// Reads a character from the machine's input stream and places it 468 | /// on top of the stack 469 | #[cfg_attr(feature = "nightly", stable(feature="vm_io", since="0.2.0"))] 470 | READC, 471 | /// `writec`: `write` `c`haracter 472 | /// 473 | /// Writes a character from the top of the stack to the machine's 474 | /// output stream. 475 | #[cfg_attr(feature = "nightly", stable(feature="vm_io", since="0.2.0"))] 476 | WRITEC, 477 | /// `apcc`: `ap`ply with `c`urrent `c`ontinuation 478 | /// 479 | /// Applies a closure and captures the continuation that can 480 | /// then be applied with `ap`. 481 | #[cfg_attr(feature = "nightly", unstable(feature="callcc"))] 482 | APCC, 483 | } 484 | 485 | #[cfg(test)] 486 | mod tests { 487 | use super::Atom; 488 | use super::Atom::*; 489 | #[test] 490 | fn test_atom_show () { 491 | let mut a: Atom; 492 | 493 | a = Char('a'); 494 | assert_eq!(format!("{}", a), "'a'"); 495 | 496 | a = UInt(1u64); 497 | assert_eq!(format!("{}", a), "1"); 498 | 499 | a = SInt(42i64); 500 | assert_eq!(format!("{}", a), "42"); 501 | 502 | a = SInt(-1i64); 503 | assert_eq!(format!("{}", a), "-1"); 504 | 505 | a = Float(5.55f64); 506 | assert_eq!(format!("{}", a), "5.55"); 507 | 508 | a = Float(1f64); 509 | assert_eq!(format!("{}", a), "1"); 510 | 511 | } 512 | } 513 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![crate_name = "seax_svm"] 2 | #![crate_type = "lib"] 3 | #![cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.1.2") )] 4 | #![cfg_attr(test, feature(test))] 5 | #![cfg_attr(feature = "nightly", feature(vec_push_all) )] 6 | #![cfg_attr(feature = "nightly", feature(staged_api) )] 7 | #![cfg_attr(feature = "nightly", staged_api)] 8 | #![feature(box_patterns)] 9 | 10 | 11 | #[cfg(test)] extern crate quickcheck; 12 | #[cfg(test)] extern crate test; 13 | 14 | #[macro_use] extern crate log; 15 | extern crate byteorder; 16 | 17 | /// Singly-linked list and stack implementations. 18 | /// 19 | /// `List` is a singly-linked `cons` list. 20 | /// `Stack` is a trait providing stack operations(`push()`, `pop()`, and 21 | /// `peek()`), and an implementation for `List`. 22 | #[macro_use] 23 | #[cfg_attr(feature = "nightly", stable(feature="list", since="0.1.0"))] 24 | pub mod slist; 25 | 26 | /// SVM cell types. 27 | /// 28 | /// A cell in the VM can be either an atom (single item, either unsigned 29 | /// int, signed int, float, or string), a pointer to a list cell, or an 30 | /// instruction. 31 | #[macro_use] 32 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.1.2"))] 33 | pub mod cell; 34 | 35 | #[cfg_attr(feature = "nightly", unstable(feature="bytecode"))] 36 | pub mod bytecode; 37 | 38 | #[cfg(test)] 39 | mod tests; 40 | 41 | /// Contains the Seax Virtual Machine (SVM) and miscellaneous 42 | /// support code. 43 | 44 | // Reexports 45 | pub use self::slist::{List, Stack}; 46 | pub use self::slist::List::{Cons,Nil}; 47 | pub use self::cell::{SVMCell,Atom,Inst}; 48 | 49 | use self::cell::SVMCell::*; 50 | use self::cell::Atom::*; 51 | use self::cell::Inst::*; 52 | 53 | /// Represents a SVM machine state 54 | #[derive(PartialEq,Clone,Debug)] 55 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.1.0"))] 56 | pub struct State { 57 | stack: List, 58 | env: List, 59 | control:List, 60 | dump: List 61 | } 62 | 63 | /// A VM state's IO action 64 | /// 65 | /// Take note that this will eventually be replaced with memory-mapped IO 66 | /// when the main memory management scheme is done; therefore, it should 67 | /// never be marked as stable. Consider this struct and anything that depends 68 | /// on it to be an ugly hack. 69 | #[derive(PartialEq,Clone,Debug)] 70 | #[cfg_attr(feature = "nightly", unstable(feature="eval"))] 71 | pub enum IOEvent { 72 | /// A character was requested from the buffer 73 | Req, 74 | /// A character was buffered 75 | Buf(char) 76 | } 77 | 78 | #[cfg_attr(feature = "nightly", unstable(feature="eval"))] 79 | pub type EvalResult = Result<(State,Option), String>; 80 | 81 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.1.0"))] 82 | impl State { 83 | 84 | /// Creates a new empty state 85 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.1.0"))] 86 | pub fn new() -> State { 87 | State { 88 | stack: Stack::empty(), 89 | env: Stack::empty(), 90 | control: Stack::empty(), 91 | dump: Stack::empty() 92 | } 93 | } 94 | 95 | 96 | /// Dump state to string 97 | /// 98 | /// This produces state dumps suitable for printing as part of 99 | /// an error report. This is different from fmt::Debug since it 100 | /// includes a tag for the error reporter. 101 | #[cfg_attr(feature = "nightly", stable(feature="debug", since="0.2.0"))] 102 | pub fn dump_state(&self, tag: &str) -> String { 103 | format!( 104 | "[{t}] State dump:\n \ 105 | [{t}]\t\tStack:\t {s:?}\n \ 106 | [{t}]\t\tEnv:\t {e:?}\n \ 107 | [{t}]\t\tControl: {c:?}\n \ 108 | [{t}]\t\tDump:\t {d:?}\n", 109 | t = tag, 110 | s = &self.stack, 111 | e = &self.env, 112 | c = &self.control, 113 | d = &self.dump 114 | ) 115 | } 116 | 117 | /// Evaluates an instruction. 118 | /// 119 | /// Evaluates an instruction against a state, returning a new state. 120 | /// 121 | /// # Arguments: 122 | /// 123 | /// - `inp`: an input stream implementing `io::Read` 124 | /// - `outp`: an output stream implementing `io::Write` 125 | /// - `debug`: whether or not to snapshot the state before evaluating. This 126 | /// provides more detailed debugging information on errors, but may have 127 | /// a significant impact on performance. 128 | /// 129 | #[cfg_attr(feature = "nightly", stable(feature="vm_core", since="0.3.0"))] 130 | pub fn eval(self, 131 | input: Option, 132 | debug: bool) 133 | -> EvalResult { 134 | debug!("[eval]: Evaluating {:?}", self.control); 135 | // TODO: this (by which I mean "the whole caching deal") could likely be made 136 | // better and/or faster with some clever (mis?)use of RefCell; look into that. 137 | let mut prev = if debug { Some(self.clone()) } else { None }; 138 | // in ths pattern match, we begin The Great Work 139 | match self.control.pop().unwrap() { 140 | // NIL: pop an empty list onto the stack 141 | (InstCell(NIL), new_control) => Ok((State { 142 | stack: self.stack.push(list_cell![]), 143 | env: self.env, 144 | control: new_control, 145 | dump: self.dump 146 | }, None)), 147 | // LDC: load constant 148 | (InstCell(LDC), new_control) => { 149 | let (atom,newer_control) = try!(new_control.pop().ok_or( 150 | format!("[fatal][LDC]: pop on empty stack\n{}", 151 | prev.take().map_or(String::new(), |x| x.dump_state("fatal") ))) ); 152 | Ok((State { 153 | stack: self.stack.push(atom), 154 | env: self.env, 155 | control: newer_control, 156 | dump: self.dump 157 | }, None)) 158 | }, 159 | // LD: load variable 160 | (InstCell(LD), new_control) => match new_control.pop() { 161 | Some((ListCell( 162 | box Cons(AtomCell(UInt(lvl)), 163 | box Cons(AtomCell(UInt(idx)), 164 | box Nil)) 165 | ), newer_control)) => match self.env[(lvl-1)] { 166 | ListCell(ref level) => Ok((State { 167 | stack: match level.get(idx-1) { 168 | Some(thing) => self.stack.push(thing.clone()), 169 | None => self.stack 170 | }, 171 | env: self.env.clone(), 172 | control: newer_control, 173 | dump: self.dump 174 | }, None)), 175 | // This is a special case for something that, as far as I know, 176 | // should never happen. But despite everything, it DOES happen. 177 | ref thing @ AtomCell(_) => Ok((State { 178 | // I give up. Have your special case. 179 | stack: self.stack.push(thing.clone()), 180 | env: self.env.clone(), 181 | control: newer_control, 182 | dump: self.dump 183 | }, None)), 184 | _ => Err(format!( 185 | "[fatal][LD]: expected list in $e, found {:?}\n{}", 186 | self.env[lvl-1], prev.map_or(String::new(), |x| x.dump_state("fatal") ))) 187 | }, 188 | Some((ListCell( // TODO: this uses deprecated signed int indexing, remove 189 | box Cons(AtomCell(SInt(lvl)), 190 | box Cons(AtomCell(SInt(idx)), 191 | box Nil)) 192 | ), newer_control)) => match self.env[(lvl-1)] { 193 | ListCell(ref level) => Ok((State { 194 | stack: self.stack.push(level[(idx-1)].clone()), 195 | env: self.env.clone(), 196 | control: newer_control, 197 | dump: self.dump 198 | }, None)), 199 | _ => Err(format!( 200 | "[fatal][LD]: expected list in $e, found {:?}\n{}", 201 | self.env[lvl-1], prev.map_or(String::new(), |x| x.dump_state("fatal") ))) 202 | }, 203 | Some((thing,newer_control)) => Err(format!( 204 | "[fatal][LD]: expected pair, found {:?}\n[fatal] new control: {:?}\n{}", 205 | thing, 206 | newer_control, 207 | prev.map_or(String::new(), |x| x.dump_state("fatal") ))), 208 | None => Err(format!( 209 | "[fatal][LD]: expected pair, found empty stack\n{}", 210 | prev.map_or(String::new(), |x| x.dump_state("fatal") ))) 211 | }, 212 | 213 | // LDF: load function 214 | (InstCell(LDF), new_control) => { 215 | let (func, newer_control) = try!(match new_control.pop() { 216 | Some(thing) => Ok(thing), 217 | None => Err(format!( 218 | "[fatal][LDF]: pop on empty control stack\n{}", 219 | prev.map_or(String::new(), |x| x.dump_state("fatal") ))) 220 | }); 221 | Ok((State { 222 | stack: self.stack.push(list_cell![ 223 | func, 224 | self.env.get(0) 225 | .map_or(list_cell![], |it| it.clone()) 226 | ]), 227 | env: self.env, 228 | control: newer_control, 229 | dump: self.dump 230 | }, None)) 231 | }, 232 | 233 | (InstCell(JOIN), new_control) => { 234 | let (top, new_dump) = try!(match self.dump.pop() { 235 | Some(thing) => Ok(thing), 236 | None => Err(format!( 237 | "[fatal][JOIN]: pop on empty dump stack") ) 238 | }); 239 | match top { 240 | ListCell(box Nil) => Ok((State { 241 | stack: self.stack, 242 | env: self.env, 243 | control: new_control, 244 | dump: new_dump 245 | }, None)), 246 | ListCell(box it) => Ok((State { 247 | stack: self.stack, 248 | env: self.env, 249 | control: it, 250 | dump: new_dump 251 | }, None)), 252 | anything => Err(format!( 253 | "[fatal][JOIN]: expected list on dump, found {:?}\n{}", 254 | anything, prev.map_or(String::new(), |x| x.dump_state("fatal") )) ) 255 | } 256 | }, 257 | (InstCell(ADD), new_control) => match self.stack.pop() { 258 | Some((AtomCell(op1), new_stack)) => match new_stack.pop() { 259 | Some((AtomCell(op2), newer_stack)) => Ok((State { 260 | stack: newer_stack.push(AtomCell(op1 + op2)), 261 | env: self.env, 262 | control: new_control, 263 | dump: self.dump 264 | }, None)), 265 | any => Err(format!( 266 | "[fatal][ADD]: expected second operand, found {:?}\n{}", 267 | any, 268 | prev.map_or(String::new(), |x| x.dump_state("fatal") )) ) 269 | }, 270 | any => Err(format!( 271 | "[fatal][ADD]: expected first operand, found {:?}\n{}", 272 | any, 273 | prev.map_or(String::new(), |x| x.dump_state("fatal") )) ) 274 | }, 275 | (InstCell(SUB), new_control) => match self.stack.pop() { 276 | Some((AtomCell(op1), new_stack)) => match new_stack.pop() { 277 | Some((AtomCell(op2), newer_stack)) => Ok((State { 278 | stack: newer_stack.push(AtomCell(op1 - op2)), 279 | env: self.env, 280 | control: new_control, 281 | dump: self.dump 282 | }, None)), 283 | any => Err(format!( 284 | "[fatal][SUB]: expected second operand, found {:?}\n{}", 285 | any, 286 | prev.map_or(String::new(), |x| x.dump_state("fatal") )) ) 287 | }, 288 | any => Err(format!( 289 | "[fatal][SUB]: expected first operand, found {:?}\n{}", 290 | any, 291 | prev.map_or(String::new(), |x| x.dump_state("fatal") )) ) 292 | }, 293 | (InstCell(FDIV), new_control) => { 294 | let (op1, new_stack) = try!(match self.stack.pop() { 295 | Some(thing) => Ok(thing), 296 | None => Err(format!( 297 | "[fatal][FDIV]: pop on empty stack\n{}", 298 | prev.map_or(String::new(), |x| x.dump_state("fatal") )) ) 299 | }); 300 | match op1 { 301 | AtomCell(a) => { 302 | let (op2, newer_stack) = try!(match new_stack.pop() { 303 | Some(thing) => Ok(thing), 304 | None => Err("[fatal][FDIV]: pop on empty stack") 305 | }); 306 | match op2 { 307 | AtomCell(b) => Ok((State { 308 | stack: newer_stack.push(AtomCell( 309 | match (a, b) { 310 | // same type: coerce to float 311 | (SInt(a), SInt(b)) => Float(a as f64 / b as f64), 312 | (UInt(a), UInt(b)) => Float(a as f64 / b as f64), 313 | (Float(a), Float(b)) => Float(a / b), 314 | // float + int: coerce to float 315 | (Float(a), SInt(b)) => Float(a / b as f64), 316 | (Float(a), UInt(b)) => Float(a / b as f64), 317 | (SInt(a), Float(b)) => Float(a as f64 / b), 318 | (UInt(a), Float(b)) => Float(a as f64 / b), 319 | // uint + sint: coerce to float 320 | (UInt(a), SInt(b)) => Float(a as f64 / b as f64), 321 | (SInt(a), UInt(b)) => Float(a as f64 / b as f64), 322 | // char + any: coerce to int -> float 323 | // but if you ever actually do this, then ...wat? 324 | (Char(a), Char(b)) => Float(a as u8 as f64 / b as u8 as f64), 325 | (Char(a), UInt(b)) => Float(a as u8 as f64 / b as f64), 326 | (Char(a), SInt(b)) => Float(a as u8 as f64 / b as f64), 327 | (Char(a), Float(b)) => Float(a as u8 as f64 / b as f64), 328 | (UInt(a), Char(b)) => Float(a as f64 / b as u8 as f64), 329 | (SInt(a), Char(b)) => Float(a as f64 / b as u8 as f64), 330 | (Float(a), Char(b)) => Float(a as f64 / b as u8 as f64) 331 | } 332 | )), 333 | env: self.env, 334 | control: new_control, 335 | dump: self.dump 336 | }, None)), 337 | b => Err(format!( 338 | "[fatal][FDIV]: TypeError: expected compatible operands, found (FDIV {:?} {:?})", a, b) ) 339 | } 340 | }, 341 | _ => Err(format!( 342 | "[fatal][FDIV]: Expected first operand to be atom, found list or instruction" )), 343 | } 344 | }, 345 | (InstCell(DIV), new_control) => match self.stack.pop() { 346 | Some((AtomCell(op1), new_stack)) => match new_stack.pop() { 347 | Some((AtomCell(op2), newer_stack)) => Ok((State { 348 | stack: newer_stack.push(AtomCell(op1 / op2)), 349 | env: self.env, 350 | control: new_control, 351 | dump: self.dump 352 | },None)), 353 | any => Err(format!( 354 | "[fatal][DIV]: expected second operand, found {:?}\n{}", 355 | any, 356 | prev.map_or(String::new(), |x| x.dump_state("fatal") )) ) 357 | }, 358 | any => Err(format!( 359 | "[fatal][DIV]: expected first operand, found {:?}\n{}", 360 | any, 361 | prev.map_or(String::new(), |x| x.dump_state("fatal") )) ) 362 | }, 363 | (InstCell(MUL), new_control) => match self.stack.pop() { 364 | Some((AtomCell(op1), new_stack)) => match new_stack.pop() { 365 | Some((AtomCell(op2), newer_stack)) => Ok((State { 366 | stack: newer_stack.push(AtomCell(op1 * op2)), 367 | env: self.env, 368 | control: new_control, 369 | dump: self.dump 370 | }, None)), 371 | any => Err(format!( 372 | "[fatal][MUL]: expected second operand, found {:?}\n{}", 373 | any, 374 | prev.map_or(String::new(), |x| x.dump_state("fatal") )) ) 375 | }, 376 | any => Err(format!( 377 | "[fatal][MUL]: expected first operand, found {:?}\n{}", 378 | any, 379 | prev.map_or(String::new(), |x| x.dump_state("fatal") )) ) 380 | }, 381 | (InstCell(MOD), new_control) => match self.stack.pop() { 382 | Some((AtomCell(op1), new_stack)) => match new_stack.pop() { 383 | Some((AtomCell(op2), newer_stack)) => Ok((State { 384 | stack: newer_stack.push(AtomCell(op1 % op2)), 385 | env: self.env, 386 | control: new_control, 387 | dump: self.dump 388 | }, None)), 389 | any => Err(format!( 390 | "[fatal][MOD]: expected second operand, found {:?}\n{}", 391 | any, 392 | prev.map_or(String::new(), |x| x.dump_state("fatal") )) ) 393 | }, 394 | any => Err(format!( 395 | "[fatal][MOD]: expected first operand, found {:?}\n{}", 396 | any, 397 | prev.map_or(String::new(), |x| x.dump_state("fatal") )) ) 398 | }, 399 | (InstCell(EQ), new_control) => { 400 | let (op1, new_stack) = self.stack.pop().unwrap(); 401 | let (op2, newer_stack) = new_stack.pop().unwrap(); 402 | match (op1,op2) { 403 | (AtomCell(a), AtomCell(b)) => Ok((State { 404 | stack: newer_stack.push( 405 | match a == b { 406 | true => list_cell![AtomCell(SInt(1))], 407 | false => list_cell![] 408 | }), 409 | env: self.env, 410 | control: new_control, 411 | dump: self.dump 412 | }, None)), 413 | (_,_) => unimplemented!() // TODO: sane error pls 414 | } 415 | }, 416 | (InstCell(GT), new_control) => { 417 | let (op1, new_stack) = self.stack.pop().unwrap(); 418 | let (op2, newer_stack) = new_stack.pop().unwrap(); 419 | match (op1,op2) { 420 | (AtomCell(a), AtomCell(b)) => Ok((State { 421 | stack: newer_stack.push( 422 | match a > b { 423 | true => list_cell![AtomCell(SInt(1))], 424 | false => list_cell![] 425 | } 426 | ), 427 | env: self.env, 428 | control: new_control, 429 | dump: self.dump 430 | }, None)), 431 | (_,_) => unimplemented!() 432 | } 433 | }, 434 | (InstCell(GTE), new_control) => { 435 | let (op1, new_stack) = self.stack.pop().unwrap(); 436 | let (op2, newer_stack) = new_stack.pop().unwrap(); 437 | match (op1,op2) { 438 | (AtomCell(a), AtomCell(b)) => Ok((State { 439 | stack: newer_stack.push( 440 | match a >= b { 441 | true => list_cell![AtomCell(SInt(1))], 442 | false => list_cell![] 443 | } 444 | ), 445 | env: self.env, 446 | control: new_control, 447 | dump: self.dump 448 | }, None)), 449 | (_,_) => unimplemented!() 450 | } 451 | }, 452 | (InstCell(LT), new_control) => { 453 | let (op1, new_stack) = self.stack.pop().unwrap(); 454 | let (op2, newer_stack) = new_stack.pop().unwrap(); 455 | match (op1,op2) { 456 | (AtomCell(a), AtomCell(b)) => Ok((State { 457 | stack: newer_stack.push( 458 | match a < b { 459 | true => list_cell![AtomCell(SInt(1))], 460 | false => list_cell![] 461 | } 462 | ), 463 | env: self.env, 464 | control: new_control, 465 | dump: self.dump 466 | }, None)), 467 | (_,_) => unimplemented!() 468 | } 469 | }, 470 | (InstCell(LTE), new_control) => { 471 | let (op1, new_stack) = self.stack.pop().unwrap(); 472 | let (op2, newer_stack) = new_stack.pop().unwrap(); 473 | match (op1,op2) { 474 | (AtomCell(a), AtomCell(b)) => Ok((State { 475 | stack: newer_stack.push( 476 | match a <= b { 477 | true => list_cell![AtomCell(SInt(1))], 478 | false => list_cell![] 479 | }), 480 | env: self.env, 481 | control: new_control, 482 | dump: self.dump 483 | }, None)), 484 | (_,_) => unimplemented!() 485 | } 486 | }, 487 | (InstCell(ATOM), new_control) => { 488 | let (target, new_stack) = self.stack.pop().unwrap(); 489 | Ok((State { 490 | stack: new_stack.push( 491 | match target { 492 | AtomCell(_) => list_cell![AtomCell(SInt(1))], 493 | _ => list_cell![] 494 | } 495 | ), 496 | env: self.env, 497 | control: new_control, 498 | dump: self.dump 499 | },None)) 500 | }, 501 | (InstCell(AP), new_control) => match self.stack.pop().unwrap() { 502 | (ListCell(box Cons(ListCell(box func), box Cons(ListCell(params), box Nil))), new_stack) => { 503 | match new_stack.pop() { 504 | Some((v, newer_stack)) => Ok((State { 505 | stack: Stack::empty(), 506 | env: match v { 507 | ListCell(_) => params.push(v), 508 | _ => list!(v) 509 | }, 510 | control: func, 511 | dump: self.dump 512 | .push(ListCell(Box::new(newer_stack))) 513 | .push(ListCell(Box::new(self.env))) 514 | .push(ListCell(Box::new(new_control))) 515 | }, None)),/* 516 | Some((v @ AtomCell(_), newer_stack)) => State { 517 | stack: Stack::empty(), 518 | env: list!( params,ListCell(box list!(v)) ), 519 | control: func, 520 | dump: self.dump 521 | .push(ListCell(box newer_stack)) 522 | .push(ListCell(box self.env)) 523 | .push(ListCell(box new_control)) 524 | }, 525 | Some((thing, _)) => panic!( 526 | "[fatal][AP]: Expected closure on stack, got:\n[fatal]\t{:?}\n{}", 527 | thing, 528 | prev.map_or(String::new(), |x| x.dump_state("fatal") )),*/ 529 | None => Err(format!( 530 | "[fatal][AP]: expected non-empty stack\n{}", 531 | prev.map_or(String::new(), |x| x.dump_state("fatal") )) ) 532 | } 533 | }, 534 | (_, thing) => Err(format!( 535 | "[fatal][AP]: Expected closure on stack, got:\n[fatal]\t{:?}\n{}", 536 | thing, prev.map_or(String::new(), |x| x.dump_state("fatal") )) ) 537 | }, 538 | (InstCell(RAP), new_control) => match self.stack.pop().unwrap() { 539 | (ListCell(box Cons(ListCell(box func), box Cons(ListCell(box params), box Nil))), new_stack) => { 540 | match new_stack.pop() { 541 | Some((v @ ListCell(_), newer_stack)) => Ok(( State { 542 | stack: Stack::empty(), 543 | env: params.push(v), 544 | control: func, 545 | dump: self.dump 546 | .push(ListCell(Box::new(new_control))) 547 | .push(ListCell(Box::new(self.env.pop().unwrap().1))) 548 | .push(ListCell(Box::new(newer_stack))) 549 | }, None)), 550 | Some((thing, _)) => Err(format!( 551 | "[fatal][RAP]: Expected closure on stack, got:\n[fatal]\t{:?}\n{}", 552 | thing, prev.map_or(String::new(), |x| x.dump_state("fatal") )) ), 553 | None => Err(format!( 554 | "[fatal][RAP]: expected non-empty stack\n{}", 555 | prev.map_or(String::new(), |x| x.dump_state("fatal") )) ) 556 | } 557 | }, 558 | (_, thing) => Err(format!( 559 | "[fatal][RAP]: Expected closure on stack, got:\n[fatal]\t{:?}\n{}", 560 | thing, prev.map_or(String::new(), |x| x.dump_state("fatal") )) ) 561 | }, 562 | (InstCell(RET), _) => { 563 | let (head, _) = self.stack.pop().unwrap(); 564 | let (new_stack, new_dump) = try!(match self.dump.pop() { 565 | Some((ListCell(s), d)) => Ok((*s, d)), 566 | Some(it @ (AtomCell(_),_)) => Ok((list!(it.0), it.1)), 567 | _ => Err( 568 | "[fatal][RET]: Expected non-empty stack") 569 | }); 570 | let (new_env, newer_dump) = try!(match new_dump.pop() { 571 | Some((ListCell(e), d)) => Ok((*e, d)), 572 | _ => Err( 573 | "[fatal][RET]: Expected new environment on dump stack") 574 | }); 575 | let (newer_control, newest_dump) = try!(match newer_dump.pop() { 576 | Some((ListCell(c), d)) => Ok((*c, d)), 577 | Some(it @ (InstCell(_),_)) => Ok((list!(it.0), it.1)), 578 | _ => Err( 579 | "[fatal][RET]: Expected new control stack on dump stack") 580 | }); 581 | Ok((State { 582 | stack: new_stack.push(head), 583 | env: new_env, 584 | control: newer_control, 585 | dump: newest_dump 586 | }, None)) 587 | }, 588 | (InstCell(DUM), new_control) => Ok((State { 589 | stack: self.stack, 590 | env: self.env.push(ListCell(list!())), 591 | control: new_control, 592 | dump: self.dump 593 | }, None)), 594 | (InstCell(SEL), new_control) => match new_control.pop() { 595 | Some((ListCell(box true_case), newer_control)) => { 596 | match newer_control.pop() { 597 | Some((ListCell(box false_case), newest_control)) => { 598 | match self.stack.pop() { 599 | // False case 600 | Some((ListCell(box Nil), new_stack)) => Ok((State { 601 | stack: new_stack, 602 | env: self.env, 603 | control: false_case, 604 | dump: self.dump.push(ListCell(Box::new(newest_control))) 605 | }, None)), 606 | // True case 607 | Some((_, new_stack)) => Ok((State { 608 | stack: new_stack, 609 | env: self.env, 610 | control: true_case, 611 | dump: self.dump.push(ListCell(Box::new(newest_control))) 612 | }, None)), 613 | None => Err(format!( 614 | "[fatal][SEL]: expected non-empty stack\n{}", 615 | prev.map_or(String::new(), |x| x.dump_state("fatal") )) ) 616 | } 617 | }, 618 | Some((thing, _)) => Err(format!( 619 | "[fatal][SEL]: expected list on control, found {:?}\n{}", 620 | thing,prev.map_or(String::new(), |x| x.dump_state("fatal") )) ), 621 | None => Err(format!( 622 | "[fatal][SEL]: expected list on control, found nothing\n{}", 623 | prev.map_or(String::new(), |x| x.dump_state("fatal") )) ) 624 | } 625 | }, 626 | Some((thing, _)) => Err(format!( 627 | "[fatal][SEL]: expected list on control, found {:?}\n{}", 628 | thing,prev.map_or(String::new(), |x| x.dump_state("fatal") )) ), 629 | None => Err(format!( 630 | "[fatal][SEL]: expected list on control, found nothing\n{}", 631 | prev.map_or(String::new(), |x| x.dump_state("fatal") )) ) 632 | }, 633 | (InstCell(CAR), new_control) => match self.stack.pop() { 634 | Some((ListCell(box Cons(car, _)), new_stack)) => Ok(( State { 635 | stack: new_stack.push(car), 636 | env: self.env, 637 | control: new_control, 638 | dump: self.dump 639 | }, None)), 640 | Some((ListCell(box Nil), _)) => Err(format!( 641 | "[fatal][CAR]: expected non-empty list, found Nil\n{}", 642 | prev.map_or(String::new(), |x| x.dump_state("fatal") )) ), 643 | Some((thing, _)) => Err(format!( 644 | "[fatal][CAR]: expected non-empty list, found {:?}\n{}", 645 | thing, prev.map_or(String::new(), |x| x.dump_state("fatal") )) ), 646 | None => Err(format!( 647 | "[fatal][CAR]: Expected non-empty list, found nothing\n{}", 648 | prev.map_or(String::new(), |x| x.dump_state("fatal") )) ) 649 | }, 650 | (InstCell(CDR), new_control) => match self.stack.pop() { 651 | Some((ListCell(box Cons(_, cdr)), new_stack)) => Ok((State { 652 | stack: new_stack.push(ListCell(cdr)), 653 | env: self.env, 654 | control: new_control, 655 | dump: self.dump 656 | }, None)), 657 | Some((ListCell(box Nil), _)) => panic!( 658 | "[fatal][CDR]: expected non-empty list, found Nil\n{}", 659 | prev.map_or(String::new(), |x| x.dump_state("fatal") )), 660 | Some((thing, _)) => panic!( 661 | "[fatal][CDR]: expected non-empty list, found {:?}\n{}", 662 | thing, prev.map_or(String::new(), |x| x.dump_state("fatal") )), 663 | None => panic!( 664 | "[fatal][CDR]: Expected non-empty list, found nothing\n{}", 665 | prev.map_or(String::new(), |x| x.dump_state("fatal") )) 666 | }, 667 | (InstCell(CONS), new_control) => match self.stack.pop() { 668 | Some((thing, new_stack)) => { 669 | match new_stack.pop() { 670 | Some((ListCell(list), newer_stack)) => Ok((State { 671 | stack: newer_stack.push(ListCell(Box::new(Cons(thing, list)))), 672 | env: self.env, 673 | control: new_control, 674 | dump: self.dump 675 | }, None)), 676 | Some((thing_else, _)) => Err(format!( 677 | "[fatal][CONS]: Expected a list on the stack, found {:?}\n{}", 678 | thing_else, 679 | prev.map_or(String::new(), |x| x.dump_state("fatal") )) ), 680 | None => Err(format!( 681 | "[fatal][CONS]: Expected a list on the stack, found nothing.\n{}", 682 | prev.map_or(String::new(), |x| x.dump_state("fatal") )) ) 683 | } 684 | }, 685 | None => Err(format!( 686 | "[fatal][CONS]: Expected an item on the stack, found nothing\n{}", 687 | prev.map_or(String::new(), |x| x.dump_state("fatal") )) ) 688 | }, 689 | (InstCell(NULL), new_control) => { 690 | let (target, new_stack) = self.stack.pop().unwrap(); 691 | Ok((State { 692 | stack: new_stack.push( 693 | match target { 694 | ListCell(box Nil) => list_cell![AtomCell(SInt(1))], 695 | _ => list_cell![] 696 | } 697 | ), 698 | env: self.env, 699 | control: new_control, 700 | dump: self.dump 701 | }, None)) 702 | }, 703 | (InstCell(WRITEC), new_control) => match self.stack.pop() { 704 | Some((AtomCell(Char(ch)), new_stack)) => {/* 705 | if let Err(msg) = outp.write(&[ch as u8,1]) { 706 | panic!("[fatal][WRITEC]: writing failed: {:?}\n{}", 707 | msg, 708 | prev.map_or(String::new(), |x| x.dump_state("fatal") )) 709 | };*/ 710 | Ok((State { 711 | stack: new_stack, 712 | env: self.env, 713 | control: new_control, 714 | dump: self.dump 715 | }, Some(IOEvent::Buf(ch))) ) 716 | }, 717 | Some((thing_else,_)) => panic!( 718 | "[fatal][WRITEC]: expected char, found {:?}\n{}", 719 | thing_else,prev.map_or(String::new(), |x| x.dump_state("fatal") )), 720 | None => panic!( 721 | "[fatal][WRITEC]: expected char, found nothing\n{}", 722 | prev.map_or(String::new(), |x| x.dump_state("fatal") )) 723 | }, 724 | (InstCell(READC), new_control) => { 725 | // todo: figure out how to make it work with the new thing 726 | match input { 727 | Some(ch) => Ok((State { 728 | stack: self.stack.push(AtomCell(Char(ch as char))), 729 | env: self.env, 730 | control: new_control, 731 | dump: self.dump 732 | }, None)), 733 | _ => panic!("No input, something went wrong (this is not supposed to happen") 734 | } /*, 735 | .map_err(|msg| format!( 736 | "[fatal][READC]: could not read, {:?}\n{}", 737 | msg,prev.map_or(String::new(), |x| x.dump_state("fatal") )))*/ 738 | }, 739 | (InstCell(STOP), _) => panic!( 740 | "[fatal]: undefined behaviour\n[fatal]: evaluation of STOP word\n{}", 741 | prev.map_or(String::new(), |x| x.dump_state("fatal") ) 742 | ), 743 | (thing, _) => panic!( 744 | "[fatal]: Tried to evaluate an unsupported cell type {:?}.\n{}", 745 | thing, 746 | prev.map_or(String::new(), |x| x.dump_state("fatal") )) 747 | } 748 | } 749 | } 750 | 751 | 752 | /// Evaluates a program. 753 | /// 754 | /// Evaluates a program (control stack) and returns the final state. 755 | /// TODO: add (optional?) parameters for stdin and stdout 756 | #[cfg_attr(feature = "nightly", stable(feature="vm_core",since="0.2.0"))] 757 | pub fn eval_program(program: List, 758 | debug: bool) 759 | -> Result,String> { 760 | debug!("evaluating {:?}", program); 761 | let mut machine = State { 762 | stack: Stack::empty(), 763 | env: Stack::empty(), 764 | control: program, 765 | dump: Stack::empty() 766 | }; 767 | // while there are more instructions, 768 | while { 769 | machine.control.length() > 0usize && 770 | machine.control.peek()!= Some(&InstCell(STOP)) 771 | } { //TODO: this is kinda heavyweight 772 | machine = try!(machine.eval(None,debug)).0 // continue evaling 773 | }; 774 | Ok(machine.stack) 775 | } 776 | -------------------------------------------------------------------------------- /src/slist.rs: -------------------------------------------------------------------------------- 1 | pub use slist::List::{Cons,Nil}; 2 | 3 | use std::fmt; 4 | use std::ops::Index; 5 | use std::iter::{IntoIterator, FromIterator}; 6 | 7 | /// Convenience macro for making lists. 8 | /// 9 | /// # Example: 10 | /// 11 | /// ``` 12 | /// # #[macro_use] extern crate seax_svm; 13 | /// # use seax_svm::slist; 14 | /// # use seax_svm::slist::List::{Cons, Nil}; 15 | /// # fn main () { 16 | /// assert_eq!( 17 | /// list!(1i32, 2i32, 3i32), 18 | /// Cons(1i32, Box::new(Cons(2i32, Box::new(Cons(3i32, Box::new(Nil)))))) 19 | /// ); 20 | /// # } 21 | /// ``` 22 | #[macro_export] 23 | #[cfg_attr(feature = "nightly", stable(feature="list", since="0.1.0") )] 24 | macro_rules! list( 25 | ( $e:expr, $($rest:expr),+ ) => ( Cons($e, Box::new(list!( $( $rest ),+ )) )); 26 | ( $e:expr ) => ( Cons($e, Box::new(Nil)) ); 27 | () => ( Box::new(Nil) ); 28 | ); 29 | 30 | /// Common functions for an immutable Stack abstract data type. 31 | #[cfg_attr(feature = "nightly", stable(feature="stack", since="0.1.0") )] 32 | pub trait Stack { 33 | 34 | /// Push an item to the top of the stack, returning a new stack 35 | #[cfg_attr(feature = "nightly", stable(feature="stack", since="0.1.0") )] 36 | fn push(self, item : T) -> Self; 37 | 38 | /// Pop the top element of the stack. Returns an Option on a T and 39 | /// a new Stack to replace this. 40 | #[cfg_attr(feature = "nightly", stable(feature="stack", since="0.1.0") )] 41 | fn pop(self) -> Option<(T, Self)>; 42 | 43 | /// Peek at the top item of the stack. 44 | /// 45 | /// Returns Some if there is an item on top of the stack, 46 | /// and None if the stack is empty. 47 | #[cfg_attr(feature = "nightly", stable(feature="stack", since="0.1.0") )] 48 | fn peek(&self) -> Option<&T>; 49 | 50 | /// Returns an empty stack. 51 | #[cfg_attr(feature = "nightly", stable(feature="stack", since="0.1.0") )] 52 | fn empty() -> Self; 53 | } 54 | 55 | /// Stack implementation using a `cons` list 56 | impl Stack for List { 57 | 58 | /// Push an item to the top of the stack, returning a new stack. 59 | /// 60 | /// # Examples: 61 | /// ``` 62 | /// use seax_svm::slist::{List,Stack}; 63 | /// 64 | /// let mut s: List = Stack::empty(); 65 | /// assert_eq!(s.peek(), None); 66 | /// s = s.push(1); 67 | /// assert_eq!(s.peek(), Some(&1)); 68 | /// s = s.push(6); 69 | /// assert_eq!(s.peek(), Some(&6)); 70 | /// ``` 71 | #[inline] 72 | #[cfg_attr(feature = "nightly", stable(feature="stack", since="0.1.0") )] 73 | fn push(self, item: T) -> List { Cons(item, Box::new(self)) } 74 | 75 | /// Pop the top element of the stack. 76 | /// 77 | /// Pop the top element of the stack. Returns an 78 | /// `Option<(T,List)>` containing the top element and a new 79 | /// `List` with that item removed, or `None` if the stack is 80 | /// empty. 81 | /// 82 | /// # Examples: 83 | /// ``` 84 | /// # use seax_svm::slist::{List,Stack}; 85 | /// 86 | /// let mut s: List = Stack::empty(); 87 | /// s = s.push(2); 88 | /// s = s.push(1); 89 | /// let pop_result = s.pop().unwrap(); 90 | /// s = pop_result.1; 91 | /// assert_eq!(s.peek(), Some(&2)); 92 | /// assert_eq!(pop_result.0, 1); 93 | /// ``` 94 | #[inline] 95 | #[cfg_attr(feature = "nightly", stable(feature="stack", since="0.1.0") )] 96 | fn pop(self) -> Option<(T,List)> { 97 | match self { 98 | Cons(item, new_self) => Some((item, *new_self)), 99 | Nil => None 100 | } 101 | } 102 | 103 | #[inline] 104 | #[cfg_attr(feature = "nightly", 105 | stable(feature="stack", since="0.1.0") 106 | )] 107 | fn empty() -> List { Nil } 108 | 109 | /// Peek at the top element of the stack. 110 | /// 111 | /// Peek at the top element of the stack. Returns an `Option<&T>` 112 | /// with a borrowed pointer to the top element, or `None` if the 113 | /// stack is empty. 114 | /// 115 | /// # Examples: 116 | /// ``` 117 | /// # use seax_svm::slist::{List,Stack}; 118 | /// 119 | /// let mut s: List = Stack::empty(); 120 | /// s = s.push(2); 121 | /// s = s.push(1); 122 | /// let pop_result = s.pop().unwrap(); 123 | /// s = pop_result.1; 124 | /// assert_eq!(s.peek(), Some(&2)); 125 | /// assert_eq!(pop_result.0, 1); 126 | /// ``` 127 | #[inline] 128 | #[cfg_attr(feature = "nightly", stable(feature="stack", since="0.1.0") )] 129 | fn peek(&self) -> Option<&T> { 130 | match self { 131 | &Nil => None, 132 | &Cons(ref it,_) => Some(it) 133 | } 134 | } 135 | 136 | } 137 | 138 | /// Singly-linked `cons` list. 139 | /// 140 | /// This is used internally to represent list primitives in the 141 | /// Seax virtual machine. 142 | /// 143 | // TODO: potentially, a pointer to the last itemof the list could be 144 | // cached using a `RefCell` or something to speed up access for 145 | // appends/tail access. We could also check the length and decide whether 146 | // to link hop from the head or tail when indexing. It would be necessary 147 | // to investigate whether the added overhead of caching (both in terms of 148 | // space and in terms of time taken to update the cache) would be worth 149 | // the performance benefits --- my guess is that caching is worth the added 150 | // costs (as usual). 151 | #[derive(PartialEq,Clone)] 152 | #[cfg_attr(feature = "nightly", stable(feature="list", since="0.1.0") )] 153 | pub enum List { 154 | /// Cons cell containing a `T` and a link to the tail 155 | #[cfg_attr(feature = "nightly", stable(feature="list", since="0.1.0") )] 156 | Cons(T, Box>), 157 | /// The empty list. 158 | #[cfg_attr(feature = "nightly", stable(feature="list", since="0.1.0") )] 159 | Nil, 160 | } 161 | 162 | /// Public implementation for List. 163 | #[cfg_attr(feature = "nightly", stable(feature="list", since="0.1.0") )] 164 | impl List { 165 | 166 | 167 | /// Creates a new empty list 168 | #[cfg_attr(feature = "nightly", stable(feature="list", since="0.1.0") )] 169 | #[inline] 170 | pub fn new() -> List { Nil } 171 | 172 | /// Prepends the given item to the list. 173 | /// 174 | /// This is an O(1) operation. 175 | /// 176 | /// # Arguments 177 | /// 178 | /// + `item` - the item to prepend 179 | /// 180 | /// # Return Value 181 | /// 182 | /// + The list with the new head item prepended 183 | /// 184 | /// # Examples 185 | /// ``` 186 | /// # #[macro_use] extern crate seax_svm; 187 | /// # use seax_svm::slist::List; 188 | /// # use seax_svm::slist::List::{Cons,Nil}; 189 | /// # fn main() { 190 | /// let mut a_list: List = List::new(); 191 | /// a_list = a_list.prepend(1); 192 | /// assert_eq!(a_list, list![1]); 193 | /// 194 | /// a_list = a_list.prepend(2); 195 | /// assert_eq!(a_list, list![2,1]); 196 | /// # } 197 | /// ``` 198 | #[inline] 199 | #[cfg_attr(feature = "nightly", stable(feature="list", since="0.1.0") )] 200 | pub fn prepend(self, it: T) -> List { Cons(it, Box::new(self)) } 201 | 202 | /// Appends an item to the end of the list. 203 | /// 204 | /// This is an O(_n_) operation. 205 | /// 206 | /// # Arguments 207 | /// 208 | /// + `item` - the item to append 209 | /// 210 | /// # Examples 211 | /// ``` 212 | /// # #![feature(list)] 213 | /// # #[macro_use] extern crate seax_svm; 214 | /// # use seax_svm::slist::List; 215 | /// # use seax_svm::slist::List::{Cons,Nil}; 216 | /// # fn main() { 217 | /// let mut a_list: List = List::new(); 218 | /// a_list.append(1); 219 | /// assert_eq!(a_list, list![1]); 220 | /// 221 | /// a_list.append(2); 222 | /// assert_eq!(a_list, list![1,2]); 223 | /// # } 224 | /// ``` 225 | #[inline] 226 | #[cfg_attr(feature = "nightly", stable(feature="list", since="0.2.3") )] 227 | pub fn append(&mut self, it: T) { 228 | match *self { 229 | Cons(_, ref mut tail) => (*tail).append(it), 230 | Nil => *self = Cons(it, Box::new(Nil)) 231 | } 232 | 233 | } 234 | 235 | /// Appends an item to the end of the list. 236 | /// 237 | /// Returns the last element of the list to support 'append chaining' 238 | /// of a large number of items; this allows you to omit a complete traversal 239 | /// of the list for every append and should be used in situations 240 | /// such as `fold`s. 241 | /// 242 | /// The first append is still O(_n_), but long as you hang on to your 243 | /// pointer to the tail, subsequent appends should all be O(1). However, 244 | /// this requires you to keep a `&mut` pointer to the list, so use it 245 | /// sparingly, especially in situations of concurrent access. 246 | /// 247 | /// # Arguments 248 | /// 249 | /// + `item` - the item to append 250 | /// 251 | /// # Examples 252 | /// ``` 253 | /// # #![feature(list)] 254 | /// # #[macro_use] extern crate seax_svm; 255 | /// # use seax_svm::slist::List; 256 | /// # use seax_svm::slist::List::{Cons,Nil}; 257 | /// # fn main() { 258 | /// let mut a_list: List = List::new(); 259 | /// 260 | /// // this is a function so that the `&mut` borrow is released. 261 | /// fn append_two_items(l: &mut List, first: T, second: T) { 262 | /// l.append_chain(first).append_chain(second); 263 | /// } 264 | /// 265 | /// append_two_items(&mut a_list, 1, 2); 266 | /// assert_eq!(a_list, list![1,2]); 267 | /// # } 268 | #[inline] 269 | #[cfg_attr(feature = "nightly", stable(feature="list", since="0.2.3") )] 270 | pub fn append_chain(&mut self, it: T) -> &mut List { 271 | match *self { 272 | Cons(_, ref mut tail) => (*tail).append_chain(it), 273 | Nil => { *self = Cons(it, Box::new(Nil)); self } 274 | } 275 | 276 | } 277 | 278 | /// Returns the length of the list. 279 | /// 280 | /// # Examples 281 | /// ``` 282 | /// # #[macro_use] extern crate seax_svm; 283 | /// # use seax_svm::slist::List; 284 | /// # use seax_svm::slist::List::{Cons,Nil}; 285 | /// # fn main() { 286 | /// let a_list = list!(1,2,3,4); 287 | /// assert_eq!(a_list.length(), 4) 288 | /// # } 289 | /// ``` 290 | #[inline] 291 | #[cfg_attr(feature = "nightly", stable(feature="list", since="0.1.0") )] 292 | pub fn length (&self) -> usize { 293 | match *self { 294 | Cons(_, ref tail) => 1 + tail.length(), 295 | Nil => 0 296 | } 297 | } 298 | 299 | /// Returns true if the list is empty, false otherwise. 300 | /// 301 | /// # Examples 302 | /// ``` 303 | /// # #[macro_use] extern crate seax_svm; 304 | /// # use seax_svm::slist::List; 305 | /// # use seax_svm::slist::List::{Cons,Nil}; 306 | /// # fn main() { 307 | /// let list = list!(1,2,3,4); 308 | /// assert_eq!(list.is_empty(), false) 309 | /// # } 310 | /// ``` 311 | /// ``` 312 | /// # #[macro_use] extern crate seax_svm; 313 | /// # use seax_svm::slist::List; 314 | /// # use seax_svm::slist::List::{Cons,Nil}; 315 | /// # fn main() { 316 | /// let list: List = Nil; 317 | /// assert_eq!(list.is_empty(), true) 318 | /// # } 319 | /// ``` 320 | #[inline] 321 | #[cfg_attr(feature = "nightly", stable(feature="list", since="0.2.8") )] 322 | pub fn is_empty (&self) -> bool { 323 | match *self { 324 | Cons(_,_) => false, 325 | Nil => true 326 | } 327 | } 328 | 329 | /// Returns the tail of the list from this element. 330 | /// 331 | /// # Examples 332 | /// ``` 333 | /// # #[macro_use] extern crate seax_svm; 334 | /// # use seax_svm::slist::List; 335 | /// # use seax_svm::slist::List::{Cons,Nil}; 336 | /// # fn main() { 337 | /// let list = list!(1,2,3,4); 338 | /// assert_eq!(list.tail(), &list!(2,3,4)) 339 | /// # } 340 | /// ``` 341 | #[inline] 342 | #[cfg_attr(feature = "nightly", stable(feature="list", since="0.2.8") )] 343 | pub fn tail<'a>(&'a self) -> &'a Self { 344 | match self { 345 | &Cons(_, ref cdr) => cdr, 346 | nil @ &Nil => nil 347 | } 348 | } 349 | 350 | /// Provide a forward iterator 351 | #[inline] 352 | #[cfg_attr(feature = "nightly", stable(feature="list", since="0.1.0") )] 353 | pub fn iter<'a>(&'a self) -> ListIterator<'a, T> { 354 | ListIterator{current: self} 355 | } 356 | 357 | /// Returns the last element of the list 358 | /// 359 | /// # Examples 360 | /// ``` 361 | /// # #[macro_use] extern crate seax_svm; 362 | /// # use seax_svm::slist::List; 363 | /// # use seax_svm::slist::List::{Cons,Nil}; 364 | /// # fn main() { 365 | /// let a_list = list!(1,2,3,4); 366 | /// assert_eq!(a_list.last(), &4) 367 | /// # } 368 | /// ``` 369 | #[inline] 370 | #[cfg_attr(feature = "nightly", stable(feature="list", since="0.1.0") )] 371 | pub fn last(&self) -> &T { 372 | match *self { 373 | Cons(ref car, ref cdr) if cdr.is_empty() => &car, 374 | Cons(_, ref cdr) => (*cdr).last(), 375 | Nil => panic!("Last called on empty list") 376 | } 377 | } 378 | 379 | 380 | /// Optionally index the list. 381 | /// 382 | /// Unlike list indexing syntax (`list[i]`), this returns `None` 383 | /// if the index is out of bound rather than panicking. 384 | /// 385 | /// Lists are zero-indexed, so just as when using list indexing syntax, 386 | /// the head of the list is index 0 and the last element of the list is 387 | /// index (length - 1). 388 | /// 389 | /// # Arguments 390 | /// 391 | /// + `idx` - the index to attempt to access 392 | /// 393 | /// # Return Value 394 | /// 395 | /// + `Some(&T)` if the index exists within the list, `None` otherwise. 396 | /// 397 | /// # Examples 398 | /// ``` 399 | /// # #[macro_use] extern crate seax_svm; 400 | /// # use seax_svm::slist::List::{Cons,Nil}; 401 | /// # fn main() { 402 | /// let a_list = list!(1,2,3,4); 403 | /// assert_eq!(a_list.get(1), Some(&2)); 404 | /// assert_eq!(a_list.get(3), Some(&4)); 405 | /// assert_eq!(a_list.get(10), None); 406 | /// # } 407 | /// ``` 408 | #[cfg_attr(feature = "nightly", 409 | stable(feature="list",since="0.3.0") 410 | )] 411 | pub fn get<'a>(&'a self, index: u64) -> Option<&'a T> { 412 | match (0..index).fold(self, |acc, _| acc.tail()) { 413 | &Cons(ref car,_) => Some(car), 414 | &Nil => None 415 | } 416 | 417 | } 418 | } 419 | 420 | #[cfg_attr(feature = "nightly", stable(feature="list", since="0.2.5") )] 421 | impl<'a, T> fmt::Display for List where T: fmt::Display { 422 | #[cfg_attr(feature = "nightly", stable(feature="list", since="0.2.5") )] 423 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 424 | let mut it = self.iter(); 425 | write!(f, "({}{})", it.next().unwrap(), it.fold( 426 | String::new(), 427 | |mut a, i| { a.push_str(format!(", {}", i).as_ref()); a} ) 428 | ) 429 | } 430 | } 431 | 432 | #[cfg_attr(feature = "nightly", stable(feature="list", since="0.2.5") )] 433 | impl<'a, T> fmt::Debug for List where T: fmt::Debug { 434 | #[cfg_attr(feature = "nightly", stable(feature="list", since="0.2.5") )] 435 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 436 | match *self { 437 | Cons(ref head, ref tail) => write!(f, "({:?} . {:?})", head, tail), 438 | Nil => write!(f,"nil") 439 | } 440 | } 441 | 442 | } 443 | 444 | 445 | #[cfg_attr(feature = "nightly", stable(feature="list", since="0.2.3") )] 446 | impl FromIterator for List { 447 | /// Build a `List` from a structure implementing `IntoIterator`. 448 | /// 449 | /// This takes advantage of the `List.append_chain()` method under the 450 | /// hood to provide roughly O(_n_) performance. 451 | /// 452 | /// # Examples 453 | /// 454 | /// ``` 455 | /// # use seax_svm::slist::List; 456 | /// # use std::iter::FromIterator; 457 | /// let mut a_vec = vec![1,2,3,4]; 458 | /// let another_vec = a_vec.clone(); 459 | /// let a_list = List::from_iter(a_vec); 460 | /// for i in 0..a_list.length() { 461 | /// assert_eq!(a_list[i], another_vec[i]) 462 | /// } 463 | /// ``` 464 | #[inline] 465 | #[cfg_attr(feature = "nightly", stable(feature="list", since="0.2.3") )] 466 | fn from_iter(iterable: I) -> List where I: IntoIterator { 467 | let mut result = List::new(); 468 | iterable 469 | .into_iter() 470 | .fold(&mut result, |l, it| l.append_chain(it)); 471 | result 472 | } 473 | 474 | } 475 | 476 | /// Wraps a List to allow it to be used as an Iterator 477 | #[cfg_attr(feature = "nightly", stable(feature="list", since="0.1.0") )] 478 | pub struct ListIterator<'a, T:'a> { current: &'a List } 479 | 480 | /// Implementation of Iterator for List. This allows iteration by 481 | /// link hopping. 482 | #[cfg_attr(feature = "nightly", stable(feature="list", since="0.1.0") )] 483 | impl<'a, T> Iterator for ListIterator<'a, T> { 484 | type Item = &'a T; 485 | 486 | /// Get the next element from the list. 487 | /// 488 | /// Get the next element from the list. Returns a `Some`, or `Nil` 489 | /// if at the end of the list. 490 | /// 491 | /// # Examples: 492 | /// ``` 493 | /// # #[macro_use] extern crate seax_svm; 494 | /// # use seax_svm::slist; 495 | /// # use seax_svm::slist::List; 496 | /// # use seax_svm::slist::List::{Cons, Nil}; 497 | /// # fn main () { 498 | /// let list = list!(1,2,3); 499 | /// let mut iter = list.iter(); 500 | /// assert_eq!(iter.next().unwrap(), &1); 501 | /// assert_eq!(iter.next().unwrap(), &2); 502 | /// assert_eq!(iter.next().unwrap(), &3); 503 | /// # } 504 | /// ``` 505 | /// ``` 506 | /// # #![feature(convert)] 507 | /// # #[macro_use] extern crate seax_svm; 508 | /// # use seax_svm::slist; 509 | /// # use seax_svm::slist::List; 510 | /// # use seax_svm::slist::List::{Cons, Nil}; 511 | /// # fn main () { 512 | /// let l: List = list!(1,2,3,4,5,6); 513 | /// let mut string = String::new(); 514 | /// for item in l.iter() { 515 | /// string.push_str((item.to_string() + ", ").as_ref()); 516 | /// } 517 | /// assert_eq!(string, "1, 2, 3, 4, 5, 6, ".to_string()) 518 | /// # } 519 | /// ``` 520 | #[inline] 521 | #[cfg_attr(feature = "nightly", 522 | stable(feature="list", since="0.2.8") 523 | )] 524 | fn next(&mut self) -> Option<&'a T> { 525 | match self.current { 526 | &Cons(ref head, ref tail) => { 527 | self.current = &(**tail); 528 | Some(head) 529 | }, 530 | &Nil => None 531 | } 532 | } 533 | } 534 | 535 | #[cfg_attr(feature = "nightly", stable(feature="list", since="0.1.0") )] 536 | impl<'a, T> ExactSizeIterator for ListIterator<'a, T> { 537 | fn len(&self) -> usize { 538 | self.current.length() 539 | } 540 | } 541 | /// Implementation of indexing for `List`. 542 | /// 543 | /// # Examples: 544 | /// ``` 545 | /// # #[macro_use] extern crate seax_svm; 546 | /// # use seax_svm::slist; 547 | /// # use seax_svm::slist::List; 548 | /// # use seax_svm::slist::List::{Cons, Nil}; 549 | /// # fn main () { 550 | /// let list: List = list!(1,2,3,4,5,6); 551 | /// assert_eq!(list[0usize], 1); 552 | /// # } 553 | /// ``` 554 | #[cfg_attr(feature = "nightly", stable(feature="list", since="0.1.0") )] 555 | impl Index for List { 556 | #[cfg_attr(feature = "nightly", stable(feature="list", since="0.1.0") )] 557 | type Output = T; 558 | 559 | #[inline] 560 | #[cfg_attr(feature = "nightly", stable(feature="list", since="0.2.8") )] 561 | fn index<'a>(&'a self, _index: usize) -> &'a T { 562 | &self[_index as u64] 563 | } 564 | } 565 | 566 | /// Implementation of indexing for `List`. 567 | /// 568 | /// # Examples: 569 | /// ``` 570 | /// # #[macro_use] extern crate seax_svm; 571 | /// # use seax_svm::slist; 572 | /// # use seax_svm::slist::List; 573 | /// # use seax_svm::slist::List::{Cons, Nil}; 574 | /// # fn main () { 575 | /// let list = list!(1,2,3,4,5,6); 576 | /// assert_eq!(list[0usize], 1); 577 | /// # } 578 | /// ``` 579 | #[cfg_attr(feature = "nightly", stable(feature="list", since="0.2.0") )] 580 | impl Index for List { 581 | #[cfg_attr(feature = "nightly", stable(feature="list", since="0.2.0") )] 582 | type Output = T; 583 | 584 | #[inline] 585 | #[cfg_attr(feature = "nightly", stable(feature="list", since="0.2.8") )] 586 | fn index<'a>(&'a self, _index: u64) -> &'a T { 587 | self.get(_index) 588 | .expect(&format!("list index {} out of range", _index)) 589 | } 590 | } 591 | 592 | /// Implementation of indexing for `List`. 593 | /// 594 | /// # Examples: 595 | /// ``` 596 | /// # #[macro_use] extern crate seax_svm; 597 | /// # use seax_svm::slist; 598 | /// # use seax_svm::slist::List; 599 | /// # use seax_svm::slist::List::{Cons, Nil}; 600 | /// # fn main () { 601 | /// let list = list!(1,2,3,4,5,6); 602 | /// assert_eq!(list[0isize], 1); 603 | /// # } 604 | /// ``` 605 | #[cfg_attr(feature = "nightly", stable(feature="list", since="0.1.0") )] 606 | #[cfg_attr(feature = "nightly", 607 | deprecated(since="0.2.0", reason="use unsigned indices instead") 608 | )] 609 | impl Index for List { 610 | #[cfg_attr(feature = "nightly", stable(feature="list", since="0.1.0") )] 611 | #[cfg_attr(feature = "nightly", 612 | deprecated(since="0.2.0", reason="use unsigned indices instead") 613 | )] 614 | type Output = T; 615 | 616 | #[inline] 617 | #[cfg_attr(feature = "nightly", stable(feature="list", since="0.1.0") )] 618 | #[cfg_attr(feature = "nightly", 619 | deprecated(since="0.2.0", reason="use unsigned indices instead") 620 | )] 621 | fn index<'a>(&'a self, _index: i64) -> &'a T { 622 | if _index < 0 { 623 | panic!("attempt to access negative index {}", _index) 624 | } else { 625 | &self[_index as u64] 626 | } 627 | } 628 | } 629 | /// Implementation of indexing for `List`. 630 | /// 631 | /// # Examples: 632 | /// ``` 633 | /// # #[macro_use] extern crate seax_svm; 634 | /// # use seax_svm::slist; 635 | /// # use seax_svm::slist::List; 636 | /// # use seax_svm::slist::List::{Cons, Nil}; 637 | /// # fn main () { 638 | /// let list = list!(1,2,3,4,5,6); 639 | /// assert_eq!(list[0isize], 1); 640 | /// # } 641 | /// ``` 642 | #[cfg_attr(feature = "nightly", stable(feature="list", since="0.1.0") )] 643 | #[cfg_attr(feature = "nightly", 644 | deprecated(since="0.2.0", reason="use unsigned indices instead") 645 | )] 646 | impl Index for List { 647 | #[cfg_attr(feature = "nightly", stable(feature="list", since="0.1.0") )] 648 | #[cfg_attr(feature = "nightly", 649 | deprecated(since="0.2.0", reason="use unsigned indices instead") 650 | )] 651 | type Output = T; 652 | 653 | #[inline] 654 | #[cfg_attr(feature = "nightly", stable(feature="list", since="0.1.0") )] 655 | #[cfg_attr(feature = "nightly", 656 | deprecated(since="0.2.0", reason="use unsigned indices instead") 657 | )] 658 | fn index<'a>(&'a self, _index: isize) -> &'a T { 659 | if _index < 0 { 660 | panic!("attempt to access negative index {}", _index) 661 | } else { 662 | &self[_index as u64] 663 | } 664 | } 665 | } 666 | 667 | #[cfg(test)] 668 | mod tests { 669 | use super::{List, Stack}; 670 | use super::List::{Cons,Nil}; 671 | 672 | #[test] 673 | fn test_list_length() { 674 | let full_list: List = list!(1i32, 2i32, 3i32); 675 | let empty_list: List = List::new(); 676 | assert_eq!(full_list.length(), 3); 677 | assert_eq!(empty_list.length(), 0); 678 | } 679 | 680 | #[test] 681 | fn test_list_to_string() { 682 | let l: List = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil)))))); 683 | assert_eq!(l.to_string(), "(1, 2, 3)"); 684 | } 685 | 686 | #[test] 687 | fn test_stack_length() { 688 | let full_stack: List = list!(1i32, 2i32, 3i32); 689 | let empty_stack: List = Stack::empty(); 690 | assert_eq!(full_stack.length(), 3); 691 | assert_eq!(empty_stack.length(), 0); 692 | } 693 | 694 | #[test] 695 | fn test_stack_peek() { 696 | let full_stack: List = list!(1i32, 2i32, 3i32); 697 | let empty_stack: List = Stack::empty(); 698 | assert_eq!(full_stack.peek(), Some(&1)); 699 | assert_eq!(empty_stack.peek(), None); 700 | } 701 | 702 | #[test] 703 | fn test_stack_push() { 704 | let mut s: List = Stack::empty(); 705 | assert_eq!(s.peek(), None); 706 | s = s.push(1); 707 | assert_eq!(s.peek(), Some(&1)); 708 | s = s.push(6); 709 | assert_eq!(s.peek(), Some(&6)); 710 | } 711 | 712 | #[test] 713 | fn test_stack_pop() { 714 | let mut s: List = Stack::empty(); 715 | assert_eq!(s.peek(), None); 716 | s = s.push(1); 717 | assert_eq!(s.peek(), Some(&1)); 718 | s = s.push(6); 719 | assert_eq!(s.peek(), Some(&6)); 720 | let pop_result = s.pop().unwrap(); // should not break 721 | s = pop_result.1; 722 | assert_eq!(s.peek(), Some(&1)); 723 | assert_eq!(pop_result.0, 6); 724 | } 725 | 726 | #[test] 727 | fn test_list_usize_indexing() { 728 | let l: List = list!(1,2,3,4,5,6); 729 | assert_eq!(l[0usize],1); 730 | assert_eq!(l[1usize],2); 731 | assert_eq!(l[2usize],3); 732 | assert_eq!(l[3usize],4); 733 | assert_eq!(l[4usize],5); 734 | assert_eq!(l[5usize],6); 735 | } 736 | 737 | #[test] 738 | fn test_list_isize_indexing() { 739 | let l: List = list!(1,2,3,4,5,6); 740 | assert_eq!(l[0isize],1); 741 | assert_eq!(l[1isize],2); 742 | assert_eq!(l[2isize],3); 743 | assert_eq!(l[3isize],4); 744 | assert_eq!(l[4isize],5); 745 | assert_eq!(l[5isize],6); 746 | } 747 | 748 | #[test] 749 | fn test_list_macro() { 750 | let l: List = list!(1i32, 2i32, 3i32); 751 | assert_eq!(l.to_string(), "(1, 2, 3)") 752 | } 753 | 754 | #[test] 755 | fn test_list_iter() { 756 | let l: List = list!(1,2,3,4,5,6); 757 | let mut string = String::new(); 758 | for item in l.iter() { 759 | string.push_str((item.to_string() + ", ").as_ref()); 760 | } 761 | let slice: &str = string.as_ref(); // this is necessary because assert_eq! is weird 762 | assert_eq!(slice, "1, 2, 3, 4, 5, 6, ") 763 | } 764 | 765 | } 766 | -------------------------------------------------------------------------------- /src/tests.rs: -------------------------------------------------------------------------------- 1 | use ::slist::Stack; 2 | use ::slist::List::{Cons,Nil}; 3 | use super::State; 4 | use super::cell::Atom::*; 5 | use super::cell::SVMCell::*; 6 | use super::Inst::*; 7 | 8 | use quickcheck::quickcheck; 9 | 10 | use test::Bencher; 11 | /* 12 | #[test] 13 | #[should_panic(expected="[fatal]: expected an instruction on control stack")] 14 | fn test_empty_eval_fail() { 15 | State::new().eval(None,false); 16 | } 17 | 18 | #[test] 19 | #[should_panic(expected="List index 0 out of range")] 20 | fn test_ld_empty_env_fail() { 21 | State { 22 | stack: Stack::empty(), 23 | env: Stack::empty(), 24 | control: list!(InstCell(LD),ListCell(box list!(AtomCell(SInt(1)), AtomCell(SInt(0))))), 25 | dump: Stack::empty(), 26 | }.eval(None, false); 27 | } 28 | 29 | #[test] 30 | #[should_panic(expected="[fatal][LD]: expected list in $e, found 'w'")] 31 | fn test_ld_unexpected_env_fail() { 32 | State { 33 | stack: Stack::empty(), 34 | env: list!(AtomCell(Char('w'))), 35 | control: list!(InstCell(LD),ListCell(box list!(AtomCell(SInt(1)), AtomCell(SInt(1))))), 36 | dump: Stack::empty(), 37 | }.eval(None, false); 38 | } 39 | 40 | #[test] 41 | #[should_panic(expected="fatal][LD]: expected pair, found (0 . nil)\n[fatal] new control: nil")] 42 | fn test_ld_arg_too_short_fail() { 43 | State { 44 | stack: Stack::empty(), 45 | env: Stack::empty(), 46 | control: list!(InstCell(LD),ListCell(box list!(AtomCell(SInt(0))))), 47 | dump: Stack::empty(), 48 | }.eval(None, false); 49 | } 50 | #[test] 51 | #[should_panic(expected="[fatal][LD]: expected pair, found (0 . (1 . (1 . nil)))\n[fatal] new control: nil")] 52 | fn test_ld_arg_too_long_fail() { 53 | State { 54 | stack: Stack::empty(), 55 | env: Stack::empty(), 56 | control: list!(InstCell(LD),ListCell(box list!(AtomCell(SInt(0)), AtomCell(SInt(1)), AtomCell(SInt(1))))), 57 | dump: Stack::empty(), 58 | }.eval(None, false); 59 | } 60 | 61 | #[test] 62 | #[should_panic(expected="[fatal][ADD]: expected first operand, found Some(((1 . nil), nil))")] 63 | fn test_add_unexpected_first_arg_fail () { 64 | State { 65 | stack: list!(ListCell(box list!(AtomCell(SInt(1))))), 66 | env: Stack::empty(), 67 | control: list!(InstCell(ADD)), 68 | dump: Stack::empty(), 69 | }.eval(None, false); 70 | } 71 | 72 | 73 | #[test] 74 | #[should_panic(expected="[fatal][SUB]: expected first operand, found Some(((1 . nil), nil))")] 75 | fn test_sub_unexpected_first_arg_fail () { 76 | State { 77 | stack: list!(ListCell(box list!(AtomCell(SInt(1))))), 78 | env: Stack::empty(), 79 | control: list!(InstCell(SUB)), 80 | dump: Stack::empty(), 81 | }.eval(None, false); 82 | } 83 | 84 | #[test] 85 | #[should_panic(expected="[fatal][DIV]: expected first operand, found Some(((1 . nil), nil))")] 86 | fn test_div_unexpected_first_arg_fail () { 87 | State { 88 | stack: list!(ListCell(box list!(AtomCell(SInt(1))))), 89 | env: Stack::empty(), 90 | control: list!(InstCell(DIV)), 91 | dump: Stack::empty(), 92 | }.eval(None, false); 93 | } 94 | 95 | #[test] 96 | #[should_panic(expected="[fatal][FDIV]: Expected first operand to be atom, found list or instruction")] 97 | fn test_fdiv_unexpected_first_arg_fail () { 98 | State { 99 | stack: list!(ListCell(box list!(AtomCell(SInt(1))))), 100 | env: Stack::empty(), 101 | control: list!(InstCell(FDIV)), 102 | dump: Stack::empty(), 103 | }.eval(None, false); 104 | } 105 | 106 | #[test] 107 | #[should_panic(expected="[fatal][MUL]: expected first operand, found Some(((1 . nil), nil))")] 108 | fn test_mul_unexpected_first_arg_fail () { 109 | State { 110 | stack: list!(ListCell(box list!(AtomCell(SInt(1))))), 111 | env: Stack::empty(), 112 | control: list!(InstCell(MUL)), 113 | dump: Stack::empty(), 114 | }.eval(None, false); 115 | } 116 | 117 | #[test] 118 | #[should_panic(expected="[fatal][ADD]: expected second operand, found Some((nil, nil))")] 119 | fn test_add_type_error () { 120 | State { 121 | stack: list!(AtomCell(SInt(1)), list_cell![]), 122 | env: Stack::empty(), 123 | control: list!(InstCell(ADD)), 124 | dump: Stack::empty(), 125 | }.eval(None, false); 126 | } 127 | #[test] 128 | #[should_panic(expected="[fatal][SUB]: expected second operand, found Some((nil, nil))")] 129 | fn test_sub_type_error () { 130 | State { 131 | stack: list!(AtomCell(SInt(1)), list_cell![]), 132 | env: Stack::empty(), 133 | control: list!(InstCell(SUB)), 134 | dump: Stack::empty(), 135 | }.eval(None, false); 136 | } 137 | 138 | #[test] 139 | #[should_panic(expected="[fatal][DIV]: expected second operand, found Some((nil, nil))")] 140 | fn test_div_type_error () { 141 | State { 142 | stack: list!(AtomCell(SInt(1)), list_cell![]), 143 | env: Stack::empty(), 144 | control: list!(InstCell(DIV)), 145 | dump: Stack::empty(), 146 | }.eval(None, false); 147 | } 148 | 149 | #[test] 150 | #[should_panic(expected="[fatal][FDIV]: TypeError: expected compatible operands, found (FDIV 1 nil)")] 151 | fn test_fdiv_type_error () { 152 | State { 153 | stack: list!(AtomCell(SInt(1)), list_cell![]), 154 | env: Stack::empty(), 155 | control: list!(InstCell(FDIV)), 156 | dump: Stack::empty(), 157 | }.eval(None, false); 158 | } 159 | 160 | #[test] 161 | #[should_panic(expected="[fatal][MUL]: expected second operand, found Some((nil, nil))")] 162 | fn test_mul_type_error () { 163 | State { 164 | stack: list!(AtomCell(SInt(1)), list_cell![]), 165 | env: Stack::empty(), 166 | control: list!(InstCell(MUL)), 167 | dump: Stack::empty(), 168 | }.eval(None, false); 169 | }*/ 170 | 171 | // ----- QuickCheck property tests (WIP) ------------------------------ 172 | #[test] 173 | fn prop_eval_ldc_sint () { 174 | fn prop (x: i64) -> bool { 175 | let state = State { 176 | stack: Stack::empty(), 177 | env: Stack::empty(), 178 | control: list!(InstCell(LDC),AtomCell(SInt(x))), 179 | dump: Stack::empty() 180 | }.eval(None, true).unwrap().0; 181 | 182 | state.stack.peek() == Some(&AtomCell(SInt(x))) 183 | } 184 | quickcheck(prop as fn(i64) -> bool); 185 | } 186 | 187 | #[test] 188 | fn prop_eval_ldc_uint () { 189 | fn prop (x: u64) -> bool { 190 | let state = State { 191 | stack: Stack::empty(), 192 | env: Stack::empty(), 193 | control: list!(InstCell(LDC),AtomCell(UInt(x))), 194 | dump: Stack::empty() 195 | }.eval(None, true).unwrap().0; 196 | 197 | state.stack.peek() == Some(&AtomCell(UInt(x))) 198 | } 199 | 200 | quickcheck(prop as fn(u64) -> bool); 201 | } 202 | 203 | #[test] 204 | fn prop_eval_ldc_float () { 205 | fn prop (x: f64) -> bool { 206 | let state = State { 207 | stack: Stack::empty(), 208 | env: Stack::empty(), 209 | control: list!(InstCell(LDC),AtomCell(Float(x))), 210 | dump: Stack::empty() 211 | }.eval(None, true).unwrap().0; 212 | 213 | state.stack.peek() == Some(&AtomCell(Float(x))) 214 | } 215 | quickcheck(prop as fn(f64) -> bool); 216 | } 217 | 218 | #[test] 219 | fn test_empty_state() { 220 | let state = State::new(); 221 | assert_eq!(state.stack.length(), 0); 222 | assert_eq!(state.env.length(), 0); 223 | assert_eq!(state.control.length(), 0); 224 | assert_eq!(state.dump.length(), 0); 225 | } 226 | 227 | #[test] 228 | fn test_eval_nil () { 229 | let mut state = State { 230 | stack: Stack::empty(), 231 | env: Stack::empty(), 232 | control: list!(InstCell(NIL),AtomCell(SInt(1))), 233 | dump: Stack::empty() 234 | }; 235 | assert_eq!(state.stack.peek(), None); 236 | state = state.eval(None,true).unwrap().0; 237 | assert_eq!(state.stack.peek(), Some(&list_cell![])); 238 | } 239 | 240 | #[test] 241 | fn test_eval_ldc () { 242 | let mut state = State::new(); 243 | assert_eq!(state.stack.peek(), None); 244 | state = State { 245 | stack: state.stack, 246 | env: state.env, 247 | control: list!(InstCell(LDC),AtomCell(SInt(1))), 248 | dump: state.dump 249 | }.eval(None, true).unwrap().0; 250 | assert_eq!(state.stack.peek(), Some(&AtomCell(SInt(1)))); 251 | 252 | state = State { 253 | stack: state.stack, 254 | env: state.env, 255 | control: list!(InstCell(LDC),AtomCell(Char('a'))), 256 | dump: state.dump 257 | }.eval(None, true).unwrap().0; 258 | assert_eq!(state.stack.peek(), Some(&AtomCell(Char('a')))); 259 | 260 | state = State { 261 | stack: state.stack, 262 | env: state.env, 263 | control: list!(InstCell(LDC),AtomCell(Float(1.0f64))), 264 | dump: state.dump 265 | }.eval(None, true).unwrap().0; 266 | assert_eq!(state.stack.peek(), Some(&AtomCell(Float(1.0f64)))); 267 | } 268 | 269 | #[test] 270 | fn test_eval_ld () { 271 | let state = State { 272 | stack: Stack::empty(), 273 | env: list!(list_cell![ 274 | AtomCell(SInt(155)), 275 | AtomCell(UInt(388)) 276 | ]), 277 | control: list!( 278 | InstCell(LD), 279 | list_cell![ AtomCell(SInt(1)), AtomCell(SInt(2)) ] 280 | ), 281 | dump: Stack::empty() 282 | }.eval(None, true).unwrap().0; 283 | assert_eq!(state.stack.peek(), Some(&AtomCell(UInt(388)))); 284 | } 285 | 286 | #[test] 287 | fn test_eval_ldf () { 288 | let state = State { 289 | stack: Stack::empty(), 290 | env: list!( 291 | list_cell![ AtomCell(SInt(155)), AtomCell(UInt(388)) ], 292 | list_cell![ AtomCell(Float(6.66)), AtomCell(SInt(666)) ] 293 | ), 294 | control: list!(InstCell(LDF), list_cell![AtomCell(SInt(133))] ), 295 | dump: Stack::empty() 296 | }.eval(None, true).unwrap().0; 297 | assert_eq!( 298 | state.stack.peek(), 299 | Some(&list_cell![ 300 | list_cell![ AtomCell(SInt(133)) ], 301 | list_cell![ AtomCell(SInt(155)), AtomCell(UInt(388)) ] 302 | ]) 303 | ); 304 | } 305 | 306 | #[test] 307 | fn test_eval_join() { 308 | let state = State { 309 | stack: Stack::empty(), 310 | env: Stack::empty(), 311 | control: list!(InstCell(JOIN)), 312 | dump: list!(list_cell![ 313 | AtomCell(SInt(1)), 314 | AtomCell(SInt(2)) 315 | ]) 316 | }.eval(None, true).unwrap().0; 317 | assert_eq!(state.dump.peek(), None); 318 | assert_eq!(state.control[0u64], AtomCell(SInt(1))); 319 | assert_eq!(state.control[1u64], AtomCell(SInt(2))); 320 | } 321 | 322 | #[test] 323 | fn test_eval_add () { 324 | // ---- Unsigned int addition ---- 325 | let mut state = State { 326 | stack: list!(AtomCell(UInt(1)), AtomCell(UInt(1))), 327 | env: Stack::empty(), 328 | control: list!(InstCell(ADD)), 329 | dump: Stack::empty(), 330 | }.eval(None, true).unwrap().0; 331 | assert_eq!(state.stack.peek(), Some(&AtomCell(UInt(2)))); 332 | 333 | // ---- Signed int addition ---- 334 | state = State { 335 | stack: list!(AtomCell(SInt(-1)), AtomCell(SInt(-1))), 336 | env: Stack::empty(), 337 | control: list!(InstCell(ADD)), 338 | dump: Stack::empty(), 339 | }.eval(None, true).unwrap().0; 340 | assert_eq!(state.stack.peek(), Some(&AtomCell(SInt(-2)))); 341 | 342 | // ---- Float-float addition ---- 343 | state = State { 344 | stack: list!(AtomCell(Float(1.5)), AtomCell(Float(1.5))), 345 | env: Stack::empty(), 346 | control: list!(InstCell(ADD)), 347 | dump: Stack::empty(), 348 | }.eval(None, true).unwrap().0; 349 | assert_eq!(state.stack.peek(), Some(&AtomCell(Float(3.0)))); 350 | 351 | // ---- Float-int type lifting addition ---- 352 | state = State { 353 | stack: list!(AtomCell(Float(1.5)), AtomCell(SInt(1))), 354 | env: Stack::empty(), 355 | control: list!(InstCell(ADD)), 356 | dump: Stack::empty(), 357 | }.eval(None, true).unwrap().0; 358 | assert_eq!(state.stack.peek(), Some(&AtomCell(Float(2.5)))); 359 | state = State { 360 | stack: list!(AtomCell(Float(3.5)), AtomCell(UInt(1))), 361 | env: Stack::empty(), 362 | control: list!(InstCell(ADD)), 363 | dump: Stack::empty(), 364 | }.eval(None, true).unwrap().0; 365 | assert_eq!(state.stack.peek(), Some(&AtomCell(Float(4.5)))); 366 | } 367 | 368 | #[test] 369 | fn test_eval_sub () { 370 | // ---- Unsigned int subtraction ---- 371 | let mut state = State { 372 | stack: list!(AtomCell(UInt(3)), AtomCell(UInt(3))), 373 | env: Stack::empty(), 374 | control: list!(InstCell(SUB)), 375 | dump: Stack::empty(), 376 | }.eval(None, true).unwrap().0; 377 | assert_eq!(state.stack.peek(), Some(&AtomCell(UInt(0)))); 378 | 379 | // ---- Signed int subtraction---- 380 | state = State { 381 | stack: list!(AtomCell(SInt(-3)), AtomCell(SInt(3))), 382 | env: Stack::empty(), 383 | control: list!(InstCell(SUB)), 384 | dump: Stack::empty(), 385 | }.eval(None, true).unwrap().0; 386 | assert_eq!(state.stack.peek(), Some(&AtomCell(SInt(-6)))); 387 | 388 | // ---- Float-float subtraction ---- 389 | state = State { 390 | stack: list!(AtomCell(Float(1.5)), AtomCell(Float(2.0))), 391 | env: Stack::empty(), 392 | control: list!(InstCell(SUB)), 393 | dump: Stack::empty(), 394 | }.eval(None, true).unwrap().0; 395 | assert_eq!(state.stack.peek(), Some(&AtomCell(Float(-0.5)))); 396 | 397 | // ---- Float-int type lifting subtraction ---- 398 | state = State { 399 | stack: list!(AtomCell(Float(2.5)), AtomCell(SInt(-2))), 400 | env: Stack::empty(), 401 | control: list!(InstCell(SUB)), 402 | dump: Stack::empty(), 403 | }.eval(None, true).unwrap().0; 404 | assert_eq!(state.stack.peek(), Some(&AtomCell(Float(4.5)))); 405 | 406 | state = State { 407 | stack: list!(AtomCell(Float(3.5)), AtomCell(UInt(2))), 408 | env: Stack::empty(), 409 | control: list!(InstCell(SUB)), 410 | dump: Stack::empty(), 411 | }.eval(None, true).unwrap().0; 412 | assert_eq!(state.stack.peek(), Some(&AtomCell(Float(1.5)))); 413 | } 414 | 415 | #[test] 416 | fn test_eval_mul () { 417 | // ---- Unsigned int multiplication ---- 418 | let mut state = State { 419 | stack: list!(AtomCell(UInt(2)), AtomCell(UInt(3))), 420 | env: Stack::empty(), 421 | control: list!(InstCell(MUL)), 422 | dump: Stack::empty(), 423 | }.eval(None, true).unwrap().0; 424 | assert_eq!(state.stack.peek(), Some(&AtomCell(UInt(6)))); 425 | 426 | // ---- Signed int multiplication---- 427 | state = State { 428 | stack: list!(AtomCell(SInt(-2)), AtomCell(SInt(-3))), 429 | env: Stack::empty(), 430 | control: list!(InstCell(MUL)), 431 | dump: Stack::empty(), 432 | }.eval(None, true).unwrap().0; 433 | assert_eq!(state.stack.peek(), Some(&AtomCell(SInt(6)))); 434 | 435 | // ---- Float-float multiplication ---- 436 | state = State { 437 | stack: list!(AtomCell(Float(1.5)), AtomCell(Float(2.0))), 438 | env: Stack::empty(), 439 | control: list!(InstCell(MUL)), 440 | dump: Stack::empty(), 441 | }.eval(None, true).unwrap().0; 442 | assert_eq!(state.stack.peek(), Some(&AtomCell(Float(3.0)))); 443 | 444 | // ---- Float-int type lifting multiplication ---- 445 | state = State { 446 | stack: list!(AtomCell(Float(1.5)), AtomCell(SInt(2))), 447 | env: Stack::empty(), 448 | control: list!(InstCell(MUL)), 449 | dump: Stack::empty(), 450 | }.eval(None, true).unwrap().0; 451 | assert_eq!(state.stack.peek(), Some(&AtomCell(Float(3.0)))); 452 | 453 | state = State { 454 | stack: list!(AtomCell(Float(3.5)), AtomCell(UInt(2))), 455 | env: Stack::empty(), 456 | control: list!(InstCell(MUL)), 457 | dump: Stack::empty(), 458 | }.eval(None, true).unwrap().0; 459 | assert_eq!(state.stack.peek(), Some(&AtomCell(Float(7.0)))); 460 | } 461 | 462 | #[test] 463 | fn test_eval_div () { 464 | // ---- Unsigned int divison ---- 465 | let mut state = State { 466 | stack: list!(AtomCell(UInt(6)), AtomCell(UInt(2))), 467 | env: Stack::empty(), 468 | control: list!(InstCell(DIV)), 469 | dump: Stack::empty(), 470 | }.eval(None, true).unwrap().0; 471 | assert_eq!(state.stack.peek(), Some(&AtomCell(UInt(3)))); 472 | 473 | // ---- Signed int divison ---- 474 | state = State { 475 | stack: list!(AtomCell(SInt(-6)), AtomCell(SInt(2))), 476 | env: Stack::empty(), 477 | control: list!(InstCell(DIV)), 478 | dump: Stack::empty(), 479 | }.eval(None, true).unwrap().0; 480 | assert_eq!(state.stack.peek(), Some(&AtomCell(SInt(-3)))); 481 | 482 | // ---- Float-float divison ---- 483 | state = State { 484 | stack: list!(AtomCell(Float(3.0)), AtomCell(Float(2.0))), 485 | env: Stack::empty(), 486 | control: list!(InstCell(DIV)), 487 | dump: Stack::empty(), 488 | }.eval(None, true).unwrap().0; 489 | assert_eq!(state.stack.peek(), Some(&AtomCell(Float(1.5)))); 490 | 491 | // ---- Float-int type lifting divison ---- 492 | state = State { 493 | stack: list!(AtomCell(Float(3.0)), AtomCell(SInt(2))), 494 | env: Stack::empty(), 495 | control: list!(InstCell(DIV)), 496 | dump: Stack::empty(), 497 | }.eval(None, true).unwrap().0; 498 | assert_eq!(state.stack.peek(), Some(&AtomCell(Float(1.5)))); 499 | 500 | state = State { 501 | stack: list!(AtomCell(Float(3.0)), AtomCell(UInt(2))), 502 | env: Stack::empty(), 503 | control: list!(InstCell(DIV)), 504 | dump: Stack::empty(), 505 | }.eval(None, true).unwrap().0; 506 | assert_eq!(state.stack.peek(), Some(&AtomCell(Float(1.5)))); 507 | } 508 | 509 | #[test] 510 | fn test_eval_fdiv () { 511 | // ---- Unsigned int divison ---- 512 | let mut state = State { 513 | stack: list!(AtomCell(UInt(3)), AtomCell(UInt(2))), 514 | env: Stack::empty(), 515 | control: list!(InstCell(FDIV)), 516 | dump: Stack::empty(), 517 | }.eval(None, true).unwrap().0; 518 | assert_eq!(state.stack.peek(), Some(&AtomCell(Float(1.5)))); 519 | 520 | // ---- Signed int divison ---- 521 | state = State { 522 | stack: list!(AtomCell(SInt(-3)), AtomCell(SInt(2))), 523 | env: Stack::empty(), 524 | control: list!(InstCell(FDIV)), 525 | dump: Stack::empty(), 526 | }.eval(None, true).unwrap().0; 527 | assert_eq!(state.stack.peek(), Some(&AtomCell(Float(-1.5)))); 528 | 529 | // ---- Float-float divison --- 530 | state = State { 531 | stack: list!(AtomCell(Float(3.0)), AtomCell(Float(2.0))), 532 | env: Stack::empty(), 533 | control: list!(InstCell(FDIV)), 534 | dump: Stack::empty(), 535 | }.eval(None, true).unwrap().0; 536 | assert_eq!(state.stack.peek(), Some(&AtomCell(Float(1.5)))); 537 | } 538 | 539 | #[test] 540 | fn test_eval_mod () { 541 | // ---- Unsigned int modulus ---- 542 | let mut state = State { 543 | stack: list!(AtomCell(UInt(3)), AtomCell(UInt(2))), 544 | env: Stack::empty(), 545 | control: list!(InstCell(MOD)), 546 | dump: Stack::empty(), 547 | }.eval(None, true).unwrap().0; 548 | assert_eq!(state.stack.peek(), Some(&AtomCell(UInt(3%2)))); 549 | 550 | // ---- Signed int modulus ---- 551 | state = State { 552 | stack: list!(AtomCell(SInt(-3)), AtomCell(SInt(2))), 553 | env: Stack::empty(), 554 | control: list!(InstCell(MOD)), 555 | dump: Stack::empty(), 556 | }.eval(None, true).unwrap().0; 557 | assert_eq!(state.stack.peek(), Some(&AtomCell(SInt(-3%2)))); 558 | 559 | // ---- Float-float modulus--- 560 | state = State { 561 | stack: list!(AtomCell(Float(3.0)), AtomCell(Float(2.0))), 562 | env: Stack::empty(), 563 | control: list!(InstCell(MOD)), 564 | dump: Stack::empty(), 565 | }.eval(None, true).unwrap().0; 566 | assert_eq!(state.stack.peek(), Some(&AtomCell(Float(3.0%2.0)))); 567 | 568 | // ---- Float-int type lifting modulus---- 569 | state = State { 570 | stack: list!(AtomCell(Float(3.0)), AtomCell(SInt(2))), 571 | env: Stack::empty(), 572 | control: list!(InstCell(MOD)), 573 | dump: Stack::empty(), 574 | }.eval(None, true).unwrap().0; 575 | assert_eq!(state.stack.peek(), Some(&AtomCell(Float(3.0%2.0)))); 576 | 577 | state = State { 578 | stack: list!(AtomCell(Float(3.0)), AtomCell(UInt(2))), 579 | env: Stack::empty(), 580 | control: list!(InstCell(MOD)), 581 | dump: Stack::empty(), 582 | }.eval(None, true).unwrap().0; 583 | assert_eq!(state.stack.peek(), Some(&AtomCell(Float(3.0%2.0)))); 584 | } 585 | 586 | #[test] 587 | fn test_eval_eq () { 588 | // ---- Unsigned int equality ---- 589 | let mut state = State { 590 | stack: list!(AtomCell(UInt(3)), AtomCell(UInt(3))), 591 | env: Stack::empty(), 592 | control: list!(InstCell(EQ)), 593 | dump: Stack::empty(), 594 | }.eval(None, true).unwrap().0; 595 | assert!( 596 | state.stack.peek() != Some(&list_cell![]) && 597 | state.stack.peek() != None 598 | ); 599 | 600 | state = State { 601 | stack: list!(AtomCell(UInt(1)), AtomCell(UInt(2))), 602 | env: Stack::empty(), 603 | control: list!(InstCell(EQ)), 604 | dump: Stack::empty(), 605 | }.eval(None, true).unwrap().0; 606 | assert_eq!(state.stack.peek(), Some(&list_cell![])); 607 | 608 | // ---- Signed int equality ---- 609 | state = State { 610 | stack: list!(AtomCell(SInt(3)), AtomCell(SInt(3))), 611 | env: Stack::empty(), 612 | control: list!(InstCell(EQ)), 613 | dump: Stack::empty(), 614 | }.eval(None, true).unwrap().0; 615 | assert!( 616 | state.stack.peek() != Some(&list_cell![]) && 617 | state.stack.peek() != None 618 | ); 619 | 620 | state = State { 621 | stack: list!(AtomCell(SInt(-2)), AtomCell(SInt(2))), 622 | env: Stack::empty(), 623 | control: list!(InstCell(EQ)), 624 | dump: Stack::empty(), 625 | }.eval(None, true).unwrap().0; 626 | assert_eq!(state.stack.peek(), Some(&list_cell![])); 627 | 628 | 629 | // ---- Float equality ---- 630 | state = State { 631 | stack: list!(AtomCell(Float(3.0)), AtomCell(Float(3.0))), 632 | env: Stack::empty(), 633 | control: list!(InstCell(EQ)), 634 | dump: Stack::empty(), 635 | }.eval(None, true).unwrap().0; 636 | assert!( 637 | state.stack.peek() != Some(&list_cell![]) && 638 | state.stack.peek() != None 639 | ); 640 | 641 | state = State { 642 | stack: list!(AtomCell(Float(-2.0)), AtomCell(Float(2.0))), 643 | env: Stack::empty(), 644 | control: list!(InstCell(EQ)), 645 | dump: Stack::empty(), 646 | }.eval(None, true).unwrap().0; 647 | assert_eq!(state.stack.peek(), Some(&list_cell![])); 648 | 649 | state = State { 650 | stack: list!(AtomCell(Float(2.11)), AtomCell(Float(2.1))), 651 | env: Stack::empty(), 652 | control: list!(InstCell(EQ)), 653 | dump: Stack::empty(), 654 | }.eval(None, true).unwrap().0; 655 | assert_eq!(state.stack.peek(), Some(&list_cell![])); 656 | 657 | } 658 | 659 | #[test] 660 | fn test_eval_gt () { 661 | // ---- Unsigned int greater-than ---- 662 | let mut state = State { 663 | stack: list!(AtomCell(UInt(3)), AtomCell(UInt(2))), 664 | env: Stack::empty(), 665 | control: list!(InstCell(GT)), 666 | dump: Stack::empty(), 667 | }.eval(None, true).unwrap().0; 668 | assert!( 669 | state.stack.peek() != Some(&list_cell![]) && 670 | state.stack.peek() != None 671 | ); 672 | 673 | state = State { 674 | stack: list!(AtomCell(UInt(1)), AtomCell(UInt(2))), 675 | env: Stack::empty(), 676 | control: list!(InstCell(GT)), 677 | dump: Stack::empty(), 678 | }.eval(None, true).unwrap().0; 679 | assert_eq!(state.stack.peek(), Some(&list_cell![])); 680 | 681 | // ---- Signed int greater-than ---- 682 | state = State { 683 | stack: list!(AtomCell(SInt(3)), AtomCell(SInt(2))), 684 | env: Stack::empty(), 685 | control: list!(InstCell(GT)), 686 | dump: Stack::empty(), 687 | }.eval(None, true).unwrap().0; 688 | assert!( 689 | state.stack.peek() != Some(&list_cell![]) && 690 | state.stack.peek() != None 691 | ); 692 | 693 | state = State { 694 | stack: list!(AtomCell(SInt(-2)), AtomCell(SInt(2))), 695 | env: Stack::empty(), 696 | control: list!(InstCell(GT)), 697 | dump: Stack::empty(), 698 | }.eval(None, true).unwrap().0; 699 | assert_eq!(state.stack.peek(), Some(&list_cell![])); 700 | 701 | 702 | // ---- Float greater-than---- 703 | state = State { 704 | stack: list!(AtomCell(Float(3.0)), AtomCell(Float(1.0))), 705 | env: Stack::empty(), 706 | control: list!(InstCell(GT)), 707 | dump: Stack::empty(), 708 | }.eval(None, true).unwrap().0; 709 | assert!( 710 | state.stack.peek() != Some(&list_cell![]) && 711 | state.stack.peek() != None 712 | ); 713 | 714 | state = State { 715 | stack: list!(AtomCell(Float(-2.0)), AtomCell(Float(2.0))), 716 | env: Stack::empty(), 717 | control: list!(InstCell(GT)), 718 | dump: Stack::empty(), 719 | }.eval(None, true).unwrap().0; 720 | assert_eq!(state.stack.peek(), Some(&list_cell![])); 721 | 722 | state = State { 723 | stack: list!(AtomCell(Float(2.11)), AtomCell(Float(2.1))), 724 | env: Stack::empty(), 725 | control: list!(InstCell(GT)), 726 | dump: Stack::empty(), 727 | }.eval(None, true).unwrap().0; 728 | assert!( 729 | state.stack.peek() != Some(&list_cell![]) && 730 | state.stack.peek() != None 731 | ); 732 | 733 | // ---- Mixed type greater-than --- 734 | state = State { 735 | stack: list!(AtomCell(Float(3.0)), AtomCell(SInt(2))), 736 | env: Stack::empty(), 737 | control: list!(InstCell(GT)), 738 | dump: Stack::empty(), 739 | }.eval(None, true).unwrap().0; 740 | assert!( 741 | state.stack.peek() != Some(&list_cell![]) && 742 | state.stack.peek() != None 743 | ); 744 | 745 | state = State { 746 | stack: list!(AtomCell(Float(1.0)), AtomCell(SInt(1))), 747 | env: Stack::empty(), 748 | control: list!(InstCell(GT)), 749 | dump: Stack::empty(), 750 | }.eval(None, true).unwrap().0; 751 | assert!( 752 | state.stack.peek() != Some(&list_cell![]) && 753 | state.stack.peek() != None 754 | ); 755 | 756 | state = State { 757 | stack: list!(AtomCell(UInt(1)), AtomCell(Float(2.0))), 758 | env: Stack::empty(), 759 | control: list!(InstCell(GT)), 760 | dump: Stack::empty(), 761 | }.eval(None, true).unwrap().0; 762 | assert_eq!(state.stack.peek(), Some(&list_cell![])); 763 | 764 | } 765 | 766 | #[test] 767 | fn test_eval_gte () { 768 | // ---- Unsigned int greater-than ---- 769 | let mut state = State { 770 | stack: list!(AtomCell(UInt(3)), AtomCell(UInt(2))), 771 | env: Stack::empty(), 772 | control: list!(InstCell(GTE)), 773 | dump: Stack::empty(), 774 | }.eval(None, true).unwrap().0; 775 | assert!( 776 | state.stack.peek() != Some(&list_cell![]) && 777 | state.stack.peek() != None 778 | ); 779 | 780 | state = State { 781 | stack: list!(AtomCell(UInt(1)), AtomCell(UInt(1))), 782 | env: Stack::empty(), 783 | control: list!(InstCell(GTE)), 784 | dump: Stack::empty(), 785 | }.eval(None, true).unwrap().0; 786 | assert!( 787 | state.stack.peek() != Some(&list_cell![]) && 788 | state.stack.peek() != None 789 | ); 790 | 791 | state = State { 792 | stack: list!(AtomCell(UInt(1)), AtomCell(UInt(2))), 793 | env: Stack::empty(), 794 | control: list!(InstCell(GTE)), 795 | dump: Stack::empty(), 796 | }.eval(None, true).unwrap().0; 797 | assert_eq!(state.stack.peek(), Some(&list_cell![])); 798 | 799 | 800 | // ---- Signed int greater-than ---- 801 | state = State { 802 | stack: list!(AtomCell(SInt(3)), AtomCell(SInt(2))), 803 | env: Stack::empty(), 804 | control: list!(InstCell(GTE)), 805 | dump: Stack::empty(), 806 | }.eval(None, true).unwrap().0; 807 | assert!( 808 | state.stack.peek() != Some(&list_cell![]) && 809 | state.stack.peek() != None 810 | ); 811 | 812 | state = State { 813 | stack: list!(AtomCell(SInt(1)), AtomCell(SInt(1))), 814 | env: Stack::empty(), 815 | control: list!(InstCell(GTE)), 816 | dump: Stack::empty(), 817 | }.eval(None, true).unwrap().0; 818 | assert!( 819 | state.stack.peek() != Some(&list_cell![]) && 820 | state.stack.peek() != None 821 | ); 822 | 823 | state = State { 824 | stack: list!(AtomCell(SInt(1)), AtomCell(SInt(2))), 825 | env: Stack::empty(), 826 | control: list!(InstCell(GTE)), 827 | dump: Stack::empty(), 828 | }.eval(None, true).unwrap().0; 829 | assert_eq!(state.stack.peek(), Some(&list_cell![])); 830 | 831 | 832 | // ---- Float greater-than---- 833 | state = State { 834 | stack: list!(AtomCell(Float(3.0)), AtomCell(Float(2.0))), 835 | env: Stack::empty(), 836 | control: list!(InstCell(GTE)), 837 | dump: Stack::empty(), 838 | }.eval(None, true).unwrap().0; 839 | assert!( 840 | state.stack.peek() != Some(&list_cell![]) && 841 | state.stack.peek() != None 842 | ); 843 | 844 | state = State { 845 | stack: list!(AtomCell(Float(1.0)), AtomCell(Float(1.0))), 846 | env: Stack::empty(), 847 | control: list!(InstCell(GTE)), 848 | dump: Stack::empty(), 849 | }.eval(None, true).unwrap().0; 850 | assert!( 851 | state.stack.peek() != Some(&list_cell![]) && 852 | state.stack.peek() != None 853 | ); 854 | 855 | state = State { 856 | stack: list!(AtomCell(Float(1.0)), AtomCell(Float(2.0))), 857 | env: Stack::empty(), 858 | control: list!(InstCell(GTE)), 859 | dump: Stack::empty(), 860 | }.eval(None, true).unwrap().0; 861 | assert_eq!(state.stack.peek(), Some(&list_cell![])); 862 | 863 | // ---- Mixed type greater-than-equal --- 864 | state = State { 865 | stack: list!(AtomCell(Float(3.0)), AtomCell(SInt(2))), 866 | env: Stack::empty(), 867 | control: list!(InstCell(GTE)), 868 | dump: Stack::empty(), 869 | }.eval(None, true).unwrap().0; 870 | assert!( 871 | state.stack.peek() != Some(&list_cell![]) && 872 | state.stack.peek() != None 873 | ); 874 | 875 | state = State { 876 | stack: list!(AtomCell(Float(1.0)), AtomCell(SInt(1))), 877 | env: Stack::empty(), 878 | control: list!(InstCell(GTE)), 879 | dump: Stack::empty(), 880 | }.eval(None, true).unwrap().0; 881 | assert!( 882 | state.stack.peek() != Some(&list_cell![]) && 883 | state.stack.peek() != None 884 | ); 885 | 886 | state = State { 887 | stack: list!(AtomCell(UInt(1)), AtomCell(Float(2.0))), 888 | env: Stack::empty(), 889 | control: list!(InstCell(GTE)), 890 | dump: Stack::empty(), 891 | }.eval(None, true).unwrap().0; 892 | assert_eq!(state.stack.peek(), Some(&list_cell![])); 893 | } 894 | 895 | #[test] 896 | fn test_eval_lt () { 897 | // ---- Unsigned int greater-than ---- 898 | let mut state = State { 899 | stack: list!(AtomCell(UInt(3)), AtomCell(UInt(2))), 900 | env: Stack::empty(), 901 | control: list!(InstCell(LT)), 902 | dump: Stack::empty(), 903 | }.eval(None, true).unwrap().0; 904 | assert_eq!(state.stack.peek(), Some(&list_cell![])); 905 | 906 | state = State { 907 | stack: list!(AtomCell(UInt(1)), AtomCell(UInt(2))), 908 | env: Stack::empty(), 909 | control: list!(InstCell(LT)), 910 | dump: Stack::empty(), 911 | }.eval(None, true).unwrap().0; 912 | assert!( 913 | state.stack.peek() != Some(&list_cell![]) && 914 | state.stack.peek() != None 915 | ); 916 | 917 | state = State { 918 | stack: list!(AtomCell(UInt(1)), AtomCell(UInt(1))), 919 | env: Stack::empty(), 920 | control: list!(InstCell(LT)), 921 | dump: Stack::empty(), 922 | }.eval(None, true).unwrap().0; 923 | assert_eq!(state.stack.peek(), Some(&list_cell![])); 924 | 925 | 926 | // ---- Signed int greater-than ---- 927 | state = State { 928 | stack: list!(AtomCell(SInt(3)), AtomCell(SInt(2))), 929 | env: Stack::empty(), 930 | control: list!(InstCell(LT)), 931 | dump: Stack::empty(), 932 | }.eval(None, true).unwrap().0; 933 | assert_eq!(state.stack.peek(), Some(&list_cell![])); 934 | 935 | state = State { 936 | stack: list!(AtomCell(SInt(-2)), AtomCell(SInt(2))), 937 | env: Stack::empty(), 938 | control: list!(InstCell(LT)), 939 | dump: Stack::empty(), 940 | }.eval(None, true).unwrap().0; 941 | assert!( 942 | state.stack.peek() != Some(&list_cell![]) && 943 | state.stack.peek() != None 944 | ); 945 | 946 | state = State { 947 | stack: list!(AtomCell(SInt(2)), AtomCell(SInt(2))), 948 | env: Stack::empty(), 949 | control: list!(InstCell(LT)), 950 | dump: Stack::empty(), 951 | }.eval(None, true).unwrap().0; 952 | assert_eq!(state.stack.peek(), Some(&list_cell![])); 953 | 954 | 955 | // ---- Float greater-than---- 956 | state = State { 957 | stack: list!(AtomCell(Float(3.0)), AtomCell(Float(1.0))), 958 | env: Stack::empty(), 959 | control: list!(InstCell(LT)), 960 | dump: Stack::empty(), 961 | }.eval(None, true).unwrap().0; 962 | assert_eq!(state.stack.peek(), Some(&list_cell![])); 963 | 964 | state = State { 965 | stack: list!(AtomCell(Float(-2.0)), AtomCell(Float(2.0))), 966 | env: Stack::empty(), 967 | control: list!(InstCell(LT)), 968 | dump: Stack::empty(), 969 | }.eval(None, true).unwrap().0; 970 | assert!( 971 | state.stack.peek() != Some(&list_cell![]) && 972 | state.stack.peek() != None 973 | ); 974 | 975 | state = State { 976 | stack: list!(AtomCell(Float(2.11)), AtomCell(Float(2.1))), 977 | env: Stack::empty(), 978 | control: list!(InstCell(LT)), 979 | dump: Stack::empty(), 980 | }.eval(None, true).unwrap().0; 981 | assert_eq!(state.stack.peek(), Some(&list_cell![])); 982 | 983 | state = State { 984 | stack: list!(AtomCell(Float(2.0)), AtomCell(Float(2.0))), 985 | env: Stack::empty(), 986 | control: list!(InstCell(LT)), 987 | dump: Stack::empty(), 988 | }.eval(None, true).unwrap().0; 989 | assert_eq!(state.stack.peek(), Some(&list_cell![])); 990 | 991 | // ---- Mixed type greater-than --- 992 | state = State { 993 | stack: list!(AtomCell(Float(3.0)), AtomCell(SInt(2))), 994 | env: Stack::empty(), 995 | control: list!(InstCell(LT)), 996 | dump: Stack::empty(), 997 | }.eval(None, true).unwrap().0; 998 | assert_eq!(state.stack.peek(), Some(&list_cell![])); 999 | 1000 | state = State { 1001 | stack: list!(AtomCell(Float(1.0)), AtomCell(SInt(1))), 1002 | env: Stack::empty(), 1003 | control: list!(InstCell(LT)), 1004 | dump: Stack::empty(), 1005 | }.eval(None, true).unwrap().0; 1006 | assert_eq!(state.stack.peek(), Some(&list_cell![])); 1007 | 1008 | state = State { 1009 | stack: list!(AtomCell(UInt(1)), AtomCell(Float(2.0))), 1010 | env: Stack::empty(), 1011 | control: list!(InstCell(LT)), 1012 | dump: Stack::empty(), 1013 | }.eval(None, true).unwrap().0; 1014 | assert!( 1015 | state.stack.peek() != Some(&list_cell![]) && 1016 | state.stack.peek() != None 1017 | ); 1018 | 1019 | } 1020 | 1021 | #[test] 1022 | fn test_eval_lte () { 1023 | // ---- Unsigned int greater-than ---- 1024 | let mut state = State { 1025 | stack: list!(AtomCell(UInt(3)), AtomCell(UInt(2))), 1026 | env: Stack::empty(), 1027 | control: list!(InstCell(LTE)), 1028 | dump: Stack::empty(), 1029 | }.eval(None, true).unwrap().0; 1030 | assert_eq!(state.stack.peek(), Some(&list_cell![])); 1031 | 1032 | state = State { 1033 | stack: list!(AtomCell(UInt(1)), AtomCell(UInt(1))), 1034 | env: Stack::empty(), 1035 | control: list!(InstCell(LTE)), 1036 | dump: Stack::empty(), 1037 | }.eval(None, true).unwrap().0; 1038 | assert!( 1039 | state.stack.peek() != Some(&list_cell![]) && 1040 | state.stack.peek() != None 1041 | ); 1042 | 1043 | state = State { 1044 | stack: list!(AtomCell(UInt(1)), AtomCell(UInt(2))), 1045 | env: Stack::empty(), 1046 | control: list!(InstCell(LTE)), 1047 | dump: Stack::empty(), 1048 | }.eval(None, true).unwrap().0; 1049 | assert!( 1050 | state.stack.peek() != Some(&list_cell![]) && 1051 | state.stack.peek() != None 1052 | ); 1053 | 1054 | 1055 | // ---- Signed int greater-than ---- 1056 | state = State { 1057 | stack: list!(AtomCell(SInt(3)), AtomCell(SInt(2))), 1058 | env: Stack::empty(), 1059 | control: list!(InstCell(LTE)), 1060 | dump: Stack::empty(), 1061 | }.eval(None, true).unwrap().0; 1062 | assert_eq!(state.stack.peek(), Some(&list_cell![])); 1063 | 1064 | state = State { 1065 | stack: list!(AtomCell(SInt(1)), AtomCell(SInt(1))), 1066 | env: Stack::empty(), 1067 | control: list!(InstCell(LTE)), 1068 | dump: Stack::empty(), 1069 | }.eval(None, true).unwrap().0; 1070 | assert!( 1071 | state.stack.peek() != Some(&list_cell![]) && 1072 | state.stack.peek() != None 1073 | ); 1074 | 1075 | state = State { 1076 | stack: list!(AtomCell(SInt(1)), AtomCell(SInt(2))), 1077 | env: Stack::empty(), 1078 | control: list!(InstCell(LTE)), 1079 | dump: Stack::empty(), 1080 | }.eval(None, true).unwrap().0; 1081 | assert!( 1082 | state.stack.peek() != Some(&list_cell![]) && 1083 | state.stack.peek() != None 1084 | ); 1085 | 1086 | 1087 | // ---- Float greater-than---- 1088 | state = State { 1089 | stack: list!(AtomCell(Float(3.0)), AtomCell(Float(2.0))), 1090 | env: Stack::empty(), 1091 | control: list!(InstCell(LTE)), 1092 | dump: Stack::empty(), 1093 | }.eval(None, true).unwrap().0; 1094 | assert_eq!(state.stack.peek(), Some(&list_cell![])); 1095 | 1096 | state = State { 1097 | stack: list!(AtomCell(Float(1.0)), AtomCell(Float(1.0))), 1098 | env: Stack::empty(), 1099 | control: list!(InstCell(LTE)), 1100 | dump: Stack::empty(), 1101 | }.eval(None, true).unwrap().0; 1102 | assert!( 1103 | state.stack.peek() != Some(&list_cell![]) && 1104 | state.stack.peek() != None 1105 | ); 1106 | 1107 | state = State { 1108 | stack: list!(AtomCell(Float(1.0)), AtomCell(Float(2.0))), 1109 | env: Stack::empty(), 1110 | control: list!(InstCell(LTE)), 1111 | dump: Stack::empty(), 1112 | }.eval(None, true).unwrap().0; 1113 | assert!( 1114 | state.stack.peek() != Some(&list_cell![]) && 1115 | state.stack.peek() != None 1116 | ); 1117 | 1118 | // ---- Mixed type greater-than-equal --- 1119 | state = State { 1120 | stack: list!(AtomCell(Float(3.0)), AtomCell(SInt(2))), 1121 | env: Stack::empty(), 1122 | control: list!(InstCell(LTE)), 1123 | dump: Stack::empty(), 1124 | }.eval(None, true).unwrap().0; 1125 | assert_eq!(state.stack.peek(), Some(&list_cell![])); 1126 | 1127 | state = State { 1128 | stack: list!(AtomCell(Float(1.0)), AtomCell(SInt(1))), 1129 | env: Stack::empty(), 1130 | control: list!(InstCell(LTE)), 1131 | dump: Stack::empty(), 1132 | }.eval(None, true).unwrap().0; 1133 | assert_eq!(state.stack.peek(), Some(&list_cell![]) 1134 | // TODO: this expects wrong float behaviour, fix 1135 | ); 1136 | 1137 | state = State { 1138 | stack: list!(AtomCell(UInt(1)), AtomCell(Float(2.0))), 1139 | env: Stack::empty(), 1140 | control: list!(InstCell(LTE)), 1141 | dump: Stack::empty(), 1142 | }.eval(None, true).unwrap().0; 1143 | assert!( 1144 | state.stack.peek() != Some(&list_cell![]) && 1145 | state.stack.peek() != None 1146 | ); 1147 | } 1148 | 1149 | #[test] 1150 | fn test_eval_ret() { 1151 | let state = State { 1152 | stack: list!(AtomCell(SInt(100)), AtomCell(SInt(320))), 1153 | env: Stack::empty(), 1154 | control: list!(InstCell(RET)), 1155 | dump: list!( 1156 | list_cell![ AtomCell(Char('S')), AtomCell(Char('L')) ], 1157 | list_cell![ 1158 | list_cell![ AtomCell(Char('E')), AtomCell(Char('L')) ], 1159 | list_cell![ AtomCell(Char('E')), AtomCell(Char('D')) ] 1160 | ], 1161 | list_cell![ AtomCell(Char('C')), AtomCell(Char('L')) ] 1162 | ) 1163 | }.eval(None, true).unwrap().0; 1164 | // stack should have return arg + first elem on dump 1165 | assert_eq!(state.stack.peek(), Some(&AtomCell(SInt(100)))); // test these using peek for now since indexing is borked 1166 | assert_eq!(state.stack[0u64], AtomCell(SInt(100))); 1167 | assert_eq!(state.stack[1u64], AtomCell(Char('S'))); 1168 | assert_eq!(state.stack[2u64], AtomCell(Char('L'))); 1169 | // env should have second element from dump 1170 | assert_eq!( 1171 | state.env.peek(), 1172 | Some(&list_cell![ AtomCell(Char('E')), AtomCell(Char('L')) ]) 1173 | ); 1174 | assert_eq!( 1175 | state.env[0u64], 1176 | list_cell![ AtomCell(Char('E')), AtomCell(Char('L')) ] 1177 | ); 1178 | assert_eq!( 1179 | state.env[1u64], 1180 | list_cell![ AtomCell(Char('E')), AtomCell(Char('D')) ] 1181 | ); 1182 | // control should have third element from dump 1183 | assert_eq!(state.control.peek(), Some(&AtomCell(Char('C')))); 1184 | assert_eq!(state.control[0u64], AtomCell(Char('C'))); 1185 | assert_eq!(state.control[1u64], AtomCell(Char('L'))); 1186 | assert_eq!(state.dump.peek(), None); 1187 | } 1188 | 1189 | #[test] 1190 | fn test_eval_dum() { 1191 | let state = State { 1192 | stack: Stack::empty(), 1193 | env: list!(list_cell![ AtomCell(Char('a')) ]), 1194 | control: list!(InstCell(DUM)), 1195 | dump: Stack::empty(), 1196 | }.eval(None, true).unwrap().0; 1197 | assert_eq!(state.env.peek(), Some(&list_cell![])); 1198 | } 1199 | 1200 | #[test] 1201 | fn test_eval_ap() { 1202 | let state = State { 1203 | stack: list!(list_cell![ 1204 | list_cell![ 1205 | InstCell(RET), 1206 | InstCell(ADD), 1207 | AtomCell(SInt(1)), 1208 | InstCell(LDC), 1209 | list_cell![ 1210 | AtomCell(UInt(0)), 1211 | AtomCell(UInt(0)) 1212 | ], 1213 | InstCell(LD) 1214 | ], 1215 | list_cell![ list_cell![ AtomCell(SInt(1)) ] ] 1216 | ], 1217 | list_cell![ AtomCell(Char('Q')) ] 1218 | ), 1219 | env: list!(list_cell![ AtomCell(Char('D')) ]), 1220 | control: list!(InstCell(AP), InstCell(DUM)), 1221 | dump: Stack::empty() 1222 | }.eval(None, true).unwrap().0; 1223 | assert_eq!(state.stack.peek(), None ); 1224 | assert_eq!( 1225 | state.control, 1226 | list!( 1227 | InstCell(RET), 1228 | InstCell(ADD), 1229 | AtomCell(SInt(1)), 1230 | InstCell(LDC), 1231 | list_cell![ AtomCell(UInt(0)), AtomCell(UInt(0)) ], 1232 | InstCell(LD))); 1233 | assert_eq!( 1234 | state.env, 1235 | list!( 1236 | list_cell![ AtomCell(Char('Q')) ], 1237 | list_cell![ AtomCell(SInt(1)) ] 1238 | ) 1239 | ); 1240 | //assert_eq!(state.dump, list!(ListCell(box list!(InstCell(DUM))),ListCell(box list!(ListCell(box list!(AtomCell(Char('D')))))))); 1241 | } 1242 | 1243 | #[test] 1244 | fn test_eval_atom() { 1245 | // true cases 1246 | let mut state = State { 1247 | stack: list!(AtomCell(SInt(1))), 1248 | env: Stack::empty(), 1249 | control: list!(InstCell(ATOM)), 1250 | dump: Stack::empty() 1251 | }.eval(None, true).unwrap().0; 1252 | assert!( 1253 | state.stack.peek() != Some(&list_cell![]) && 1254 | state.stack.peek() != None 1255 | ); 1256 | 1257 | state = State { 1258 | stack: list!(AtomCell(UInt(0))), 1259 | env: Stack::empty(), 1260 | control: list!(InstCell(ATOM)), 1261 | dump: Stack::empty() 1262 | }.eval(None, true).unwrap().0; 1263 | assert!( 1264 | state.stack.peek() != Some(&list_cell![]) && 1265 | state.stack.peek() != None 1266 | ); 1267 | 1268 | state = State { 1269 | stack: list!(AtomCell(Char('C'))), 1270 | env: Stack::empty(), 1271 | control: list!(InstCell(ATOM)), 1272 | dump: Stack::empty() 1273 | }.eval(None, true).unwrap().0; 1274 | assert!( 1275 | state.stack.peek() != Some(&list_cell![]) && 1276 | state.stack.peek() != None 1277 | ); 1278 | 1279 | state = State { 1280 | stack: list!(AtomCell(Char('A'))), 1281 | env: Stack::empty(), 1282 | control: list!(InstCell(ATOM)), 1283 | dump: Stack::empty() 1284 | }.eval(None, true).unwrap().0; 1285 | assert!( 1286 | state.stack.peek() != Some(&list_cell![]) && 1287 | state.stack.peek() != None 1288 | ); 1289 | 1290 | state = State { 1291 | stack: list!(AtomCell(Float(1.23f64))), 1292 | env: Stack::empty(), 1293 | control: list!(InstCell(ATOM)), 1294 | dump: Stack::empty() 1295 | }.eval(None, true).unwrap().0; 1296 | assert!( 1297 | state.stack.peek() != Some(&list_cell![]) && 1298 | state.stack.peek() != None 1299 | ); 1300 | 1301 | // false cases 1302 | state = State { 1303 | stack: list!(InstCell(DUM)), 1304 | env: Stack::empty(), 1305 | control: list!(InstCell(ATOM)), 1306 | dump: Stack::empty() 1307 | }.eval(None, true).unwrap().0; 1308 | assert_eq!(state.stack.peek(), Some(&list_cell![])); 1309 | 1310 | state = State { 1311 | stack: list!(list_cell![]), 1312 | env: Stack::empty(), 1313 | control: list!(InstCell(ATOM)), 1314 | dump: Stack::empty() 1315 | }.eval(None, true).unwrap().0; 1316 | assert_eq!(state.stack.peek(), Some(&list_cell![])); 1317 | } 1318 | 1319 | #[test] 1320 | fn test_eval_car() { 1321 | let state = State { 1322 | stack: list!(list_cell![ AtomCell(Char('A')),AtomCell(Char('B')) ]), 1323 | env: Stack::empty(), 1324 | control: list!(InstCell(CAR)), 1325 | dump: Stack::empty() 1326 | }.eval(None, true).unwrap().0; 1327 | assert_eq!(state.stack.peek(), Some(&AtomCell(Char('A')))); 1328 | } 1329 | 1330 | #[test] 1331 | fn test_eval_cdr() { 1332 | let state = State { 1333 | stack:list!(list_cell![ AtomCell(Char('A')),AtomCell(Char('B')) ]), 1334 | env: Stack::empty(), 1335 | control: list!(InstCell(CDR)), 1336 | dump: Stack::empty() 1337 | }.eval(None, true).unwrap().0; 1338 | assert_eq!(state.stack.peek(), Some(&list_cell![ AtomCell(Char('B')) ]) ); 1339 | } 1340 | 1341 | #[test] 1342 | fn test_eval_cons() { 1343 | let state = State { 1344 | stack: list!( 1345 | AtomCell(Char('A')), 1346 | list_cell![ AtomCell(Char('B')) ,AtomCell(Char('C')) ] 1347 | ), 1348 | env: Stack::empty(), 1349 | control: list!(InstCell(CONS)), 1350 | dump: Stack::empty() 1351 | }.eval(None, true).unwrap().0; 1352 | assert_eq!( 1353 | state.stack.peek(), 1354 | Some(&list_cell![ 1355 | AtomCell(Char('A')), AtomCell(Char('B')), AtomCell(Char('C')) 1356 | ]) 1357 | ); 1358 | } 1359 | 1360 | #[test] 1361 | fn test_eval_sel_true() { 1362 | // true case 1363 | let state = State { 1364 | stack: list!(list_cell![]), 1365 | env: Stack::empty(), 1366 | control: list!( 1367 | InstCell(SEL), 1368 | list_cell![ InstCell(ATOM) ], // should be on stack if true 1369 | list_cell![ InstCell(NIL) ], // should be on stack if false 1370 | InstCell(JOIN) // this is just here so that we can assert that it goes on the dump 1371 | ), 1372 | dump: Stack::empty() 1373 | }.eval(None, true).unwrap().0; 1374 | assert_eq!(state.stack.peek(), None); // stack should be empty 1375 | assert_eq!(state.control.peek(), Some(&InstCell(NIL))); 1376 | assert_eq!(state.dump.peek(), Some(&list_cell![ InstCell(JOIN) ]) ); // next instruction on dump 1377 | } 1378 | 1379 | #[test] 1380 | fn test_eval_sel_false() { 1381 | // false case 1382 | let state = State { 1383 | stack: list!(list_cell![ AtomCell(SInt(1)) ]), 1384 | env: Stack::empty(), 1385 | control: list!( 1386 | InstCell(SEL), 1387 | list_cell![ InstCell(ATOM) ], // should be on stack if true 1388 | list_cell![ InstCell(NIL) ], // should be on stack if false 1389 | InstCell(JOIN) // this is just here so that we can assert that it goes on the dump 1390 | ), 1391 | dump: Stack::empty() 1392 | }.eval(None, true).unwrap().0; 1393 | assert_eq!(state.stack.peek(), None); // stack should be empty 1394 | assert_eq!(state.control.peek(), Some(&InstCell(ATOM))); 1395 | assert_eq!(state.dump.peek(), Some(&list_cell![ InstCell(JOIN) ]) ); // next instruction on dump 1396 | } 1397 | 1398 | #[test] 1399 | fn test_eval_null() { 1400 | // true case 1401 | assert_eq!( 1402 | State { 1403 | stack: list!(AtomCell(SInt(1))), 1404 | env: Stack::empty(), 1405 | control: list!(InstCell(NULL)), 1406 | dump: Stack::empty(), 1407 | }.eval(None,true).unwrap().0.stack.peek(), 1408 | Some(&list_cell![]) 1409 | ); 1410 | // false case 1411 | assert_eq!( 1412 | State { 1413 | stack: list!(list_cell![]), 1414 | env: Stack::empty(), 1415 | control: list!(InstCell(NULL)), 1416 | dump: Stack::empty(), 1417 | }.eval(None,true).unwrap().0.stack.peek(), 1418 | Some(&list_cell![ AtomCell(SInt(1)) ]) 1419 | ); 1420 | } 1421 | 1422 | #[bench] 1423 | fn bench_list_creation(b: &mut Bencher) { 1424 | b.iter(|| { 1425 | super::eval_program(list!( 1426 | InstCell(NIL), 1427 | InstCell(LDC), AtomCell(SInt(20)), InstCell(CONS), 1428 | InstCell(LDC), AtomCell(SInt(10)), InstCell(CONS) 1429 | ), true) 1430 | }) 1431 | } 1432 | 1433 | #[bench] 1434 | fn bench_list_car(b: &mut Bencher) { 1435 | b.iter(|| { 1436 | super::eval_program(list!( 1437 | InstCell(NIL), 1438 | InstCell(LDC), AtomCell(SInt(10)), InstCell(CONS), 1439 | InstCell(LDC), AtomCell(SInt(20)), InstCell(CONS), 1440 | InstCell(CAR) 1441 | ), true) 1442 | }) 1443 | } 1444 | 1445 | #[bench] 1446 | fn bench_list_cdr(b: &mut Bencher) { 1447 | b.iter(|| { 1448 | super::eval_program(list!( 1449 | InstCell(NIL), 1450 | InstCell(LDC), AtomCell(SInt(10)), InstCell(CONS), 1451 | InstCell(LDC), AtomCell(SInt(20)), InstCell(CONS), 1452 | InstCell(CDR) 1453 | ), true) 1454 | }) 1455 | } 1456 | 1457 | #[bench] 1458 | fn bench_simple_add(b: &mut Bencher) { 1459 | b.iter(|| { 1460 | super::eval_program(list!( 1461 | InstCell(LDC), AtomCell(SInt(10)), 1462 | InstCell(LDC), AtomCell(SInt(10)), 1463 | InstCell(ADD) 1464 | ), true) 1465 | }) 1466 | } 1467 | 1468 | #[bench] 1469 | fn bench_nested_arith(b: &mut Bencher) { 1470 | b.iter(|| { 1471 | super::eval_program(list!( 1472 | InstCell(LDC), AtomCell(SInt(5)), 1473 | InstCell(LDC), AtomCell(SInt(5)), 1474 | InstCell(ADD), 1475 | InstCell(LDC), AtomCell(SInt(20)), 1476 | InstCell(SUB) 1477 | ), true) 1478 | }) 1479 | } 1480 | 1481 | #[bench] 1482 | fn bench_basic_branching_1(b: &mut Bencher) { 1483 | b.iter(|| { 1484 | super::eval_program(list!( 1485 | InstCell(LDC), AtomCell(SInt(1)), InstCell(LDC), AtomCell(SInt(1)), 1486 | InstCell(SUB), 1487 | InstCell(LDC), AtomCell(SInt(0)), 1488 | InstCell(EQ), 1489 | InstCell(SEL), 1490 | list_cell![ 1491 | InstCell(LDC), 1492 | AtomCell(SInt(1)), 1493 | InstCell(JOIN) 1494 | ], 1495 | list_cell![ 1496 | InstCell(NIL), 1497 | InstCell(JOIN) 1498 | ] 1499 | ), true) 1500 | }) 1501 | } 1502 | 1503 | #[bench] 1504 | fn bench_basic_branching_2(b: &mut Bencher) { 1505 | b.iter(|| { 1506 | super::eval_program(list!( 1507 | InstCell(NIL), InstCell(NULL), 1508 | InstCell(SEL), 1509 | list_cell![ InstCell(LDC), AtomCell(SInt(10)), InstCell(JOIN) ], 1510 | list_cell![ InstCell(LDC), AtomCell(SInt(20)), InstCell(JOIN) ], 1511 | InstCell(LDC), AtomCell(SInt(10)), 1512 | InstCell(ADD) 1513 | ), true) 1514 | }) 1515 | } 1516 | -------------------------------------------------------------------------------- /tests/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(box_syntax,box_patterns)] 2 | 3 | #[macro_use] 4 | extern crate seax_svm as svm; 5 | 6 | use svm::slist::Stack; 7 | use svm::slist::List::{Cons,Nil}; 8 | use svm::cell::Atom::*; 9 | use svm::cell::SVMCell::*; 10 | use svm::Inst::*; 11 | 12 | /// SVM integration tests. 13 | /// 14 | /// These are based on the sample programs in Zach Allaun's Clojure SECD 15 | /// [implementation](https://github.com/zachallaun/secd). Each example also 16 | /// provides the source code for the equivalent Lisp program. 17 | 18 | /// Test for simple list construction through CONS. 19 | /// 20 | /// ```lisp 21 | /// (cons 10 (cons 20 nil)) 22 | /// ``` 23 | #[test] 24 | fn test_list_creation() { 25 | assert_eq!( 26 | svm::eval_program(list!( 27 | InstCell(NIL), 28 | InstCell(LDC), AtomCell(SInt(20)), InstCell(CONS), 29 | InstCell(LDC), AtomCell(SInt(10)), InstCell(CONS) 30 | ), true).unwrap().peek(), 31 | Some(&ListCell( box list!(AtomCell(SInt(10)), AtomCell(SInt(20))))) 32 | ); 33 | } 34 | 35 | /// Test for simple list construction and destructuring 36 | /// 37 | /// ```lisp 38 | /// (car (cons 20 (cons 10 nil))) 39 | /// ``` 40 | #[test] 41 | fn test_list_car() { 42 | assert_eq!( 43 | svm::eval_program(list!( 44 | InstCell(NIL), 45 | InstCell(LDC), AtomCell(SInt(10)), InstCell(CONS), 46 | InstCell(LDC), AtomCell(SInt(20)), InstCell(CONS), 47 | InstCell(CAR) 48 | ), true).unwrap().peek(), 49 | Some(&AtomCell(SInt(20))) 50 | ); 51 | } 52 | /// Test for simple list construction and destructuring 53 | /// 54 | /// ```lisp 55 | /// (cdr (cons 20 (cons 10 nil))) 56 | /// ``` 57 | #[test] 58 | fn test_list_cdr() { 59 | assert_eq!( 60 | svm::eval_program(list!( 61 | InstCell(NIL), 62 | InstCell(LDC), AtomCell(SInt(10)), InstCell(CONS), 63 | InstCell(LDC), AtomCell(SInt(20)), InstCell(CONS), 64 | InstCell(CDR) 65 | ), true).unwrap().peek(), 66 | Some(&ListCell(box list!(AtomCell(SInt(10))))) 67 | ); 68 | } 69 | 70 | /// Test for simple mathematics application 71 | /// 72 | /// ```lisp 73 | /// (+ 10 10) 74 | /// ``` 75 | #[test] 76 | fn test_simple_add() { 77 | assert_eq!( 78 | svm::eval_program(list!( 79 | InstCell(LDC), AtomCell(SInt(10)), 80 | InstCell(LDC), AtomCell(SInt(10)), 81 | InstCell(ADD) 82 | ), true).unwrap().peek(), 83 | Some(&AtomCell(SInt(20))) 84 | ); 85 | } 86 | 87 | /// Test for nested arithmetic 88 | /// 89 | /// ```lisp 90 | /// (- 20 (+ 5 5)) 91 | /// ``` 92 | #[test] 93 | fn test_nested_arith() { 94 | assert_eq!( 95 | svm::eval_program(list!( 96 | InstCell(LDC), AtomCell(SInt(5)), 97 | InstCell(LDC), AtomCell(SInt(5)), 98 | InstCell(ADD), 99 | InstCell(LDC), AtomCell(SInt(20)), 100 | InstCell(SUB) 101 | ), true).unwrap().peek(), 102 | Some(&AtomCell(SInt(10))) 103 | ); 104 | } 105 | 106 | 107 | /// Tests for basic branching 108 | /// 109 | /// ```lisp 110 | /// ((if (= 0 (- 1 1)) true false) 111 | /// ``` 112 | /// 113 | /// ```lisp 114 | /// (+ 10 (if (nil? nil) 10 20)) 115 | /// ``` 116 | #[test] 117 | fn test_basic_branching() { 118 | assert_eq!( 119 | svm::eval_program(list!( 120 | InstCell(LDC), AtomCell(SInt(1)), InstCell(LDC), AtomCell(SInt(1)), 121 | InstCell(SUB), 122 | InstCell(LDC), AtomCell(SInt(0)), 123 | InstCell(EQ), 124 | InstCell(SEL), 125 | ListCell(box list!(InstCell(LDC), AtomCell(SInt(1)), InstCell(JOIN))), 126 | ListCell(box list!(InstCell(NIL), InstCell(JOIN)) 127 | ) 128 | ), true).unwrap().peek(), 129 | Some(&AtomCell(SInt(1))) 130 | ); 131 | assert_eq!( 132 | svm::eval_program(list!( 133 | InstCell(NIL), InstCell(NULL), 134 | InstCell(SEL), 135 | ListCell(box list!(InstCell(LDC), AtomCell(SInt(10)), InstCell(JOIN))), 136 | ListCell(box list!(InstCell(LDC), AtomCell(SInt(20)), InstCell(JOIN))), 137 | InstCell(LDC), AtomCell(SInt(10)), 138 | InstCell(ADD) 139 | ), true).unwrap().peek(), 140 | Some(&AtomCell(SInt(20))) 141 | ); 142 | } 143 | 144 | --------------------------------------------------------------------------------