Compare commits

..

No commits in common. "1993f39d933531696c86a4aaf055602974673995" and "aa56570d7df68ec0bb0441fb81aee3211e20de79" have entirely different histories.

47 changed files with 1436 additions and 1743 deletions

View file

@ -1,2 +0,0 @@
[env]
POSIX_LOAD_NAME = "load"

View file

@ -6,30 +6,25 @@ stages:
- test - test
- release - release
compile-shell-with-posix-features: compile-with-posix-features:
stage: build stage: build
script: script:
- cargo build -p flesh-shell -F posix - cargo build
compile-shell-without-posix-features: compile-without-posix-features:
stage: build stage: build
script: script:
- cargo build -p flesh-shell --no-default-features - cargo build --no-default-features
compile-core: compile-implicit-load:
stage: build stage: build
script: script:
- cargo build -p flesh-core - cargo build -F implicit-load
unit-test-shell: unit-tests:
stage: test stage: test
script: script:
- cargo test -p flesh-shell - cargo test
unit-test-core:
stage: test
script:
- cargo test -p flesh-core
userlib-tests: userlib-tests:
stage: test stage: test
@ -39,7 +34,7 @@ userlib-tests:
prepare-release: prepare-release:
stage: release stage: release
script: script:
- cargo build --release - cargo build -F implicit-load --release
- | - |
tar -czf flesh-$CI_COMMIT_TAG.tar.gz \ tar -czf flesh-$CI_COMMIT_TAG.tar.gz \
--xform='s,target/release/,,' \ --xform='s,target/release/,,' \

View file

@ -1,6 +1,25 @@
[workspace] [package]
resolver = "2" name = "flesh"
members = [ version = "0.4.0"
"shell", authors = ["Ava <ava@sunnypup.io>"]
"core" edition = "2021"
]
[dependencies]
# used in config (src/run.rs)
dirs = "3.0"
# these two are used in src/bin/relish.rs to manage a prompt
nu-ansi-term = "0.47.0"
reedline = "0.17.0"
# used by main shell to update console dimensions
termion = "2.0.1"
# these two used in posix shell layer (src/stl/posix.rs)
nix = { version = "0.26.2", optional = true }
libc = { version = "0.2.144", optional = true }
# this one provides a global constant lookup table for simple
# string escaping in the lexer
phf = { version = "0.11", default-features = false, features = ["macros"] }
[features]
default = ["posix"]
posix = ["dep:nix", "dep:libc"]
implicit-load = ["posix"]

View file

@ -92,13 +92,13 @@ For installation, consider adding the following to your ~\~/.emacs~:
* Compilation * Compilation
Compiling Flesh is as simple as kicking off a build with Cargo. Compiling Flesh is as simple as kicking off a build with Cargo.
#+BEGIN_EXAMPLE sh #+BEGIN_EXAMPLE sh
cargo build -p flesh-shell cargo build
#+END_EXAMPLE #+END_EXAMPLE
This will produce a binary at [[file:target/debug/flesh][target/debug/flesh]] which includes all of the features flesh has to offer. This provides a REPL with a full interactive shell that also can manage variables in the Unix environment. It is possible to compile a smaller REPL that does not interact with environment variables and does not offer any shell features. Simply pass the ~--no-default-features~ flag to cargo: This will produce a binary at [[file:target/debug/flesh][target/debug/flesh]] which includes all of the features flesh has to offer. This provides a REPL with a full interactive shell that also can manage variables in the Unix environment. It is possible to compile a smaller REPL that does not interact with environment variables and does not offer any shell features. Simply pass the ~--no-default-features~ flag to cargo:
#+BEGIN_EXAMPLE sh #+BEGIN_EXAMPLE sh
cargo build -p flesh-shell --no-default-features cargo build --no-default-features
#+END_EXAMPLE #+END_EXAMPLE
In order to run Flesh it is recommended to run the resulting binary at [[file:target/debug/flesh][target/debug/flesh]]. In order to run Flesh it is recommended to run the resulting binary at [[file:target/debug/flesh][target/debug/flesh]].
@ -114,6 +114,46 @@ Userlib tests can be triggered by loading the userlib as well as its test suite
cargo run snippets/userlib.f snippets/userlib-tests.f cargo run snippets/userlib.f snippets/userlib-tests.f
#+END_EXAMPLE #+END_EXAMPLE
* The codebase
** The [[file:tests][tests directory]]
Start here if you are new.
*** [[file:tests/test_eval.rs][Eval tests]]
These are particularly easy to read and write tests.
They primarily cover execution paths in the evaluation process.
*** [[file:tests/test_func.rs][Func tests]]
These tests extend the eval tests to cover the co-recursive nature between eval and func calls.
*** [[file:tests/test_lex.rs][Lex tests]]
These tests verify the handling of syntax.
*** Lib tests: (tests/test_lib*)
These tests are unique per stdlib module and work to prove the functionality of builtin functions in the language.
** [[file:src][Source directory]]
This directory contains all of the user facing code in flesh.
Just a few entries of note:
*** [[file:src/segment.rs][Segment module]]
This file lays out the data structures that the interpreter operates on.
Representation of code trees, traversals, and type annotations all live here.
It provides the core representation of data used in Flesh, and could provide supplementary refrence material for users seeking a deeper understanding of how their code is stored in memory.
*** [[file:src/lib.rs][lib.rs]]
This defines a library that can be included to provide an interpreter interface within any Rust project.
The components defined here can certainly be used to support language development for other LISP (or non LISP) langauges. An external project may use or not use any number of these components.
*** [[file:src/sym.rs][Symbol module]]
This file contains all code related to symbol expansion and function calling.
The types defined in this file include SymTable, Args, Symbol, and more.
Code to call Lambda functions also exists in here.
*** [[file:src/run.rs][Run module]]
This file contains functions which load and run the configuration file script.
For more information see the configuraiton section above in this Readme.
*** [[file:src/stl.rs][Standard library module]]
This defines the ~static_stdlib~ function and the ~dynamic_stdlib~ function.
The ~static_stdlib~ function loads all symbols in the standard library which do not need further configuration into the symbol table.
The ~dynamic_stdlib~ function loads all symbols in the standard library which *do* need configuration into the symbol table.
The ~dynamic_stdlib~ function uses variables saved in the symbol table to configure the functions and variables it loads.
This module also contains definitions for the default configuration values.
Any new addition to the stdlib must make its way here to be included in the main shell (and any other shell using the included stdlib functions).
You may choose to override these functions if you would like to include your own special functions in your own special interpreter, or if you would like to pare down the stdlib to a lighter subet of what it is.
You can view the code for standard library functions in [[file:src/stl/][the standard library directory]].
*** [[file:src/bin/][binary directory]]
This contains any executable target of this project. Notably [[file:src/bin/flesh.rs][the main shell]].
* Current Status / TODO list * Current Status / TODO list
Note: this section will not show the status of each item unless you are viewing it with a proper orgmode viewer. Note: this section will not show the status of each item unless you are viewing it with a proper orgmode viewer.
Note: this section only tracks the state of incomplete TODO items. Having everything on here would be cluttered. Note: this section only tracks the state of incomplete TODO items. Having everything on here would be cluttered.

View file

@ -10,24 +10,24 @@ This document outlines the ways that Flesh can be used as a shell for daily admi
For the most common uses (executing shell commands) a function is provided to find and load binaries from entries in the ~PATH~ variable. This function may be called either with ~load~ or ~l~. For the most common uses (executing shell commands) a function is provided to find and load binaries from entries in the ~PATH~ variable. This function may be called either with ~load~ or ~l~.
#+BEGIN_SRC lisp #+BEGIN_SRC lisp
(load htop) ;; executes the htop binary, if htop is found on the users PATH (load htop) ;; executes the htop binary, if htop is found on the users PATH
(load emacs) ;; executes the emacs binary, if emacs is found on the users PATH (l emacs) ;; executes the emacs binary, if emacs is found on the users PATH
#+END_SRC #+END_SRC
The load command takes an infinite number of arguments and uses the following rules to construct a shell command from them: The load command takes an infinite number of arguments and uses the following rules to construct a shell command from them:
+ symbols that are not set are taken as strings + symbols that are not set are taken as strings
#+BEGIN_SRC lisp #+BEGIN_SRC lisp
(load emacs -nw) ;; 'emacs' and '-nw' not defined (l emacs -nw) ;; 'emacs' and '-nw' not defined
#+END_SRC #+END_SRC
+ symbols that are set are replaced by their values + symbols that are set are replaced by their values
#+BEGIN_SRC lisp #+BEGIN_SRC lisp
(load ls -la HOME) (l ls -la HOME)
(let ((ping-count 4) (let ((ping-count 4)
(domain "sunnypup.io")) (domain "sunnypup.io"))
(load ping -c ping-count domain) (l ping -c ping-count domain)
#+END_SRC #+END_SRC
+ nested forms are evaluated + nested forms are evaluated
#+BEGIN_SRC lisp #+BEGIN_SRC lisp
(load cat (concat HOME "/notes.txt")) (l cat (concat HOME "/notes.txt"))
#+END_SRC #+END_SRC
Shell command evaluation rules apply to the following functions: Shell command evaluation rules apply to the following functions:
@ -53,9 +53,9 @@ In a shell such as Bash or Zsh, commands can be chained with the ~&&~ operator:
In these chains, if one command fails the next one(s) are not run. Colloquially, the command short-circuits. A similar construct is offered in Flesh called ~circuit~. Circuit will evaluate one or more forms (all expected to evaluate to either an integer (shell command) or a boolean (more general form). If a form returns false (or non-zero) no other forms are evaluated. The printed error message will identify where in the sequence evaluation was halted. In these chains, if one command fails the next one(s) are not run. Colloquially, the command short-circuits. A similar construct is offered in Flesh called ~circuit~. Circuit will evaluate one or more forms (all expected to evaluate to either an integer (shell command) or a boolean (more general form). If a form returns false (or non-zero) no other forms are evaluated. The printed error message will identify where in the sequence evaluation was halted.
#+BEGIN_EXAMPLE lisp #+BEGIN_EXAMPLE lisp
(circuit (circuit
(load apt update) ;; if this fails, no upgrade is made (l apt update) ;; if this fails, no upgrade is made
(load apt upgrade) ;; if this fails, "Success!" is not printed (l apt upgrade) ;; if this fails, "Success!" is not printed
(load echo "Success!")) (l echo "Success!"))
#+END_EXAMPLE #+END_EXAMPLE
*** Command piping *** Command piping
@ -129,7 +129,7 @@ To launch a background process use the ~bg~ function:
To get all jobs in your shell use the system ~ps~ binary: To get all jobs in your shell use the system ~ps~ binary:
#+BEGIN_EXAMPLE lisp #+BEGIN_EXAMPLE lisp
(load ps) (l ps)
#+END_EXAMPLE #+END_EXAMPLE
To foreground a background process use the ~fg~ function: To foreground a background process use the ~fg~ function:
@ -156,7 +156,7 @@ Daily Flesh users will long for first class shell commands that are accounted fo
A simple solution: A simple solution:
#+BEGIN_EXAMPLE lisp #+BEGIN_EXAMPLE lisp
(def lis 'shortcut for ls -la' (def lis 'shortcut for ls -la'
(lambda (dir) (load ls -la dir))) (lambda (dir) (l ls -la dir)))
(lis HOME) (lis HOME)
#+END_EXAMPLE #+END_EXAMPLE
@ -196,4 +196,8 @@ With the ~implicit load~ feature any call to an undefined function will trigger
...... (redacted directory list) ..... ...... (redacted directory list) .....
#+END_EXAMPLE #+END_EXAMPLE
Implicit load is now enabled by default on any 1.0 build of flesh. Implicit load can be used by building flesh with the following command:
#+BEGIN_EXAMPLE
cargo build -F implicit-load
#+END_EXAMPLE

View file

@ -283,7 +283,7 @@ Removing a symbol consists of a call to ~def~ with no additional arguments:
The following table is up to date as of Flesh 0.4.0. For latest information try the following: The following table is up to date as of Flesh 0.4.0. For latest information try the following:
- Call ~env~ from a fresh shell: ~(env)~ - Call ~env~ from a fresh shell: ~(env)~
This will output all variables and functions defined This will output all variables and functions defined
- Read the [[file:src/stl.rs][std library declaration code]]
| *Control Flow* | *Declaration* | *Shell* | *List* | *Math* | *Strings* | *Boolean* | *Userlib* | *Misc* | *Files* | | *Control Flow* | *Declaration* | *Shell* | *List* | *Math* | *Strings* | *Boolean* | *Userlib* | *Misc* | *Files* |
|----------------+---------------+----------------+---------+--------+-----------+-----------+-----------+--------+-------------| |----------------+---------------+----------------+---------+--------+-----------+-----------+-----------+--------+-------------|
| if | lambda | pipe | car | float | strlen | toggle | reduce | call | read-file | | if | lambda | pipe | car | float | strlen | toggle | reduce | call | read-file |
@ -314,14 +314,13 @@ The following table is up to date as of Flesh 0.4.0. For latest information try
|----------------+---------------+----------------+---------+--------+-----------+-----------+-----------+--------+-------------| |----------------+---------------+----------------+---------+--------+-----------+-----------+-----------+--------+-------------|
| | | | | lte? | | | | | | | | | | | lte? | | | | | |
The Shell module and file module both are specific to the shell project and are not present in the core.
To learn how to use a given function use the ~help~ command. See the Documentation section for more information. To learn how to use a given function use the ~help~ command. See the Documentation section for more information.
* Documentation * Documentation
** Tests ** Tests
Most of the tests evaluate small scripts (single forms) and check their output. Most of the tests evaluate small scripts (single forms) and check their output.
Perusing them may yield answers on all the cases a given builtin can handle. Perusing them may yield answers on all the cases a given builtin can handle.
[[file:tests][The test directory]]
** Help function ** Help function
Flesh is self documenting. The *help* function can be used to inspect any variable or function. Flesh is self documenting. The *help* function can be used to inspect any variable or function.

View file

@ -1,12 +0,0 @@
[package]
name = "flesh-core"
version = "0.4.0"
authors = ["Ava <ava@sunnypup.io>"]
edition = "2021"
[lib]
name = "flesh"
# only turn this on if you set POSIX_LOAD_NAME at build time
[features]
implicit-load = []

View file

@ -1,197 +0,0 @@
/* Flesh: Flexible Shell
* Copyright (C) 2021 Ava Affine
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
use alloc::slice;
use alloc::vec::Vec;
use alloc::boxed::Box;
use alloc::string::String;
/* Use a prime number so that the modulus operation
* provides better avalanche effect
*/
const INDEXED_BUCKETS: u8 = 199;
/* This only has to work to make quasi unique indexes from
* variable names. Any given program will not have so many
* symbols that this becomes a bottleneck in runtime.
*
* Priorities:
* - SPEED in embedded code
* - avalanche effect
*
* Not a priority: minimal collisions
*
* Just to make sure this is not misused I keep it private.
* And yes, I am sure a B-Tree would be better.
*/
#[inline]
fn string_hash(input: &String) -> u8 {
input
.chars()
.map(|c| c.to_digit(36) // each letter and number get a digit
.or_else(|| Some(0)) // all else is 0
.unwrap())
.reduce(|acc, i| (acc + i) % INDEXED_BUCKETS as u32)
.or_else(|| Some(0))
.unwrap() as u8
}
#[derive(Clone)]
pub struct Bucket<T: Clone>(Vec<(String, T)>);
#[derive(Clone)]
pub struct QuickMap<T: Clone>(Box<[Bucket<T>; INDEXED_BUCKETS as usize]>);
impl<'a, T: Clone> QuickMap<T> {
const ARRAY_REPEAT_VALUE: Bucket<T> = Bucket(vec![]);
pub fn new() -> QuickMap<T> {
QuickMap(Box::new([QuickMap::ARRAY_REPEAT_VALUE; INDEXED_BUCKETS as usize]))
}
pub fn get(&self, arg: &String) -> Option<&T> {
let idx = string_hash(&arg);
for kv in self.0[idx as usize].0.iter() {
if &kv.0 == arg {
return Some(&kv.1);
}
}
return None;
}
pub fn remove(&mut self, arg: &String) -> Option<T> {
let idx = string_hash(&arg);
let len = self.0[idx as usize].0.len();
for i in 0..len {
if &self
.0[idx as usize]
.0[i as usize]
.0 == arg {
return Some(self.0[idx as usize].0.swap_remove(i).1);
}
}
return None;
}
pub fn contains_key(&self, arg: &String) -> bool {
let idx = string_hash(arg);
for kv in self.0[idx as usize].0.iter() {
if &kv.0 == arg {
return true;
}
}
return false;
}
pub fn insert(&mut self, k: String, v: T) -> Option<T> {
let idx = string_hash(&k);
for kv in self.0[idx as usize].0.iter_mut() {
if kv.0 == k {
let tmp = kv.1.clone();
kv.1 = v;
return Some(tmp);
}
}
self.0[idx as usize].0.push((k, v));
return None
}
pub fn iter(&'a self) -> QuickMapIter<'a, T> {
QuickMapIter::<'a, T>{
buckets: &self.0,
bucket_cursor: 0,
vec_iter: self.0[0].0.iter(),
}
}
}
#[derive(Clone)]
pub struct QuickMapIter<'a, T: Clone> {
buckets: &'a [Bucket<T>; INDEXED_BUCKETS as usize],
bucket_cursor: usize,
vec_iter: slice::Iter<'a, (String, T)>,
}
impl<'a, T: Clone> Iterator for QuickMapIter<'a, T> {
type Item = &'a (String, T);
fn next(&mut self) -> Option<Self::Item> {
self.vec_iter
.next()
.or_else(|| {
self.bucket_cursor += 1;
if self.bucket_cursor == INDEXED_BUCKETS as usize{
None
} else {
self.vec_iter = self.buckets[self.bucket_cursor].0.iter();
self.next()
}
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn add_fetch_and_remove_simple() {
let mut q = QuickMap::<u8>::new();
let key = String::from("test");
q.insert(String::from("test"), 1);
assert_eq!(*q.get(&key).unwrap(), 1);
assert!(q.contains_key(&key));
assert_eq!(
q.remove(&key),
Some(1),
);
assert_eq!(q.contains_key(&key), false);
assert_eq!(q.get(&key), None);
}
#[test]
fn iter_test() {
let mut q = QuickMap::<u8>::new();
let k1 = String::from("test1");
let k2 = String::from("test1@"); // will be in same bucket
let k3 = String::from("test2");
let k4 = String::from("test2--"); // will be in same bucket
q.insert(k1.clone(), 1);
q.insert(k2.clone(), 2);
q.insert(k3.clone(), 3);
q.insert(k4.clone(), 4);
let mut i = q.iter();
let entry1 = i.next().unwrap();
let entry2 = i.next().unwrap();
let entry3 = i.next().unwrap();
let entry4 = i.next().unwrap();
assert_eq!(i.next(), None);
assert_eq!(entry1.0, k1);
assert_eq!(entry1.1, 1);
assert_eq!(entry2.0, k2);
assert_eq!(entry2.1, 2);
assert_eq!(entry3.0, k3);
assert_eq!(entry3.1, 3);
assert_eq!(entry4.0, k4);
assert_eq!(entry4.1, 4);
}
}

View file

@ -1,41 +0,0 @@
/* Flesh: Flexible Shell
* Copyright (C) 2021 Ava Affine
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
use crate::sym::SymTable;
use alloc::string::String;
pub mod append;
pub mod boolean;
pub mod control;
pub mod decl;
pub mod math;
pub mod strings;
pub const CFG_FILE_VNAME: &str = "FLESH_CFG_FILE";
/// static_stdlib
/// inserts all stdlib functions that can be inserted without
/// any kind of further configuration data into a symtable
#[inline]
pub fn static_stdlib(syms: &mut SymTable, print: fn(&String), read: fn() -> String) {
append::add_list_lib(syms);
strings::add_string_lib(syms, print, read);
decl::add_decl_lib_static(syms, print);
control::add_control_lib(syms);
boolean::add_bool_lib(syms);
math::add_math_lib(syms);
}

View file

@ -1,313 +0,0 @@
mod str_lib_tests {
use flesh::ast::{eval, lex, SymTable};
use flesh::stdlib::static_stdlib;
#[test]
fn test_simple_concat() {
let document = "(concat \"test\")";
let result = "\"test\"";
let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new());
assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap()
.to_string(),
result.to_string(),
);
}
#[test]
fn test_poly_concat() {
let document = "(concat \"test\" 1 2 3)";
let result = "\"test123\"";
let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new());
assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap()
.to_string(),
result.to_string(),
);
}
#[test]
fn test_empty_concat() {
let document = "(concat)";
let result = "\"\"";
let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new());
assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap()
.to_string(),
result.to_string(),
);
}
#[test]
fn test_strlen_str() {
let document = "(strlen \"test\")";
let result = 4;
let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new());
assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap()
.to_string(),
result.to_string(),
);
}
#[test]
fn test_strlen_int() {
let document = "(strlen 1000)";
let result = 4;
let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new());
assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap()
.to_string(),
result.to_string(),
);
}
#[test]
fn test_strlen_float() {
let document = "(strlen 10.2)";
let result = 4;
let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new());
assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap()
.to_string(),
result.to_string(),
);
}
#[test]
fn test_strlen_seg() {
let document = "(strlen (1 2 3))";
let result = 7;
let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new());
assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap()
.to_string(),
result.to_string(),
);
}
#[test]
fn test_strlen_bool() {
let document = "(strlen true)";
let result = 4;
let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new());
assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap()
.to_string(),
result.to_string(),
);
}
#[test]
fn test_strcast_i() {
let document = "(string 4)";
let result = "\"4\"";
let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new());
assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap()
.to_string(),
result.to_string(),
);
}
#[test]
fn test_strcast_seg() {
let document = "(string (1 2 3))";
let result = "\"(1 2 3)\"";
let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new());
assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap()
.to_string(),
result.to_string(),
);
}
#[test]
fn test_contains() {
let document = "(substr? \"bigger\" \"ger\")";
let result = "true";
let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new());
assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap()
.to_string(),
result.to_string(),
);
}
#[test]
fn test_doesnt_contain() {
let document = "(substr? \"smaller\" \"ger\")";
let result = "false";
let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new());
assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap()
.to_string(),
result.to_string(),
);
}
#[test]
fn test_split() {
let document = "(split \"one.two.three\" \".\")";
let result = "(\"one\" \"two\" \"three\")";
let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new());
assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap()
.to_string(),
result.to_string(),
);
}
#[test]
fn test_split_big_delim() {
let document = "(split \"one:d:two:d:three\" \":d:\")";
let result = "(\"one\" \"two\" \"three\")";
let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new());
assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap()
.to_string(),
result.to_string(),
);
}
#[test]
fn test_splitnt() {
let document = "(split \"one.two.three\" \"-\")";
let result = "(\"one.two.three\")";
let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new());
assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap()
.to_string(),
result.to_string(),
);
}
#[test]
fn test_substr_valid() {
let document = "(substr \"test\" 0 4)";
let result = "\"test\"";
let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new());
assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap()
.to_string(),
result.to_string(),
);
}
#[test]
fn test_substr_start_neg() {
let document = "(substr \"test\" -1 3)";
let result = "start index cannot be negative";
let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new());
let doc_res= eval(&lex(&document.to_string()).unwrap(), &mut syms);
if let Err(e) = doc_res {
assert_eq!(
e.0.first().unwrap().message,
result.to_string(),
);
} else {
assert!(false);
}
}
#[test]
fn test_substr_end_neg() {
let document = "(substr \"test\" 1 -3)";
let result = "end index cannot be negative";
let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new());
let doc_res= eval(&lex(&document.to_string()).unwrap(), &mut syms);
if let Err(e) = doc_res {
assert_eq!(
e.0.first().unwrap().message,
result.to_string(),
);
} else {
assert!(false);
}
}
#[test]
fn test_substr_start_out_of_bounds() {
let document = "(substr \"test\" 5 3)";
let result = "start index larger than source string";
let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new());
let doc_res= eval(&lex(&document.to_string()).unwrap(), &mut syms);
if let Err(e) = doc_res {
assert_eq!(
e.0.first().unwrap().message,
result.to_string(),
);
} else {
assert!(false);
}
}
#[test]
fn test_substr_end_out_of_bounds() {
let document = "(substr \"test\" 1 5)";
let result = "end index larger than source string";
let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new());
let doc_res= eval(&lex(&document.to_string()).unwrap(), &mut syms);
if let Err(e) = doc_res {
assert_eq!(
e.0.first().unwrap().message,
result.to_string(),
);
} else {
assert!(false);
}
}
#[test]
fn test_substr_index_order() {
let document = "(substr \"test\" 3 2)";
let result = "end index must be larger than start index";
let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new());
let doc_res= eval(&lex(&document.to_string()).unwrap(), &mut syms);
if let Err(e) = doc_res {
assert_eq!(
e.0.first().unwrap().message,
result.to_string(),
);
} else {
assert!(false);
}
}
}

View file

@ -1,26 +0,0 @@
[package]
name = "flesh-shell"
version = "0.4.0"
authors = ["Ava Affine <ava@sunnypup.io>"]
edition = "2021"
[[bin]]
name = "flesh"
path = "src/main.rs"
[dependencies]
# used in config (src/run.rs)
dirs = "3.0"
# these two are used in src/bin/relish.rs to manage a prompt
nu-ansi-term = "0.47.0"
reedline = "0.17.0"
# used by main shell to update console dimensions
termion = "2.0.1"
# these two used in posix shell layer (src/stl/posix.rs)
nix = { version = "0.26.2", optional = true }
libc = { version = "0.2.144", optional = true }
flesh-core = { path = "../core", features = ["implicit-load"]}
[features]
default = ["posix"]
posix = ["dep:nix", "dep:libc"]

View file

@ -1,53 +0,0 @@
/* Flesh: Flexible Shell
* Copyright (C) 2021 Ava Hahn
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
use std::env;
use flesh::ast::{Ctr, Traceback, Seg, SymTable, Type, ValueType};
use flesh::stdlib::store_callback;
pub fn store_callback_with_env_integration(
ast: &Seg,
syms: &mut SymTable
) -> Result<Ctr, Traceback> {
let name = store_callback(ast, syms)?;
if let Ctr::String(n) = &name {
match ast.len() {
1 => {
env::remove_var(n);
},
3 if syms.contains_key(&n) => {
if let ValueType::VarForm(val) = &syms.get(&n).unwrap().value {
match val.to_type() {
Type::Lambda => {},
Type::Seg => {},
_ => {
let mut s = val.to_string();
// eat printed quotes on a string val
if let Ctr::String(tok) = &**val {
s = tok.to_string();
}
env::set_var(n.clone(), s);
}
}
}
},
_ => {}
}
}
Ok(name)
}

View file

@ -1,110 +0,0 @@
/* Flesh: Flexible Shell
* Copyright (C) 2021 Ava Hahn
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
use flesh::ast::{Traceback, Ctr, Symbol, Args, ValueType, Seg, SymTable, Type};
use crate::stl::{CONSOLE_XDIM_VNAME, FLESH_DEFAULT_CONS_WIDTH};
use std::rc::Rc;
pub fn add_window_lib_funcs(syms: &mut SymTable) {
syms.insert(
"env".to_string(),
Symbol {
name: String::from("env"),
args: Args::None,
conditional_branches: false,
docs: ENV_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(env_callback)),
..Default::default()
},
);
}
const ENV_DOCSTRING: &str = "takes no arguments
prints out all available symbols and their associated values";
fn env_callback(_ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback> {
// get width of current output
let xdim: i128;
if let Ctr::Integer(dim) = *syms
.call_symbol(&CONSOLE_XDIM_VNAME.to_string(), &Seg::new(), true)
.unwrap_or_else(|_: Traceback| Box::new(Ctr::None)) {
xdim = dim;
} else {
println!("{} contains non integer value, defaulting to {}",
CONSOLE_XDIM_VNAME, FLESH_DEFAULT_CONS_WIDTH);
xdim = FLESH_DEFAULT_CONS_WIDTH as i128;
}
let mut v_col_len = 0;
let mut f_col_len = 0;
let mut functions = vec![];
let mut variables = vec![];
for (name, val) in syms.iter() {
if let ValueType::VarForm(l) = &val.value {
let token: String = match l.to_type() {
Type::Lambda => format!("{}: <lambda>", name),
Type::Seg => format!("{}: <form>", name),
_ => format!("{}: {}", name, val.value),
};
if token.len() > v_col_len && token.len() < xdim as usize {
v_col_len = token.len();
}
variables.push(token);
} else {
if f_col_len < name.len() && name.len() < xdim as usize {
f_col_len = name.len();
}
functions.push(name.clone());
}
}
let mut n_v_cols = xdim / v_col_len as i128;
// now decrement to make sure theres room for two spaces of padding
while n_v_cols > 1 && xdim % (v_col_len as i128) < (2 * n_v_cols) {
n_v_cols -= 1;
}
// again for functions
let mut n_f_cols = xdim / f_col_len as i128;
while n_f_cols > 1 && xdim & (f_col_len as i128) < (2 * n_f_cols) {
n_f_cols -= 1;
}
let mut col_iter = 0;
println!("VARIABLES:");
for var in variables {
print!("{:v_col_len$}", var);
col_iter += 1;
if col_iter % n_v_cols == 0 {
println!();
} else {
print!(" ");
}
}
println!("\nFUNCTIONS:");
col_iter = 0;
for func in functions {
print!("{:f_col_len$}", func);
col_iter += 1;
if col_iter % n_f_cols == 0 {
println!();
} else {
print!(" ");
}
}
Ok(Ctr::None)
}

View file

@ -21,9 +21,9 @@
;; username, and percentages for all batteries on the system. ;; username, and percentages for all batteries on the system.
;; -> requires CFG_FLESH_POSIX=true and userlib. ;; -> requires CFG_FLESH_POSIX=true and userlib.
(def _batteries "paths to batteries powering system" (def _batteries 'paths to batteries powering system'
(lambda () (lambda ()
(let ((power-srcs-dir "/sys/class/power_supply") (let ((power-srcs-dir '/sys/class/power_supply')
(power-srcs (power-srcs
(split (load-to-string find /sys/class/power_supply -printf "%p ") (split (load-to-string find /sys/class/power_supply -printf "%p ")
" ")) " "))
@ -39,7 +39,7 @@
(set (q pwr-iter) (pop rem)))) (set (q pwr-iter) (pop rem))))
pwr-list))) pwr-list)))
(def display-batteries "display battery capacity" (def display-batteries 'display battery capacity'
(lambda () (lambda ()
(let ((bat-iter (pop (_batteries))) (let ((bat-iter (pop (_batteries)))
(display "")) (display ""))
@ -53,14 +53,14 @@
(set (q bat-iter) (pop rem)))) (set (q bat-iter) (pop rem))))
display))) display)))
(def CFG_FLESH_R_PROMPT "display battery info" (def CFG_FLESH_R_PROMPT 'display battery info'
() ()
(display-batteries)) (display-batteries))
(def _fancy-cwd "prints (up to) last three segments of current path" (def _fancy-cwd 'prints (up to) last three segments of current path'
() ()
(let ((cdir (load-to-string pwd)) (let ((cdir (load-to-string pwd))
(dir-segs (split cdir "/")) (dir-segs (split cdir '/'))
(dir-iter (dq dir-segs)) (dir-iter (dq dir-segs))
(i 0)) (i 0))
(if (lte? (len dir-segs) 4) (if (lte? (len dir-segs) 4)
@ -73,14 +73,14 @@
(concat "..." final))))) (concat "..." final)))))
(def in-a-git-repo? (def in-a-git-repo?
"returns true or false depending on if currently in a git repo" 'returns true or false depending on if currently in a git repo'
() (eq? (load-to-string git rev-parse --is-inside-work-tree) "true")) () (eq? (load-to-string git rev-parse --is-inside-work-tree) "true"))
(def git-repo-is-dirty? (def git-repo-is-dirty?
"returns true or false depending on if current dir is a dirty git repo" 'returns true or false depending on if current dir is a dirty git repo'
() (not (eq? (load-to-string git diff "--stat") ""))) () (not (eq? (load-to-string git diff '--stat') "")))
(def git-status "returns '(git:<branch>{!,})' if dir is in a git repository" (def git-status 'returns "(git:<branch>{!,})" if dir is in a git repository'
() ()
(if (in-a-git-repo?) (if (in-a-git-repo?)
(concat (concat
@ -90,9 +90,9 @@
"!" "!"
"") "")
")") ")")
"")) ''))
(def CFG_FLESH_L_PROMPT "display user and dir (git info in future)" (def CFG_FLESH_L_PROMPT 'display user and dir (git info in future)'
() ()
(concat (concat
"[" USER "]" "\t" "[" USER "]" "\t"

View file

@ -52,6 +52,6 @@ Or, a more advanced use:
(cons while (cons gt? (cons len (q arg-iter)) 1) (cons while (cons gt? (cons len (q arg-iter)) 1)
(cons set (cons q (q func-call)) (cons cons (q func-call) (cons car (q arg-iter)))) (cons set (cons q (q func-call)) (cons cons (q func-call) (cons car (q arg-iter))))
(cons set (cons q (q arg-iter)) (cons pop (cons cdr (q arg-iter))))) (cons set (cons q (q arg-iter)) (cons pop (cons cdr (q arg-iter)))))
(cons echo "+ " (cons cdr (cons pop (q func-call)))) (cons echo '+ ' (cons cdr (cons pop (q func-call))))
(cons eval (q func-call)))))) (cons eval (q func-call))))))
(eval lam))) (eval lam)))

View file

@ -1,20 +0,0 @@
#!/bin/flesh
;; Flesh: Flexible Shell
;; Copyright (C) 2021 Ava Affine
;;
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;; INTERACTIVE DEVELOPMENT TESTS
;; This file contains test cases for interactive-devel.f

View file

@ -18,31 +18,19 @@
;; INTERACTIVE DEVELOPMENT ;; INTERACTIVE DEVELOPMENT
;; This file contains features that help you develop new features at the REPL ;; This file contains features that help you develop new features at the REPL
;; TODO: Need an is_symbol impl or some way to reflect
;; lib setup
(if (not (set? reduce))
((echo "need map. Include userlib.f")
(exit -1)))
;; basic goals ;; basic goals
;; (def get-tree-deps ;; (def get-sym-deps
;; 'Gets all global symbols referenced by the definition of a symbol. ;; 'gets all global symbols referenced by the definition of a symbol'
;; assumes symbol is passed in quoted. Essentially this function expects ;; (sym) ;; probably just iter over and call 'set?' on symbols
;; a tree.' ;; ()) ;; then return a stack of symbols
;; (sym)
;; (reduce (lambda (res elem) (cond ((sym? elem) ;; symbol case
;; ()
;; ((list? elem) ;; list case
;; (extend results (get-tree-deps (eval elem)))))))
;; sym)) ;; then return a stack of symbols
;; (def dump-tree ;; (def dump-sym
;; 'writes a symbol to a library file' ;; 'writes a symbol to a library file'
;; (sym lib) ;; check if lib exists ;; (sym lib) ;; check if lib exists
;; ()) ;; write to lib ;; ()) ;; write to lib
;; (def dump-tree-with-deps ;; (def dump-sym-with-deps
;; 'writes a symbol, with all its dependencies, to a file' ;; 'writes a symbol, with all its dependencies, to a file'
;; (sym lib) ;; make a list of sym and all its deps ;; (sym lib) ;; make a list of sym and all its deps
;; ()) ;; iterate over that list and add new elements that are deps of iter ;; ()) ;; iterate over that list and add new elements that are deps of iter

View file

@ -20,44 +20,44 @@
;; this file implements unit tests for handwritten userlib functions ;; this file implements unit tests for handwritten userlib functions
(def passed (def passed
"prints if a test has passed" 'prints if a test has passed'
(test) (test)
(echo (concat "PASSED: " test))) (echo (concat "PASSED: " test)))
(def failed (def failed
"prints if a test has failed" 'prints if a test has failed'
(test) (test)
(let (()) (let (())
(echo (concat "FAILED: " test)) (echo (concat "FAILED: " test))
(exit 1))) (exit 1)))
(def test-cases "all test cases" (def test-cases 'all test cases'
(("set updates var" (('set updates var'
(quote (quote
(let ((test-val 0)) (let ((test-val 0))
(set (q test-val) 3) (set (q test-val) 3)
(eq? test-val 3)))) (eq? test-val 3))))
("prepend prepends to list" ('prepend prepends to list'
(quote (quote
(let ((list (2 3 4)) (let ((list (2 3 4))
(list (prepend 1 list)) (list (prepend 1 list))
(list-head (pop list))) (list-head (pop list)))
(eq? (car list-head) 1)))) (eq? (car list-head) 1))))
("map applies function across list" ('map applies function across list'
(quote (quote
(let ((list (1 2 3)) (let ((list (1 2 3))
(adder (lambda (x) (add 1 x)))) (adder (lambda (x) (add 1 x))))
(eq? (map adder list) (2 3 4))))) (eq? (map adder list) (2 3 4)))))
("reduce function adds numbers" ('reduce function adds numbers'
(quote (quote
(let ((list (1 2 3)) (let ((list (1 2 3))
(adder (lambda (x y) (add x y)))) (adder (lambda (x y) (add x y))))
(eq? (reduce adder list) (add 1 2 3))))) (eq? (reduce adder list) (add 1 2 3)))))
("cond evaluates the first branch that returns true" ('cond evaluates the first branch that returns true'
(quote (quote
(let ((switch-one false) (let ((switch-one false)
(switch-two false) (switch-two false)
@ -68,7 +68,7 @@
(true (toggle switch-three))))) (true (toggle switch-three)))))
(and (not switch-one) switch-two (not switch-three))))) (and (not switch-one) switch-two (not switch-three)))))
("cond doesnt do anything if all the branches are false" ('cond doesnt do anything if all the branches are false'
(quote (quote
(let ((switch-one false) (let ((switch-one false)
(switch-two false) (switch-two false)
@ -79,32 +79,27 @@
(false (toggle switch-three))))) (false (toggle switch-three)))))
(and (not switch-one) (not switch-two) (not switch-three))))) (and (not switch-one) (not switch-two) (not switch-three)))))
("cond returns the result of the branch that is evaluated" ('cond returns the result of the branch that is evaluated'
(quote (quote
(let ((variable "2")) (let ((variable false))
(set (q variable) (set (q variable)
(cond (q (cond (q
((true "1"))))) ((true true)))))
(eq? variable "1")))) variable)))
("cond does not choke on nil evaluated result" ('contains? finds elem in list'
(quote
(cdr ((cond (q ((true (assert true)))))
true))))
("contains? finds elem in list"
(quote (quote
(contains? (1 2 3) 1))) (contains? (1 2 3) 1)))
("contains? finds last elem in list" ('contains? finds last elem in list'
(quote (quote
(contains? (1 2 3) 3))) (contains? (1 2 3) 3)))
("contains? doesnt find elem not in list" ('contains? doesnt find elem not in list'
(quote (quote
(not (contains? (1 2 3) 4)))) (not (contains? (1 2 3) 4))))
("get-paths properly splits path into segments" ('get-paths properly splits path into segments'
(quote (quote
(let ((PATH "/seg1:/seg2") (let ((PATH "/seg1:/seg2")
(split-path (get-paths))) (split-path (get-paths)))
@ -112,20 +107,20 @@
(contains? split-path "/seg1") (contains? split-path "/seg1")
(contains? split-path "/seg2"))))) (contains? split-path "/seg2")))))
("add-path properly adds a new path segment to PATH" ('add-path properly adds a new path segment to PATH'
(quote (quote
(let ((PATH "/seg1:/seg2") (let ((PATH "/seg1:/seg2")
(new-path "/seg3")) (new-path "/seg3"))
(add-path new-path) (add-path new-path)
(contains? (get-paths) new-path)))) (contains? (get-paths) new-path))))
("join operates as expected" ('join operates as expected'
(quote (quote
(let ((l ("1" 2 (3)))) (let ((l ("1" 2 (3))))
(eq? (join l ".") (eq? (join l ".")
"1.2.(3)")))) "1.2.(3)"))))
("extend extends sets" ('extend extends sets'
(quote (quote
(let ((s1 (1 2 3)) (let ((s1 (1 2 3))
(s2 (4 5 6))) (s2 (4 5 6)))

View file

@ -36,13 +36,13 @@
;; this would be way faster as code in stl ;; this would be way faster as code in stl
;; but stl already suffers scope creep ;; but stl already suffers scope creep
(def prepend (def prepend
"takes a list and appends an element to the back of it. 'takes a list and appends an element to the back of it.
returns prepended list" returns prepended list'
(elem list) (elem list)
(reverse (cons (reverse list) elem))) (reverse (cons (reverse list) elem)))
(def set (def set
"sets an existing variable without touching its docstring. 'sets an existing variable without touching its docstring.
WARNING: WARNING:
If you find yourself struggling to debug a complex error in state access, If you find yourself struggling to debug a complex error in state access,
@ -56,16 +56,16 @@
A cozy script in flesh is one where each root level form (or eval at repl) A cozy script in flesh is one where each root level form (or eval at repl)
is self contained, and does not permanently modify any other one. is self contained, and does not permanently modify any other one.
See the userlib tests for an easy to follow example of this." See the userlib tests for an easy to follow example of this.'
(var val) (var val)
(let ((doc (get-doc var))) (let ((doc (get-doc var)))
(def (eval var) doc val))) (def (eval var) doc val)))
(def map (def map
"Takes two arguments: a function and a list. 'Takes two arguments: a function and a list.
for each element in the list, the function is applied and the for each element in the list, the function is applied and the
result is added to a new list. Returns the new list." result is added to a new list. Returns the new list.'
(func list) (func list)
(let ((list-iter (pop list)) (let ((list-iter (pop list))
(result ())) (result ()))
@ -78,13 +78,13 @@ result is added to a new list. Returns the new list."
result)) result))
(def reduce (def reduce
"Takes two arguments: a function and a list. 'Takes two arguments: a function and a list.
The function is expected to take two arguments: The function is expected to take two arguments:
* the current list item * the current list item
* the previous result * the previous result
Initially the function will take element1 and element2, outputting result1. Initially the function will take element1 and element2, outputting result1.
Then the function will take result1 and element3, outputting result2. Then the function will take result1 and element3, outputting result2.
this will continue iuntil the list is exhausted." this will continue iuntil the list is exhausted.'
(func list) (func list)
(let ((list-iter (pop list)) (let ((list-iter (pop list))
(result (car list-iter))) (result (car list-iter)))
@ -99,23 +99,22 @@ this will continue iuntil the list is exhausted."
result)) result))
(def cond (def cond
"Takes one argument: a list of pairs consisting of a form containing a bool and a form to execute 'Takes one argument: a list of pairs consisting of a form returning a bool and a form to execute
The function iterates over the list checking if the first element of each form evaluates to true, if so it will execute the second form of the pair, returning its result, and stop the loop." The function iterates over the list checking if the first form of a pair evaluates to true, in which
case it will execute the second form of the pair, returning its result, and stop the loop.'
(list) (list)
(let ((iter list)) (let ((iter list))
(while (gt? (len iter) 0) (while (gt? (len iter) 0)
(let ((current (car iter)) (let ((current (car iter))
(remaining (pop iter))) (remaining (pop iter)))
(if (eval (car current)) (if (eval (car current))
(let ((res (eval (cdr current)))) (car (pop ((set (q iter) ())
(set (q iter) ()) (eval (cdr current)))))
res)
(set (q iter) (cdr remaining))))))) (set (q iter) (cdr remaining)))))))
(def contains? (def contains?
"Takes two arguments: a list and an element. 'Takes two arguments: a list and an element.
Returns true if the list contains the element." Returns true if the list contains the element.'
(list elem) (list elem)
(let ((found false) (let ((found false)
(list-iter (pop list))) (list-iter (pop list)))
@ -129,17 +128,17 @@ Returns true if the list contains the element."
found)) found))
(def get-paths (def get-paths
"returns each individual directory in PATH" 'returns each individual directory in PATH'
() (split PATH ":")) () (split PATH ':'))
(def add-path (def add-path
"Takes one argument. 'Takes one argument.
adds a directory to PATH" adds a directory to PATH'
(path) (set (q PATH) (path) (set (q PATH)
(concat PATH ":" path))) (concat PATH ':' path)))
(def display-paths (def display-paths
"prints out each element of $PATH one by one" 'prints out each element of $PATH one by one'
() ()
(let ((path-iter (pop (get-paths)))) (let ((path-iter (pop (get-paths))))
(while (gt? (len path-iter) 1) (while (gt? (len path-iter) 1)
@ -149,8 +148,8 @@ adds a directory to PATH"
(set (q path-iter) (pop rem)))))) (set (q path-iter) (pop rem))))))
(def join (def join
"concatenates list elements into a string, while 'concatenates list elements into a string, while
interspersing a provided delimiter in between elements" interspersing a provided delimiter in between elements'
(list delim) (list delim)
(reduce (lambda (res elem) (reduce (lambda (res elem)
(if (eq? (strlen res) 0) (if (eq? (strlen res) 0)
@ -159,7 +158,7 @@ interspersing a provided delimiter in between elements"
list)) list))
(def extend (def extend
"adds all elements in set2 to set1" 'adds all elements in set2 to set1'
(set1 set2) (set1 set2)
(reduce (lambda (res elem) (cons res elem)) (reduce (lambda (res elem) (cons res elem))
(prepend set1 set2))) (prepend set1 set2)))

View file

@ -15,26 +15,17 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#[cfg(feature="posix")]
mod posix;
mod run;
mod stl;
use { use {
flesh::{ flesh::{
ast::{ ast::{
eval, lex, eval, lex, run,
Ctr, Seg, SymTable, Symbol, Ctr, Seg, SymTable, Symbol,
Traceback, Traceback,
}, },
stdlib::CFG_FILE_VNAME, stdlib::{
}, static_stdlib, dynamic_stdlib, load_defaults,
crate::{ load_environment,
run::run, CONSOLE_XDIM_VNAME, CONSOLE_YDIM_VNAME, CFG_FILE_VNAME,
stl::{
load_environment, static_stdlib_overwrites, load_defaults,
CONSOLE_XDIM_VNAME, CONSOLE_YDIM_VNAME,
L_PROMPT_VNAME, R_PROMPT_VNAME, PROMPT_DELIM_VNAME, L_PROMPT_VNAME, R_PROMPT_VNAME, PROMPT_DELIM_VNAME,
}, },
}, },
@ -60,9 +51,7 @@ use {
}; };
#[cfg(feature="posix")] #[cfg(feature="posix")]
use crate::stl::dynamic_stdlib; use flesh::aux::{ShellState, check_jobs};
#[cfg(feature="posix")]
use posix::{ShellState, check_jobs};
#[cfg(feature="posix")] #[cfg(feature="posix")]
use nix::unistd; use nix::unistd;
@ -105,7 +94,7 @@ impl Prompt for CustomPrompt {
}; };
Cow::Owned(format!( Cow::Owned(format!(
"({}search: {}) ", "({}reverse-search: {}) ",
prefix, history_search.term prefix, history_search.term
)) ))
} }
@ -250,7 +239,7 @@ fn incomplete_brackets(line: &str) -> bool {
for c in line.chars() { for c in line.chars() {
match c { match c {
c if ['"', '`'].contains(&c) => { c if ['"', '`', '\''].contains(&c) => {
match within_string { match within_string {
Some(w) if c == w => { Some(w) if c == w => {
balance.pop(); balance.pop();
@ -311,7 +300,7 @@ fn main() {
let mut syms = SymTable::new(); let mut syms = SymTable::new();
load_defaults(&mut syms); load_defaults(&mut syms);
load_environment(&mut syms); load_environment(&mut syms);
static_stdlib_overwrites(&mut syms); static_stdlib(&mut syms);
#[cfg(feature="posix")] #[cfg(feature="posix")]
let prompt_ss: Rc<RefCell<ShellState>>; let prompt_ss: Rc<RefCell<ShellState>>;
@ -328,6 +317,22 @@ fn main() {
dynamic_stdlib(&mut syms, Some(shell_state_bindings)); dynamic_stdlib(&mut syms, Some(shell_state_bindings));
} }
// reload this later with the state bindings
#[cfg(not(feature="posix"))]
dynamic_stdlib(&mut syms);
// if there are args those are scripts, run them and exit
if env::args().count() > 1 {
let mut iter = env::args();
iter.next();
for i in iter {
run(i, &mut syms).unwrap();
}
return
}
// this is a user shell. attempt to load configuration
{ {
// scope the below borrow of syms // scope the below borrow of syms
let cfg_file = env::var(CFG_FILE_VNAME).unwrap_or(cfg_file_name); let cfg_file = env::var(CFG_FILE_VNAME).unwrap_or(cfg_file_name);
@ -340,19 +345,6 @@ fn main() {
dynamic_stdlib(&mut syms, Some(prompt_ss.clone())); dynamic_stdlib(&mut syms, Some(prompt_ss.clone()));
} }
// if there are args those are scripts, run them and exit
if env::args().count() > 1 {
let mut iter = env::args();
iter.next();
for i in iter {
if let Err(e) = run(i, &mut syms) {
eprintln!("{}", e)
}
}
return
}
// setup readline // setup readline
let completion_menu = Box::new(ColumnarMenu::default().with_name("completion_menu")); let completion_menu = Box::new(ColumnarMenu::default().with_name("completion_menu"));
let mut keybindings = default_emacs_keybindings(); let mut keybindings = default_emacs_keybindings();
@ -466,9 +458,11 @@ fn make_prompt(syms: &mut SymTable) -> CustomPrompt {
} }
fn make_completer(syms: &mut SymTable) -> CustomCompleter { fn make_completer(syms: &mut SymTable) -> CustomCompleter {
CustomCompleter(syms.iter() let mut toks = vec![];
.map(|i| i.0.clone()) for i in syms.keys(){
.collect()) toks.push(i.clone());
}
CustomCompleter(toks)
} }
fn get_token_to_complete(line: &str, pos: usize) -> (String, bool, usize) { fn get_token_to_complete(line: &str, pos: usize) -> (String, bool, usize) {

View file

@ -15,10 +15,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
use alloc::vec::Vec; use std::fmt;
use alloc::string::String;
use core::fmt;
use core::convert;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct TraceItem { pub struct TraceItem {
@ -29,14 +26,13 @@ pub struct TraceItem {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Traceback(pub Vec<TraceItem>); pub struct Traceback(pub Vec<TraceItem>);
#[inline]
pub fn start_trace(item: TraceItem) -> Traceback { pub fn start_trace(item: TraceItem) -> Traceback {
Traceback::new().with_trace(item) Traceback::new().with_trace(item)
} }
impl Traceback { impl Traceback {
pub fn new() -> Traceback { pub fn new() -> Traceback {
Traceback(Vec::new()) Traceback(vec![])
} }
pub fn with_trace(mut self, item: TraceItem) -> Traceback { pub fn with_trace(mut self, item: TraceItem) -> Traceback {
@ -60,13 +56,13 @@ impl fmt::Display for Traceback {
} }
impl convert::From<Traceback> for String { impl std::convert::From<Traceback> for String {
fn from(arg: Traceback) -> Self { fn from(arg: Traceback) -> Self {
format!("{}", arg) format!("{}", arg)
} }
} }
impl convert::From<(&String, &str)> for TraceItem { impl std::convert::From<(&String, &str)> for TraceItem {
fn from(value: (&String, &str)) -> Self { fn from(value: (&String, &str)) -> Self {
TraceItem { TraceItem {
caller: value.0.clone(), caller: value.0.clone(),
@ -75,7 +71,7 @@ impl convert::From<(&String, &str)> for TraceItem {
} }
} }
impl convert::From<(&String, String)> for TraceItem { impl std::convert::From<(&String, String)> for TraceItem {
fn from(value: (&String, String)) -> Self { fn from(value: (&String, String)) -> Self {
TraceItem { TraceItem {
caller: value.0.clone(), caller: value.0.clone(),
@ -84,7 +80,7 @@ impl convert::From<(&String, String)> for TraceItem {
} }
} }
impl convert::From<(&str, String)> for TraceItem { impl std::convert::From<(&str, String)> for TraceItem {
fn from(value: (&str, String)) -> Self { fn from(value: (&str, String)) -> Self {
TraceItem { TraceItem {
caller: String::from(value.0), caller: String::from(value.0),
@ -93,7 +89,7 @@ impl convert::From<(&str, String)> for TraceItem {
} }
} }
impl convert::From<(&str, &str)> for TraceItem { impl std::convert::From<(&str, &str)> for TraceItem {
fn from(value: (&str, &str)) -> Self { fn from(value: (&str, &str)) -> Self {
TraceItem { TraceItem {
caller: String::from(value.0), caller: String::from(value.0),

View file

@ -15,7 +15,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
use alloc::boxed::Box;
use crate::segment::{Ctr, Seg}; use crate::segment::{Ctr, Seg};
use crate::sym::{SymTable, call_lambda}; use crate::sym::{SymTable, call_lambda};
use crate::error::Traceback; use crate::error::Traceback;

View file

@ -15,23 +15,19 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
use alloc::boxed::Box;
use alloc::string::{String, ToString};
use crate::segment::{Ctr, Seg}; use crate::segment::{Ctr, Seg};
use crate::error::{Traceback, start_trace}; use crate::error::{Traceback, start_trace};
use phf::{Map, phf_map};
const UNMATCHED_STR_DELIM: &str = "Unmatched string delimiter in input"; const UNMATCHED_STR_DELIM: &str = "Unmatched string delimiter in input";
const UNMATCHED_LIST_DELIM: &str = "Unmatched list delimiter in input"; const UNMATCHED_LIST_DELIM: &str = "Unmatched list delimiter in input";
#[inline]
fn escape(input: &char) -> char { static ESCAPES: Map<char, char> = phf_map! {
match input {
'n' => '\n', 'n' => '\n',
't' => '\t', 't' => '\t',
'\\' => '\\', '\\' => '\\',
_ => input.clone(), };
}
}
/* takes a line of user input /* takes a line of user input
* returns an unsimplified tree of tokens. * returns an unsimplified tree of tokens.
@ -59,7 +55,6 @@ pub fn lex(document: &String) -> Result<Box<Seg>, Traceback> {
* Returns Ok(Rc<Seg>) if lexing passes * Returns Ok(Rc<Seg>) if lexing passes
* Returns Err(String) if an error occurs * Returns Err(String) if an error occurs
*/ */
#[inline]
fn process(document: &String) -> Result<Box<Seg>, String> { fn process(document: &String) -> Result<Box<Seg>, String> {
let doc_len = document.len(); let doc_len = document.len();
@ -68,13 +63,13 @@ fn process(document: &String) -> Result<Box<Seg>, String> {
} }
/* State variables /* State variables
* TODO: describe all of them
*/ */
let mut is_str = false; let mut is_str = false;
let mut ign = false; let mut ign = false;
let mut token = String::new(); let mut token = String::new();
let mut delim_stack = vec![]; let mut delim_stack = Vec::new();
let mut ref_stack = vec![]; let mut ref_stack = vec![];
let mut cursor = 0;
/* Iterate over document /* Iterate over document
* Manage currently sought delimiter * Manage currently sought delimiter
@ -83,13 +78,14 @@ fn process(document: &String) -> Result<Box<Seg>, String> {
let mut needs_alloc = false; let mut needs_alloc = false;
let mut alloc_list = false; let mut alloc_list = false;
let delim: char; let delim: char;
cursor += 1;
if let Some(d) = delim_stack.last() { if let Some(d) = delim_stack.last() {
delim = *d; delim = *d;
if delim == '*' { if delim == '*' {
token.push(escape(&c)); token.push(ESCAPES.get(&c)
.cloned()
.or(Some(c))
.unwrap());
delim_stack.pop(); delim_stack.pop();
continue; continue;
@ -120,8 +116,7 @@ fn process(document: &String) -> Result<Box<Seg>, String> {
} }
// try to generalize all whitespace // try to generalize all whitespace
if !needs_alloc && char::is_whitespace(c) && !is_str { if !needs_alloc && char::is_whitespace(c) && !is_str {
// dont make empty tokens just because the document // dont make empty tokens just because the document has consecutive whitespace
// has consecutive whitespace
if token.is_empty() { if token.is_empty() {
continue; continue;
} }
@ -131,23 +126,30 @@ fn process(document: &String) -> Result<Box<Seg>, String> {
if !needs_alloc { if !needs_alloc {
match c { match c {
// add a new Seg reference to the stack // add a new Seg reference to the stack
'(' if !is_str && token.is_empty() => { '(' => {
if is_str {
token.push(c);
continue;
}
if !token.is_empty() {
return Err("list started in middle of another token".to_string());
}
ref_stack.push(Seg::new()); ref_stack.push(Seg::new());
delim_stack.push(')'); delim_stack.push(')');
} }
// specific error case
'(' if !is_str && !token.is_empty() => {
return Err(
format!("list started in middle of another token: {}", token)
)
}
// begin parsing a string // begin parsing a string
'"' | '`' if !is_str => { '"' | '\'' | '`' if !is_str => {
is_str = true; is_str = true;
delim_stack.push(c); delim_stack.push(c);
} }
// eat the whole line // eat the whole line
'#' | ';' if !is_str => { '#' | ';' => {
if is_str {
token.push(c);
continue;
}
ign = true; ign = true;
delim_stack.push('\n'); delim_stack.push('\n');
} }
@ -174,7 +176,6 @@ fn process(document: &String) -> Result<Box<Seg>, String> {
return_singlet = true; return_singlet = true;
Seg::new() Seg::new()
}); });
let obj; let obj;
if is_str { if is_str {
obj = Box::from(Ctr::String(token)); obj = Box::from(Ctr::String(token));
@ -193,7 +194,7 @@ fn process(document: &String) -> Result<Box<Seg>, String> {
} else if let Some(s) = tok_is_symbol(&token) { } else if let Some(s) = tok_is_symbol(&token) {
obj = Box::from(Ctr::Symbol(s)); obj = Box::from(Ctr::Symbol(s));
} else { } else {
return Err(format!("Unparsable token \"{}\" at char {}", token, cursor)); return Err(format!("Unparsable token: {}", token));
} }
token = String::new(); token = String::new();
@ -201,7 +202,6 @@ fn process(document: &String) -> Result<Box<Seg>, String> {
} }
if alloc_list || return_singlet { if alloc_list || return_singlet {
// return if we have finished the document // return if we have finished the document
if ref_stack.is_empty() { if ref_stack.is_empty() {
return Ok(Box::new(current_seg)); return Ok(Box::new(current_seg));
@ -228,7 +228,6 @@ fn process(document: &String) -> Result<Box<Seg>, String> {
* - equals is also allowed but only for shell command compatibility * - equals is also allowed but only for shell command compatibility
* else returns false * else returns false
*/ */
#[inline]
fn tok_is_symbol(token: &str) -> Option<String> { fn tok_is_symbol(token: &str) -> Option<String> {
for t in token.chars() { for t in token.chars() {
if !t.is_alphanumeric() && if !t.is_alphanumeric() &&

View file

@ -15,8 +15,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#![cfg_attr(not(test), no_std)] mod run;
mod eval; mod eval;
mod lex; mod lex;
mod segment; mod segment;
@ -24,10 +23,8 @@ mod stl;
mod sym; mod sym;
mod error; mod error;
#[macro_use]
extern crate alloc;
pub mod ast { pub mod ast {
pub use crate::run::run;
pub use crate::eval::eval; pub use crate::eval::eval;
pub use crate::lex::lex; pub use crate::lex::lex;
pub use crate::segment::{Ctr, Seg, Type}; pub use crate::segment::{Ctr, Seg, Type};
@ -37,9 +34,28 @@ pub mod ast {
pub mod stdlib { pub mod stdlib {
pub use crate::stl::{ pub use crate::stl::{
static_stdlib, dynamic_stdlib, static_stdlib,
load_defaults, load_environment,
CONSOLE_XDIM_VNAME,
CONSOLE_YDIM_VNAME,
POSIX_CFG_VNAME,
MODENV_CFG_VNAME,
L_PROMPT_VNAME,
R_PROMPT_VNAME,
PROMPT_DELIM_VNAME,
CFG_FILE_VNAME, CFG_FILE_VNAME,
decl::STORE_DOCSTRING, FLESH_DEFAULT_CONS_HEIGHT,
decl::store_callback, FLESH_DEFAULT_CONS_WIDTH,
}; };
} }
pub mod aux {
#[cfg(feature="posix")]
pub use crate::stl::posix::args_from_ast;
#[cfg(feature="posix")]
pub use crate::stl::posix::ShellState;
#[cfg(feature="posix")]
pub use crate::stl::posix::check_jobs;
#[cfg(feature="posix")]
pub use crate::stl::posix::POSIX_LOAD_NAME;
}

View file

@ -15,10 +15,11 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
use flesh::ast::{ use crate::eval::eval;
eval, lex, start_trace, use crate::lex::lex;
Ctr, Seg, SymTable, Traceback, use crate::error::{Traceback, start_trace};
}; use crate::segment::{Ctr, Seg};
use crate::sym::SymTable;
use std::path::Path; use std::path::Path;
use std::fs; use std::fs;
use std::iter::FromIterator; use std::iter::FromIterator;

View file

@ -15,12 +15,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
use crate::sym::UserFn; use crate::sym::UserFn;
use alloc::fmt; use std::fmt;
use alloc::string::{String, ToString}; use std::marker::PhantomData;
use alloc::boxed::Box; use std::ops::{Add, Div, Index, Mul, Sub};
use core::convert;
use core::marker::PhantomData;
use core::ops::{Add, Div, Index, Mul, Sub};
// Container // Container
#[derive(Debug, Default)] #[derive(Debug, Default)]
@ -95,6 +92,9 @@ impl Ctr {
impl Seg { impl Seg {
/* recurs over tree assumed to be list in standard form /* recurs over tree assumed to be list in standard form
* appends object to end of list * appends object to end of list
*
* TODO: figure out how not to call CLONE on a CTR via obj arg
* TODO: return result
*/ */
pub fn append(&mut self, obj: Box<Ctr>) { pub fn append(&mut self, obj: Box<Ctr>) {
if let Ctr::None = &*(self.car) { if let Ctr::None = &*(self.car) {
@ -185,7 +185,6 @@ impl Seg {
} }
} }
#[inline]
fn seg_to_string(s: &Seg, parens: bool) -> String { fn seg_to_string(s: &Seg, parens: bool) -> String {
let mut string = String::new(); let mut string = String::new();
if parens { if parens {
@ -255,7 +254,7 @@ impl fmt::Display for Ctr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
Ctr::Symbol(s) => write!(f, "{}", s), Ctr::Symbol(s) => write!(f, "{}", s),
Ctr::String(s) => write!(f, "\"{}\"", s), Ctr::String(s) => write!(f, "\'{}\'", s),
Ctr::Integer(s) => write!(f, "{}", s), Ctr::Integer(s) => write!(f, "{}", s),
Ctr::Float(s) => write!(f, "{}", s), Ctr::Float(s) => write!(f, "{}", s),
Ctr::Bool(s) => { Ctr::Bool(s) => {
@ -413,7 +412,7 @@ impl fmt::Display for Type {
} }
} }
impl convert::From<String> for Type { impl std::convert::From<String> for Type {
fn from(value: String) -> Self { fn from(value: String) -> Self {
match value.as_str() { match value.as_str() {
"symbol" => Type::Symbol, "symbol" => Type::Symbol,

View file

@ -15,27 +15,24 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
use flesh::{ use crate::segment::{Ctr, Seg, Type};
ast::{Ctr, Seg, Type, Args, SymTable, Symbol, ValueType, Traceback},
stdlib::{STORE_DOCSTRING, static_stdlib},
};
use crate::run::{run_callback, RUN_DOCSTRING}; use crate::run::{run_callback, RUN_DOCSTRING};
use crate::sym::{Args, SymTable, Symbol, ValueType};
use crate::error::Traceback;
use std::rc::Rc; use std::rc::Rc;
use std::cell::RefCell; use std::cell::RefCell;
use std::env::vars; use std::env::vars;
use std::io;
#[cfg(feature = "posix")] #[cfg(feature = "posix")]
use crate::posix; pub mod posix;
#[path = "window.rs"] pub mod append;
mod window; pub mod boolean;
#[path = "store.rs"] pub mod control;
mod store; pub mod decl;
#[path = "file.rs"] pub mod math;
mod file; pub mod strings;
pub mod file;
pub const CONSOLE_XDIM_VNAME: &str = "_FLESH_WIDTH"; pub const CONSOLE_XDIM_VNAME: &str = "_FLESH_WIDTH";
pub const CONSOLE_YDIM_VNAME: &str = "_FLESH_HEIGHT"; pub const CONSOLE_YDIM_VNAME: &str = "_FLESH_HEIGHT";
@ -44,10 +41,10 @@ pub const MODENV_CFG_VNAME: &str = "CFG_FLESH_ENV";
pub const L_PROMPT_VNAME: &str = "CFG_FLESH_L_PROMPT"; pub const L_PROMPT_VNAME: &str = "CFG_FLESH_L_PROMPT";
pub const R_PROMPT_VNAME: &str = "CFG_FLESH_R_PROMPT"; pub const R_PROMPT_VNAME: &str = "CFG_FLESH_R_PROMPT";
pub const PROMPT_DELIM_VNAME: &str = "CFG_FLESH_PROMPT_DELIMITER"; pub const PROMPT_DELIM_VNAME: &str = "CFG_FLESH_PROMPT_DELIMITER";
pub const CFG_FILE_VNAME: &str = "FLESH_CFG_FILE";
pub const FLESH_DEFAULT_CONS_HEIGHT: i16 = 24; pub const FLESH_DEFAULT_CONS_HEIGHT: i16 = 24;
pub const FLESH_DEFAULT_CONS_WIDTH: i16 = 80; pub const FLESH_DEFAULT_CONS_WIDTH: i16 = 80;
fn l_prompt_default_callback(_: &Seg, _: &mut SymTable) -> Result<Ctr, Traceback> { fn l_prompt_default_callback(_: &Seg, _: &mut SymTable) -> Result<Ctr, Traceback> {
Ok(Ctr::String(">".to_string())) Ok(Ctr::String(">".to_string()))
} }
@ -63,18 +60,13 @@ fn prompt_delimiter_default_callback(_: &Seg, _: &mut SymTable) -> Result<Ctr, T
/// static_stdlib /// static_stdlib
/// inserts all stdlib functions that can be inserted without /// inserts all stdlib functions that can be inserted without
/// any kind of further configuration data into a symtable /// any kind of further configuration data into a symtable
pub fn static_stdlib_overwrites(syms: &mut SymTable) { pub fn static_stdlib(syms: &mut SymTable) {
static_stdlib( append::add_list_lib(syms);
syms, strings::add_string_lib(syms);
|arg: &String| print!("{}", arg), decl::add_decl_lib_static(syms);
|| -> String { control::add_control_lib(syms);
let _= io::stdout(); boolean::add_bool_lib(syms);
let mut input = String::new(); math::add_math_lib(syms);
io::stdin().read_line(&mut input).expect("couldnt read user input");
input.trim().to_string()
},
);
window::add_window_lib_funcs(syms);
file::add_file_lib(syms); file::add_file_lib(syms);
syms.insert( syms.insert(
@ -95,26 +87,13 @@ pub fn static_stdlib_overwrites(syms: &mut SymTable) {
/// callbacks with configuration into a symtable /// callbacks with configuration into a symtable
#[cfg(feature="posix")] #[cfg(feature="posix")]
pub fn dynamic_stdlib(syms: &mut SymTable, shell: Option<Rc<RefCell<posix::ShellState>>>) { pub fn dynamic_stdlib(syms: &mut SymTable, shell: Option<Rc<RefCell<posix::ShellState>>>) {
// get CFG_FLESH_ENV from syms
let env_cfg_user_form = syms let env_cfg_user_form = syms
.call_symbol(&MODENV_CFG_VNAME.to_string(), &Seg::new(), true) .call_symbol(&MODENV_CFG_VNAME.to_string(), &Seg::new(), true)
.unwrap_or_else(|_: Traceback| Box::new(Ctr::None)) .unwrap_or_else(|_: Traceback| Box::new(Ctr::None))
.to_string() .to_string()
.eq("true"); .eq("true");
if env_cfg_user_form { decl::add_decl_lib_dynamic(syms, env_cfg_user_form);
syms.insert(
"def".to_string(),
Symbol {
name: String::from("define"),
args: Args::Infinite,
conditional_branches: true,
docs: STORE_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(
store::store_callback_with_env_integration
)),
..Default::default()
}
);
}
if let Some(shell_state) = shell { if let Some(shell_state) = shell {
let posix_cfg_user_form = syms let posix_cfg_user_form = syms
@ -129,6 +108,27 @@ pub fn dynamic_stdlib(syms: &mut SymTable, shell: Option<Rc<RefCell<posix::Shell
} }
} }
#[cfg(not(feature="posix"))]
pub fn dynamic_stdlib(syms: &mut SymTable) {
decl::add_decl_lib_dynamic(syms, false);
}
pub fn load_environment(syms: &mut SymTable) {
for (key, value) in vars() {
syms.insert(
key.clone(),
Symbol{
name: key,
args: Args::None,
conditional_branches: false,
docs: String::from("from env vars at time of load"),
value: ValueType::VarForm(Box::new(Ctr::String(value))),
..Default::default()
}
);
}
}
pub fn load_defaults(syms: &mut SymTable) { pub fn load_defaults(syms: &mut SymTable) {
syms.insert( syms.insert(
POSIX_CFG_VNAME.to_string(), POSIX_CFG_VNAME.to_string(),
@ -225,19 +225,3 @@ default value: 1 (set)
) )
); );
} }
pub fn load_environment(syms: &mut SymTable) {
for (key, value) in vars() {
syms.insert(
key.clone(),
Symbol{
name: key,
args: Args::None,
conditional_branches: false,
docs: String::from("from env vars at time of load"),
value: ValueType::VarForm(Box::new(Ctr::String(value))),
..Default::default()
}
);
}
}

View file

@ -18,10 +18,7 @@
use crate::segment::{Ctr, Seg, Type}; use crate::segment::{Ctr, Seg, Type};
use crate::sym::{SymTable, Symbol, Args, ValueType}; use crate::sym::{SymTable, Symbol, Args, ValueType};
use crate::error::{Traceback, start_trace}; use crate::error::{Traceback, start_trace};
use alloc::rc::Rc; use std::rc::Rc;
use alloc::boxed::Box;
use alloc::string::{String, ToString};
const CONS_DOCSTRING: &str = "traverses any number of arguments collecting them into a list. const CONS_DOCSTRING: &str = "traverses any number of arguments collecting them into a list.
If the first argument is a list, all other arguments are added sequentially to the end of the list contained in the first argument."; If the first argument is a list, all other arguments are added sequentially to the end of the list contained in the first argument.";

View file

@ -18,9 +18,7 @@
use crate::segment::{Ctr, Seg, Type}; use crate::segment::{Ctr, Seg, Type};
use crate::error::{Traceback, start_trace}; use crate::error::{Traceback, start_trace};
use crate::sym::{SymTable, ValueType, Args, Symbol}; use crate::sym::{SymTable, ValueType, Args, Symbol};
use alloc::rc::Rc; use std::rc::Rc;
use alloc::boxed::Box;
use alloc::string::{String, ToString};
const AND_DOCSTRING: &str = const AND_DOCSTRING: &str =
"traverses a list of N arguments, all of which are expected to be boolean. "traverses a list of N arguments, all of which are expected to be boolean.

View file

@ -19,9 +19,8 @@ use crate::eval::eval;
use crate::error::{Traceback, start_trace}; use crate::error::{Traceback, start_trace};
use crate::segment::{Ctr, Seg, Type}; use crate::segment::{Ctr, Seg, Type};
use crate::sym::{SymTable, Symbol, ValueType, Args}; use crate::sym::{SymTable, Symbol, ValueType, Args};
use alloc::rc::Rc; use std::rc::Rc;
use alloc::boxed::Box; use std::process;
use alloc::string::{String, ToString};
const IF_DOCSTRING: &str = const IF_DOCSTRING: &str =
"accepts three bodies, a condition, an unevaluated consequence, and an alternative consequence. "accepts three bodies, a condition, an unevaluated consequence, and an alternative consequence.
@ -398,7 +397,33 @@ fn assert_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
} }
} }
const EXIT_DOCSTRING: &str = "Takes on input: an integer used as an exit code.
Entire REPL process quits with exit code.";
fn exit_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
if let Ctr::Integer(i) = *ast.car {
if i > 2 ^ 32 {
panic!("argument to exit too large!")
} else {
process::exit(i as i32);
}
} else {
panic!("impossible argument to exit")
}
}
pub fn add_control_lib(syms: &mut SymTable) { pub fn add_control_lib(syms: &mut SymTable) {
syms.insert(
"exit".to_string(),
Symbol {
name: String::from("exit"),
args: Args::Strict(vec![Type::Integer]),
conditional_branches: false,
docs: EXIT_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(exit_callback)),
..Default::default()
},
);
syms.insert( syms.insert(
"assert".to_string(), "assert".to_string(),
Symbol { Symbol {

View file

@ -18,10 +18,10 @@
use crate::eval::eval; use crate::eval::eval;
use crate::error::{Traceback, start_trace}; use crate::error::{Traceback, start_trace};
use crate::segment::{Ctr, Seg, Type}; use crate::segment::{Ctr, Seg, Type};
use crate::stdlib::{CONSOLE_XDIM_VNAME, FLESH_DEFAULT_CONS_WIDTH};
use crate::sym::{SymTable, Symbol, UserFn, ValueType, Args}; use crate::sym::{SymTable, Symbol, UserFn, ValueType, Args};
use alloc::rc::Rc; use std::env;
use alloc::boxed::Box; use std::rc::Rc;
use alloc::string::{String, ToString};
const QUOTE_DOCSTRING: &str = "takes a single unevaluated tree and returns it as it is: unevaluated."; const QUOTE_DOCSTRING: &str = "takes a single unevaluated tree and returns it as it is: unevaluated.";
fn quote_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> { fn quote_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
@ -86,7 +86,7 @@ fn eval_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback> {
} }
const HELP_DOCSTRING: &str = "prints help text for a given symbol. Expects only one argument."; const HELP_DOCSTRING: &str = "prints help text for a given symbol. Expects only one argument.";
fn help_callback(ast: &Seg, syms: &mut SymTable, print: fn(&String)) -> Result<Ctr, Traceback> { fn help_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback> {
if ast.len() != 1 { if ast.len() != 1 {
return Err(start_trace(("help", "expected one input").into())); return Err(start_trace(("help", "expected one input").into()));
} }
@ -98,7 +98,7 @@ fn help_callback(ast: &Seg, syms: &mut SymTable, print: fn(&String)) -> Result<C
} else { } else {
args_str = sym.args.to_string(); args_str = sym.args.to_string();
} }
print(&format!( println!(
"NAME: {0}\n "NAME: {0}\n
ARGS: {1}\n ARGS: {1}\n
DOCUMENTATION:\n DOCUMENTATION:\n
@ -106,7 +106,7 @@ DOCUMENTATION:\n
CURRENT VALUE AND/OR BODY: CURRENT VALUE AND/OR BODY:
{3}", {3}",
sym.name, args_str, sym.docs, sym.value sym.name, args_str, sym.docs, sym.value
)); );
} else { } else {
return Err(start_trace(("help", format!("{symbol} is undefined")).into())); return Err(start_trace(("help", format!("{symbol} is undefined")).into()));
} }
@ -131,7 +131,21 @@ fn isset_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback> {
const ENV_DOCSTRING: &str = "takes no arguments const ENV_DOCSTRING: &str = "takes no arguments
prints out all available symbols and their associated values"; prints out all available symbols and their associated values";
fn env_callback(_ast: &Seg, syms: &mut SymTable, print: fn(&String)) -> Result<Ctr, Traceback> { fn env_callback(_ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback> {
// get width of current output
let xdim: i128;
if let Ctr::Integer(dim) = *syms
.call_symbol(&CONSOLE_XDIM_VNAME.to_string(), &Seg::new(), true)
.unwrap_or_else(|_: Traceback| Box::new(Ctr::None)) {
xdim = dim;
} else {
println!("{} contains non integer value, defaulting to {}",
CONSOLE_XDIM_VNAME, FLESH_DEFAULT_CONS_WIDTH);
xdim = FLESH_DEFAULT_CONS_WIDTH as i128;
}
let mut v_col_len = 0;
let mut f_col_len = 0;
let mut functions = vec![]; let mut functions = vec![];
let mut variables = vec![]; let mut variables = vec![];
for (name, val) in syms.iter() { for (name, val) in syms.iter() {
@ -142,19 +156,51 @@ fn env_callback(_ast: &Seg, syms: &mut SymTable, print: fn(&String)) -> Result<C
_ => format!("{}: {}", name, val.value), _ => format!("{}: {}", name, val.value),
}; };
if token.len() > v_col_len && token.len() < xdim as usize {
v_col_len = token.len();
}
variables.push(token); variables.push(token);
} else { } else {
if f_col_len < name.len() && name.len() < xdim as usize {
f_col_len = name.len();
}
functions.push(name.clone()); functions.push(name.clone());
} }
} }
print(&String::from("VARIABLES:")); let mut n_v_cols = xdim / v_col_len as i128;
for var in variables { // now decrement to make sure theres room for two spaces of padding
print(&var) while n_v_cols > 1 && xdim % (v_col_len as i128) < (2 * n_v_cols) {
n_v_cols -= 1;
} }
print(&String::from("\nFUNCTIONS:")); // again for functions
let mut n_f_cols = xdim / f_col_len as i128;
while n_f_cols > 1 && xdim & (f_col_len as i128) < (2 * n_f_cols) {
n_f_cols -= 1;
}
let mut col_iter = 0;
println!("VARIABLES:");
for var in variables {
print!("{:v_col_len$}", var);
col_iter += 1;
if col_iter % n_v_cols == 0 {
println!();
} else {
print!(" ");
}
}
println!("\nFUNCTIONS:");
col_iter = 0;
for func in functions { for func in functions {
print(&func); print!("{:f_col_len$}", func);
col_iter += 1;
if col_iter % n_f_cols == 0 {
println!();
} else {
print!(" ");
}
} }
Ok(Ctr::None) Ok(Ctr::None)
} }
@ -261,7 +307,7 @@ fn setdoc_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback> {
} }
} }
pub const STORE_DOCSTRING: &str = "allows user to define functions and variables. const STORE_DOCSTRING: &str = "allows user to define functions and variables.
A call may take one of three forms: A call may take one of three forms:
1. variable declaration: 1. variable declaration:
Takes a name, doc string, and a value. Takes a name, doc string, and a value.
@ -275,10 +321,8 @@ pub const STORE_DOCSTRING: &str = "allows user to define functions and variables
(def useless-var) (def useless-var)
Additionally, passing a tree as a name will trigger def to evaluate the tree and try to derive Additionally, passing a tree as a name will trigger def to evaluate the tree and try to derive
a value from it. If it does not return a String or a Symbol this will result in a failure. a value from it. If it does not return a ";
fn store_callback(ast: &Seg, syms: &mut SymTable, env_cfg: bool) -> Result<Ctr, Traceback> {
The name of the symbol operated on is returned (as a string).";
pub fn store_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback> {
let is_var = ast.len() == 3; let is_var = ast.len() == 3;
let name: String; let name: String;
let docs: String; let docs: String;
@ -306,8 +350,11 @@ pub fn store_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback>
// remove var case // remove var case
if ast.len() == 1 { if ast.len() == 1 {
syms.remove(&name); syms.remove(&name);
return Ok(Ctr::String(name)) if env_cfg {
env::remove_var(name);
}
return Ok(Ctr::None)
} else if ast.len() < 3 || ast.len() > 4 { } else if ast.len() < 3 || ast.len() > 4 {
return Err(start_trace(("def", "expected 3 or 4 inputs").into())) return Err(start_trace(("def", "expected 3 or 4 inputs").into()))
} }
@ -379,7 +426,20 @@ pub fn store_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback>
name.clone(), name.clone(),
Symbol::from_ast(&name, &docs, &outer_seg, None), Symbol::from_ast(&name, &docs, &outer_seg, None),
); );
return Ok(Ctr::String(name)) if env_cfg {
match var_val.to_type() {
Type::Lambda => {},
Type::Seg => {},
_ => {
let mut s = var_val.to_string();
if let Ctr::String(tok) = var_val {
s = tok;
}
env::set_var(name.clone(), s);
}
}
}
return Ok(Ctr::None)
} }
let mut arg_list = vec![]; let mut arg_list = vec![];
@ -408,7 +468,7 @@ pub fn store_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback>
Some(arg_list), Some(arg_list),
), ),
); );
Ok(Ctr::String(name)) Ok(Ctr::None)
} else { } else {
Err(start_trace( Err(start_trace(
("def", "expected one or more forms to evaluate in function body") ("def", "expected one or more forms to evaluate in function body")
@ -416,8 +476,7 @@ pub fn store_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback>
} }
} }
pub fn add_decl_lib_static(syms: &mut SymTable, print: fn(&String)) { pub fn add_decl_lib_static(syms: &mut SymTable) {
let help_print = print.clone();
syms.insert( syms.insert(
"help".to_string(), "help".to_string(),
Symbol { Symbol {
@ -425,11 +484,7 @@ pub fn add_decl_lib_static(syms: &mut SymTable, print: fn(&String)) {
args: Args::Strict(vec![Type::Symbol]), args: Args::Strict(vec![Type::Symbol]),
conditional_branches: true, conditional_branches: true,
docs: HELP_DOCSTRING.to_string(), docs: HELP_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new( value: ValueType::Internal(Rc::new(help_callback)),
move |ast: &Seg, syms: &mut SymTable| -> Result<Ctr, Traceback> {
help_callback(ast, syms, help_print)
}
)),
..Default::default() ..Default::default()
}, },
); );
@ -446,6 +501,18 @@ pub fn add_decl_lib_static(syms: &mut SymTable, print: fn(&String)) {
}, },
); );
syms.insert(
"env".to_string(),
Symbol {
name: String::from("env"),
args: Args::None,
conditional_branches: false,
docs: ENV_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(env_callback)),
..Default::default()
},
);
syms.insert( syms.insert(
"quote".to_string(), "quote".to_string(),
Symbol { Symbol {
@ -517,24 +584,9 @@ pub fn add_decl_lib_static(syms: &mut SymTable, print: fn(&String)) {
..Default::default() ..Default::default()
} }
); );
}
let env_print = print.clone(); pub fn add_decl_lib_dynamic(syms: &mut SymTable, env: bool) {
syms.insert(
"env".to_string(),
Symbol {
name: String::from("env"),
args: Args::None,
conditional_branches: false,
docs: ENV_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(
move |ast: &Seg, syms: &mut SymTable| -> Result<Ctr, Traceback> {
env_callback(ast, syms, env_print)
}
)),
..Default::default()
},
);
syms.insert( syms.insert(
"def".to_string(), "def".to_string(),
Symbol { Symbol {
@ -544,7 +596,7 @@ pub fn add_decl_lib_static(syms: &mut SymTable, print: fn(&String)) {
docs: STORE_DOCSTRING.to_string(), docs: STORE_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new( value: ValueType::Internal(Rc::new(
move |ast: &Seg, syms: &mut SymTable| -> Result<Ctr, Traceback> { move |ast: &Seg, syms: &mut SymTable| -> Result<Ctr, Traceback> {
store_callback(ast, syms) store_callback(ast, syms, env)
}, },
)), )),
..Default::default() ..Default::default()

View file

@ -15,11 +15,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
use flesh::ast::{ use crate::segment::{Ctr, Seg, Type};
start_trace, Ctr, Seg, Type, SymTable, use crate::sym::{SymTable, Symbol, ValueType, Args};
Symbol, ValueType, Args, Traceback, use crate::error::{Traceback, start_trace};
};
use std::io::Write; use std::io::Write;
use std::fs::{File, read_to_string, OpenOptions}; use std::fs::{File, read_to_string, OpenOptions};
use std::rc::Rc; use std::rc::Rc;
@ -172,81 +170,3 @@ pub fn add_file_lib(syms: &mut SymTable) {
); );
} }
#[cfg(test)]
mod tests {
use flesh::ast::{eval, lex, SymTable};
use flesh::stdlib::static_stdlib;
use crate::stl::static_stdlib_overwrites;
#[test]
fn test_fexists() {
let document = "(exists? \"/tmp\")";
let result = "true";
let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new());
static_stdlib_overwrites(&mut syms);
assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap()
.to_string(),
result.to_string(),
);
}
#[test]
fn test_fexists_doesnt() {
let document = "(exists? \"cargo.timtam\")";
let result = "false";
let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new());
static_stdlib_overwrites(&mut syms);
assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap()
.to_string(),
result.to_string(),
);
}
#[test]
fn test_write_file() {
let document = "
(let ((s \"test\")
(t \"/tmp/flesh-lib-test-file-1\"))
(write-file t s)
(echo (read-file t))
(eq? (read-file t) s))";
let result = "true";
let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new());
static_stdlib_overwrites(&mut syms);
assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap()
.to_string(),
result.to_string(),
);
}
#[test]
fn test_append_file() {
let document = "
(let ((s \"test\")
(t \"/tmp/flesh-lib-test-file-2\"))
(write-file t s)
(append-file t s)
(eq? (read-file t)
(concat s s)))";
let result = "true";
let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new());
static_stdlib_overwrites(&mut syms);
assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap()
.to_string(),
result.to_string(),
);
}
}

View file

@ -18,17 +18,12 @@
use crate::segment::{Ctr, Seg}; use crate::segment::{Ctr, Seg};
use crate::sym::{SymTable, ValueType, Symbol, Args}; use crate::sym::{SymTable, ValueType, Symbol, Args};
use crate::error::{Traceback, start_trace}; use crate::error::{Traceback, start_trace};
use std::rc::Rc;
use alloc::rc::Rc;
use alloc::boxed::Box;
use alloc::string::{String, ToString};
#[inline]
fn isnumeric(arg: &Ctr) -> bool { fn isnumeric(arg: &Ctr) -> bool {
matches!(arg, Ctr::Integer(_) | Ctr::Float(_)) matches!(arg, Ctr::Integer(_) | Ctr::Float(_))
} }
const ADD_DOCSTRING: &str = const ADD_DOCSTRING: &str =
"traverses over N args, which must all evaluate to an Integer or Float. "traverses over N args, which must all evaluate to an Integer or Float.
Adds each arg up to a final result. WARNING: does not acocunt for under/overflows. Adds each arg up to a final result. WARNING: does not acocunt for under/overflows.
@ -192,6 +187,57 @@ fn floatcast_callback(ast: &Seg, _: &mut SymTable) -> Result<Ctr, Traceback> {
} }
} }
const EXP_DOCSTRING: &str = "Takes two args, both expected to be numeric.
Returns the first arg to the power of the second arg.
Does not handle overflow or underflow.
PANIC CASES:
- arg1 is a float and arg2 is greater than an int32
- an integer exceeding the size of a float64 is raised to a float power
- an integer is rased to the power of another integer exceeding the max size of an unsigned 32bit integer";
fn exp_callback(ast: &Seg, _: &mut SymTable) -> Result<Ctr, Traceback> {
let first = *ast.car.clone();
if !isnumeric(&first) {
return Err(start_trace(
("exp", format!("{} is not a number!", first))
.into()))
}
let second: Ctr;
if let Ctr::Seg(ref s) = *ast.cdr {
second = *s.car.clone();
} else {
return Err(start_trace(
("exp", "expected at least two inputs")
.into()))
}
if !isnumeric(&second) {
return Err(start_trace(
("exp", format!("{} is not a number!", second))
.into()))
}
match first {
Ctr::Float(lf) => match second {
Ctr::Float(rf) => Ok(Ctr::Float(f64::powf(lf, rf))),
Ctr::Integer(ri) => Ok(Ctr::Float(f64::powi(lf, ri as i32))),
_ => Err(start_trace(
("exp", "not implemented for these input types")
.into())),
},
Ctr::Integer(li) => match second {
Ctr::Float(rf) => Ok(Ctr::Float(f64::powf(li as f64, rf))),
Ctr::Integer(ri) => Ok(Ctr::Integer(li.pow(ri as u32))),
_ => Err(start_trace(
("exp", "not implemented for these input types")
.into())),
},
_ => Err(start_trace(
("exp", "not implemented for these input types")
.into())),
}
}
const MOD_DOCSTRING: &str = "Takes two args, both expected to be numeric. const MOD_DOCSTRING: &str = "Takes two args, both expected to be numeric.
Returns a list of two values: the modulus and the remainder. Returns a list of two values: the modulus and the remainder.
Example: (mod 5 3) -> (1 2) Example: (mod 5 3) -> (1 2)
@ -556,6 +602,19 @@ pub fn add_math_lib(syms: &mut SymTable) {
}, },
); );
syms.insert(
"exp".to_string(),
Symbol {
name: String::from("exp"),
args: Args::Lazy(2),
conditional_branches: false,
docs: EXP_DOCSTRING.to_string(),
optimizable: true,
value: ValueType::Internal(Rc::new(exp_callback)),
..Default::default()
},
);
syms.insert( syms.insert(
"mod".to_string(), "mod".to_string(),
Symbol { Symbol {

View file

@ -16,14 +16,22 @@
*/ */
use { use {
flesh::ast::{ crate::{
eval, start_trace, segment::{
Ctr, Seg, Type, SymTable, ValueType, Ctr, Seg, Type
Symbol, Args, Traceback, },
sym::{
SymTable, ValueType,
Symbol, Args,
},
error::{
Traceback, start_trace,
},
eval::eval,
run,
}, },
crate::run,
libc::{ libc::{
sigaddset, sigemptyset, sigprocmask, sigaddset, sigemptyset, sigprocmask, exit,
SIGINT, SIGCHLD, SIGTTOU, SIGTTIN, SIGQUIT, SIGTSTP, SIGINT, SIGCHLD, SIGTTOU, SIGTTIN, SIGQUIT, SIGTSTP,
SIG_BLOCK, SIG_UNBLOCK SIG_BLOCK, SIG_UNBLOCK
}, },
@ -42,7 +50,8 @@ use {
env::set_current_dir, env::set_current_dir,
os::unix::process::CommandExt, os::unix::process::CommandExt,
mem, mem,
process, thread,
time::Duration,
}, },
nix::{ nix::{
unistd, unistd::Pid, unistd, unistd::Pid,
@ -53,7 +62,7 @@ use {
}, },
}; };
pub const POSIX_LOAD_NAME: &str = env!("POSIX_LOAD_NAME"); pub const POSIX_LOAD_NAME: &str = "load";
pub const CD_USER_CB: &str = "CFG_FLESH_CD_CB"; pub const CD_USER_CB: &str = "CFG_FLESH_CD_CB";
pub struct ShellState { pub struct ShellState {
@ -226,25 +235,31 @@ fn make_foreground(pid: u32, state: &mut ShellState) -> Result<(), String> {
loop { loop {
match i.wait() { match i.try_wait() {
Ok(maybe) => { Ok(maybe) => {
if let Some(exit) = maybe {
if let Err(e) = unistd::tcsetpgrp(0, state.parent_pid) { if let Err(e) = unistd::tcsetpgrp(0, state.parent_pid) {
return Err(format!( return Err(format!(
"error re-acquiring terminal: {}!", e "error re-acquiring terminal: {}!", e
)); ));
} }
// TODO: this could be more elegant
if let Some(ref attr) = state.attr { if let Some(ref attr) = state.attr {
tcsetattr(0, SetArg::TCSADRAIN, attr).ok(); tcsetattr(0, SetArg::TCSADRAIN, attr).ok();
} else { } else {
panic!("somehow no attrs") panic!("somehow no attrs")
} }
state.last_exit_code = maybe state.last_exit_code = exit
.code() .code()
.unwrap_or(-1); .unwrap_or(-1);
break; break;
} else {
// sleep 1 sec
thread::sleep(Duration::from_millis(1000));
}
}, },
Err(e) => { Err(e) => {
return Err(format!("error waiting on child: {}", e)) return Err(format!("error waiting on child: {}", e))
@ -612,6 +627,19 @@ fn q_callback(_ast: &Seg, _syms: &SymTable, state: &mut ShellState) -> Result<Ct
Ok(Ctr::Integer(state.last_exit_code.into())) Ok(Ctr::Integer(state.last_exit_code.into()))
} }
const EXIT_DOCSTRING: &str = "Takes on integer input. calls libc exit() with given argument.";
fn exit_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
let mut ret = -1;
if let Ctr::Integer(i) = *ast.car {
ret = i;
} else {
eprintln!("WARNING: input was not an integer. Exiting -1.")
}
unsafe {
exit(ret as i32);
}
}
const BG_DOCSTRING: &str = "Calls a binary off disk with given arguments. const BG_DOCSTRING: &str = "Calls a binary off disk with given arguments.
Arguments may be of any type except lambda. If a symbol is not defined it will be passed as a string. Arguments may be of any type except lambda. If a symbol is not defined it will be passed as a string.
first argument (command name) will be found on path (or an error returned). first argument (command name) will be found on path (or an error returned).
@ -760,34 +788,7 @@ fn cd_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback> {
} }
} }
const EXIT_DOCSTRING: &str = "Takes on input: an integer used as an exit code.
Entire REPL process quits with exit code.";
fn exit_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
if let Ctr::Integer(i) = *ast.car {
if i > 2 ^ 32 {
panic!("argument to exit too large!")
} else {
process::exit(i as i32);
}
} else {
panic!("impossible argument to exit")
}
}
pub fn load_posix_shell(syms: &mut SymTable, shell_state: Rc<RefCell<ShellState>>) { pub fn load_posix_shell(syms: &mut SymTable, shell_state: Rc<RefCell<ShellState>>) {
syms.insert(
"exit".to_string(),
Symbol {
name: String::from("exit"),
args: Args::Strict(vec![Type::Integer]),
conditional_branches: false,
docs: EXIT_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(exit_callback)),
..Default::default()
},
);
let pid = unistd::getpid(); let pid = unistd::getpid();
let pgid_res = unistd::getpgid(Some(pid)); let pgid_res = unistd::getpgid(Some(pid));
let sid_res = unistd::getsid(Some(pid)); let sid_res = unistd::getsid(Some(pid));
@ -846,6 +847,20 @@ pub fn load_posix_shell(syms: &mut SymTable, shell_state: Rc<RefCell<ShellState>
ign_sigs(); ign_sigs();
syms.insert(
String::from("l"),
Symbol {
name: String::from(POSIX_LOAD_NAME),
args: Args::Infinite,
conditional_branches: true,
docs: String::from(LOAD_DOCSTRING),
value: ValueType::Internal(Rc::new(move |ast: &Seg, symtable: &mut SymTable| -> Result<Ctr, Traceback> {
load_callback(ast, symtable, &mut shell_state.borrow_mut())
})),
..Default::default()
},
);
syms.insert( syms.insert(
String::from(POSIX_LOAD_NAME), String::from(POSIX_LOAD_NAME),
Symbol { Symbol {
@ -980,103 +995,3 @@ pub fn load_posix_shell(syms: &mut SymTable, shell_state: Rc<RefCell<ShellState>
}, },
); );
} }
#[cfg(test)]
mod tests {
use super::*;
use flesh::ast::{lex, eval, SymTable};
use flesh::stdlib::static_stdlib;
#[test]
fn test_cmd_singlet() {
let document = "(binary)";
let result = vec!["binary"];
let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new());
if let Ok(ref s) = lex(&document.to_string()) {
assert_eq!(
args_from_ast(s, &mut syms),
result
)
} else {
panic!()
}
}
#[test]
fn test_cmd_list() {
let document = "(binary --flag=1 122 \"yeet\" true)";
let result = vec!["binary", "--flag=1", "122", "yeet", "true"];
let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new());
if let Ok(ref s) = lex(&document.to_string()) {
assert_eq!(
args_from_ast(s, &mut syms),
result
)
} else {
panic!()
}
}
#[test]
fn test_cmd_syms_undef() {
let document = "(binary --flag=1 122 \"yeet\" true syms)";
let result = vec!["binary", "--flag=1", "122", "yeet", "true", "syms"];
let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new());
if let Ok(ref s) = lex(&document.to_string()) {
assert_eq!(
args_from_ast(s, &mut syms),
result
)
} else {
panic!()
}
}
#[test]
fn test_cmd_syms_unwrap_simple() {
let decl = "(def syms \"\" 1)";
let document = "(binary --flag=1 122 \"yeet\" true syms)";
let result = vec!["binary", "--flag=1", "122", "yeet", "true", "1"];
let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new());
eval(&lex(&decl.to_string()).unwrap(), &mut syms).unwrap();
if let Ok(ref s) = lex(&document.to_string()) {
assert_eq!(
args_from_ast(s, &mut syms),
result
)
} else {
panic!()
}
}
#[test]
fn test_cmd_syms_unwrap_eval() {
let document = "(binary --flag=1 122 \"yeet\" true (add 1 2))";
let result = vec!["binary", "--flag=1", "122", "yeet", "true", "3"];
let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new());
if let Ok(ref s) = lex(&document.to_string()) {
assert_eq!(
args_from_ast(s, &mut syms),
result
)
} else {
panic!()
}
}
}

View file

@ -18,19 +18,18 @@
use crate::segment::{Ctr, Seg, Type}; use crate::segment::{Ctr, Seg, Type};
use crate::sym::{SymTable, Symbol, ValueType, Args}; use crate::sym::{SymTable, Symbol, ValueType, Args};
use crate::error::{Traceback, start_trace}; use crate::error::{Traceback, start_trace};
use alloc::rc::Rc; use std::io::Write;
use alloc::boxed::Box; use std::io;
use alloc::string::{String, ToString}; use std::rc::Rc;
const ECHO_DOCSTRING: &str = const ECHO_DOCSTRING: &str =
"traverses any number of arguments. Prints their evaluated values on a new line for each."; "traverses any number of arguments. Prints their evaluated values on a new line for each.";
fn echo_callback(ast: &Seg, _syms: &mut SymTable, print_callback: fn(&String)) -> Result<Ctr, Traceback> { fn echo_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
ast.circuit(&mut |arg: &Ctr| match arg { ast.circuit(&mut |arg: &Ctr| match arg {
Ctr::String(s) => print_callback(s) == (), Ctr::String(s) => print!("{}", s) == (),
_ => print_callback(&arg.to_string()) == (), _ => print!("{}", arg) == (),
}); });
print_callback(&String::from("\n")); println!();
Ok(Ctr::None) Ok(Ctr::None)
} }
@ -94,9 +93,7 @@ fn strcast_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
} }
const SUBSTR_DOCSTRING: &str = const SUBSTR_DOCSTRING: &str =
"Takes a string and two integers (arg1, arg2 and arg3 respectively). "Takes two strings. Returns true if string1 contains at least one instance of string2";
Returns the substring of arg1 starting at arg2 and ending at arg3.
Returns error if arg2 or arg3 are negative, and if arg3 is larger than the length of arg1.";
fn substr_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> { fn substr_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
let parent_str: String; let parent_str: String;
if let Ctr::String(ref s) = *ast.car { if let Ctr::String(ref s) = *ast.car {
@ -107,84 +104,13 @@ fn substr_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
.into())) .into()))
} }
let second_arg_obj: &Ctr;
let third_arg_obj: &Ctr;
let start: usize;
if let Ctr::Seg(ref s) = *ast.cdr {
second_arg_obj = &*s.car;
third_arg_obj = &*s.cdr;
} else {
return Err(start_trace(
("substr", "expected three inputs")
.into()))
}
if let Ctr::Integer(i) = &*second_arg_obj {
if i < &0 {
return Err(start_trace(("substr", "start index cannot be negative").into()))
}
start = i.clone() as usize;
} else {
return Err(start_trace(
("substr", "expected second input to be an integer")
.into()))
}
if start > parent_str.len() {
return Err(start_trace(("substr", "start index larger than source string").into()))
}
let end: usize;
let third_arg_inner: &Ctr;
if let Ctr::Seg(ref s) = *third_arg_obj {
third_arg_inner = &*s.car;
} else {
return Err(start_trace(
("substr", "expected three inputs")
.into()))
}
if let Ctr::Integer(i) = &*third_arg_inner {
if i < &0 {
return Err(start_trace(("substr", "end index cannot be negative").into()))
}
end = i.clone() as usize;
} else {
return Err(start_trace(
("substr", "expected third input to be an integer")
.into()))
}
if end > parent_str.len() {
return Err(start_trace(("substr", "end index larger than source string").into()))
}
if end <= start {
return Err(start_trace(("substr", "end index must be larger than start index").into()))
}
Ok(Ctr::String(parent_str[start..end].to_string()))
}
const IS_SUBSTR_DOCSTRING: &str =
"Takes two strings. Returns true if string1 contains at least one instance of string2";
fn is_substr_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
let parent_str: String;
if let Ctr::String(ref s) = *ast.car {
parent_str = s.to_string();
} else {
return Err(start_trace(
("substr?", "expected first input to be a string")
.into()))
}
let second_arg_obj: &Ctr; let second_arg_obj: &Ctr;
let child_str: String; let child_str: String;
if let Ctr::Seg(ref s) = *ast.cdr { if let Ctr::Seg(ref s) = *ast.cdr {
second_arg_obj = &*s.car; second_arg_obj = &*s.car;
} else { } else {
return Err(start_trace( return Err(start_trace(
("substr?", "expected two inputs") ("substr", "expected two inputs")
.into())) .into()))
} }
@ -192,7 +118,7 @@ fn is_substr_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback>
child_str = s.clone(); child_str = s.clone();
} else { } else {
return Err(start_trace( return Err(start_trace(
("substr?", "expected second input to be a string") ("substr", "expected second input to be a string")
.into())) .into()))
} }
@ -240,15 +166,13 @@ fn split_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
const INPUT_DOCSTRING: &str = "Takes one argument (string) and prints it. const INPUT_DOCSTRING: &str = "Takes one argument (string) and prints it.
Then prompts for user input. Then prompts for user input.
User input is returned as a string"; User input is returned as a string";
fn input_callback( fn input_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
ast: &Seg,
_syms: &mut SymTable,
print_callback: fn(&String),
read_callback: fn() -> String,
) -> Result<Ctr, Traceback> {
if let Ctr::String(ref s) = *ast.car { if let Ctr::String(ref s) = *ast.car {
print_callback(s); print!("{}", s);
Ok(Ctr::String(read_callback())) let _= io::stdout().flush();
let mut input = String::new();
io::stdin().read_line(&mut input).expect("couldnt read user input");
Ok(Ctr::String(input.trim().to_string()))
} else { } else {
Err(start_trace( Err(start_trace(
("input", "expected a string input to prompt user with") ("input", "expected a string input to prompt user with")
@ -256,8 +180,7 @@ fn input_callback(
} }
} }
pub fn add_string_lib(syms: &mut SymTable, print: fn(&String), read: fn() -> String) { pub fn add_string_lib(syms: &mut SymTable) {
let echo_print = print.clone();
syms.insert( syms.insert(
"echo".to_string(), "echo".to_string(),
Symbol { Symbol {
@ -265,11 +188,7 @@ pub fn add_string_lib(syms: &mut SymTable, print: fn(&String), read: fn() -> Str
args: Args::Infinite, args: Args::Infinite,
conditional_branches: false, conditional_branches: false,
docs: ECHO_DOCSTRING.to_string(), docs: ECHO_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new( value: ValueType::Internal(Rc::new(echo_callback)),
move |ast: &Seg, syms: &mut SymTable| -> Result<Ctr, Traceback> {
echo_callback(ast, syms, echo_print)
}
)),
..Default::default() ..Default::default()
}, },
); );
@ -293,19 +212,6 @@ pub fn add_string_lib(syms: &mut SymTable, print: fn(&String), read: fn() -> Str
name: String::from("substr?"), name: String::from("substr?"),
args: Args::Strict(vec![Type::String, Type::String]), args: Args::Strict(vec![Type::String, Type::String]),
conditional_branches: false, conditional_branches: false,
docs: IS_SUBSTR_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(is_substr_callback)),
optimizable: true,
..Default::default()
},
);
syms.insert(
"substr".to_string(),
Symbol {
name: String::from("substr"),
args: Args::Strict(vec![Type::String, Type::Integer, Type::Integer]),
conditional_branches: false,
docs: SUBSTR_DOCSTRING.to_string(), docs: SUBSTR_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(substr_callback)), value: ValueType::Internal(Rc::new(substr_callback)),
optimizable: true, optimizable: true,
@ -352,8 +258,6 @@ pub fn add_string_lib(syms: &mut SymTable, print: fn(&String), read: fn() -> Str
}, },
); );
let input_print = print.clone();
let input_read = read.clone();
syms.insert( syms.insert(
"input".to_string(), "input".to_string(),
Symbol { Symbol {
@ -361,11 +265,7 @@ pub fn add_string_lib(syms: &mut SymTable, print: fn(&String), read: fn() -> Str
args: Args::Strict(vec![Type::String]), args: Args::Strict(vec![Type::String]),
conditional_branches: false, conditional_branches: false,
docs: INPUT_DOCSTRING.to_string(), docs: INPUT_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new( value: ValueType::Internal(Rc::new(input_callback)),
move |ast: &Seg, syms: &mut SymTable| -> Result<Ctr, Traceback> {
input_callback(ast, syms, input_print, input_read)
}
)),
..Default::default() ..Default::default()
} }
); );

View file

@ -15,21 +15,17 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#[path = "hashmap.rs"]
mod hashmap;
use hashmap::{QuickMap, QuickMapIter};
use crate::eval::eval; use crate::eval::eval;
use crate::error::{Traceback, start_trace}; use crate::error::{Traceback, start_trace};
use crate::segment::{Ctr, Seg, Type}; use crate::segment::{Ctr, Seg, Type};
use alloc::fmt; #[cfg(feature="implicit-load")]
use alloc::rc::Rc; use crate::stl::posix::POSIX_LOAD_NAME;
use alloc::vec::Vec; use std::collections::HashMap;
use alloc::boxed::Box; use std::fmt;
use alloc::string::{String, ToString}; use std::rc::Rc;
#[derive(Clone)] #[derive(Clone)]
pub struct SymTable(QuickMap<Symbol>, usize); pub struct SymTable(HashMap<String, Symbol>, usize);
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct UserFn { pub struct UserFn {
@ -82,7 +78,7 @@ pub struct Symbol {
impl SymTable { impl SymTable {
pub fn new() -> SymTable { pub fn new() -> SymTable {
SymTable(QuickMap::<Symbol>::new(), 0) SymTable(HashMap::<String, Symbol>::new(), 0)
} }
pub fn get(&self, arg: &String) -> Option<&Symbol> { pub fn get(&self, arg: &String) -> Option<&Symbol> {
@ -103,30 +99,33 @@ impl SymTable {
self.0.remove(arg) self.0.remove(arg)
} }
pub fn iter(&self) -> QuickMapIter<'_, Symbol>{ pub fn iter(&self) -> std::collections::hash_map::Iter<'_, String, Symbol> {
self.0.iter() self.0.iter()
} }
pub fn keys(&self) -> std::collections::hash_map::Keys<String, Symbol> {
self.0.keys()
}
pub fn update(&mut self, other: &mut SymTable) { pub fn update(&mut self, other: &mut SymTable) {
/* updates self with all syms in other that match the following cases: /* updates self with all syms in other that match the following cases:
* * sym is not in self * * sym is not in self
* * sym has a newer generation than the entry in self * * sym has a newer generation than the entry in self
*
* TODO: Write a method for QuickMap for update in place
* so that none of these need to be reallocated
*/ */
let tmp = self.1; let tmp = self.1;
for i in other.iter() { for i in other.iter() {
let s = self.0 self.0.entry(i.0.to_string())
.remove(&i.0) .and_modify(|inner: &mut Symbol| {
.map_or( if tmp < i.1.__generation {
i.1.clone(), inner.__generation = i.1.__generation;
|existing| if tmp < i.1.__generation { inner.value = i.1.value.clone();
i.1.clone() inner.args = i.1.args.clone();
} else { inner.docs = i.1.docs.clone();
existing inner.conditional_branches = i.1.conditional_branches;
}); inner.name = i.1.name.clone();
self.0.insert(i.0.to_string(), s); }
})
.or_insert(i.1.clone());
} }
} }
@ -147,9 +146,9 @@ impl SymTable {
* assume a shell command is being run * assume a shell command is being run
*/ */
#[cfg(feature="implicit-load")] #[cfg(feature="implicit-load")]
None if call_func => match self.remove(&env!("POSIX_LOAD_NAME").to_string()) { None if call_func => match self.remove(&POSIX_LOAD_NAME.to_string()) {
Some(s) => { Some(s) => {
name_token = String::from(env!("POSIX_LOAD_NAME").to_string()); name_token = String::from(POSIX_LOAD_NAME);
/* highly unfortunate circumstance /* highly unfortunate circumstance
* we must now rebuild the original ast * we must now rebuild the original ast
* costs a whole clone of the args * costs a whole clone of the args
@ -168,9 +167,7 @@ impl SymTable {
}, },
None => return Err( None => return Err(
Traceback::new() Traceback::new()
.with_trace(( .with_trace(("(implicit load)", "(load function not found)").into())
&format!("(implicit load ({}))", name),
"(load function not found)").into())
) )
}, },

View file

@ -1,7 +1,7 @@
mod eval_tests { mod eval_tests {
use flesh::ast::{eval, lex, SymTable}; use flesh::ast::{eval, lex, SymTable};
use flesh::ast::{Args, Ctr, Seg, Symbol, UserFn, ValueType}; use flesh::ast::{Args, Ctr, Seg, Symbol, UserFn, ValueType};
use flesh::stdlib::static_stdlib; use flesh::stdlib::{dynamic_stdlib, static_stdlib};
#[test] #[test]
fn eval_simple() { fn eval_simple() {
@ -23,8 +23,8 @@ mod eval_tests {
#[test] #[test]
fn eval_function_call() { fn eval_function_call() {
let test_doc = "(\"one\" (echo \"unwrap_me\"))".to_string(); let test_doc = "('one' (echo 'unwrap_me'))".to_string();
let output = "(\"one\" \"unwrap_me\")"; let output = "('one' 'unwrap_me')";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
let test_external_func: Symbol = Symbol { let test_external_func: Symbol = Symbol {
@ -50,8 +50,8 @@ mod eval_tests {
#[test] #[test]
fn eval_embedded_func_calls() { fn eval_embedded_func_calls() {
let test_doc = "(\"one\" (echo (echo \"unwrap_me\")))".to_string(); let test_doc = "('one' (echo (echo 'unwrap_me')))".to_string();
let output = "(\"one\" \"unwrap_me\")"; let output = "('one' 'unwrap_me')";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
let test_external_func: Symbol = Symbol { let test_external_func: Symbol = Symbol {
@ -75,7 +75,6 @@ mod eval_tests {
assert_eq!(reduced.to_string(), output); assert_eq!(reduced.to_string(), output);
} }
#[cfg(not(feature = "implicit-load"))]
#[test] #[test]
fn eval_bad_syms() { fn eval_bad_syms() {
let test_doc = "(undefined)".to_string(); let test_doc = "(undefined)".to_string();
@ -97,12 +96,13 @@ mod eval_tests {
#[test] #[test]
fn func_lambda_equivalency() { fn func_lambda_equivalency() {
let comparator = "(def apply \"applicator\" (fn x) (fn x))"; let comparator = "(def apply 'applicator' (fn x) (fn x))";
let lh_doc = "(apply car (1 2 3))"; let lh_doc = "(apply car (1 2 3))";
let rh_doc = "(apply (lambda (x) (car x)) (1 2 3))"; let rh_doc = "(apply (lambda (x) (car x)) (1 2 3))";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
eval(&lex(&comparator.to_string()).unwrap(), &mut syms).unwrap(); eval(&lex(&comparator.to_string()).unwrap(), &mut syms).unwrap();
assert_eq!( assert_eq!(

View file

@ -91,7 +91,7 @@ mod func_tests {
.call_symbol(&"echo_2".to_string(), &args, true) .call_symbol(&"echo_2".to_string(), &args, true)
.unwrap() .unwrap()
.to_string(), .to_string(),
"\"test\"".to_string() "'test'".to_string()
); );
} }
@ -140,7 +140,7 @@ mod func_tests {
syms.call_symbol(&"test_outer".to_string(), &args, true) syms.call_symbol(&"test_outer".to_string(), &args, true)
.unwrap() .unwrap()
.to_string(), .to_string(),
"\"test\"".to_string() "'test'".to_string()
); );
} }

View file

@ -3,44 +3,44 @@ mod lex_tests {
#[test] #[test]
fn test_lex_basic_pair() { fn test_lex_basic_pair() {
let document = String::from("(hello \"world\")"); let document = String::from("(hello 'world')");
assert_eq!(lex(&document).unwrap().to_string(), document); assert_eq!(lex(&document).unwrap().to_string(), document);
} }
#[test] #[test]
fn test_lex_basic_list() { fn test_lex_basic_list() {
let document = String::from("(hello \"world\" 1 2 3)"); let document = String::from("(hello 'world' 1 2 3)");
assert_eq!(lex(&document).unwrap().to_string(), document); assert_eq!(lex(&document).unwrap().to_string(), document);
} }
#[test] #[test]
fn test_bad_symbol() { fn test_bad_symbol() {
let document = String::from("(as@dd)"); let document = String::from("(as@dd)");
let output: &str = "Problem lexing document: \"Unparsable token \\\"as@dd\\\" at char 7\""; let output: &str = "Problem lexing document: \"Unparsable token: as@dd\"";
assert_eq!(lex(&document).err().unwrap().0.first().unwrap().message, output.to_string(),); assert_eq!(lex(&document).err().unwrap().0.first().unwrap().message, output.to_string(),);
} }
#[test] #[test]
fn test_lex_complex_list() { fn test_lex_complex_list() {
let document = String::from("(hello \"world\" (1 2 (1 2 3)) 1 2 3)"); let document = String::from("(hello 'world' (1 2 (1 2 3)) 1 2 3)");
assert_eq!(lex(&document).unwrap().to_string(), document); assert_eq!(lex(&document).unwrap().to_string(), document);
} }
#[test] #[test]
fn test_list_delim_in_str() { fn test_list_delim_in_str() {
let document = String::from("(\"(\")"); let document = String::from("('(')");
assert_eq!(lex(&document).unwrap().to_string(), document); assert_eq!(lex(&document).unwrap().to_string(), document);
} }
#[test] #[test]
fn test_comment_delim_in_str() { fn test_comment_delim_in_str() {
let document = String::from("(\"#\")"); let document = String::from("('#')");
assert_eq!(lex(&document).unwrap().to_string(), document); assert_eq!(lex(&document).unwrap().to_string(), document);
} }
#[test] #[test]
fn test_empty_string() { fn test_empty_string() {
let document = String::from("(\"\")"); let document = String::from("('')");
assert_eq!(lex(&document).unwrap().to_string(), document); assert_eq!(lex(&document).unwrap().to_string(), document);
} }
@ -75,8 +75,8 @@ mod lex_tests {
#[test] #[test]
fn test_postline_comment_1() { fn test_postline_comment_1() {
let document = let document =
String::from("#!/bin/flesh\n((one two)# another doc comment\n(\"three\" four))"); String::from("#!/bin/flesh\n((one two)# another doc comment\n('three' four))");
let output: &str = "((one two) (\"three\" four))"; let output: &str = "((one two) ('three' four))";
assert_eq!(lex(&document).unwrap().to_string(), output.to_string(),); assert_eq!(lex(&document).unwrap().to_string(), output.to_string(),);
} }
@ -105,7 +105,7 @@ mod lex_tests {
#[test] #[test]
fn test_bad_token_list() { fn test_bad_token_list() {
let document = String::from("(one t(wo)"); let document = String::from("(one t(wo)");
let output: &str = "Problem lexing document: \"list started in middle of another token: t\""; let output: &str = "Problem lexing document: \"list started in middle of another token\"";
assert_eq!(lex(&document).err().unwrap().0.first().unwrap().message, output.to_string(),); assert_eq!(lex(&document).err().unwrap().0.first().unwrap().message, output.to_string(),);
} }
} }

View file

@ -1,13 +1,13 @@
mod append_lib_tests { mod append_lib_tests {
use flesh::ast::{eval, lex, SymTable}; use flesh::ast::{eval, lex, SymTable};
use flesh::stdlib::static_stdlib; use flesh::stdlib::{dynamic_stdlib, static_stdlib};
#[test] #[test]
fn test_cons_to_empty_list() { fn test_cons_to_empty_list() {
let document = "(cons () 1)"; let document = "(cons () 1)";
let result = "(1)"; let result = "(1)";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -18,11 +18,11 @@ mod append_lib_tests {
#[test] #[test]
fn test_multi_cons_to_empty_list() { fn test_multi_cons_to_empty_list() {
let document = "(cons () 1 \"two\" 3.4)"; let document = "(cons () 1 'two' 3.4)";
let result = "(1 \"two\" 3.4)"; let result = "(1 'two' 3.4)";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -37,7 +37,7 @@ mod append_lib_tests {
let result = "(1 2 3)"; let result = "(1 2 3)";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -52,7 +52,7 @@ mod append_lib_tests {
let result = "(<nil>)"; let result = "(<nil>)";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -63,11 +63,11 @@ mod append_lib_tests {
#[test] #[test]
fn test_cons_no_list() { fn test_cons_no_list() {
let document = "(cons \"test\" 1 2 3)"; let document = "(cons 'test' 1 2 3)";
let result = "(\"test\" 1 2 3)"; let result = "('test' 1 2 3)";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -82,7 +82,7 @@ mod append_lib_tests {
let result = "0"; let result = "0";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -97,7 +97,7 @@ mod append_lib_tests {
let result = "3"; let result = "3";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -111,7 +111,7 @@ mod append_lib_tests {
let document = "(car ())"; let document = "(car ())";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.err() .err()
@ -130,7 +130,7 @@ mod append_lib_tests {
let result = "1"; let result = "1";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -144,7 +144,7 @@ mod append_lib_tests {
let document = "(cdr ())"; let document = "(cdr ())";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
@ -164,7 +164,7 @@ mod append_lib_tests {
let result = "3"; let result = "3";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
@ -176,15 +176,15 @@ mod append_lib_tests {
#[test] #[test]
fn test_pop() { fn test_pop() {
let document = "(def test \"\" (pop (1 2 3)))"; let document = "(def test '' (pop (1 2 3)))";
let check1 = "(car test)"; let check1 = "(car test)";
let result1 = "1"; let result1 = "1";
let check2 = "(cdr test)"; let check2 = "(cdr test)";
let result2 = "(2 3)"; let result2 = "(2 3)";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap(); eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap();
let ch1 = lex(&check1.to_string()).unwrap(); let ch1 = lex(&check1.to_string()).unwrap();
let ch2 = lex(&check2.to_string()).unwrap(); let ch2 = lex(&check2.to_string()).unwrap();
@ -202,15 +202,15 @@ mod append_lib_tests {
#[test] #[test]
fn test_pop_mono() { fn test_pop_mono() {
let document = "(def test \"\" (pop (1)))"; let document = "(def test '' (pop (1)))";
let check1 = "(car test)"; let check1 = "(car test)";
let result1 = "1"; let result1 = "1";
let check2 = "(cdr test)"; let check2 = "(cdr test)";
let result2 = "(<nil>)"; let result2 = "(<nil>)";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap(); eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap();
let ch1 = lex(&check1.to_string()).unwrap(); let ch1 = lex(&check1.to_string()).unwrap();
let ch2 = lex(&check2.to_string()).unwrap(); let ch2 = lex(&check2.to_string()).unwrap();
@ -228,15 +228,15 @@ mod append_lib_tests {
#[test] #[test]
fn test_dq() { fn test_dq() {
let document = "(def test \"\" (dq (1 2 3)))"; let document = "(def test '' (dq (1 2 3)))";
let check1 = "(car test)"; let check1 = "(car test)";
let result1 = "3"; let result1 = "3";
let check2 = "(cdr test)"; let check2 = "(cdr test)";
let result2 = "(1 2)"; let result2 = "(1 2)";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap(); eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap();
let ch1 = lex(&check1.to_string()).unwrap(); let ch1 = lex(&check1.to_string()).unwrap();
let ch2 = lex(&check2.to_string()).unwrap(); let ch2 = lex(&check2.to_string()).unwrap();
@ -254,15 +254,15 @@ mod append_lib_tests {
#[test] #[test]
fn test_dq_mono() { fn test_dq_mono() {
let document = "(def test \"\" (dq (1)))"; let document = "(def test '' (dq (1)))";
let check1 = "(car test)"; let check1 = "(car test)";
let result1 = "1"; let result1 = "1";
let check2 = "(cdr test)"; let check2 = "(cdr test)";
let result2 = "(<nil>)"; let result2 = "(<nil>)";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap(); eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap();
let ch1 = lex(&check1.to_string()).unwrap(); let ch1 = lex(&check1.to_string()).unwrap();
let ch2 = lex(&check2.to_string()).unwrap(); let ch2 = lex(&check2.to_string()).unwrap();
@ -280,11 +280,12 @@ mod append_lib_tests {
#[test] #[test]
fn test_reverse() { fn test_reverse() {
let document = "(reverse (\"test\" 1 2 3))"; let document = "(reverse ('test' 1 2 3))";
let result = "(3 2 1 \"test\")"; let result = "(3 2 1 'test')";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
@ -296,11 +297,12 @@ mod append_lib_tests {
#[test] #[test]
fn test_reverse_mono() { fn test_reverse_mono() {
let document = "(reverse (\"test\"))"; let document = "(reverse ('test'))";
let result = "(\"test\")"; let result = "('test')";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
@ -316,7 +318,8 @@ mod append_lib_tests {
let result = "(<nil>)"; let result = "(<nil>)";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
@ -332,7 +335,8 @@ mod append_lib_tests {
let result = "true"; let result = "true";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
@ -348,7 +352,8 @@ mod append_lib_tests {
let result = "false"; let result = "false";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)

View file

@ -1,13 +1,14 @@
mod bool_lib_tests { mod bool_lib_tests {
use flesh::ast::{eval, lex, Ctr, SymTable}; use flesh::ast::{eval, lex, Ctr, SymTable};
use flesh::stdlib::static_stdlib; use flesh::stdlib::{dynamic_stdlib, static_stdlib};
#[test] #[test]
fn test_and_true_chain() { fn test_and_true_chain() {
let document = "(and true true true true true)"; let document = "(and true true true true true)";
let result = "true"; let result = "true";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -21,7 +22,8 @@ mod bool_lib_tests {
let document = "(and true true false true true)"; let document = "(and true true false true true)";
let result = "false"; let result = "false";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -35,7 +37,8 @@ mod bool_lib_tests {
let document = "(and false false false false false)"; let document = "(and false false false false false)";
let result = "false"; let result = "false";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -49,7 +52,8 @@ mod bool_lib_tests {
let document = "(or true true true true true)"; let document = "(or true true true true true)";
let result = "true"; let result = "true";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -63,7 +67,8 @@ mod bool_lib_tests {
let document = "(or true true false true true)"; let document = "(or true true false true true)";
let result = "true"; let result = "true";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -77,7 +82,8 @@ mod bool_lib_tests {
let document = "(or false false false false false)"; let document = "(or false false false false false)";
let result = "false"; let result = "false";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -91,7 +97,8 @@ mod bool_lib_tests {
let document = "(not true)"; let document = "(not true)";
let result = "false"; let result = "false";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -102,12 +109,13 @@ mod bool_lib_tests {
#[test] #[test]
fn test_toggle_a_bool() { fn test_toggle_a_bool() {
let document = "(def tester \"\" true)"; let document = "(def tester '' true)";
let change = "(toggle tester)"; let change = "(toggle tester)";
let check = "(tester)"; let check = "(tester)";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
let doc_tree = lex(&document.to_string()).unwrap(); let doc_tree = lex(&document.to_string()).unwrap();
let change_tree = lex(&change.to_string()).unwrap(); let change_tree = lex(&change.to_string()).unwrap();
@ -140,12 +148,13 @@ mod bool_lib_tests {
#[test] #[test]
fn test_toggle_errors_dont_lose_vars() { fn test_toggle_errors_dont_lose_vars() {
let document = "(def tester \"\" \"oops\")"; let document = "(def tester '' 'oops')";
let change = "(toggle tester)"; let change = "(toggle tester)";
let check = "(tester)"; let check = "(tester)";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
let doc_tree = lex(&document.to_string()).unwrap(); let doc_tree = lex(&document.to_string()).unwrap();
let change_tree = lex(&change.to_string()).unwrap(); let change_tree = lex(&change.to_string()).unwrap();
@ -159,7 +168,7 @@ mod bool_lib_tests {
); );
let intermediate = *eval(&check_tree, &mut syms).unwrap(); let intermediate = *eval(&check_tree, &mut syms).unwrap();
if let Ctr::Seg(ref s) = intermediate { if let Ctr::Seg(ref s) = intermediate {
assert_eq!(s.to_string(), "(\"oops\")".to_string()); assert_eq!(s.to_string(), "('oops')".to_string());
} else { } else {
eprintln!("did not expect: {}", intermediate); eprintln!("did not expect: {}", intermediate);
panic!() panic!()
@ -172,12 +181,13 @@ mod bool_lib_tests {
#[test] #[test]
fn test_toggle_errors_dont_lose_funcs() { fn test_toggle_errors_dont_lose_funcs() {
let document = "(def tester \"\" (oops) oops)"; let document = "(def tester '' (oops) oops)";
let change = "(toggle tester)"; let change = "(toggle tester)";
let check = "(tester \"1\")"; let check = "(tester '1')";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
let doc_tree = lex(&document.to_string()).unwrap(); let doc_tree = lex(&document.to_string()).unwrap();
let change_tree = lex(&change.to_string()).unwrap(); let change_tree = lex(&change.to_string()).unwrap();
@ -206,7 +216,8 @@ mod bool_lib_tests {
let test = lex(&document.to_string()).unwrap(); let test = lex(&document.to_string()).unwrap();
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
if let Ctr::Bool(b) = *eval(&test, &mut syms).unwrap() { if let Ctr::Bool(b) = *eval(&test, &mut syms).unwrap() {
assert!(b) assert!(b)
@ -221,7 +232,8 @@ mod bool_lib_tests {
let test = lex(&document.to_string()).unwrap(); let test = lex(&document.to_string()).unwrap();
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
if let Ctr::Bool(b) = *eval(&test, &mut syms).unwrap() { if let Ctr::Bool(b) = *eval(&test, &mut syms).unwrap() {
assert!(!b) assert!(!b)
@ -236,7 +248,8 @@ mod bool_lib_tests {
let test = lex(&document.to_string()).unwrap(); let test = lex(&document.to_string()).unwrap();
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
if let Ctr::Bool(b) = *eval(&test, &mut syms).unwrap() { if let Ctr::Bool(b) = *eval(&test, &mut syms).unwrap() {
assert!(!b) assert!(!b)
@ -251,7 +264,8 @@ mod bool_lib_tests {
let test = lex(&document.to_string()).unwrap(); let test = lex(&document.to_string()).unwrap();
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
if let Ctr::Bool(b) = *eval(&test, &mut syms).unwrap() { if let Ctr::Bool(b) = *eval(&test, &mut syms).unwrap() {
assert!(!b) assert!(!b)
@ -262,11 +276,12 @@ mod bool_lib_tests {
#[test] #[test]
fn test_iseq_long_t_str() { fn test_iseq_long_t_str() {
let document = "(eq? \"1\" \"1\" \"1\" \"1\" \"1\" \"1\" \"1\" \"1\" \"1\" \"1\" \"1\")"; let document = "(eq? '1' '1' '1' '1' '1' '1' '1' '1' '1' '1' '1' '1' '1' '1')";
let test = lex(&document.to_string()).unwrap(); let test = lex(&document.to_string()).unwrap();
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
if let Ctr::Bool(b) = *eval(&test, &mut syms).unwrap() { if let Ctr::Bool(b) = *eval(&test, &mut syms).unwrap() {
assert!(b) assert!(b)
@ -281,7 +296,8 @@ mod bool_lib_tests {
let test = lex(&document.to_string()).unwrap(); let test = lex(&document.to_string()).unwrap();
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
if let Ctr::Bool(b) = *eval(&test, &mut syms).unwrap() { if let Ctr::Bool(b) = *eval(&test, &mut syms).unwrap() {
assert!(b) assert!(b)
@ -292,11 +308,12 @@ mod bool_lib_tests {
#[test] #[test]
fn test_iseq_f_wrong_type() { fn test_iseq_f_wrong_type() {
let document = "(eq? 1 \"1\")"; let document = "(eq? 1 '1')";
let test = lex(&document.to_string()).unwrap(); let test = lex(&document.to_string()).unwrap();
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
if let Ctr::Bool(b) = *eval(&test, &mut syms).unwrap() { if let Ctr::Bool(b) = *eval(&test, &mut syms).unwrap() {
assert!(!b) assert!(!b)
@ -307,11 +324,12 @@ mod bool_lib_tests {
#[test] #[test]
fn test_boolcast_str_t() { fn test_boolcast_str_t() {
let document = "(bool \"true\")"; let document = "(bool 'true')";
let test = lex(&document.to_string()).unwrap(); let test = lex(&document.to_string()).unwrap();
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
if let Ctr::Bool(b) = *eval(&test, &mut syms).unwrap() { if let Ctr::Bool(b) = *eval(&test, &mut syms).unwrap() {
assert!(b) assert!(b)
@ -322,11 +340,12 @@ mod bool_lib_tests {
#[test] #[test]
fn test_boolcast_str_f() { fn test_boolcast_str_f() {
let document = "(bool \"false\")"; let document = "(bool 'false')";
let test = lex(&document.to_string()).unwrap(); let test = lex(&document.to_string()).unwrap();
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
if let Ctr::Bool(b) = *eval(&test, &mut syms).unwrap() { if let Ctr::Bool(b) = *eval(&test, &mut syms).unwrap() {
assert!(!b) assert!(!b)
@ -341,7 +360,8 @@ mod bool_lib_tests {
let test = lex(&document.to_string()).unwrap(); let test = lex(&document.to_string()).unwrap();
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
if let Ctr::Bool(b) = *eval(&test, &mut syms).unwrap() { if let Ctr::Bool(b) = *eval(&test, &mut syms).unwrap() {
assert!(b) assert!(b)
@ -356,7 +376,8 @@ mod bool_lib_tests {
let test = lex(&document.to_string()).unwrap(); let test = lex(&document.to_string()).unwrap();
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
if let Ctr::Bool(b) = *eval(&test, &mut syms).unwrap() { if let Ctr::Bool(b) = *eval(&test, &mut syms).unwrap() {
assert!(!b) assert!(!b)
@ -371,7 +392,8 @@ mod bool_lib_tests {
let test = lex(&document.to_string()).unwrap(); let test = lex(&document.to_string()).unwrap();
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
if let Ctr::Bool(b) = *eval(&test, &mut syms).unwrap() { if let Ctr::Bool(b) = *eval(&test, &mut syms).unwrap() {
assert!(b) assert!(b)
@ -386,7 +408,8 @@ mod bool_lib_tests {
let test = lex(&document.to_string()).unwrap(); let test = lex(&document.to_string()).unwrap();
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
if let Ctr::Bool(b) = *eval(&test, &mut syms).unwrap() { if let Ctr::Bool(b) = *eval(&test, &mut syms).unwrap() {
assert!(!b) assert!(!b)

View file

@ -1,12 +1,13 @@
mod control_lib_tests { mod control_lib_tests {
use flesh::ast::{eval, lex, SymTable}; use flesh::ast::{eval, lex, SymTable};
use flesh::stdlib::static_stdlib; use flesh::stdlib::{dynamic_stdlib, static_stdlib};
#[test] #[test]
fn test_assert_t() { fn test_assert_t() {
let document = "(assert true)"; let document = "(assert true)";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap(); eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap();
} }
@ -14,7 +15,8 @@ mod control_lib_tests {
fn test_assert_f() { fn test_assert_f() {
let document = "(assert false)"; let document = "(assert false)";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert!(eval(&lex(&document.to_string()).unwrap(), &mut syms).is_err()) assert!(eval(&lex(&document.to_string()).unwrap(), &mut syms).is_err())
} }
@ -23,7 +25,8 @@ mod control_lib_tests {
let document = "(if true 1 2)"; let document = "(if true 1 2)";
let result = 1; let result = 1;
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -37,7 +40,8 @@ mod control_lib_tests {
let document = "(if false 1 2)"; let document = "(if false 1 2)";
let result = 2; let result = 2;
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -51,7 +55,8 @@ mod control_lib_tests {
let document = "(if true (cons () 1) 2)"; let document = "(if true (cons () 1) 2)";
let result = "(1)"; let result = "(1)";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -63,12 +68,13 @@ mod control_lib_tests {
#[test] #[test]
fn test_let_multiphase_locals() { fn test_let_multiphase_locals() {
let document = "(let ( let document = "(let (
(temp \"1\") (temp '1')
(temp (cons () temp \"2\"))) (temp (cons () temp '2')))
temp)"; temp)";
let result = "(\"1\" \"2\")"; let result = "('1' '2')";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -80,13 +86,14 @@ mod control_lib_tests {
#[test] #[test]
fn test_let_def_escapes_locals() { fn test_let_def_escapes_locals() {
let document1 = "(let ( let document1 = "(let (
(temp \"hello\") (temp 'hello')
(temp (concat temp \" \" \"world\"))) (temp (concat temp ' ' 'world')))
(def global \"\" temp))"; (def global '' temp))";
let document2 = "global"; let document2 = "global";
let result = "(\"hello world\")"; let result = "('hello world')";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
eval(&lex(&document1.to_string()).unwrap(), &mut syms).unwrap(); eval(&lex(&document1.to_string()).unwrap(), &mut syms).unwrap();
assert_eq!( assert_eq!(
*eval(&lex(&document2.to_string()).unwrap(), &mut syms) *eval(&lex(&document2.to_string()).unwrap(), &mut syms)
@ -98,10 +105,11 @@ mod control_lib_tests {
#[test] #[test]
fn test_let_multibody_evals() { fn test_let_multibody_evals() {
let document = "(let ((temp \"1\")) temp (cons () temp \"2\"))"; let document = "(let ((temp '1')) temp (cons () temp '2'))";
let result = "(\"1\" \"2\")"; let result = "('1' '2')";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -113,14 +121,15 @@ mod control_lib_tests {
#[test] #[test]
fn test_let_multiphase_local_multibody_evals() { fn test_let_multiphase_local_multibody_evals() {
let document = "(let ( let document = "(let (
(temp \"1\") (temp '1')
(temp (cons () temp \"2\"))) (temp (cons () temp '2')))
(echo \"first body\") (echo 'first body')
(cons temp \"3\"))"; (cons temp '3'))";
let result = "(\"1\" \"2\" \"3\")"; let result = "('1' '2' '3')";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -131,15 +140,15 @@ mod control_lib_tests {
#[test] #[test]
fn test_while_basic() { fn test_while_basic() {
let switch_dec = "(def switch \"\" true)"; let switch_dec = "(def switch '' true)";
// if prev is true, switch looped once and only once // if prev is true, switch looped once and only once
// else prev will have a problematic type // else prev will have a problematic type
let while_loop = " let while_loop = "
(while switch (while switch
(def prev \"\" switch) (def prev '' switch)
(toggle switch) (toggle switch)
(if switch (if switch
(def \"\" prev) (def '' prev)
()))"; ()))";
let test_check = "prev"; let test_check = "prev";
@ -148,7 +157,8 @@ mod control_lib_tests {
let check_tree = lex(&test_check.to_string()).unwrap(); let check_tree = lex(&test_check.to_string()).unwrap();
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
eval(&switch_tree, &mut syms).unwrap(); eval(&switch_tree, &mut syms).unwrap();
eval(&while_tree, &mut syms).unwrap(); eval(&while_tree, &mut syms).unwrap();
@ -157,15 +167,15 @@ mod control_lib_tests {
#[test] #[test]
fn test_while_eval_cond() { fn test_while_eval_cond() {
let switch_dec = "(def switch \"\" true)"; let switch_dec = "(def switch '' true)";
// if prev is true, switch looped once and only once // if prev is true, switch looped once and only once
// else prev will have a problematic type // else prev will have a problematic type
let while_loop = " let while_loop = "
(while (or switch switch) (while (or switch switch)
(def prev \"\" switch) (def prev '' switch)
(toggle switch) (toggle switch)
(if switch (if switch
(def \"\" prev) (def '' prev)
()))"; ()))";
let test_check = "prev"; let test_check = "prev";
@ -174,7 +184,8 @@ mod control_lib_tests {
let check_tree = lex(&test_check.to_string()).unwrap(); let check_tree = lex(&test_check.to_string()).unwrap();
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
eval(&switch_tree, &mut syms).unwrap(); eval(&switch_tree, &mut syms).unwrap();
eval(&while_tree, &mut syms).unwrap(); eval(&while_tree, &mut syms).unwrap();
@ -183,15 +194,15 @@ mod control_lib_tests {
#[test] #[test]
fn test_while_2_iter() { fn test_while_2_iter() {
let additional = "(def sw1 \"\" true)"; let additional = "(def sw1 '' true)";
let switch_dec = "(def sw2 \"\" true)"; let switch_dec = "(def sw2 '' true)";
// while should loop twice and define result // while should loop twice and define result
let while_loop = " let while_loop = "
(while sw1 (while sw1
(toggle sw2) (toggle sw2)
(if (and sw1 sw2) (if (and sw1 sw2)
(def sw1 \"\" false) (def sw1 '' false)
(def result \"\" \"yay\")))"; (def result '' 'yay')))";
let test_check = "result"; let test_check = "result";
let another_tree = lex(&additional.to_string()).unwrap(); let another_tree = lex(&additional.to_string()).unwrap();
@ -200,7 +211,8 @@ mod control_lib_tests {
let check_tree = lex(&test_check.to_string()).unwrap(); let check_tree = lex(&test_check.to_string()).unwrap();
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
eval(&another_tree, &mut syms).unwrap(); eval(&another_tree, &mut syms).unwrap();
eval(&switch_tree, &mut syms).unwrap(); eval(&switch_tree, &mut syms).unwrap();
@ -210,31 +222,32 @@ mod control_lib_tests {
#[test] #[test]
fn test_circuit_basic() { fn test_circuit_basic() {
let document = "(if (circuit true (and true true) 0 true) (def result \"\" \"passed\") ())"; let document = "(if (circuit true (and true true) 0 true) (def result '' 'passed') ())";
let test = "result"; let test = "result";
let doc_tree = lex(&document.to_string()).unwrap(); let doc_tree = lex(&document.to_string()).unwrap();
let test_tree = lex(&test.to_string()).unwrap(); let test_tree = lex(&test.to_string()).unwrap();
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
eval(&doc_tree, &mut syms).unwrap(); eval(&doc_tree, &mut syms).unwrap();
let res = eval(&test_tree, &mut syms); let res = eval(&test_tree, &mut syms);
res.unwrap(); res.unwrap();
} }
#[cfg(not(feature = "implicit-load"))]
#[test] #[test]
fn test_circuit_fail() { fn test_circuit_fail() {
let document = "(if (circuit true (and false true) true) (def result \"\" \"passed\") ())"; let document = "(if (circuit true (and false true) true) (def result '' 'passed') ())";
let test = "result"; let test = "result";
let doc_tree = lex(&document.to_string()).unwrap(); let doc_tree = lex(&document.to_string()).unwrap();
let test_tree = lex(&test.to_string()).unwrap(); let test_tree = lex(&test.to_string()).unwrap();
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
eval(&doc_tree, &mut syms).unwrap(); eval(&doc_tree, &mut syms).unwrap();
assert_eq!( assert_eq!(

View file

@ -1,15 +1,16 @@
mod decl_lib_tests { mod decl_lib_tests {
use flesh::ast::{eval, lex, Ctr, SymTable}; use flesh::ast::{eval, lex, Ctr, SymTable};
use flesh::stdlib::static_stdlib; use flesh::stdlib::{dynamic_stdlib, static_stdlib};
#[test] #[test]
fn test_variable_def_and_lookup() { fn test_variable_def_and_lookup() {
let doc1 = "(def test \"my test var\" 1)"; let doc1 = "(def test 'my test var' 1)";
let doc2 = "test"; let doc2 = "test";
let result = "(1)"; let result = "(1)";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
eval(&lex(&doc1.to_string()).unwrap(), &mut syms).unwrap(); eval(&lex(&doc1.to_string()).unwrap(), &mut syms).unwrap();
let res = *eval(&lex(&doc2.to_string()).unwrap(), &mut syms).unwrap(); let res = *eval(&lex(&doc2.to_string()).unwrap(), &mut syms).unwrap();
@ -18,12 +19,13 @@ mod decl_lib_tests {
#[test] #[test]
fn test_variable_def_and_lookup_list() { fn test_variable_def_and_lookup_list() {
let doc1 = "(def test \"my test var\" (1))"; let doc1 = "(def test 'my test var' (1))";
let doc2 = "test"; let doc2 = "test";
let result = "((1))"; let result = "((1))";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
eval(&lex(&doc1.to_string()).unwrap(), &mut syms).unwrap(); eval(&lex(&doc1.to_string()).unwrap(), &mut syms).unwrap();
let res = *eval(&lex(&doc2.to_string()).unwrap(), &mut syms).unwrap(); let res = *eval(&lex(&doc2.to_string()).unwrap(), &mut syms).unwrap();
@ -32,12 +34,13 @@ mod decl_lib_tests {
#[test] #[test]
fn test_func_def_and_lookup() { fn test_func_def_and_lookup() {
let doc1 = "(def test \"my test func\" (hello) hello)"; let doc1 = "(def test 'my test func' (hello) hello)";
let doc2 = "(test 1)"; let doc2 = "(test 1)";
let result = "1"; let result = "1";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
eval(&lex(&doc1.to_string()).unwrap(), &mut syms).unwrap(); eval(&lex(&doc1.to_string()).unwrap(), &mut syms).unwrap();
let res = *eval(&lex(&doc2.to_string()).unwrap(), &mut syms).unwrap(); let res = *eval(&lex(&doc2.to_string()).unwrap(), &mut syms).unwrap();
@ -46,13 +49,14 @@ mod decl_lib_tests {
#[test] #[test]
fn test_variable_def_redef_and_lookup() { fn test_variable_def_redef_and_lookup() {
let doc1 = "(def test \"my test var\" 1)"; let doc1 = "(def test 'my test var' 1)";
let doc2 = "(def test \"my test var\" \"2\")"; let doc2 = "(def test 'my test var' '2')";
let doc3 = "(test)"; let doc3 = "(test)";
let result = "(\"2\")"; let result = "('2')";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
eval(&lex(&doc1.to_string()).unwrap(), &mut syms).unwrap(); eval(&lex(&doc1.to_string()).unwrap(), &mut syms).unwrap();
eval(&lex(&doc2.to_string()).unwrap(), &mut syms).unwrap(); eval(&lex(&doc2.to_string()).unwrap(), &mut syms).unwrap();
@ -60,15 +64,15 @@ mod decl_lib_tests {
assert_eq!(res.to_string(), result); assert_eq!(res.to_string(), result);
} }
#[cfg(not(feature = "implicit-load"))]
#[test] #[test]
fn test_variable_def_undef_and_lookup_fail() { fn test_variable_def_undef_and_lookup_fail() {
let doc1 = "(def test \"my test var\" 1)"; let doc1 = "(def test 'my test var' 1)";
let doc2 = "(def test)"; let doc2 = "(def test)";
let doc3 = "(test)"; let doc3 = "(test)";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
eval(&lex(&doc1.to_string()).unwrap(), &mut syms).unwrap(); eval(&lex(&doc1.to_string()).unwrap(), &mut syms).unwrap();
eval(&lex(&doc2.to_string()).unwrap(), &mut syms).unwrap(); eval(&lex(&doc2.to_string()).unwrap(), &mut syms).unwrap();
@ -86,16 +90,17 @@ mod decl_lib_tests {
#[test] #[test]
fn test_variable_def_redef_via_reference_and_lookup() { fn test_variable_def_redef_via_reference_and_lookup() {
let doc1 = "(def test \"my test var\" 1)"; let doc1 = "(def test 'my test var' 1)";
let doc2 = "(def ref \"references test\" (quote test))"; let doc2 = "(def ref 'references test' (quote test))";
let doc3 = "(def ref \"my test var\" \"2\")"; let doc3 = "(def ref 'my test var' '2')";
let test = "(test)"; let test = "(test)";
let res1 = "(1)"; let res1 = "(1)";
let doc4 = "(def (eval ref) \"my test var\" \"2\")"; let doc4 = "(def (eval ref) 'my test var' '2')";
let res2 = "(\"2\")"; let res2 = "('2')";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
eval(&lex(&doc1.to_string()).unwrap(), &mut syms).unwrap(); eval(&lex(&doc1.to_string()).unwrap(), &mut syms).unwrap();
eval(&lex(&doc2.to_string()).unwrap(), &mut syms).unwrap(); eval(&lex(&doc2.to_string()).unwrap(), &mut syms).unwrap();
@ -110,14 +115,15 @@ mod decl_lib_tests {
#[test] #[test]
fn test_variable_doc_dynamic() { fn test_variable_doc_dynamic() {
let doc1 = "(def test-doc \"docs for test\" \"test tests tests test\")"; let doc1 = "(def test-doc 'docs for test' 'test tests tests test')";
let doc2 = "(def test test-doc \"one\")"; let doc2 = "(def test test-doc 'one')";
let doc3 = "(eq? (and let doc3 = "(eq? (and
(eq? (get-doc (quote test)) test-doc) (eq? (get-doc (quote test)) test-doc)
(eq? test \"one\")))"; (eq? test 'one')))";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
eval(&lex(&doc1.to_string()).unwrap(), &mut syms).unwrap(); eval(&lex(&doc1.to_string()).unwrap(), &mut syms).unwrap();
eval(&lex(&doc2.to_string()).unwrap(), &mut syms).unwrap(); eval(&lex(&doc2.to_string()).unwrap(), &mut syms).unwrap();
@ -130,12 +136,13 @@ mod decl_lib_tests {
#[test] #[test]
fn test_func_def_no_args() { fn test_func_def_no_args() {
let doc1 = "(def test \"my test func\" () 1)"; let doc1 = "(def test 'my test func' () 1)";
let doc2 = "(test)"; let doc2 = "(test)";
let result = "1"; let result = "1";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
eval(&lex(&doc1.to_string()).unwrap(), &mut syms).unwrap(); eval(&lex(&doc1.to_string()).unwrap(), &mut syms).unwrap();
let res = *eval(&lex(&doc2.to_string()).unwrap(), &mut syms).unwrap(); let res = *eval(&lex(&doc2.to_string()).unwrap(), &mut syms).unwrap();
@ -144,11 +151,12 @@ mod decl_lib_tests {
#[test] #[test]
fn test_isset_true() { fn test_isset_true() {
let doc1 = "(def test \"\" 1)"; let doc1 = "(def test '' 1)";
let doc2 = "(set? test)"; let doc2 = "(set? test)";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
let def_tree = lex(&doc1.to_string()).unwrap(); let def_tree = lex(&doc1.to_string()).unwrap();
let set_tree = lex(&doc2.to_string()).unwrap(); let set_tree = lex(&doc2.to_string()).unwrap();
@ -162,7 +170,8 @@ mod decl_lib_tests {
fn test_isset_false() { fn test_isset_false() {
let doc = "(set? test)"; let doc = "(set? test)";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
let set_tree = lex(&doc.to_string()).unwrap(); let set_tree = lex(&doc.to_string()).unwrap();
if let Ctr::Bool(b) = *eval(&set_tree, &mut syms).unwrap() { if let Ctr::Bool(b) = *eval(&set_tree, &mut syms).unwrap() {
assert!(!b); assert!(!b);
@ -171,11 +180,12 @@ mod decl_lib_tests {
#[test] #[test]
fn test_env_doesnt_lose_elements() { fn test_env_doesnt_lose_elements() {
let doc1 = "(def t \"\" 1)"; let doc1 = "(def t '' 1)";
let doc2 = "(env)"; let doc2 = "(env)";
let doc3 = "t"; let doc3 = "t";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
let set_tree = lex(&doc1.to_string()).unwrap(); let set_tree = lex(&doc1.to_string()).unwrap();
let env_tree = lex(&doc2.to_string()).unwrap(); let env_tree = lex(&doc2.to_string()).unwrap();
let tst_tree = lex(&doc3.to_string()).unwrap(); let tst_tree = lex(&doc3.to_string()).unwrap();
@ -189,7 +199,8 @@ mod decl_lib_tests {
let document = "(quote (add 1 2))"; let document = "(quote (add 1 2))";
let result = "(add 1 2)"; let result = "(add 1 2)";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -205,7 +216,8 @@ mod decl_lib_tests {
(eval stored-tree)))"; (eval stored-tree)))";
let result = "3"; let result = "3";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -219,7 +231,8 @@ mod decl_lib_tests {
let document = "(eval (1 2 3))"; let document = "(eval (1 2 3))";
let result = "(1 2 3)"; let result = "(1 2 3)";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -232,7 +245,8 @@ mod decl_lib_tests {
fn test_lambda_str_equivalency_list() { fn test_lambda_str_equivalency_list() {
let document = "(lambda (x y) (add x y))"; let document = "(lambda (x y) (add x y))";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -245,7 +259,8 @@ mod decl_lib_tests {
fn test_lambda_str_equivalency_no_args() { fn test_lambda_str_equivalency_no_args() {
let document = "(lambda () (add 1 2))"; let document = "(lambda () (add 1 2))";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -258,7 +273,8 @@ mod decl_lib_tests {
fn test_lambda_inline_call() { fn test_lambda_inline_call() {
let document = "((lambda (x y) (add x y)) 1 2)"; let document = "((lambda (x y) (add x y)) 1 2)";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
let it = *eval( let it = *eval(
&lex(&document.to_string()).unwrap(), &lex(&document.to_string()).unwrap(),
&mut syms).unwrap(); &mut syms).unwrap();
@ -274,7 +290,8 @@ mod decl_lib_tests {
let document = "(let ((adder (lambda (x y) (add x y)))) let document = "(let ((adder (lambda (x y) (add x y))))
(adder 1 2))"; (adder 1 2))";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
let it = *eval( let it = *eval(
&lex(&document.to_string()).unwrap(), &lex(&document.to_string()).unwrap(),
&mut syms).unwrap(); &mut syms).unwrap();
@ -288,10 +305,11 @@ mod decl_lib_tests {
#[test] #[test]
fn test_lambda_var_bound_call() { fn test_lambda_var_bound_call() {
let document = "(let (()) let document = "(let (())
(def adder \"my adder\" (lambda (x y) (add x y))) (def adder 'my adder' (lambda (x y) (add x y)))
(adder 1 2))"; (adder 1 2))";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
let it = *eval( let it = *eval(
&lex(&document.to_string()).unwrap(), &lex(&document.to_string()).unwrap(),
&mut syms).unwrap(); &mut syms).unwrap();
@ -305,11 +323,12 @@ mod decl_lib_tests {
#[test] #[test]
fn test_lambda_arg_call() { fn test_lambda_arg_call() {
let document = "(let (()) let document = "(let (())
(def appl \"\" (func item) (func item)) (def appl '' (func item) (func item))
(def adder \"my adder\" (lambda (x) (add x 1))) (def adder 'my adder' (lambda (x) (add x 1)))
(appl adder 2))"; (appl adder 2))";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
let it = *eval( let it = *eval(
&lex(&document.to_string()).unwrap(), &lex(&document.to_string()).unwrap(),
&mut syms).unwrap(); &mut syms).unwrap();
@ -323,10 +342,11 @@ mod decl_lib_tests {
#[test] #[test]
fn test_setget_doc_string() { fn test_setget_doc_string() {
let highly_inadvisable = "(set-doc (q help) \"help\")"; let highly_inadvisable = "(set-doc (q help) 'help')";
let document = "(get-doc (q help))"; let document = "(get-doc (q help))";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
let _ = *eval( let _ = *eval(
&lex(&highly_inadvisable.to_string()).unwrap(), &lex(&highly_inadvisable.to_string()).unwrap(),
&mut syms).unwrap(); &mut syms).unwrap();
@ -344,7 +364,8 @@ mod decl_lib_tests {
fn test_eval_quote() { fn test_eval_quote() {
let doc = "(eval (quote (add 1 1)))"; let doc = "(eval (quote (add 1 1)))";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!( assert_eq!(
*eval(&lex(&doc.to_string()).unwrap(), &mut syms).unwrap().to_string(), *eval(&lex(&doc.to_string()).unwrap(), &mut syms).unwrap().to_string(),
2.to_string() 2.to_string()

80
tests/test_lib_file.rs Normal file
View file

@ -0,0 +1,80 @@
mod file_lib_tests {
use flesh::ast::{eval, lex, SymTable};
use flesh::stdlib::{dynamic_stdlib, static_stdlib};
#[test]
fn test_fexists() {
let document = "(exists? '/tmp')";
let result = "true";
let mut syms = SymTable::new();
static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap()
.to_string(),
result.to_string(),
);
}
#[test]
fn test_fexists_doesnt() {
let document = "(exists? 'cargo.timtam')";
let result = "false";
let mut syms = SymTable::new();
static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap()
.to_string(),
result.to_string(),
);
}
#[test]
fn test_write_file() {
let document = "
(let ((s 'test')
(t '/tmp/flesh-lib-test-file-1'))
(write-file t s)
(echo (read-file t))
(eq? (read-file t) s))";
let result = "true";
let mut syms = SymTable::new();
static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap()
.to_string(),
result.to_string(),
);
}
#[test]
fn test_append_file() {
let document = "
(let ((s 'test')
(t '/tmp/flesh-lib-test-file-2'))
(write-file t s)
(append-file t s)
(eq? (read-file t)
(concat s s)))";
let result = "true";
let mut syms = SymTable::new();
static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap()
.to_string(),
result.to_string(),
);
}
}

View file

@ -1,6 +1,6 @@
mod math_lib_tests { mod math_lib_tests {
use flesh::ast::{eval, lex, Ctr, SymTable}; use flesh::ast::{eval, lex, Ctr, SymTable};
use flesh::stdlib::static_stdlib; use flesh::stdlib::{dynamic_stdlib, static_stdlib};
#[test] #[test]
fn test_add_chain() { fn test_add_chain() {
@ -8,7 +8,8 @@ mod math_lib_tests {
let result = "10"; let result = "10";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -23,7 +24,8 @@ mod math_lib_tests {
let result = "10.2"; let result = "10.2";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -38,7 +40,8 @@ mod math_lib_tests {
let result = "24"; let result = "24";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -53,7 +56,8 @@ mod math_lib_tests {
let result = "-8.2"; let result = "-8.2";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -68,7 +72,8 @@ mod math_lib_tests {
let result = "2"; let result = "2";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -83,7 +88,8 @@ mod math_lib_tests {
let result = "10"; let result = "10";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -94,11 +100,12 @@ mod math_lib_tests {
#[test] #[test]
fn test_string_to_int() { fn test_string_to_int() {
let document = "(int \"10\")"; let document = "(int '10')";
let result = "10"; let result = "10";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -113,7 +120,8 @@ mod math_lib_tests {
let result = "10"; let result = "10";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -124,11 +132,76 @@ mod math_lib_tests {
#[test] #[test]
fn test_string_to_float() { fn test_string_to_float() {
let document = "(float \"10.3\")"; let document = "(float '10.3')";
let result = "10.3"; let result = "10.3";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap()
.to_string(),
result.to_string(),
);
}
#[test]
fn test_ii_exp() {
let document = "(exp 7 20)";
let result = 7i128.pow(20);
let mut syms = SymTable::new();
static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap()
.to_string(),
result.to_string(),
);
}
#[test]
fn test_if_exp() {
let document = "(exp 1 10.2)";
let result = f64::powf(1f64, 10.2);
let mut syms = SymTable::new();
static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap()
.to_string(),
result.to_string(),
);
}
#[test]
fn test_fi_exp() {
let document = "(exp 1.2 5)";
let result = f64::powf(1.2, 5f64);
let mut syms = SymTable::new();
static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap()
.to_string(),
format!("{:.5}", result),
);
}
#[test]
fn test_ff_exp() {
let document = "(exp 1.3 1.5)";
let result = f64::powf(1.3, 1.5);
let mut syms = SymTable::new();
static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -139,14 +212,15 @@ mod math_lib_tests {
#[test] #[test]
fn test_ii_mod() { fn test_ii_mod() {
let document = "(def test \"\" (mod 7 3))"; let document = "(def test '' (mod 7 3))";
let check1 = "(car test)"; let check1 = "(car test)";
let result1 = "2"; let result1 = "2";
let check2 = "(cdr test)"; let check2 = "(cdr test)";
let result2 = "1"; let result2 = "1";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
let _ = *eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap(); let _ = *eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap();
assert_eq!( assert_eq!(
*eval(&lex(&check1.to_string()).unwrap(), &mut syms) *eval(&lex(&check1.to_string()).unwrap(), &mut syms)
@ -164,12 +238,13 @@ mod math_lib_tests {
#[test] #[test]
fn test_if_mod() { fn test_if_mod() {
let document = "(def test \"\" (mod 7 3.3))"; let document = "(def test '' (mod 7 3.3))";
let check1 = "(car test)"; let check1 = "(car test)";
let result1 = "2"; let result1 = "2";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
let _ = *eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap(); let _ = *eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap();
assert_eq!( assert_eq!(
*eval(&lex(&check1.to_string()).unwrap(), &mut syms) *eval(&lex(&check1.to_string()).unwrap(), &mut syms)
@ -181,11 +256,12 @@ mod math_lib_tests {
#[test] #[test]
fn test_fi_mod() { fn test_fi_mod() {
let document = "(def test \"\" (mod 7.2 2))"; let document = "(def test '' (mod 7.2 2))";
let check1 = "(car test)"; let check1 = "(car test)";
let result1 = "3"; let result1 = "3";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
let _ = *eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap(); let _ = *eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap();
assert_eq!( assert_eq!(
*eval(&lex(&check1.to_string()).unwrap(), &mut syms) *eval(&lex(&check1.to_string()).unwrap(), &mut syms)
@ -197,12 +273,13 @@ mod math_lib_tests {
#[test] #[test]
fn test_ff_mod() { fn test_ff_mod() {
let document = "(def test \"\" (mod 7.2 3.3))"; let document = "(def test '' (mod 7.2 3.3))";
let check1 = "(car test)"; let check1 = "(car test)";
let result1 = "2"; let result1 = "2";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
let _ = *eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap(); let _ = *eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap();
assert_eq!( assert_eq!(
*eval(&lex(&check1.to_string()).unwrap(), &mut syms) *eval(&lex(&check1.to_string()).unwrap(), &mut syms)
@ -218,7 +295,8 @@ mod math_lib_tests {
let result = true; let result = true;
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -233,7 +311,8 @@ mod math_lib_tests {
let result = false; let result = false;
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -248,7 +327,8 @@ mod math_lib_tests {
let result = true; let result = true;
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -263,7 +343,8 @@ mod math_lib_tests {
let result = false; let result = false;
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -278,7 +359,8 @@ mod math_lib_tests {
let result = true; let result = true;
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -293,7 +375,8 @@ mod math_lib_tests {
let result = false; let result = false;
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -308,7 +391,8 @@ mod math_lib_tests {
let result = true; let result = true;
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -323,7 +407,8 @@ mod math_lib_tests {
let result = false; let result = false;
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -338,7 +423,8 @@ mod math_lib_tests {
let result = false; let result = false;
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -353,7 +439,8 @@ mod math_lib_tests {
let result = true; let result = true;
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -368,7 +455,8 @@ mod math_lib_tests {
let result = false; let result = false;
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -383,7 +471,8 @@ mod math_lib_tests {
let result = true; let result = true;
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -398,7 +487,8 @@ mod math_lib_tests {
let result = false; let result = false;
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -413,7 +503,8 @@ mod math_lib_tests {
let result = true; let result = true;
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -428,7 +519,8 @@ mod math_lib_tests {
let result = false; let result = false;
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -443,7 +535,8 @@ mod math_lib_tests {
let result = true; let result = true;
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -458,7 +551,8 @@ mod math_lib_tests {
let result = false; let result = false;
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -473,7 +567,8 @@ mod math_lib_tests {
let result = true; let result = true;
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -488,7 +583,8 @@ mod math_lib_tests {
let result = false; let result = false;
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -503,7 +599,8 @@ mod math_lib_tests {
let result = true; let result = true;
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!( assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms) *eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap() .unwrap()
@ -514,12 +611,13 @@ mod math_lib_tests {
#[test] #[test]
fn test_inc() { fn test_inc() {
let document = "(def tester \"\" 1)"; let document = "(def tester '' 1)";
let change = "(inc tester)"; let change = "(inc tester)";
let check = "(tester)"; let check = "(tester)";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
let doc_tree = lex(&document.to_string()).unwrap(); let doc_tree = lex(&document.to_string()).unwrap();
let change_tree = lex(&change.to_string()).unwrap(); let change_tree = lex(&change.to_string()).unwrap();
@ -552,12 +650,13 @@ mod math_lib_tests {
#[test] #[test]
fn test_inc_errors_dont_lose_vars() { fn test_inc_errors_dont_lose_vars() {
let document = "(def tester \"\" \"oops\")"; let document = "(def tester '' 'oops')";
let change = "(inc tester)"; let change = "(inc tester)";
let check = "(tester)"; let check = "(tester)";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
let doc_tree = lex(&document.to_string()).unwrap(); let doc_tree = lex(&document.to_string()).unwrap();
let change_tree = lex(&change.to_string()).unwrap(); let change_tree = lex(&change.to_string()).unwrap();
@ -571,7 +670,7 @@ mod math_lib_tests {
); );
let intermediate = *eval(&check_tree, &mut syms).unwrap(); let intermediate = *eval(&check_tree, &mut syms).unwrap();
if let Ctr::Seg(ref s) = intermediate { if let Ctr::Seg(ref s) = intermediate {
assert_eq!(s.to_string(), "(\"oops\")".to_string()); assert_eq!(s.to_string(), "('oops')".to_string());
} else { } else {
eprintln!("did not expect: {}", intermediate); eprintln!("did not expect: {}", intermediate);
panic!() panic!()
@ -584,12 +683,13 @@ mod math_lib_tests {
#[test] #[test]
fn test_inc_errors_dont_lose_funcs() { fn test_inc_errors_dont_lose_funcs() {
let document = "(def tester \"\" (oops) oops)"; let document = "(def tester '' (oops) oops)";
let change = "(inc tester)"; let change = "(inc tester)";
let check = "(tester \"1\")"; let check = "(tester '1')";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
let doc_tree = lex(&document.to_string()).unwrap(); let doc_tree = lex(&document.to_string()).unwrap();
let change_tree = lex(&change.to_string()).unwrap(); let change_tree = lex(&change.to_string()).unwrap();
@ -614,12 +714,13 @@ mod math_lib_tests {
#[test] #[test]
fn test_dec() { fn test_dec() {
let document = "(def tester \"\" 1)"; let document = "(def tester '' 1)";
let change = "(dec tester)"; let change = "(dec tester)";
let check = "(tester)"; let check = "(tester)";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
let doc_tree = lex(&document.to_string()).unwrap(); let doc_tree = lex(&document.to_string()).unwrap();
let change_tree = lex(&change.to_string()).unwrap(); let change_tree = lex(&change.to_string()).unwrap();
@ -652,12 +753,13 @@ mod math_lib_tests {
#[test] #[test]
fn test_dec_errors_dont_lose_vars() { fn test_dec_errors_dont_lose_vars() {
let document = "(def tester \"\" \"oops\")"; let document = "(def tester '' 'oops')";
let change = "(dec tester)"; let change = "(dec tester)";
let check = "(tester)"; let check = "(tester)";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
let doc_tree = lex(&document.to_string()).unwrap(); let doc_tree = lex(&document.to_string()).unwrap();
let change_tree = lex(&change.to_string()).unwrap(); let change_tree = lex(&change.to_string()).unwrap();
@ -671,7 +773,7 @@ mod math_lib_tests {
); );
let intermediate = *eval(&check_tree, &mut syms).unwrap(); let intermediate = *eval(&check_tree, &mut syms).unwrap();
if let Ctr::Seg(ref s) = intermediate { if let Ctr::Seg(ref s) = intermediate {
assert_eq!(s.to_string(), "(\"oops\")".to_string()); assert_eq!(s.to_string(), "('oops')".to_string());
} else { } else {
eprintln!("did not expect: {}", intermediate); eprintln!("did not expect: {}", intermediate);
panic!() panic!()
@ -684,12 +786,13 @@ mod math_lib_tests {
#[test] #[test]
fn test_dec_errors_dont_lose_funcs() { fn test_dec_errors_dont_lose_funcs() {
let document = "(def tester \"\" (oops) oops)"; let document = "(def tester '' (oops) oops)";
let change = "(dec tester)"; let change = "(dec tester)";
let check = "(tester \"1\")"; let check = "(tester '1')";
let mut syms = SymTable::new(); let mut syms = SymTable::new();
static_stdlib(&mut syms, |_: &String| (), || String::new()); static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
let doc_tree = lex(&document.to_string()).unwrap(); let doc_tree = lex(&document.to_string()).unwrap();
let change_tree = lex(&change.to_string()).unwrap(); let change_tree = lex(&change.to_string()).unwrap();

103
tests/test_lib_posix.rs Normal file
View file

@ -0,0 +1,103 @@
mod posix_tests {
use flesh::aux::args_from_ast;
use flesh::stdlib::{dynamic_stdlib, static_stdlib};
use flesh::ast::{lex, eval, SymTable};
#[test]
fn test_cmd_singlet() {
let document = "(binary)";
let result = vec!["binary"];
let mut syms = SymTable::new();
static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
if let Ok(ref s) = lex(&document.to_string()) {
assert_eq!(
args_from_ast(s, &mut syms),
result
)
} else {
panic!()
}
}
#[test]
fn test_cmd_list() {
let document = "(binary --flag=1 122 'yeet' true)";
let result = vec!["binary", "--flag=1", "122", "yeet", "true"];
let mut syms = SymTable::new();
static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
if let Ok(ref s) = lex(&document.to_string()) {
assert_eq!(
args_from_ast(s, &mut syms),
result
)
} else {
panic!()
}
}
#[test]
fn test_cmd_syms_undef() {
let document = "(binary --flag=1 122 'yeet' true syms)";
let result = vec!["binary", "--flag=1", "122", "yeet", "true", "syms"];
let mut syms = SymTable::new();
static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
if let Ok(ref s) = lex(&document.to_string()) {
assert_eq!(
args_from_ast(s, &mut syms),
result
)
} else {
panic!()
}
}
#[test]
fn test_cmd_syms_unwrap_simple() {
let decl = "(def syms '' 1)";
let document = "(binary --flag=1 122 'yeet' true syms)";
let result = vec!["binary", "--flag=1", "122", "yeet", "true", "1"];
let mut syms = SymTable::new();
static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
eval(&lex(&decl.to_string()).unwrap(), &mut syms).unwrap();
if let Ok(ref s) = lex(&document.to_string()) {
assert_eq!(
args_from_ast(s, &mut syms),
result
)
} else {
panic!()
}
}
#[test]
fn test_cmd_syms_unwrap_eval() {
let document = "(binary --flag=1 122 'yeet' true (add 1 2))";
let result = vec!["binary", "--flag=1", "122", "yeet", "true", "3"];
let mut syms = SymTable::new();
static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
if let Ok(ref s) = lex(&document.to_string()) {
assert_eq!(
args_from_ast(s, &mut syms),
result
)
} else {
panic!()
}
}
}

229
tests/test_lib_str.rs Normal file
View file

@ -0,0 +1,229 @@
mod str_lib_tests {
use flesh::ast::{eval, lex, SymTable};
use flesh::stdlib::{dynamic_stdlib, static_stdlib};
#[test]
fn test_simple_concat() {
let document = "(concat 'test')";
let result = "'test'";
let mut syms = SymTable::new();
static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap()
.to_string(),
result.to_string(),
);
}
#[test]
fn test_poly_concat() {
let document = "(concat 'test' 1 2 3)";
let result = "'test123'";
let mut syms = SymTable::new();
static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap()
.to_string(),
result.to_string(),
);
}
#[test]
fn test_empty_concat() {
let document = "(concat)";
let result = "''";
let mut syms = SymTable::new();
static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap()
.to_string(),
result.to_string(),
);
}
#[test]
fn test_strlen_str() {
let document = "(strlen 'test')";
let result = 4;
let mut syms = SymTable::new();
static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap()
.to_string(),
result.to_string(),
);
}
#[test]
fn test_strlen_int() {
let document = "(strlen 1000)";
let result = 4;
let mut syms = SymTable::new();
static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap()
.to_string(),
result.to_string(),
);
}
#[test]
fn test_strlen_float() {
let document = "(strlen 10.2)";
let result = 4;
let mut syms = SymTable::new();
static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap()
.to_string(),
result.to_string(),
);
}
#[test]
fn test_strlen_seg() {
let document = "(strlen (1 2 3))";
let result = 7;
let mut syms = SymTable::new();
static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap()
.to_string(),
result.to_string(),
);
}
#[test]
fn test_strlen_bool() {
let document = "(strlen true)";
let result = 4;
let mut syms = SymTable::new();
static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap()
.to_string(),
result.to_string(),
);
}
#[test]
fn test_strcast_i() {
let document = "(string 4)";
let result = "'4'";
let mut syms = SymTable::new();
static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap()
.to_string(),
result.to_string(),
);
}
#[test]
fn test_strcast_seg() {
let document = "(string (1 2 3))";
let result = "'(1 2 3)'";
let mut syms = SymTable::new();
static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap()
.to_string(),
result.to_string(),
);
}
#[test]
fn test_contains() {
let document = "(substr? 'bigger' 'ger')";
let result = "true";
let mut syms = SymTable::new();
static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap()
.to_string(),
result.to_string(),
);
}
#[test]
fn test_doesnt_contain() {
let document = "(substr? 'smaller' 'ger')";
let result = "false";
let mut syms = SymTable::new();
static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap()
.to_string(),
result.to_string(),
);
}
#[test]
fn test_split() {
let document = "(split 'one.two.three' '.')";
let result = "('one' 'two' 'three')";
let mut syms = SymTable::new();
static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap()
.to_string(),
result.to_string(),
);
}
#[test]
fn test_split_big_delim() {
let document = "(split 'one:d:two:d:three' ':d:')";
let result = "('one' 'two' 'three')";
let mut syms = SymTable::new();
static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap()
.to_string(),
result.to_string(),
);
}
#[test]
fn test_splitnt() {
let document = "(split 'one.two.three' '-')";
let result = "('one.two.three')";
let mut syms = SymTable::new();
static_stdlib(&mut syms);
dynamic_stdlib(&mut syms, None);
assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap()
.to_string(),
result.to_string(),
);
}
}