Compare commits
10 commits
aa56570d7d
...
1993f39d93
| Author | SHA1 | Date | |
|---|---|---|---|
| 1993f39d93 | |||
| 48178b5a48 | |||
| a93fb512aa | |||
| 7800b483da | |||
| d79e87dbd2 | |||
| d6a0e68460 | |||
| 0c2aad2cb6 | |||
| cdba0dfe89 | |||
| 74f73fb493 | |||
| 6d2925984f |
47 changed files with 1744 additions and 1437 deletions
2
.cargo/config.toml
Normal file
2
.cargo/config.toml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
[env]
|
||||
POSIX_LOAD_NAME = "load"
|
||||
|
|
@ -6,25 +6,30 @@ stages:
|
|||
- test
|
||||
- release
|
||||
|
||||
compile-with-posix-features:
|
||||
compile-shell-with-posix-features:
|
||||
stage: build
|
||||
script:
|
||||
- cargo build
|
||||
- cargo build -p flesh-shell -F posix
|
||||
|
||||
compile-without-posix-features:
|
||||
compile-shell-without-posix-features:
|
||||
stage: build
|
||||
script:
|
||||
- cargo build --no-default-features
|
||||
- cargo build -p flesh-shell --no-default-features
|
||||
|
||||
compile-implicit-load:
|
||||
compile-core:
|
||||
stage: build
|
||||
script:
|
||||
- cargo build -F implicit-load
|
||||
- cargo build -p flesh-core
|
||||
|
||||
unit-tests:
|
||||
unit-test-shell:
|
||||
stage: test
|
||||
script:
|
||||
- cargo test
|
||||
- cargo test -p flesh-shell
|
||||
|
||||
unit-test-core:
|
||||
stage: test
|
||||
script:
|
||||
- cargo test -p flesh-core
|
||||
|
||||
userlib-tests:
|
||||
stage: test
|
||||
|
|
@ -34,7 +39,7 @@ userlib-tests:
|
|||
prepare-release:
|
||||
stage: release
|
||||
script:
|
||||
- cargo build -F implicit-load --release
|
||||
- cargo build --release
|
||||
- |
|
||||
tar -czf flesh-$CI_COMMIT_TAG.tar.gz \
|
||||
--xform='s,target/release/,,' \
|
||||
|
|
|
|||
31
Cargo.toml
31
Cargo.toml
|
|
@ -1,25 +1,6 @@
|
|||
[package]
|
||||
name = "flesh"
|
||||
version = "0.4.0"
|
||||
authors = ["Ava <ava@sunnypup.io>"]
|
||||
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"]
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
members = [
|
||||
"shell",
|
||||
"core"
|
||||
]
|
||||
|
|
|
|||
44
Readme.org
44
Readme.org
|
|
@ -92,13 +92,13 @@ For installation, consider adding the following to your ~\~/.emacs~:
|
|||
* Compilation
|
||||
Compiling Flesh is as simple as kicking off a build with Cargo.
|
||||
#+BEGIN_EXAMPLE sh
|
||||
cargo build
|
||||
cargo build -p flesh-shell
|
||||
#+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:
|
||||
|
||||
#+BEGIN_EXAMPLE sh
|
||||
cargo build --no-default-features
|
||||
cargo build -p flesh-shell --no-default-features
|
||||
#+END_EXAMPLE
|
||||
|
||||
In order to run Flesh it is recommended to run the resulting binary at [[file:target/debug/flesh][target/debug/flesh]].
|
||||
|
|
@ -114,46 +114,6 @@ Userlib tests can be triggered by loading the userlib as well as its test suite
|
|||
cargo run snippets/userlib.f snippets/userlib-tests.f
|
||||
#+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
|
||||
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.
|
||||
|
|
|
|||
26
Shell.org
26
Shell.org
|
|
@ -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~.
|
||||
#+BEGIN_SRC lisp
|
||||
(load htop) ;; executes the htop binary, if htop is found on the users PATH
|
||||
(l emacs) ;; executes the emacs binary, if emacs is found on the users PATH
|
||||
(load emacs) ;; executes the emacs binary, if emacs is found on the users PATH
|
||||
#+END_SRC
|
||||
|
||||
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
|
||||
#+BEGIN_SRC lisp
|
||||
(l emacs -nw) ;; 'emacs' and '-nw' not defined
|
||||
(load emacs -nw) ;; 'emacs' and '-nw' not defined
|
||||
#+END_SRC
|
||||
+ symbols that are set are replaced by their values
|
||||
#+BEGIN_SRC lisp
|
||||
(l ls -la HOME)
|
||||
(load ls -la HOME)
|
||||
(let ((ping-count 4)
|
||||
(domain "sunnypup.io"))
|
||||
(l ping -c ping-count domain)
|
||||
(load ping -c ping-count domain)
|
||||
#+END_SRC
|
||||
+ nested forms are evaluated
|
||||
#+BEGIN_SRC lisp
|
||||
(l cat (concat HOME "/notes.txt"))
|
||||
(load cat (concat HOME "/notes.txt"))
|
||||
#+END_SRC
|
||||
|
||||
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.
|
||||
#+BEGIN_EXAMPLE lisp
|
||||
(circuit
|
||||
(l apt update) ;; if this fails, no upgrade is made
|
||||
(l apt upgrade) ;; if this fails, "Success!" is not printed
|
||||
(l echo "Success!"))
|
||||
(load apt update) ;; if this fails, no upgrade is made
|
||||
(load apt upgrade) ;; if this fails, "Success!" is not printed
|
||||
(load echo "Success!"))
|
||||
#+END_EXAMPLE
|
||||
|
||||
*** 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:
|
||||
#+BEGIN_EXAMPLE lisp
|
||||
(l ps)
|
||||
(load ps)
|
||||
#+END_EXAMPLE
|
||||
|
||||
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:
|
||||
#+BEGIN_EXAMPLE lisp
|
||||
(def lis 'shortcut for ls -la'
|
||||
(lambda (dir) (l ls -la dir)))
|
||||
(lambda (dir) (load ls -la dir)))
|
||||
|
||||
(lis HOME)
|
||||
#+END_EXAMPLE
|
||||
|
|
@ -196,8 +196,4 @@ With the ~implicit load~ feature any call to an undefined function will trigger
|
|||
...... (redacted directory list) .....
|
||||
#+END_EXAMPLE
|
||||
|
||||
Implicit load can be used by building flesh with the following command:
|
||||
|
||||
#+BEGIN_EXAMPLE
|
||||
cargo build -F implicit-load
|
||||
#+END_EXAMPLE
|
||||
Implicit load is now enabled by default on any 1.0 build of flesh.
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
- Call ~env~ from a fresh shell: ~(env)~
|
||||
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* |
|
||||
|----------------+---------------+----------------+---------+--------+-----------+-----------+-----------+--------+-------------|
|
||||
| if | lambda | pipe | car | float | strlen | toggle | reduce | call | read-file |
|
||||
|
|
@ -314,13 +314,14 @@ The following table is up to date as of Flesh 0.4.0. For latest information try
|
|||
|----------------+---------------+----------------+---------+--------+-----------+-----------+-----------+--------+-------------|
|
||||
| | | | | 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.
|
||||
|
||||
* Documentation
|
||||
** Tests
|
||||
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.
|
||||
[[file:tests][The test directory]]
|
||||
|
||||
** Help function
|
||||
Flesh is self documenting. The *help* function can be used to inspect any variable or function.
|
||||
|
|
|
|||
12
core/Cargo.toml
Normal file
12
core/Cargo.toml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
[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 = []
|
||||
|
|
@ -15,7 +15,10 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use std::fmt;
|
||||
use alloc::vec::Vec;
|
||||
use alloc::string::String;
|
||||
use core::fmt;
|
||||
use core::convert;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TraceItem {
|
||||
|
|
@ -26,13 +29,14 @@ pub struct TraceItem {
|
|||
#[derive(Debug, Clone)]
|
||||
pub struct Traceback(pub Vec<TraceItem>);
|
||||
|
||||
#[inline]
|
||||
pub fn start_trace(item: TraceItem) -> Traceback {
|
||||
Traceback::new().with_trace(item)
|
||||
}
|
||||
|
||||
impl Traceback {
|
||||
pub fn new() -> Traceback {
|
||||
Traceback(vec![])
|
||||
Traceback(Vec::new())
|
||||
}
|
||||
|
||||
pub fn with_trace(mut self, item: TraceItem) -> Traceback {
|
||||
|
|
@ -56,13 +60,13 @@ impl fmt::Display for Traceback {
|
|||
}
|
||||
|
||||
|
||||
impl std::convert::From<Traceback> for String {
|
||||
impl convert::From<Traceback> for String {
|
||||
fn from(arg: Traceback) -> Self {
|
||||
format!("{}", arg)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<(&String, &str)> for TraceItem {
|
||||
impl convert::From<(&String, &str)> for TraceItem {
|
||||
fn from(value: (&String, &str)) -> Self {
|
||||
TraceItem {
|
||||
caller: value.0.clone(),
|
||||
|
|
@ -71,7 +75,7 @@ impl std::convert::From<(&String, &str)> for TraceItem {
|
|||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<(&String, String)> for TraceItem {
|
||||
impl convert::From<(&String, String)> for TraceItem {
|
||||
fn from(value: (&String, String)) -> Self {
|
||||
TraceItem {
|
||||
caller: value.0.clone(),
|
||||
|
|
@ -80,7 +84,7 @@ impl std::convert::From<(&String, String)> for TraceItem {
|
|||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<(&str, String)> for TraceItem {
|
||||
impl convert::From<(&str, String)> for TraceItem {
|
||||
fn from(value: (&str, String)) -> Self {
|
||||
TraceItem {
|
||||
caller: String::from(value.0),
|
||||
|
|
@ -89,7 +93,7 @@ impl std::convert::From<(&str, String)> for TraceItem {
|
|||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<(&str, &str)> for TraceItem {
|
||||
impl convert::From<(&str, &str)> for TraceItem {
|
||||
fn from(value: (&str, &str)) -> Self {
|
||||
TraceItem {
|
||||
caller: String::from(value.0),
|
||||
|
|
@ -15,6 +15,7 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use crate::segment::{Ctr, Seg};
|
||||
use crate::sym::{SymTable, call_lambda};
|
||||
use crate::error::Traceback;
|
||||
197
core/src/hashmap.rs
Normal file
197
core/src/hashmap.rs
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
/* 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -15,19 +15,23 @@
|
|||
* 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::error::{Traceback, start_trace};
|
||||
use phf::{Map, phf_map};
|
||||
|
||||
const UNMATCHED_STR_DELIM: &str = "Unmatched string delimiter in input";
|
||||
const UNMATCHED_LIST_DELIM: &str = "Unmatched list delimiter in input";
|
||||
|
||||
|
||||
static ESCAPES: Map<char, char> = phf_map! {
|
||||
'n' => '\n',
|
||||
't' => '\t',
|
||||
'\\' => '\\',
|
||||
};
|
||||
#[inline]
|
||||
fn escape(input: &char) -> char {
|
||||
match input {
|
||||
'n' => '\n',
|
||||
't' => '\t',
|
||||
'\\' => '\\',
|
||||
_ => input.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
/* takes a line of user input
|
||||
* returns an unsimplified tree of tokens.
|
||||
|
|
@ -55,6 +59,7 @@ pub fn lex(document: &String) -> Result<Box<Seg>, Traceback> {
|
|||
* Returns Ok(Rc<Seg>) if lexing passes
|
||||
* Returns Err(String) if an error occurs
|
||||
*/
|
||||
#[inline]
|
||||
fn process(document: &String) -> Result<Box<Seg>, String> {
|
||||
let doc_len = document.len();
|
||||
|
||||
|
|
@ -63,13 +68,13 @@ fn process(document: &String) -> Result<Box<Seg>, String> {
|
|||
}
|
||||
|
||||
/* State variables
|
||||
* TODO: describe all of them
|
||||
*/
|
||||
let mut is_str = false;
|
||||
let mut ign = false;
|
||||
let mut token = String::new();
|
||||
let mut delim_stack = Vec::new();
|
||||
let mut delim_stack = vec![];
|
||||
let mut ref_stack = vec![];
|
||||
let mut cursor = 0;
|
||||
|
||||
/* Iterate over document
|
||||
* Manage currently sought delimiter
|
||||
|
|
@ -78,14 +83,13 @@ fn process(document: &String) -> Result<Box<Seg>, String> {
|
|||
let mut needs_alloc = false;
|
||||
let mut alloc_list = false;
|
||||
let delim: char;
|
||||
cursor += 1;
|
||||
|
||||
if let Some(d) = delim_stack.last() {
|
||||
delim = *d;
|
||||
|
||||
if delim == '*' {
|
||||
token.push(ESCAPES.get(&c)
|
||||
.cloned()
|
||||
.or(Some(c))
|
||||
.unwrap());
|
||||
token.push(escape(&c));
|
||||
delim_stack.pop();
|
||||
continue;
|
||||
|
||||
|
|
@ -116,7 +120,8 @@ fn process(document: &String) -> Result<Box<Seg>, String> {
|
|||
}
|
||||
// try to generalize all whitespace
|
||||
if !needs_alloc && char::is_whitespace(c) && !is_str {
|
||||
// dont make empty tokens just because the document has consecutive whitespace
|
||||
// dont make empty tokens just because the document
|
||||
// has consecutive whitespace
|
||||
if token.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -126,30 +131,23 @@ fn process(document: &String) -> Result<Box<Seg>, String> {
|
|||
if !needs_alloc {
|
||||
match c {
|
||||
// add a new Seg reference to the stack
|
||||
'(' => {
|
||||
if is_str {
|
||||
token.push(c);
|
||||
continue;
|
||||
}
|
||||
|
||||
if !token.is_empty() {
|
||||
return Err("list started in middle of another token".to_string());
|
||||
}
|
||||
|
||||
'(' if !is_str && token.is_empty() => {
|
||||
ref_stack.push(Seg::new());
|
||||
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
|
||||
'"' | '\'' | '`' if !is_str => {
|
||||
'"' | '`' if !is_str => {
|
||||
is_str = true;
|
||||
delim_stack.push(c);
|
||||
}
|
||||
// eat the whole line
|
||||
'#' | ';' => {
|
||||
if is_str {
|
||||
token.push(c);
|
||||
continue;
|
||||
}
|
||||
'#' | ';' if !is_str => {
|
||||
ign = true;
|
||||
delim_stack.push('\n');
|
||||
}
|
||||
|
|
@ -176,6 +174,7 @@ fn process(document: &String) -> Result<Box<Seg>, String> {
|
|||
return_singlet = true;
|
||||
Seg::new()
|
||||
});
|
||||
|
||||
let obj;
|
||||
if is_str {
|
||||
obj = Box::from(Ctr::String(token));
|
||||
|
|
@ -194,7 +193,7 @@ fn process(document: &String) -> Result<Box<Seg>, String> {
|
|||
} else if let Some(s) = tok_is_symbol(&token) {
|
||||
obj = Box::from(Ctr::Symbol(s));
|
||||
} else {
|
||||
return Err(format!("Unparsable token: {}", token));
|
||||
return Err(format!("Unparsable token \"{}\" at char {}", token, cursor));
|
||||
}
|
||||
|
||||
token = String::new();
|
||||
|
|
@ -202,6 +201,7 @@ fn process(document: &String) -> Result<Box<Seg>, String> {
|
|||
}
|
||||
|
||||
if alloc_list || return_singlet {
|
||||
|
||||
// return if we have finished the document
|
||||
if ref_stack.is_empty() {
|
||||
return Ok(Box::new(current_seg));
|
||||
|
|
@ -228,6 +228,7 @@ fn process(document: &String) -> Result<Box<Seg>, String> {
|
|||
* - equals is also allowed but only for shell command compatibility
|
||||
* else returns false
|
||||
*/
|
||||
#[inline]
|
||||
fn tok_is_symbol(token: &str) -> Option<String> {
|
||||
for t in token.chars() {
|
||||
if !t.is_alphanumeric() &&
|
||||
|
|
@ -15,7 +15,8 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
mod run;
|
||||
#![cfg_attr(not(test), no_std)]
|
||||
|
||||
mod eval;
|
||||
mod lex;
|
||||
mod segment;
|
||||
|
|
@ -23,8 +24,10 @@ mod stl;
|
|||
mod sym;
|
||||
mod error;
|
||||
|
||||
#[macro_use]
|
||||
extern crate alloc;
|
||||
|
||||
pub mod ast {
|
||||
pub use crate::run::run;
|
||||
pub use crate::eval::eval;
|
||||
pub use crate::lex::lex;
|
||||
pub use crate::segment::{Ctr, Seg, Type};
|
||||
|
|
@ -34,28 +37,9 @@ pub mod ast {
|
|||
|
||||
pub mod stdlib {
|
||||
pub use crate::stl::{
|
||||
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,
|
||||
static_stdlib,
|
||||
CFG_FILE_VNAME,
|
||||
FLESH_DEFAULT_CONS_HEIGHT,
|
||||
FLESH_DEFAULT_CONS_WIDTH,
|
||||
decl::STORE_DOCSTRING,
|
||||
decl::store_callback,
|
||||
};
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
@ -15,9 +15,12 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
use crate::sym::UserFn;
|
||||
use std::fmt;
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::{Add, Div, Index, Mul, Sub};
|
||||
use alloc::fmt;
|
||||
use alloc::string::{String, ToString};
|
||||
use alloc::boxed::Box;
|
||||
use core::convert;
|
||||
use core::marker::PhantomData;
|
||||
use core::ops::{Add, Div, Index, Mul, Sub};
|
||||
|
||||
// Container
|
||||
#[derive(Debug, Default)]
|
||||
|
|
@ -92,9 +95,6 @@ impl Ctr {
|
|||
impl Seg {
|
||||
/* recurs over tree assumed to be list in standard form
|
||||
* 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>) {
|
||||
if let Ctr::None = &*(self.car) {
|
||||
|
|
@ -185,6 +185,7 @@ impl Seg {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn seg_to_string(s: &Seg, parens: bool) -> String {
|
||||
let mut string = String::new();
|
||||
if parens {
|
||||
|
|
@ -254,7 +255,7 @@ impl fmt::Display for Ctr {
|
|||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
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::Float(s) => write!(f, "{}", s),
|
||||
Ctr::Bool(s) => {
|
||||
|
|
@ -412,7 +413,7 @@ impl fmt::Display for Type {
|
|||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<String> for Type {
|
||||
impl convert::From<String> for Type {
|
||||
fn from(value: String) -> Self {
|
||||
match value.as_str() {
|
||||
"symbol" => Type::Symbol,
|
||||
41
core/src/stl.rs
Normal file
41
core/src/stl.rs
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
/* 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);
|
||||
}
|
||||
|
|
@ -18,7 +18,10 @@
|
|||
use crate::segment::{Ctr, Seg, Type};
|
||||
use crate::sym::{SymTable, Symbol, Args, ValueType};
|
||||
use crate::error::{Traceback, start_trace};
|
||||
use std::rc::Rc;
|
||||
use alloc::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.
|
||||
If the first argument is a list, all other arguments are added sequentially to the end of the list contained in the first argument.";
|
||||
|
|
@ -18,7 +18,9 @@
|
|||
use crate::segment::{Ctr, Seg, Type};
|
||||
use crate::error::{Traceback, start_trace};
|
||||
use crate::sym::{SymTable, ValueType, Args, Symbol};
|
||||
use std::rc::Rc;
|
||||
use alloc::rc::Rc;
|
||||
use alloc::boxed::Box;
|
||||
use alloc::string::{String, ToString};
|
||||
|
||||
const AND_DOCSTRING: &str =
|
||||
"traverses a list of N arguments, all of which are expected to be boolean.
|
||||
|
|
@ -19,8 +19,9 @@ use crate::eval::eval;
|
|||
use crate::error::{Traceback, start_trace};
|
||||
use crate::segment::{Ctr, Seg, Type};
|
||||
use crate::sym::{SymTable, Symbol, ValueType, Args};
|
||||
use std::rc::Rc;
|
||||
use std::process;
|
||||
use alloc::rc::Rc;
|
||||
use alloc::boxed::Box;
|
||||
use alloc::string::{String, ToString};
|
||||
|
||||
const IF_DOCSTRING: &str =
|
||||
"accepts three bodies, a condition, an unevaluated consequence, and an alternative consequence.
|
||||
|
|
@ -397,33 +398,7 @@ 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) {
|
||||
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(
|
||||
"assert".to_string(),
|
||||
Symbol {
|
||||
|
|
@ -18,10 +18,10 @@
|
|||
use crate::eval::eval;
|
||||
use crate::error::{Traceback, start_trace};
|
||||
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 std::env;
|
||||
use std::rc::Rc;
|
||||
use alloc::rc::Rc;
|
||||
use alloc::boxed::Box;
|
||||
use alloc::string::{String, ToString};
|
||||
|
||||
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> {
|
||||
|
|
@ -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.";
|
||||
fn help_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback> {
|
||||
fn help_callback(ast: &Seg, syms: &mut SymTable, print: fn(&String)) -> Result<Ctr, Traceback> {
|
||||
if ast.len() != 1 {
|
||||
return Err(start_trace(("help", "expected one input").into()));
|
||||
}
|
||||
|
|
@ -98,7 +98,7 @@ fn help_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback> {
|
|||
} else {
|
||||
args_str = sym.args.to_string();
|
||||
}
|
||||
println!(
|
||||
print(&format!(
|
||||
"NAME: {0}\n
|
||||
ARGS: {1}\n
|
||||
DOCUMENTATION:\n
|
||||
|
|
@ -106,7 +106,7 @@ DOCUMENTATION:\n
|
|||
CURRENT VALUE AND/OR BODY:
|
||||
{3}",
|
||||
sym.name, args_str, sym.docs, sym.value
|
||||
);
|
||||
));
|
||||
} else {
|
||||
return Err(start_trace(("help", format!("{symbol} is undefined")).into()));
|
||||
}
|
||||
|
|
@ -131,21 +131,7 @@ fn isset_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback> {
|
|||
|
||||
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;
|
||||
fn env_callback(_ast: &Seg, syms: &mut SymTable, print: fn(&String)) -> Result<Ctr, Traceback> {
|
||||
let mut functions = vec![];
|
||||
let mut variables = vec![];
|
||||
for (name, val) in syms.iter() {
|
||||
|
|
@ -156,51 +142,19 @@ fn env_callback(_ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback> {
|
|||
_ => 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:");
|
||||
print(&String::from("VARIABLES:"));
|
||||
for var in variables {
|
||||
print!("{:v_col_len$}", var);
|
||||
col_iter += 1;
|
||||
if col_iter % n_v_cols == 0 {
|
||||
println!();
|
||||
} else {
|
||||
print!(" ");
|
||||
}
|
||||
print(&var)
|
||||
}
|
||||
println!("\nFUNCTIONS:");
|
||||
col_iter = 0;
|
||||
print(&String::from("\nFUNCTIONS:"));
|
||||
for func in functions {
|
||||
print!("{:f_col_len$}", func);
|
||||
col_iter += 1;
|
||||
if col_iter % n_f_cols == 0 {
|
||||
println!();
|
||||
} else {
|
||||
print!(" ");
|
||||
}
|
||||
print(&func);
|
||||
}
|
||||
Ok(Ctr::None)
|
||||
}
|
||||
|
|
@ -307,7 +261,7 @@ fn setdoc_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback> {
|
|||
}
|
||||
}
|
||||
|
||||
const STORE_DOCSTRING: &str = "allows user to define functions and variables.
|
||||
pub const STORE_DOCSTRING: &str = "allows user to define functions and variables.
|
||||
A call may take one of three forms:
|
||||
1. variable declaration:
|
||||
Takes a name, doc string, and a value.
|
||||
|
|
@ -321,8 +275,10 @@ const STORE_DOCSTRING: &str = "allows user to define functions and variables.
|
|||
(def useless-var)
|
||||
|
||||
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 ";
|
||||
fn store_callback(ast: &Seg, syms: &mut SymTable, env_cfg: bool) -> Result<Ctr, Traceback> {
|
||||
a value from it. If it does not return a String or a Symbol this will result in a failure.
|
||||
|
||||
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 name: String;
|
||||
let docs: String;
|
||||
|
|
@ -350,11 +306,8 @@ fn store_callback(ast: &Seg, syms: &mut SymTable, env_cfg: bool) -> Result<Ctr,
|
|||
// remove var case
|
||||
if ast.len() == 1 {
|
||||
syms.remove(&name);
|
||||
if env_cfg {
|
||||
env::remove_var(name);
|
||||
}
|
||||
return Ok(Ctr::String(name))
|
||||
|
||||
return Ok(Ctr::None)
|
||||
} else if ast.len() < 3 || ast.len() > 4 {
|
||||
return Err(start_trace(("def", "expected 3 or 4 inputs").into()))
|
||||
}
|
||||
|
|
@ -426,20 +379,7 @@ fn store_callback(ast: &Seg, syms: &mut SymTable, env_cfg: bool) -> Result<Ctr,
|
|||
name.clone(),
|
||||
Symbol::from_ast(&name, &docs, &outer_seg, None),
|
||||
);
|
||||
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)
|
||||
return Ok(Ctr::String(name))
|
||||
}
|
||||
|
||||
let mut arg_list = vec![];
|
||||
|
|
@ -468,7 +408,7 @@ fn store_callback(ast: &Seg, syms: &mut SymTable, env_cfg: bool) -> Result<Ctr,
|
|||
Some(arg_list),
|
||||
),
|
||||
);
|
||||
Ok(Ctr::None)
|
||||
Ok(Ctr::String(name))
|
||||
} else {
|
||||
Err(start_trace(
|
||||
("def", "expected one or more forms to evaluate in function body")
|
||||
|
|
@ -476,7 +416,8 @@ fn store_callback(ast: &Seg, syms: &mut SymTable, env_cfg: bool) -> Result<Ctr,
|
|||
}
|
||||
}
|
||||
|
||||
pub fn add_decl_lib_static(syms: &mut SymTable) {
|
||||
pub fn add_decl_lib_static(syms: &mut SymTable, print: fn(&String)) {
|
||||
let help_print = print.clone();
|
||||
syms.insert(
|
||||
"help".to_string(),
|
||||
Symbol {
|
||||
|
|
@ -484,7 +425,11 @@ pub fn add_decl_lib_static(syms: &mut SymTable) {
|
|||
args: Args::Strict(vec![Type::Symbol]),
|
||||
conditional_branches: true,
|
||||
docs: HELP_DOCSTRING.to_string(),
|
||||
value: ValueType::Internal(Rc::new(help_callback)),
|
||||
value: ValueType::Internal(Rc::new(
|
||||
move |ast: &Seg, syms: &mut SymTable| -> Result<Ctr, Traceback> {
|
||||
help_callback(ast, syms, help_print)
|
||||
}
|
||||
)),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
|
@ -502,18 +447,6 @@ pub fn add_decl_lib_static(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()
|
||||
},
|
||||
);
|
||||
|
||||
syms.insert(
|
||||
"quote".to_string(),
|
||||
Symbol {
|
||||
name: String::from("quote"),
|
||||
|
|
@ -584,9 +517,24 @@ pub fn add_decl_lib_static(syms: &mut SymTable) {
|
|||
..Default::default()
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
pub fn add_decl_lib_dynamic(syms: &mut SymTable, env: bool) {
|
||||
let env_print = print.clone();
|
||||
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(
|
||||
"def".to_string(),
|
||||
Symbol {
|
||||
|
|
@ -596,7 +544,7 @@ pub fn add_decl_lib_dynamic(syms: &mut SymTable, env: bool) {
|
|||
docs: STORE_DOCSTRING.to_string(),
|
||||
value: ValueType::Internal(Rc::new(
|
||||
move |ast: &Seg, syms: &mut SymTable| -> Result<Ctr, Traceback> {
|
||||
store_callback(ast, syms, env)
|
||||
store_callback(ast, syms)
|
||||
},
|
||||
)),
|
||||
..Default::default()
|
||||
|
|
@ -18,12 +18,17 @@
|
|||
use crate::segment::{Ctr, Seg};
|
||||
use crate::sym::{SymTable, ValueType, Symbol, Args};
|
||||
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 {
|
||||
matches!(arg, Ctr::Integer(_) | Ctr::Float(_))
|
||||
}
|
||||
|
||||
|
||||
const ADD_DOCSTRING: &str =
|
||||
"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.
|
||||
|
|
@ -187,57 +192,6 @@ 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.
|
||||
Returns a list of two values: the modulus and the remainder.
|
||||
Example: (mod 5 3) -> (1 2)
|
||||
|
|
@ -602,19 +556,6 @@ 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(
|
||||
"mod".to_string(),
|
||||
Symbol {
|
||||
|
|
@ -18,18 +18,19 @@
|
|||
use crate::segment::{Ctr, Seg, Type};
|
||||
use crate::sym::{SymTable, Symbol, ValueType, Args};
|
||||
use crate::error::{Traceback, start_trace};
|
||||
use std::io::Write;
|
||||
use std::io;
|
||||
use std::rc::Rc;
|
||||
use alloc::rc::Rc;
|
||||
use alloc::boxed::Box;
|
||||
use alloc::string::{String, ToString};
|
||||
|
||||
|
||||
const ECHO_DOCSTRING: &str =
|
||||
"traverses any number of arguments. Prints their evaluated values on a new line for each.";
|
||||
fn echo_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
|
||||
fn echo_callback(ast: &Seg, _syms: &mut SymTable, print_callback: fn(&String)) -> Result<Ctr, Traceback> {
|
||||
ast.circuit(&mut |arg: &Ctr| match arg {
|
||||
Ctr::String(s) => print!("{}", s) == (),
|
||||
_ => print!("{}", arg) == (),
|
||||
Ctr::String(s) => print_callback(s) == (),
|
||||
_ => print_callback(&arg.to_string()) == (),
|
||||
});
|
||||
println!();
|
||||
print_callback(&String::from("\n"));
|
||||
Ok(Ctr::None)
|
||||
}
|
||||
|
||||
|
|
@ -93,7 +94,9 @@ fn strcast_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
|
|||
}
|
||||
|
||||
const SUBSTR_DOCSTRING: &str =
|
||||
"Takes two strings. Returns true if string1 contains at least one instance of string2";
|
||||
"Takes a string and two integers (arg1, arg2 and arg3 respectively).
|
||||
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> {
|
||||
let parent_str: String;
|
||||
if let Ctr::String(ref s) = *ast.car {
|
||||
|
|
@ -104,13 +107,84 @@ fn substr_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
|
|||
.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 child_str: String;
|
||||
if let Ctr::Seg(ref s) = *ast.cdr {
|
||||
second_arg_obj = &*s.car;
|
||||
} else {
|
||||
return Err(start_trace(
|
||||
("substr", "expected two inputs")
|
||||
("substr?", "expected two inputs")
|
||||
.into()))
|
||||
}
|
||||
|
||||
|
|
@ -118,7 +192,7 @@ fn substr_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
|
|||
child_str = s.clone();
|
||||
} else {
|
||||
return Err(start_trace(
|
||||
("substr", "expected second input to be a string")
|
||||
("substr?", "expected second input to be a string")
|
||||
.into()))
|
||||
}
|
||||
|
||||
|
|
@ -166,13 +240,15 @@ fn split_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
|
|||
const INPUT_DOCSTRING: &str = "Takes one argument (string) and prints it.
|
||||
Then prompts for user input.
|
||||
User input is returned as a string";
|
||||
fn input_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
|
||||
fn input_callback(
|
||||
ast: &Seg,
|
||||
_syms: &mut SymTable,
|
||||
print_callback: fn(&String),
|
||||
read_callback: fn() -> String,
|
||||
) -> Result<Ctr, Traceback> {
|
||||
if let Ctr::String(ref s) = *ast.car {
|
||||
print!("{}", s);
|
||||
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()))
|
||||
print_callback(s);
|
||||
Ok(Ctr::String(read_callback()))
|
||||
} else {
|
||||
Err(start_trace(
|
||||
("input", "expected a string input to prompt user with")
|
||||
|
|
@ -180,7 +256,8 @@ fn input_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn add_string_lib(syms: &mut SymTable) {
|
||||
pub fn add_string_lib(syms: &mut SymTable, print: fn(&String), read: fn() -> String) {
|
||||
let echo_print = print.clone();
|
||||
syms.insert(
|
||||
"echo".to_string(),
|
||||
Symbol {
|
||||
|
|
@ -188,7 +265,11 @@ pub fn add_string_lib(syms: &mut SymTable) {
|
|||
args: Args::Infinite,
|
||||
conditional_branches: false,
|
||||
docs: ECHO_DOCSTRING.to_string(),
|
||||
value: ValueType::Internal(Rc::new(echo_callback)),
|
||||
value: ValueType::Internal(Rc::new(
|
||||
move |ast: &Seg, syms: &mut SymTable| -> Result<Ctr, Traceback> {
|
||||
echo_callback(ast, syms, echo_print)
|
||||
}
|
||||
)),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
|
@ -212,6 +293,19 @@ pub fn add_string_lib(syms: &mut SymTable) {
|
|||
name: String::from("substr?"),
|
||||
args: Args::Strict(vec![Type::String, Type::String]),
|
||||
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(),
|
||||
value: ValueType::Internal(Rc::new(substr_callback)),
|
||||
optimizable: true,
|
||||
|
|
@ -258,6 +352,8 @@ pub fn add_string_lib(syms: &mut SymTable) {
|
|||
},
|
||||
);
|
||||
|
||||
let input_print = print.clone();
|
||||
let input_read = read.clone();
|
||||
syms.insert(
|
||||
"input".to_string(),
|
||||
Symbol {
|
||||
|
|
@ -265,7 +361,11 @@ pub fn add_string_lib(syms: &mut SymTable) {
|
|||
args: Args::Strict(vec![Type::String]),
|
||||
conditional_branches: false,
|
||||
docs: INPUT_DOCSTRING.to_string(),
|
||||
value: ValueType::Internal(Rc::new(input_callback)),
|
||||
value: ValueType::Internal(Rc::new(
|
||||
move |ast: &Seg, syms: &mut SymTable| -> Result<Ctr, Traceback> {
|
||||
input_callback(ast, syms, input_print, input_read)
|
||||
}
|
||||
)),
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
|
@ -15,17 +15,21 @@
|
|||
* 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::error::{Traceback, start_trace};
|
||||
use crate::segment::{Ctr, Seg, Type};
|
||||
#[cfg(feature="implicit-load")]
|
||||
use crate::stl::posix::POSIX_LOAD_NAME;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
use std::rc::Rc;
|
||||
use alloc::fmt;
|
||||
use alloc::rc::Rc;
|
||||
use alloc::vec::Vec;
|
||||
use alloc::boxed::Box;
|
||||
use alloc::string::{String, ToString};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SymTable(HashMap<String, Symbol>, usize);
|
||||
pub struct SymTable(QuickMap<Symbol>, usize);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct UserFn {
|
||||
|
|
@ -78,7 +82,7 @@ pub struct Symbol {
|
|||
|
||||
impl SymTable {
|
||||
pub fn new() -> SymTable {
|
||||
SymTable(HashMap::<String, Symbol>::new(), 0)
|
||||
SymTable(QuickMap::<Symbol>::new(), 0)
|
||||
}
|
||||
|
||||
pub fn get(&self, arg: &String) -> Option<&Symbol> {
|
||||
|
|
@ -99,33 +103,30 @@ impl SymTable {
|
|||
self.0.remove(arg)
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> std::collections::hash_map::Iter<'_, String, Symbol> {
|
||||
pub fn iter(&self) -> QuickMapIter<'_, Symbol>{
|
||||
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) {
|
||||
/* updates self with all syms in other that match the following cases:
|
||||
* * sym is not 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;
|
||||
for i in other.iter() {
|
||||
self.0.entry(i.0.to_string())
|
||||
.and_modify(|inner: &mut Symbol| {
|
||||
if tmp < i.1.__generation {
|
||||
inner.__generation = i.1.__generation;
|
||||
inner.value = i.1.value.clone();
|
||||
inner.args = i.1.args.clone();
|
||||
inner.docs = i.1.docs.clone();
|
||||
inner.conditional_branches = i.1.conditional_branches;
|
||||
inner.name = i.1.name.clone();
|
||||
}
|
||||
})
|
||||
.or_insert(i.1.clone());
|
||||
let s = self.0
|
||||
.remove(&i.0)
|
||||
.map_or(
|
||||
i.1.clone(),
|
||||
|existing| if tmp < i.1.__generation {
|
||||
i.1.clone()
|
||||
} else {
|
||||
existing
|
||||
});
|
||||
self.0.insert(i.0.to_string(), s);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -146,9 +147,9 @@ impl SymTable {
|
|||
* assume a shell command is being run
|
||||
*/
|
||||
#[cfg(feature="implicit-load")]
|
||||
None if call_func => match self.remove(&POSIX_LOAD_NAME.to_string()) {
|
||||
None if call_func => match self.remove(&env!("POSIX_LOAD_NAME").to_string()) {
|
||||
Some(s) => {
|
||||
name_token = String::from(POSIX_LOAD_NAME);
|
||||
name_token = String::from(env!("POSIX_LOAD_NAME").to_string());
|
||||
/* highly unfortunate circumstance
|
||||
* we must now rebuild the original ast
|
||||
* costs a whole clone of the args
|
||||
|
|
@ -167,7 +168,9 @@ impl SymTable {
|
|||
},
|
||||
None => return Err(
|
||||
Traceback::new()
|
||||
.with_trace(("(implicit load)", "(load function not found)").into())
|
||||
.with_trace((
|
||||
&format!("(implicit load ({}))", name),
|
||||
"(load function not found)").into())
|
||||
)
|
||||
},
|
||||
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
mod eval_tests {
|
||||
use flesh::ast::{eval, lex, SymTable};
|
||||
use flesh::ast::{Args, Ctr, Seg, Symbol, UserFn, ValueType};
|
||||
use flesh::stdlib::{dynamic_stdlib, static_stdlib};
|
||||
use flesh::stdlib::static_stdlib;
|
||||
|
||||
#[test]
|
||||
fn eval_simple() {
|
||||
|
|
@ -23,8 +23,8 @@ mod eval_tests {
|
|||
|
||||
#[test]
|
||||
fn eval_function_call() {
|
||||
let test_doc = "('one' (echo 'unwrap_me'))".to_string();
|
||||
let output = "('one' 'unwrap_me')";
|
||||
let test_doc = "(\"one\" (echo \"unwrap_me\"))".to_string();
|
||||
let output = "(\"one\" \"unwrap_me\")";
|
||||
let mut syms = SymTable::new();
|
||||
|
||||
let test_external_func: Symbol = Symbol {
|
||||
|
|
@ -50,8 +50,8 @@ mod eval_tests {
|
|||
|
||||
#[test]
|
||||
fn eval_embedded_func_calls() {
|
||||
let test_doc = "('one' (echo (echo 'unwrap_me')))".to_string();
|
||||
let output = "('one' 'unwrap_me')";
|
||||
let test_doc = "(\"one\" (echo (echo \"unwrap_me\")))".to_string();
|
||||
let output = "(\"one\" \"unwrap_me\")";
|
||||
let mut syms = SymTable::new();
|
||||
|
||||
let test_external_func: Symbol = Symbol {
|
||||
|
|
@ -75,6 +75,7 @@ mod eval_tests {
|
|||
assert_eq!(reduced.to_string(), output);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "implicit-load"))]
|
||||
#[test]
|
||||
fn eval_bad_syms() {
|
||||
let test_doc = "(undefined)".to_string();
|
||||
|
|
@ -96,13 +97,12 @@ mod eval_tests {
|
|||
|
||||
#[test]
|
||||
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 rh_doc = "(apply (lambda (x) (car x)) (1 2 3))";
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
|
||||
eval(&lex(&comparator.to_string()).unwrap(), &mut syms).unwrap();
|
||||
assert_eq!(
|
||||
|
|
@ -91,7 +91,7 @@ mod func_tests {
|
|||
.call_symbol(&"echo_2".to_string(), &args, true)
|
||||
.unwrap()
|
||||
.to_string(),
|
||||
"'test'".to_string()
|
||||
"\"test\"".to_string()
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -140,7 +140,7 @@ mod func_tests {
|
|||
syms.call_symbol(&"test_outer".to_string(), &args, true)
|
||||
.unwrap()
|
||||
.to_string(),
|
||||
"'test'".to_string()
|
||||
"\"test\"".to_string()
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -3,44 +3,44 @@ mod lex_tests {
|
|||
|
||||
#[test]
|
||||
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);
|
||||
}
|
||||
|
||||
#[test]
|
||||
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);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bad_symbol() {
|
||||
let document = String::from("(as@dd)");
|
||||
let output: &str = "Problem lexing document: \"Unparsable token: as@dd\"";
|
||||
let output: &str = "Problem lexing document: \"Unparsable token \\\"as@dd\\\" at char 7\"";
|
||||
assert_eq!(lex(&document).err().unwrap().0.first().unwrap().message, output.to_string(),);
|
||||
}
|
||||
|
||||
#[test]
|
||||
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);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_list_delim_in_str() {
|
||||
let document = String::from("('(')");
|
||||
let document = String::from("(\"(\")");
|
||||
assert_eq!(lex(&document).unwrap().to_string(), document);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_comment_delim_in_str() {
|
||||
let document = String::from("('#')");
|
||||
let document = String::from("(\"#\")");
|
||||
assert_eq!(lex(&document).unwrap().to_string(), document);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_empty_string() {
|
||||
let document = String::from("('')");
|
||||
let document = String::from("(\"\")");
|
||||
assert_eq!(lex(&document).unwrap().to_string(), document);
|
||||
}
|
||||
|
||||
|
|
@ -75,8 +75,8 @@ mod lex_tests {
|
|||
#[test]
|
||||
fn test_postline_comment_1() {
|
||||
let document =
|
||||
String::from("#!/bin/flesh\n((one two)# another doc comment\n('three' four))");
|
||||
let output: &str = "((one two) ('three' four))";
|
||||
String::from("#!/bin/flesh\n((one two)# another doc comment\n(\"three\" four))");
|
||||
let output: &str = "((one two) (\"three\" four))";
|
||||
assert_eq!(lex(&document).unwrap().to_string(), output.to_string(),);
|
||||
}
|
||||
|
||||
|
|
@ -105,7 +105,7 @@ mod lex_tests {
|
|||
#[test]
|
||||
fn test_bad_token_list() {
|
||||
let document = String::from("(one t(wo)");
|
||||
let output: &str = "Problem lexing document: \"list started in middle of another token\"";
|
||||
let output: &str = "Problem lexing document: \"list started in middle of another token: t\"";
|
||||
assert_eq!(lex(&document).err().unwrap().0.first().unwrap().message, output.to_string(),);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
mod append_lib_tests {
|
||||
use flesh::ast::{eval, lex, SymTable};
|
||||
use flesh::stdlib::{dynamic_stdlib, static_stdlib};
|
||||
use flesh::stdlib::static_stdlib;
|
||||
|
||||
#[test]
|
||||
fn test_cons_to_empty_list() {
|
||||
let document = "(cons () 1)";
|
||||
let result = "(1)";
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -18,11 +18,11 @@ mod append_lib_tests {
|
|||
|
||||
#[test]
|
||||
fn test_multi_cons_to_empty_list() {
|
||||
let document = "(cons () 1 'two' 3.4)";
|
||||
let result = "(1 'two' 3.4)";
|
||||
let document = "(cons () 1 \"two\" 3.4)";
|
||||
let result = "(1 \"two\" 3.4)";
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -37,7 +37,7 @@ mod append_lib_tests {
|
|||
let result = "(1 2 3)";
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -52,7 +52,7 @@ mod append_lib_tests {
|
|||
let result = "(<nil>)";
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -63,11 +63,11 @@ mod append_lib_tests {
|
|||
|
||||
#[test]
|
||||
fn test_cons_no_list() {
|
||||
let document = "(cons 'test' 1 2 3)";
|
||||
let result = "('test' 1 2 3)";
|
||||
let document = "(cons \"test\" 1 2 3)";
|
||||
let result = "(\"test\" 1 2 3)";
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -82,7 +82,7 @@ mod append_lib_tests {
|
|||
let result = "0";
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -97,7 +97,7 @@ mod append_lib_tests {
|
|||
let result = "3";
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -111,7 +111,7 @@ mod append_lib_tests {
|
|||
let document = "(car ())";
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.err()
|
||||
|
|
@ -130,7 +130,7 @@ mod append_lib_tests {
|
|||
let result = "1";
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -144,7 +144,7 @@ mod append_lib_tests {
|
|||
let document = "(cdr ())";
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
|
|
@ -164,7 +164,7 @@ mod append_lib_tests {
|
|||
let result = "3";
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
|
|
@ -176,15 +176,15 @@ mod append_lib_tests {
|
|||
|
||||
#[test]
|
||||
fn test_pop() {
|
||||
let document = "(def test '' (pop (1 2 3)))";
|
||||
let document = "(def test \"\" (pop (1 2 3)))";
|
||||
let check1 = "(car test)";
|
||||
let result1 = "1";
|
||||
let check2 = "(cdr test)";
|
||||
let result2 = "(2 3)";
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
|
||||
eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap();
|
||||
let ch1 = lex(&check1.to_string()).unwrap();
|
||||
let ch2 = lex(&check2.to_string()).unwrap();
|
||||
|
|
@ -202,15 +202,15 @@ mod append_lib_tests {
|
|||
|
||||
#[test]
|
||||
fn test_pop_mono() {
|
||||
let document = "(def test '' (pop (1)))";
|
||||
let document = "(def test \"\" (pop (1)))";
|
||||
let check1 = "(car test)";
|
||||
let result1 = "1";
|
||||
let check2 = "(cdr test)";
|
||||
let result2 = "(<nil>)";
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
|
||||
eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap();
|
||||
let ch1 = lex(&check1.to_string()).unwrap();
|
||||
let ch2 = lex(&check2.to_string()).unwrap();
|
||||
|
|
@ -228,15 +228,15 @@ mod append_lib_tests {
|
|||
|
||||
#[test]
|
||||
fn test_dq() {
|
||||
let document = "(def test '' (dq (1 2 3)))";
|
||||
let document = "(def test \"\" (dq (1 2 3)))";
|
||||
let check1 = "(car test)";
|
||||
let result1 = "3";
|
||||
let check2 = "(cdr test)";
|
||||
let result2 = "(1 2)";
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
|
||||
eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap();
|
||||
let ch1 = lex(&check1.to_string()).unwrap();
|
||||
let ch2 = lex(&check2.to_string()).unwrap();
|
||||
|
|
@ -254,15 +254,15 @@ mod append_lib_tests {
|
|||
|
||||
#[test]
|
||||
fn test_dq_mono() {
|
||||
let document = "(def test '' (dq (1)))";
|
||||
let document = "(def test \"\" (dq (1)))";
|
||||
let check1 = "(car test)";
|
||||
let result1 = "1";
|
||||
let check2 = "(cdr test)";
|
||||
let result2 = "(<nil>)";
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
|
||||
eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap();
|
||||
let ch1 = lex(&check1.to_string()).unwrap();
|
||||
let ch2 = lex(&check2.to_string()).unwrap();
|
||||
|
|
@ -280,12 +280,11 @@ mod append_lib_tests {
|
|||
|
||||
#[test]
|
||||
fn test_reverse() {
|
||||
let document = "(reverse ('test' 1 2 3))";
|
||||
let result = "(3 2 1 'test')";
|
||||
let document = "(reverse (\"test\" 1 2 3))";
|
||||
let result = "(3 2 1 \"test\")";
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
|
|
@ -297,12 +296,11 @@ mod append_lib_tests {
|
|||
|
||||
#[test]
|
||||
fn test_reverse_mono() {
|
||||
let document = "(reverse ('test'))";
|
||||
let result = "('test')";
|
||||
let document = "(reverse (\"test\"))";
|
||||
let result = "(\"test\")";
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
|
|
@ -318,8 +316,7 @@ mod append_lib_tests {
|
|||
let result = "(<nil>)";
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
|
|
@ -335,8 +332,7 @@ mod append_lib_tests {
|
|||
let result = "true";
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
|
|
@ -352,8 +348,7 @@ mod append_lib_tests {
|
|||
let result = "false";
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
|
|
@ -1,14 +1,13 @@
|
|||
mod bool_lib_tests {
|
||||
use flesh::ast::{eval, lex, Ctr, SymTable};
|
||||
use flesh::stdlib::{dynamic_stdlib, static_stdlib};
|
||||
use flesh::stdlib::static_stdlib;
|
||||
|
||||
#[test]
|
||||
fn test_and_true_chain() {
|
||||
let document = "(and true true true true true)";
|
||||
let result = "true";
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -22,8 +21,7 @@ mod bool_lib_tests {
|
|||
let document = "(and true true false true true)";
|
||||
let result = "false";
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -37,8 +35,7 @@ mod bool_lib_tests {
|
|||
let document = "(and false false false false false)";
|
||||
let result = "false";
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -52,8 +49,7 @@ mod bool_lib_tests {
|
|||
let document = "(or true true true true true)";
|
||||
let result = "true";
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -67,8 +63,7 @@ mod bool_lib_tests {
|
|||
let document = "(or true true false true true)";
|
||||
let result = "true";
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -82,8 +77,7 @@ mod bool_lib_tests {
|
|||
let document = "(or false false false false false)";
|
||||
let result = "false";
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -97,8 +91,7 @@ mod bool_lib_tests {
|
|||
let document = "(not true)";
|
||||
let result = "false";
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -109,13 +102,12 @@ mod bool_lib_tests {
|
|||
|
||||
#[test]
|
||||
fn test_toggle_a_bool() {
|
||||
let document = "(def tester '' true)";
|
||||
let document = "(def tester \"\" true)";
|
||||
let change = "(toggle tester)";
|
||||
let check = "(tester)";
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
|
||||
let doc_tree = lex(&document.to_string()).unwrap();
|
||||
let change_tree = lex(&change.to_string()).unwrap();
|
||||
|
|
@ -148,13 +140,12 @@ mod bool_lib_tests {
|
|||
|
||||
#[test]
|
||||
fn test_toggle_errors_dont_lose_vars() {
|
||||
let document = "(def tester '' 'oops')";
|
||||
let document = "(def tester \"\" \"oops\")";
|
||||
let change = "(toggle tester)";
|
||||
let check = "(tester)";
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
|
||||
let doc_tree = lex(&document.to_string()).unwrap();
|
||||
let change_tree = lex(&change.to_string()).unwrap();
|
||||
|
|
@ -168,7 +159,7 @@ mod bool_lib_tests {
|
|||
);
|
||||
let intermediate = *eval(&check_tree, &mut syms).unwrap();
|
||||
if let Ctr::Seg(ref s) = intermediate {
|
||||
assert_eq!(s.to_string(), "('oops')".to_string());
|
||||
assert_eq!(s.to_string(), "(\"oops\")".to_string());
|
||||
} else {
|
||||
eprintln!("did not expect: {}", intermediate);
|
||||
panic!()
|
||||
|
|
@ -181,13 +172,12 @@ mod bool_lib_tests {
|
|||
|
||||
#[test]
|
||||
fn test_toggle_errors_dont_lose_funcs() {
|
||||
let document = "(def tester '' (oops) oops)";
|
||||
let document = "(def tester \"\" (oops) oops)";
|
||||
let change = "(toggle tester)";
|
||||
let check = "(tester '1')";
|
||||
let check = "(tester \"1\")";
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
|
||||
let doc_tree = lex(&document.to_string()).unwrap();
|
||||
let change_tree = lex(&change.to_string()).unwrap();
|
||||
|
|
@ -216,8 +206,7 @@ mod bool_lib_tests {
|
|||
let test = lex(&document.to_string()).unwrap();
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
|
||||
if let Ctr::Bool(b) = *eval(&test, &mut syms).unwrap() {
|
||||
assert!(b)
|
||||
|
|
@ -232,8 +221,7 @@ mod bool_lib_tests {
|
|||
let test = lex(&document.to_string()).unwrap();
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
|
||||
if let Ctr::Bool(b) = *eval(&test, &mut syms).unwrap() {
|
||||
assert!(!b)
|
||||
|
|
@ -248,8 +236,7 @@ mod bool_lib_tests {
|
|||
let test = lex(&document.to_string()).unwrap();
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
|
||||
if let Ctr::Bool(b) = *eval(&test, &mut syms).unwrap() {
|
||||
assert!(!b)
|
||||
|
|
@ -264,8 +251,7 @@ mod bool_lib_tests {
|
|||
let test = lex(&document.to_string()).unwrap();
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
|
||||
if let Ctr::Bool(b) = *eval(&test, &mut syms).unwrap() {
|
||||
assert!(!b)
|
||||
|
|
@ -276,12 +262,11 @@ mod bool_lib_tests {
|
|||
|
||||
#[test]
|
||||
fn test_iseq_long_t_str() {
|
||||
let document = "(eq? '1' '1' '1' '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\")";
|
||||
let test = lex(&document.to_string()).unwrap();
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
|
||||
if let Ctr::Bool(b) = *eval(&test, &mut syms).unwrap() {
|
||||
assert!(b)
|
||||
|
|
@ -296,8 +281,7 @@ mod bool_lib_tests {
|
|||
let test = lex(&document.to_string()).unwrap();
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
|
||||
if let Ctr::Bool(b) = *eval(&test, &mut syms).unwrap() {
|
||||
assert!(b)
|
||||
|
|
@ -308,12 +292,11 @@ mod bool_lib_tests {
|
|||
|
||||
#[test]
|
||||
fn test_iseq_f_wrong_type() {
|
||||
let document = "(eq? 1 '1')";
|
||||
let document = "(eq? 1 \"1\")";
|
||||
let test = lex(&document.to_string()).unwrap();
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
|
||||
if let Ctr::Bool(b) = *eval(&test, &mut syms).unwrap() {
|
||||
assert!(!b)
|
||||
|
|
@ -324,12 +307,11 @@ mod bool_lib_tests {
|
|||
|
||||
#[test]
|
||||
fn test_boolcast_str_t() {
|
||||
let document = "(bool 'true')";
|
||||
let document = "(bool \"true\")";
|
||||
let test = lex(&document.to_string()).unwrap();
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
|
||||
if let Ctr::Bool(b) = *eval(&test, &mut syms).unwrap() {
|
||||
assert!(b)
|
||||
|
|
@ -340,12 +322,11 @@ mod bool_lib_tests {
|
|||
|
||||
#[test]
|
||||
fn test_boolcast_str_f() {
|
||||
let document = "(bool 'false')";
|
||||
let document = "(bool \"false\")";
|
||||
let test = lex(&document.to_string()).unwrap();
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
|
||||
if let Ctr::Bool(b) = *eval(&test, &mut syms).unwrap() {
|
||||
assert!(!b)
|
||||
|
|
@ -360,8 +341,7 @@ mod bool_lib_tests {
|
|||
let test = lex(&document.to_string()).unwrap();
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
|
||||
if let Ctr::Bool(b) = *eval(&test, &mut syms).unwrap() {
|
||||
assert!(b)
|
||||
|
|
@ -376,8 +356,7 @@ mod bool_lib_tests {
|
|||
let test = lex(&document.to_string()).unwrap();
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
|
||||
if let Ctr::Bool(b) = *eval(&test, &mut syms).unwrap() {
|
||||
assert!(!b)
|
||||
|
|
@ -392,8 +371,7 @@ mod bool_lib_tests {
|
|||
let test = lex(&document.to_string()).unwrap();
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
|
||||
if let Ctr::Bool(b) = *eval(&test, &mut syms).unwrap() {
|
||||
assert!(b)
|
||||
|
|
@ -408,8 +386,7 @@ mod bool_lib_tests {
|
|||
let test = lex(&document.to_string()).unwrap();
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
|
||||
if let Ctr::Bool(b) = *eval(&test, &mut syms).unwrap() {
|
||||
assert!(!b)
|
||||
|
|
@ -1,13 +1,12 @@
|
|||
mod control_lib_tests {
|
||||
use flesh::ast::{eval, lex, SymTable};
|
||||
use flesh::stdlib::{dynamic_stdlib, static_stdlib};
|
||||
use flesh::stdlib::static_stdlib;
|
||||
|
||||
#[test]
|
||||
fn test_assert_t() {
|
||||
let document = "(assert true)";
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap();
|
||||
}
|
||||
|
||||
|
|
@ -15,8 +14,7 @@ mod control_lib_tests {
|
|||
fn test_assert_f() {
|
||||
let document = "(assert false)";
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert!(eval(&lex(&document.to_string()).unwrap(), &mut syms).is_err())
|
||||
}
|
||||
|
||||
|
|
@ -25,8 +23,7 @@ mod control_lib_tests {
|
|||
let document = "(if true 1 2)";
|
||||
let result = 1;
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -40,8 +37,7 @@ mod control_lib_tests {
|
|||
let document = "(if false 1 2)";
|
||||
let result = 2;
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -55,8 +51,7 @@ mod control_lib_tests {
|
|||
let document = "(if true (cons () 1) 2)";
|
||||
let result = "(1)";
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -68,13 +63,12 @@ mod control_lib_tests {
|
|||
#[test]
|
||||
fn test_let_multiphase_locals() {
|
||||
let document = "(let (
|
||||
(temp '1')
|
||||
(temp (cons () temp '2')))
|
||||
(temp \"1\")
|
||||
(temp (cons () temp \"2\")))
|
||||
temp)";
|
||||
let result = "('1' '2')";
|
||||
let result = "(\"1\" \"2\")";
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -86,14 +80,13 @@ mod control_lib_tests {
|
|||
#[test]
|
||||
fn test_let_def_escapes_locals() {
|
||||
let document1 = "(let (
|
||||
(temp 'hello')
|
||||
(temp (concat temp ' ' 'world')))
|
||||
(def global '' temp))";
|
||||
(temp \"hello\")
|
||||
(temp (concat temp \" \" \"world\")))
|
||||
(def global \"\" temp))";
|
||||
let document2 = "global";
|
||||
let result = "('hello world')";
|
||||
let result = "(\"hello world\")";
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
eval(&lex(&document1.to_string()).unwrap(), &mut syms).unwrap();
|
||||
assert_eq!(
|
||||
*eval(&lex(&document2.to_string()).unwrap(), &mut syms)
|
||||
|
|
@ -105,11 +98,10 @@ mod control_lib_tests {
|
|||
|
||||
#[test]
|
||||
fn test_let_multibody_evals() {
|
||||
let document = "(let ((temp '1')) temp (cons () temp '2'))";
|
||||
let result = "('1' '2')";
|
||||
let document = "(let ((temp \"1\")) temp (cons () temp \"2\"))";
|
||||
let result = "(\"1\" \"2\")";
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -121,15 +113,14 @@ mod control_lib_tests {
|
|||
#[test]
|
||||
fn test_let_multiphase_local_multibody_evals() {
|
||||
let document = "(let (
|
||||
(temp '1')
|
||||
(temp (cons () temp '2')))
|
||||
(echo 'first body')
|
||||
(cons temp '3'))";
|
||||
(temp \"1\")
|
||||
(temp (cons () temp \"2\")))
|
||||
(echo \"first body\")
|
||||
(cons temp \"3\"))";
|
||||
|
||||
let result = "('1' '2' '3')";
|
||||
let result = "(\"1\" \"2\" \"3\")";
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -140,15 +131,15 @@ mod control_lib_tests {
|
|||
|
||||
#[test]
|
||||
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
|
||||
// else prev will have a problematic type
|
||||
let while_loop = "
|
||||
(while switch
|
||||
(def prev '' switch)
|
||||
(def prev \"\" switch)
|
||||
(toggle switch)
|
||||
(if switch
|
||||
(def '' prev)
|
||||
(def \"\" prev)
|
||||
()))";
|
||||
let test_check = "prev";
|
||||
|
||||
|
|
@ -157,8 +148,7 @@ mod control_lib_tests {
|
|||
let check_tree = lex(&test_check.to_string()).unwrap();
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
|
||||
eval(&switch_tree, &mut syms).unwrap();
|
||||
eval(&while_tree, &mut syms).unwrap();
|
||||
|
|
@ -167,15 +157,15 @@ mod control_lib_tests {
|
|||
|
||||
#[test]
|
||||
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
|
||||
// else prev will have a problematic type
|
||||
let while_loop = "
|
||||
(while (or switch switch)
|
||||
(def prev '' switch)
|
||||
(def prev \"\" switch)
|
||||
(toggle switch)
|
||||
(if switch
|
||||
(def '' prev)
|
||||
(def \"\" prev)
|
||||
()))";
|
||||
let test_check = "prev";
|
||||
|
||||
|
|
@ -184,8 +174,7 @@ mod control_lib_tests {
|
|||
let check_tree = lex(&test_check.to_string()).unwrap();
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
|
||||
eval(&switch_tree, &mut syms).unwrap();
|
||||
eval(&while_tree, &mut syms).unwrap();
|
||||
|
|
@ -194,15 +183,15 @@ mod control_lib_tests {
|
|||
|
||||
#[test]
|
||||
fn test_while_2_iter() {
|
||||
let additional = "(def sw1 '' true)";
|
||||
let switch_dec = "(def sw2 '' true)";
|
||||
let additional = "(def sw1 \"\" true)";
|
||||
let switch_dec = "(def sw2 \"\" true)";
|
||||
// while should loop twice and define result
|
||||
let while_loop = "
|
||||
(while sw1
|
||||
(toggle sw2)
|
||||
(if (and sw1 sw2)
|
||||
(def sw1 '' false)
|
||||
(def result '' 'yay')))";
|
||||
(def sw1 \"\" false)
|
||||
(def result \"\" \"yay\")))";
|
||||
let test_check = "result";
|
||||
|
||||
let another_tree = lex(&additional.to_string()).unwrap();
|
||||
|
|
@ -211,8 +200,7 @@ mod control_lib_tests {
|
|||
let check_tree = lex(&test_check.to_string()).unwrap();
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
|
||||
eval(&another_tree, &mut syms).unwrap();
|
||||
eval(&switch_tree, &mut syms).unwrap();
|
||||
|
|
@ -222,32 +210,31 @@ mod control_lib_tests {
|
|||
|
||||
#[test]
|
||||
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 doc_tree = lex(&document.to_string()).unwrap();
|
||||
let test_tree = lex(&test.to_string()).unwrap();
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
|
||||
eval(&doc_tree, &mut syms).unwrap();
|
||||
let res = eval(&test_tree, &mut syms);
|
||||
res.unwrap();
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "implicit-load"))]
|
||||
#[test]
|
||||
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 doc_tree = lex(&document.to_string()).unwrap();
|
||||
let test_tree = lex(&test.to_string()).unwrap();
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
|
||||
eval(&doc_tree, &mut syms).unwrap();
|
||||
assert_eq!(
|
||||
|
|
@ -1,16 +1,15 @@
|
|||
mod decl_lib_tests {
|
||||
use flesh::ast::{eval, lex, Ctr, SymTable};
|
||||
use flesh::stdlib::{dynamic_stdlib, static_stdlib};
|
||||
use flesh::stdlib::static_stdlib;
|
||||
|
||||
#[test]
|
||||
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 result = "(1)";
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
|
||||
eval(&lex(&doc1.to_string()).unwrap(), &mut syms).unwrap();
|
||||
let res = *eval(&lex(&doc2.to_string()).unwrap(), &mut syms).unwrap();
|
||||
|
|
@ -19,13 +18,12 @@ mod decl_lib_tests {
|
|||
|
||||
#[test]
|
||||
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 result = "((1))";
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
|
||||
eval(&lex(&doc1.to_string()).unwrap(), &mut syms).unwrap();
|
||||
let res = *eval(&lex(&doc2.to_string()).unwrap(), &mut syms).unwrap();
|
||||
|
|
@ -34,13 +32,12 @@ mod decl_lib_tests {
|
|||
|
||||
#[test]
|
||||
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 result = "1";
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
|
||||
eval(&lex(&doc1.to_string()).unwrap(), &mut syms).unwrap();
|
||||
let res = *eval(&lex(&doc2.to_string()).unwrap(), &mut syms).unwrap();
|
||||
|
|
@ -49,14 +46,13 @@ mod decl_lib_tests {
|
|||
|
||||
#[test]
|
||||
fn test_variable_def_redef_and_lookup() {
|
||||
let doc1 = "(def test 'my test var' 1)";
|
||||
let doc2 = "(def test 'my test var' '2')";
|
||||
let doc1 = "(def test \"my test var\" 1)";
|
||||
let doc2 = "(def test \"my test var\" \"2\")";
|
||||
let doc3 = "(test)";
|
||||
let result = "('2')";
|
||||
let result = "(\"2\")";
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
|
||||
eval(&lex(&doc1.to_string()).unwrap(), &mut syms).unwrap();
|
||||
eval(&lex(&doc2.to_string()).unwrap(), &mut syms).unwrap();
|
||||
|
|
@ -64,15 +60,15 @@ mod decl_lib_tests {
|
|||
assert_eq!(res.to_string(), result);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "implicit-load"))]
|
||||
#[test]
|
||||
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 doc3 = "(test)";
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
|
||||
eval(&lex(&doc1.to_string()).unwrap(), &mut syms).unwrap();
|
||||
eval(&lex(&doc2.to_string()).unwrap(), &mut syms).unwrap();
|
||||
|
|
@ -90,17 +86,16 @@ mod decl_lib_tests {
|
|||
|
||||
#[test]
|
||||
fn test_variable_def_redef_via_reference_and_lookup() {
|
||||
let doc1 = "(def test 'my test var' 1)";
|
||||
let doc2 = "(def ref 'references test' (quote test))";
|
||||
let doc3 = "(def ref 'my test var' '2')";
|
||||
let doc1 = "(def test \"my test var\" 1)";
|
||||
let doc2 = "(def ref \"references test\" (quote test))";
|
||||
let doc3 = "(def ref \"my test var\" \"2\")";
|
||||
let test = "(test)";
|
||||
let res1 = "(1)";
|
||||
let doc4 = "(def (eval ref) 'my test var' '2')";
|
||||
let res2 = "('2')";
|
||||
let doc4 = "(def (eval ref) \"my test var\" \"2\")";
|
||||
let res2 = "(\"2\")";
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
|
||||
eval(&lex(&doc1.to_string()).unwrap(), &mut syms).unwrap();
|
||||
eval(&lex(&doc2.to_string()).unwrap(), &mut syms).unwrap();
|
||||
|
|
@ -115,15 +110,14 @@ mod decl_lib_tests {
|
|||
|
||||
#[test]
|
||||
fn test_variable_doc_dynamic() {
|
||||
let doc1 = "(def test-doc 'docs for test' 'test tests tests test')";
|
||||
let doc2 = "(def test test-doc 'one')";
|
||||
let doc1 = "(def test-doc \"docs for test\" \"test tests tests test\")";
|
||||
let doc2 = "(def test test-doc \"one\")";
|
||||
let doc3 = "(eq? (and
|
||||
(eq? (get-doc (quote test)) test-doc)
|
||||
(eq? test 'one')))";
|
||||
(eq? test \"one\")))";
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
|
||||
eval(&lex(&doc1.to_string()).unwrap(), &mut syms).unwrap();
|
||||
eval(&lex(&doc2.to_string()).unwrap(), &mut syms).unwrap();
|
||||
|
|
@ -136,13 +130,12 @@ mod decl_lib_tests {
|
|||
|
||||
#[test]
|
||||
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 result = "1";
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
|
||||
eval(&lex(&doc1.to_string()).unwrap(), &mut syms).unwrap();
|
||||
let res = *eval(&lex(&doc2.to_string()).unwrap(), &mut syms).unwrap();
|
||||
|
|
@ -151,12 +144,11 @@ mod decl_lib_tests {
|
|||
|
||||
#[test]
|
||||
fn test_isset_true() {
|
||||
let doc1 = "(def test '' 1)";
|
||||
let doc1 = "(def test \"\" 1)";
|
||||
let doc2 = "(set? test)";
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
|
||||
let def_tree = lex(&doc1.to_string()).unwrap();
|
||||
let set_tree = lex(&doc2.to_string()).unwrap();
|
||||
|
|
@ -170,8 +162,7 @@ mod decl_lib_tests {
|
|||
fn test_isset_false() {
|
||||
let doc = "(set? test)";
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
let set_tree = lex(&doc.to_string()).unwrap();
|
||||
if let Ctr::Bool(b) = *eval(&set_tree, &mut syms).unwrap() {
|
||||
assert!(!b);
|
||||
|
|
@ -180,12 +171,11 @@ mod decl_lib_tests {
|
|||
|
||||
#[test]
|
||||
fn test_env_doesnt_lose_elements() {
|
||||
let doc1 = "(def t '' 1)";
|
||||
let doc1 = "(def t \"\" 1)";
|
||||
let doc2 = "(env)";
|
||||
let doc3 = "t";
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
let set_tree = lex(&doc1.to_string()).unwrap();
|
||||
let env_tree = lex(&doc2.to_string()).unwrap();
|
||||
let tst_tree = lex(&doc3.to_string()).unwrap();
|
||||
|
|
@ -199,8 +189,7 @@ mod decl_lib_tests {
|
|||
let document = "(quote (add 1 2))";
|
||||
let result = "(add 1 2)";
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -216,8 +205,7 @@ mod decl_lib_tests {
|
|||
(eval stored-tree)))";
|
||||
let result = "3";
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -231,8 +219,7 @@ mod decl_lib_tests {
|
|||
let document = "(eval (1 2 3))";
|
||||
let result = "(1 2 3)";
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -245,8 +232,7 @@ mod decl_lib_tests {
|
|||
fn test_lambda_str_equivalency_list() {
|
||||
let document = "(lambda (x y) (add x y))";
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -259,8 +245,7 @@ mod decl_lib_tests {
|
|||
fn test_lambda_str_equivalency_no_args() {
|
||||
let document = "(lambda () (add 1 2))";
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -273,8 +258,7 @@ mod decl_lib_tests {
|
|||
fn test_lambda_inline_call() {
|
||||
let document = "((lambda (x y) (add x y)) 1 2)";
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
let it = *eval(
|
||||
&lex(&document.to_string()).unwrap(),
|
||||
&mut syms).unwrap();
|
||||
|
|
@ -290,8 +274,7 @@ mod decl_lib_tests {
|
|||
let document = "(let ((adder (lambda (x y) (add x y))))
|
||||
(adder 1 2))";
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
let it = *eval(
|
||||
&lex(&document.to_string()).unwrap(),
|
||||
&mut syms).unwrap();
|
||||
|
|
@ -305,11 +288,10 @@ mod decl_lib_tests {
|
|||
#[test]
|
||||
fn test_lambda_var_bound_call() {
|
||||
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))";
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
let it = *eval(
|
||||
&lex(&document.to_string()).unwrap(),
|
||||
&mut syms).unwrap();
|
||||
|
|
@ -323,12 +305,11 @@ mod decl_lib_tests {
|
|||
#[test]
|
||||
fn test_lambda_arg_call() {
|
||||
let document = "(let (())
|
||||
(def appl '' (func item) (func item))
|
||||
(def adder 'my adder' (lambda (x) (add x 1)))
|
||||
(def appl \"\" (func item) (func item))
|
||||
(def adder \"my adder\" (lambda (x) (add x 1)))
|
||||
(appl adder 2))";
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
let it = *eval(
|
||||
&lex(&document.to_string()).unwrap(),
|
||||
&mut syms).unwrap();
|
||||
|
|
@ -342,11 +323,10 @@ mod decl_lib_tests {
|
|||
|
||||
#[test]
|
||||
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 mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
let _ = *eval(
|
||||
&lex(&highly_inadvisable.to_string()).unwrap(),
|
||||
&mut syms).unwrap();
|
||||
|
|
@ -364,8 +344,7 @@ mod decl_lib_tests {
|
|||
fn test_eval_quote() {
|
||||
let doc = "(eval (quote (add 1 1)))";
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&doc.to_string()).unwrap(), &mut syms).unwrap().to_string(),
|
||||
2.to_string()
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
mod math_lib_tests {
|
||||
use flesh::ast::{eval, lex, Ctr, SymTable};
|
||||
use flesh::stdlib::{dynamic_stdlib, static_stdlib};
|
||||
use flesh::stdlib::static_stdlib;
|
||||
|
||||
#[test]
|
||||
fn test_add_chain() {
|
||||
|
|
@ -8,8 +8,7 @@ mod math_lib_tests {
|
|||
let result = "10";
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -24,8 +23,7 @@ mod math_lib_tests {
|
|||
let result = "10.2";
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -40,8 +38,7 @@ mod math_lib_tests {
|
|||
let result = "24";
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -56,8 +53,7 @@ mod math_lib_tests {
|
|||
let result = "-8.2";
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -72,8 +68,7 @@ mod math_lib_tests {
|
|||
let result = "2";
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -88,8 +83,7 @@ mod math_lib_tests {
|
|||
let result = "10";
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -100,12 +94,11 @@ mod math_lib_tests {
|
|||
|
||||
#[test]
|
||||
fn test_string_to_int() {
|
||||
let document = "(int '10')";
|
||||
let document = "(int \"10\")";
|
||||
let result = "10";
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -120,8 +113,7 @@ mod math_lib_tests {
|
|||
let result = "10";
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -132,76 +124,11 @@ mod math_lib_tests {
|
|||
|
||||
#[test]
|
||||
fn test_string_to_float() {
|
||||
let document = "(float '10.3')";
|
||||
let document = "(float \"10.3\")";
|
||||
let result = "10.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_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);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -212,15 +139,14 @@ mod math_lib_tests {
|
|||
|
||||
#[test]
|
||||
fn test_ii_mod() {
|
||||
let document = "(def test '' (mod 7 3))";
|
||||
let document = "(def test \"\" (mod 7 3))";
|
||||
let check1 = "(car test)";
|
||||
let result1 = "2";
|
||||
let check2 = "(cdr test)";
|
||||
let result2 = "1";
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
let _ = *eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap();
|
||||
assert_eq!(
|
||||
*eval(&lex(&check1.to_string()).unwrap(), &mut syms)
|
||||
|
|
@ -238,13 +164,12 @@ mod math_lib_tests {
|
|||
|
||||
#[test]
|
||||
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 result1 = "2";
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
let _ = *eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap();
|
||||
assert_eq!(
|
||||
*eval(&lex(&check1.to_string()).unwrap(), &mut syms)
|
||||
|
|
@ -256,12 +181,11 @@ mod math_lib_tests {
|
|||
|
||||
#[test]
|
||||
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 result1 = "3";
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
let _ = *eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap();
|
||||
assert_eq!(
|
||||
*eval(&lex(&check1.to_string()).unwrap(), &mut syms)
|
||||
|
|
@ -273,13 +197,12 @@ mod math_lib_tests {
|
|||
|
||||
#[test]
|
||||
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 result1 = "2";
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
let _ = *eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap();
|
||||
assert_eq!(
|
||||
*eval(&lex(&check1.to_string()).unwrap(), &mut syms)
|
||||
|
|
@ -295,8 +218,7 @@ mod math_lib_tests {
|
|||
let result = true;
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -311,8 +233,7 @@ mod math_lib_tests {
|
|||
let result = false;
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -327,8 +248,7 @@ mod math_lib_tests {
|
|||
let result = true;
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -343,8 +263,7 @@ mod math_lib_tests {
|
|||
let result = false;
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -359,8 +278,7 @@ mod math_lib_tests {
|
|||
let result = true;
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -375,8 +293,7 @@ mod math_lib_tests {
|
|||
let result = false;
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -391,8 +308,7 @@ mod math_lib_tests {
|
|||
let result = true;
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -407,8 +323,7 @@ mod math_lib_tests {
|
|||
let result = false;
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -423,8 +338,7 @@ mod math_lib_tests {
|
|||
let result = false;
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -439,8 +353,7 @@ mod math_lib_tests {
|
|||
let result = true;
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -455,8 +368,7 @@ mod math_lib_tests {
|
|||
let result = false;
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -471,8 +383,7 @@ mod math_lib_tests {
|
|||
let result = true;
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -487,8 +398,7 @@ mod math_lib_tests {
|
|||
let result = false;
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -503,8 +413,7 @@ mod math_lib_tests {
|
|||
let result = true;
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -519,8 +428,7 @@ mod math_lib_tests {
|
|||
let result = false;
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -535,8 +443,7 @@ mod math_lib_tests {
|
|||
let result = true;
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -551,8 +458,7 @@ mod math_lib_tests {
|
|||
let result = false;
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -567,8 +473,7 @@ mod math_lib_tests {
|
|||
let result = true;
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -583,8 +488,7 @@ mod math_lib_tests {
|
|||
let result = false;
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -599,8 +503,7 @@ mod math_lib_tests {
|
|||
let result = true;
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
assert_eq!(
|
||||
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
|
||||
.unwrap()
|
||||
|
|
@ -611,13 +514,12 @@ mod math_lib_tests {
|
|||
|
||||
#[test]
|
||||
fn test_inc() {
|
||||
let document = "(def tester '' 1)";
|
||||
let document = "(def tester \"\" 1)";
|
||||
let change = "(inc tester)";
|
||||
let check = "(tester)";
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
|
||||
let doc_tree = lex(&document.to_string()).unwrap();
|
||||
let change_tree = lex(&change.to_string()).unwrap();
|
||||
|
|
@ -650,13 +552,12 @@ mod math_lib_tests {
|
|||
|
||||
#[test]
|
||||
fn test_inc_errors_dont_lose_vars() {
|
||||
let document = "(def tester '' 'oops')";
|
||||
let document = "(def tester \"\" \"oops\")";
|
||||
let change = "(inc tester)";
|
||||
let check = "(tester)";
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
|
||||
let doc_tree = lex(&document.to_string()).unwrap();
|
||||
let change_tree = lex(&change.to_string()).unwrap();
|
||||
|
|
@ -670,7 +571,7 @@ mod math_lib_tests {
|
|||
);
|
||||
let intermediate = *eval(&check_tree, &mut syms).unwrap();
|
||||
if let Ctr::Seg(ref s) = intermediate {
|
||||
assert_eq!(s.to_string(), "('oops')".to_string());
|
||||
assert_eq!(s.to_string(), "(\"oops\")".to_string());
|
||||
} else {
|
||||
eprintln!("did not expect: {}", intermediate);
|
||||
panic!()
|
||||
|
|
@ -683,13 +584,12 @@ mod math_lib_tests {
|
|||
|
||||
#[test]
|
||||
fn test_inc_errors_dont_lose_funcs() {
|
||||
let document = "(def tester '' (oops) oops)";
|
||||
let document = "(def tester \"\" (oops) oops)";
|
||||
let change = "(inc tester)";
|
||||
let check = "(tester '1')";
|
||||
let check = "(tester \"1\")";
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
|
||||
let doc_tree = lex(&document.to_string()).unwrap();
|
||||
let change_tree = lex(&change.to_string()).unwrap();
|
||||
|
|
@ -714,13 +614,12 @@ mod math_lib_tests {
|
|||
|
||||
#[test]
|
||||
fn test_dec() {
|
||||
let document = "(def tester '' 1)";
|
||||
let document = "(def tester \"\" 1)";
|
||||
let change = "(dec tester)";
|
||||
let check = "(tester)";
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
|
||||
let doc_tree = lex(&document.to_string()).unwrap();
|
||||
let change_tree = lex(&change.to_string()).unwrap();
|
||||
|
|
@ -753,13 +652,12 @@ mod math_lib_tests {
|
|||
|
||||
#[test]
|
||||
fn test_dec_errors_dont_lose_vars() {
|
||||
let document = "(def tester '' 'oops')";
|
||||
let document = "(def tester \"\" \"oops\")";
|
||||
let change = "(dec tester)";
|
||||
let check = "(tester)";
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
|
||||
let doc_tree = lex(&document.to_string()).unwrap();
|
||||
let change_tree = lex(&change.to_string()).unwrap();
|
||||
|
|
@ -773,7 +671,7 @@ mod math_lib_tests {
|
|||
);
|
||||
let intermediate = *eval(&check_tree, &mut syms).unwrap();
|
||||
if let Ctr::Seg(ref s) = intermediate {
|
||||
assert_eq!(s.to_string(), "('oops')".to_string());
|
||||
assert_eq!(s.to_string(), "(\"oops\")".to_string());
|
||||
} else {
|
||||
eprintln!("did not expect: {}", intermediate);
|
||||
panic!()
|
||||
|
|
@ -786,13 +684,12 @@ mod math_lib_tests {
|
|||
|
||||
#[test]
|
||||
fn test_dec_errors_dont_lose_funcs() {
|
||||
let document = "(def tester '' (oops) oops)";
|
||||
let document = "(def tester \"\" (oops) oops)";
|
||||
let change = "(dec tester)";
|
||||
let check = "(tester '1')";
|
||||
let check = "(tester \"1\")";
|
||||
|
||||
let mut syms = SymTable::new();
|
||||
static_stdlib(&mut syms);
|
||||
dynamic_stdlib(&mut syms, None);
|
||||
static_stdlib(&mut syms, |_: &String| (), || String::new());
|
||||
|
||||
let doc_tree = lex(&document.to_string()).unwrap();
|
||||
let change_tree = lex(&change.to_string()).unwrap();
|
||||
313
core/tests/test_lib_str.rs
Normal file
313
core/tests/test_lib_str.rs
Normal file
|
|
@ -0,0 +1,313 @@
|
|||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
26
shell/Cargo.toml
Normal file
26
shell/Cargo.toml
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
[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"]
|
||||
|
|
@ -15,9 +15,11 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use crate::segment::{Ctr, Seg, Type};
|
||||
use crate::sym::{SymTable, Symbol, ValueType, Args};
|
||||
use crate::error::{Traceback, start_trace};
|
||||
use flesh::ast::{
|
||||
start_trace, Ctr, Seg, Type, SymTable,
|
||||
Symbol, ValueType, Args, Traceback,
|
||||
};
|
||||
|
||||
use std::io::Write;
|
||||
use std::fs::{File, read_to_string, OpenOptions};
|
||||
use std::rc::Rc;
|
||||
|
|
@ -170,3 +172,81 @@ 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(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -15,17 +15,26 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#[cfg(feature="posix")]
|
||||
mod posix;
|
||||
|
||||
mod run;
|
||||
mod stl;
|
||||
|
||||
use {
|
||||
flesh::{
|
||||
ast::{
|
||||
eval, lex, run,
|
||||
eval, lex,
|
||||
Ctr, Seg, SymTable, Symbol,
|
||||
Traceback,
|
||||
},
|
||||
stdlib::{
|
||||
static_stdlib, dynamic_stdlib, load_defaults,
|
||||
load_environment,
|
||||
CONSOLE_XDIM_VNAME, CONSOLE_YDIM_VNAME, CFG_FILE_VNAME,
|
||||
stdlib::CFG_FILE_VNAME,
|
||||
},
|
||||
crate::{
|
||||
run::run,
|
||||
stl::{
|
||||
load_environment, static_stdlib_overwrites, load_defaults,
|
||||
CONSOLE_XDIM_VNAME, CONSOLE_YDIM_VNAME,
|
||||
L_PROMPT_VNAME, R_PROMPT_VNAME, PROMPT_DELIM_VNAME,
|
||||
},
|
||||
},
|
||||
|
|
@ -51,7 +60,9 @@ use {
|
|||
};
|
||||
|
||||
#[cfg(feature="posix")]
|
||||
use flesh::aux::{ShellState, check_jobs};
|
||||
use crate::stl::dynamic_stdlib;
|
||||
#[cfg(feature="posix")]
|
||||
use posix::{ShellState, check_jobs};
|
||||
#[cfg(feature="posix")]
|
||||
use nix::unistd;
|
||||
|
||||
|
|
@ -94,7 +105,7 @@ impl Prompt for CustomPrompt {
|
|||
};
|
||||
|
||||
Cow::Owned(format!(
|
||||
"({}reverse-search: {}) ",
|
||||
"({}search: {}) ",
|
||||
prefix, history_search.term
|
||||
))
|
||||
}
|
||||
|
|
@ -239,7 +250,7 @@ fn incomplete_brackets(line: &str) -> bool {
|
|||
|
||||
for c in line.chars() {
|
||||
match c {
|
||||
c if ['"', '`', '\''].contains(&c) => {
|
||||
c if ['"', '`'].contains(&c) => {
|
||||
match within_string {
|
||||
Some(w) if c == w => {
|
||||
balance.pop();
|
||||
|
|
@ -300,7 +311,7 @@ fn main() {
|
|||
let mut syms = SymTable::new();
|
||||
load_defaults(&mut syms);
|
||||
load_environment(&mut syms);
|
||||
static_stdlib(&mut syms);
|
||||
static_stdlib_overwrites(&mut syms);
|
||||
|
||||
#[cfg(feature="posix")]
|
||||
let prompt_ss: Rc<RefCell<ShellState>>;
|
||||
|
|
@ -317,22 +328,6 @@ fn main() {
|
|||
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
|
||||
let cfg_file = env::var(CFG_FILE_VNAME).unwrap_or(cfg_file_name);
|
||||
|
|
@ -345,6 +340,19 @@ fn main() {
|
|||
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
|
||||
let completion_menu = Box::new(ColumnarMenu::default().with_name("completion_menu"));
|
||||
let mut keybindings = default_emacs_keybindings();
|
||||
|
|
@ -458,11 +466,9 @@ fn make_prompt(syms: &mut SymTable) -> CustomPrompt {
|
|||
}
|
||||
|
||||
fn make_completer(syms: &mut SymTable) -> CustomCompleter {
|
||||
let mut toks = vec![];
|
||||
for i in syms.keys(){
|
||||
toks.push(i.clone());
|
||||
}
|
||||
CustomCompleter(toks)
|
||||
CustomCompleter(syms.iter()
|
||||
.map(|i| i.0.clone())
|
||||
.collect())
|
||||
}
|
||||
|
||||
fn get_token_to_complete(line: &str, pos: usize) -> (String, bool, usize) {
|
||||
|
|
@ -16,22 +16,14 @@
|
|||
*/
|
||||
|
||||
use {
|
||||
crate::{
|
||||
segment::{
|
||||
Ctr, Seg, Type
|
||||
},
|
||||
sym::{
|
||||
SymTable, ValueType,
|
||||
Symbol, Args,
|
||||
},
|
||||
error::{
|
||||
Traceback, start_trace,
|
||||
},
|
||||
eval::eval,
|
||||
run,
|
||||
flesh::ast::{
|
||||
eval, start_trace,
|
||||
Ctr, Seg, Type, SymTable, ValueType,
|
||||
Symbol, Args, Traceback,
|
||||
},
|
||||
crate::run,
|
||||
libc::{
|
||||
sigaddset, sigemptyset, sigprocmask, exit,
|
||||
sigaddset, sigemptyset, sigprocmask,
|
||||
SIGINT, SIGCHLD, SIGTTOU, SIGTTIN, SIGQUIT, SIGTSTP,
|
||||
SIG_BLOCK, SIG_UNBLOCK
|
||||
},
|
||||
|
|
@ -50,8 +42,7 @@ use {
|
|||
env::set_current_dir,
|
||||
os::unix::process::CommandExt,
|
||||
mem,
|
||||
thread,
|
||||
time::Duration,
|
||||
process,
|
||||
},
|
||||
nix::{
|
||||
unistd, unistd::Pid,
|
||||
|
|
@ -62,7 +53,7 @@ use {
|
|||
},
|
||||
};
|
||||
|
||||
pub const POSIX_LOAD_NAME: &str = "load";
|
||||
pub const POSIX_LOAD_NAME: &str = env!("POSIX_LOAD_NAME");
|
||||
pub const CD_USER_CB: &str = "CFG_FLESH_CD_CB";
|
||||
|
||||
pub struct ShellState {
|
||||
|
|
@ -235,31 +226,25 @@ fn make_foreground(pid: u32, state: &mut ShellState) -> Result<(), String> {
|
|||
|
||||
|
||||
loop {
|
||||
match i.try_wait() {
|
||||
match i.wait() {
|
||||
Ok(maybe) => {
|
||||
if let Some(exit) = maybe {
|
||||
if let Err(e) = unistd::tcsetpgrp(0, state.parent_pid) {
|
||||
return Err(format!(
|
||||
"error re-acquiring terminal: {}!", e
|
||||
));
|
||||
}
|
||||
|
||||
// TODO: this could be more elegant
|
||||
if let Some(ref attr) = state.attr {
|
||||
tcsetattr(0, SetArg::TCSADRAIN, attr).ok();
|
||||
} else {
|
||||
panic!("somehow no attrs")
|
||||
}
|
||||
|
||||
state.last_exit_code = exit
|
||||
.code()
|
||||
.unwrap_or(-1);
|
||||
|
||||
break;
|
||||
} else {
|
||||
// sleep 1 sec
|
||||
thread::sleep(Duration::from_millis(1000));
|
||||
if let Err(e) = unistd::tcsetpgrp(0, state.parent_pid) {
|
||||
return Err(format!(
|
||||
"error re-acquiring terminal: {}!", e
|
||||
));
|
||||
}
|
||||
|
||||
if let Some(ref attr) = state.attr {
|
||||
tcsetattr(0, SetArg::TCSADRAIN, attr).ok();
|
||||
} else {
|
||||
panic!("somehow no attrs")
|
||||
}
|
||||
|
||||
state.last_exit_code = maybe
|
||||
.code()
|
||||
.unwrap_or(-1);
|
||||
|
||||
break;
|
||||
},
|
||||
Err(e) => {
|
||||
return Err(format!("error waiting on child: {}", e))
|
||||
|
|
@ -627,19 +612,6 @@ fn q_callback(_ast: &Seg, _syms: &SymTable, state: &mut ShellState) -> Result<Ct
|
|||
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.
|
||||
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).
|
||||
|
|
@ -788,7 +760,34 @@ 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>>) {
|
||||
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 pgid_res = unistd::getpgid(Some(pid));
|
||||
let sid_res = unistd::getsid(Some(pid));
|
||||
|
|
@ -847,20 +846,6 @@ pub fn load_posix_shell(syms: &mut SymTable, shell_state: Rc<RefCell<ShellState>
|
|||
|
||||
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(
|
||||
String::from(POSIX_LOAD_NAME),
|
||||
Symbol {
|
||||
|
|
@ -995,3 +980,103 @@ 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!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -15,11 +15,10 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use crate::eval::eval;
|
||||
use crate::lex::lex;
|
||||
use crate::error::{Traceback, start_trace};
|
||||
use crate::segment::{Ctr, Seg};
|
||||
use crate::sym::SymTable;
|
||||
use flesh::ast::{
|
||||
eval, lex, start_trace,
|
||||
Ctr, Seg, SymTable, Traceback,
|
||||
};
|
||||
use std::path::Path;
|
||||
use std::fs;
|
||||
use std::iter::FromIterator;
|
||||
|
|
@ -15,24 +15,27 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use crate::segment::{Ctr, Seg, Type};
|
||||
use flesh::{
|
||||
ast::{Ctr, Seg, Type, Args, SymTable, Symbol, ValueType, Traceback},
|
||||
stdlib::{STORE_DOCSTRING, static_stdlib},
|
||||
};
|
||||
|
||||
use crate::run::{run_callback, RUN_DOCSTRING};
|
||||
use crate::sym::{Args, SymTable, Symbol, ValueType};
|
||||
use crate::error::Traceback;
|
||||
|
||||
use std::rc::Rc;
|
||||
use std::cell::RefCell;
|
||||
use std::env::vars;
|
||||
use std::io;
|
||||
|
||||
#[cfg(feature = "posix")]
|
||||
pub mod posix;
|
||||
use crate::posix;
|
||||
|
||||
pub mod append;
|
||||
pub mod boolean;
|
||||
pub mod control;
|
||||
pub mod decl;
|
||||
pub mod math;
|
||||
pub mod strings;
|
||||
pub mod file;
|
||||
#[path = "window.rs"]
|
||||
mod window;
|
||||
#[path = "store.rs"]
|
||||
mod store;
|
||||
#[path = "file.rs"]
|
||||
mod file;
|
||||
|
||||
pub const CONSOLE_XDIM_VNAME: &str = "_FLESH_WIDTH";
|
||||
pub const CONSOLE_YDIM_VNAME: &str = "_FLESH_HEIGHT";
|
||||
|
|
@ -41,10 +44,10 @@ pub const MODENV_CFG_VNAME: &str = "CFG_FLESH_ENV";
|
|||
pub const L_PROMPT_VNAME: &str = "CFG_FLESH_L_PROMPT";
|
||||
pub const R_PROMPT_VNAME: &str = "CFG_FLESH_R_PROMPT";
|
||||
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_WIDTH: i16 = 80;
|
||||
|
||||
|
||||
fn l_prompt_default_callback(_: &Seg, _: &mut SymTable) -> Result<Ctr, Traceback> {
|
||||
Ok(Ctr::String(">".to_string()))
|
||||
}
|
||||
|
|
@ -60,13 +63,18 @@ fn prompt_delimiter_default_callback(_: &Seg, _: &mut SymTable) -> Result<Ctr, T
|
|||
/// static_stdlib
|
||||
/// inserts all stdlib functions that can be inserted without
|
||||
/// any kind of further configuration data into a symtable
|
||||
pub fn static_stdlib(syms: &mut SymTable) {
|
||||
append::add_list_lib(syms);
|
||||
strings::add_string_lib(syms);
|
||||
decl::add_decl_lib_static(syms);
|
||||
control::add_control_lib(syms);
|
||||
boolean::add_bool_lib(syms);
|
||||
math::add_math_lib(syms);
|
||||
pub fn static_stdlib_overwrites(syms: &mut SymTable) {
|
||||
static_stdlib(
|
||||
syms,
|
||||
|arg: &String| print!("{}", arg),
|
||||
|| -> String {
|
||||
let _= io::stdout();
|
||||
let mut input = String::new();
|
||||
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);
|
||||
|
||||
syms.insert(
|
||||
|
|
@ -87,13 +95,26 @@ pub fn static_stdlib(syms: &mut SymTable) {
|
|||
/// callbacks with configuration into a symtable
|
||||
#[cfg(feature="posix")]
|
||||
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
|
||||
.call_symbol(&MODENV_CFG_VNAME.to_string(), &Seg::new(), true)
|
||||
.unwrap_or_else(|_: Traceback| Box::new(Ctr::None))
|
||||
.to_string()
|
||||
.eq("true");
|
||||
decl::add_decl_lib_dynamic(syms, env_cfg_user_form);
|
||||
if 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 {
|
||||
let posix_cfg_user_form = syms
|
||||
|
|
@ -108,27 +129,6 @@ 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) {
|
||||
syms.insert(
|
||||
POSIX_CFG_VNAME.to_string(),
|
||||
|
|
@ -225,3 +225,19 @@ 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()
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
53
shell/src/store.rs
Normal file
53
shell/src/store.rs
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
/* 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)
|
||||
}
|
||||
110
shell/src/window.rs
Normal file
110
shell/src/window.rs
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
/* 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)
|
||||
}
|
||||
|
|
@ -21,9 +21,9 @@
|
|||
;; username, and percentages for all batteries on the system.
|
||||
;; -> requires CFG_FLESH_POSIX=true and userlib.
|
||||
|
||||
(def _batteries 'paths to batteries powering system'
|
||||
(def _batteries "paths to batteries powering system"
|
||||
(lambda ()
|
||||
(let ((power-srcs-dir '/sys/class/power_supply')
|
||||
(let ((power-srcs-dir "/sys/class/power_supply")
|
||||
(power-srcs
|
||||
(split (load-to-string find /sys/class/power_supply -printf "%p ")
|
||||
" "))
|
||||
|
|
@ -39,7 +39,7 @@
|
|||
(set (q pwr-iter) (pop rem))))
|
||||
pwr-list)))
|
||||
|
||||
(def display-batteries 'display battery capacity'
|
||||
(def display-batteries "display battery capacity"
|
||||
(lambda ()
|
||||
(let ((bat-iter (pop (_batteries)))
|
||||
(display ""))
|
||||
|
|
@ -53,14 +53,14 @@
|
|||
(set (q bat-iter) (pop rem))))
|
||||
display)))
|
||||
|
||||
(def CFG_FLESH_R_PROMPT 'display battery info'
|
||||
(def CFG_FLESH_R_PROMPT "display battery info"
|
||||
()
|
||||
(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))
|
||||
(dir-segs (split cdir '/'))
|
||||
(dir-segs (split cdir "/"))
|
||||
(dir-iter (dq dir-segs))
|
||||
(i 0))
|
||||
(if (lte? (len dir-segs) 4)
|
||||
|
|
@ -73,14 +73,14 @@
|
|||
(concat "..." final)))))
|
||||
|
||||
(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"))
|
||||
|
||||
(def git-repo-is-dirty?
|
||||
'returns true or false depending on if current dir is a dirty git repo'
|
||||
() (not (eq? (load-to-string git diff '--stat') "")))
|
||||
"returns true or false depending on if current dir is a dirty git repo"
|
||||
() (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?)
|
||||
(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
|
||||
"[" USER "]" "\t"
|
||||
|
|
|
|||
|
|
@ -52,6 +52,6 @@ Or, a more advanced use:
|
|||
(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 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))))))
|
||||
(eval lam)))
|
||||
|
|
|
|||
20
snippets/interactive-devel-tests.f
Normal file
20
snippets/interactive-devel-tests.f
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
#!/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
|
||||
|
|
@ -18,19 +18,31 @@
|
|||
|
||||
;; INTERACTIVE DEVELOPMENT
|
||||
;; 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
|
||||
;; (def get-sym-deps
|
||||
;; 'gets all global symbols referenced by the definition of a symbol'
|
||||
;; (sym) ;; probably just iter over and call 'set?' on symbols
|
||||
;; ()) ;; then return a stack of symbols
|
||||
;; (def get-tree-deps
|
||||
;; 'Gets all global symbols referenced by the definition of a symbol.
|
||||
;; assumes symbol is passed in quoted. Essentially this function expects
|
||||
;; a tree.'
|
||||
;; (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-sym
|
||||
;; (def dump-tree
|
||||
;; 'writes a symbol to a library file'
|
||||
;; (sym lib) ;; check if lib exists
|
||||
;; ()) ;; write to lib
|
||||
|
||||
;; (def dump-sym-with-deps
|
||||
;; (def dump-tree-with-deps
|
||||
;; 'writes a symbol, with all its dependencies, to a file'
|
||||
;; (sym lib) ;; make a list of sym and all its deps
|
||||
;; ()) ;; iterate over that list and add new elements that are deps of iter
|
||||
|
|
|
|||
|
|
@ -20,44 +20,44 @@
|
|||
;; this file implements unit tests for handwritten userlib functions
|
||||
|
||||
(def passed
|
||||
'prints if a test has passed'
|
||||
"prints if a test has passed"
|
||||
(test)
|
||||
(echo (concat "PASSED: " test)))
|
||||
|
||||
(def failed
|
||||
'prints if a test has failed'
|
||||
"prints if a test has failed"
|
||||
(test)
|
||||
(let (())
|
||||
(echo (concat "FAILED: " test))
|
||||
(exit 1)))
|
||||
|
||||
(def test-cases 'all test cases'
|
||||
(('set updates var'
|
||||
(def test-cases "all test cases"
|
||||
(("set updates var"
|
||||
(quote
|
||||
(let ((test-val 0))
|
||||
(set (q test-val) 3)
|
||||
(eq? test-val 3))))
|
||||
|
||||
('prepend prepends to list'
|
||||
("prepend prepends to list"
|
||||
(quote
|
||||
(let ((list (2 3 4))
|
||||
(list (prepend 1 list))
|
||||
(list-head (pop list)))
|
||||
(eq? (car list-head) 1))))
|
||||
|
||||
('map applies function across list'
|
||||
("map applies function across list"
|
||||
(quote
|
||||
(let ((list (1 2 3))
|
||||
(adder (lambda (x) (add 1 x))))
|
||||
(eq? (map adder list) (2 3 4)))))
|
||||
|
||||
('reduce function adds numbers'
|
||||
("reduce function adds numbers"
|
||||
(quote
|
||||
(let ((list (1 2 3))
|
||||
(adder (lambda (x y) (add x y))))
|
||||
(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
|
||||
(let ((switch-one false)
|
||||
(switch-two false)
|
||||
|
|
@ -68,7 +68,7 @@
|
|||
(true (toggle 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
|
||||
(let ((switch-one false)
|
||||
(switch-two false)
|
||||
|
|
@ -79,27 +79,32 @@
|
|||
(false (toggle 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
|
||||
(let ((variable false))
|
||||
(let ((variable "2"))
|
||||
(set (q variable)
|
||||
(cond (q
|
||||
((true true)))))
|
||||
variable)))
|
||||
((true "1")))))
|
||||
(eq? variable "1"))))
|
||||
|
||||
('contains? finds elem in list'
|
||||
("cond does not choke on nil evaluated result"
|
||||
(quote
|
||||
(cdr ((cond (q ((true (assert true)))))
|
||||
true))))
|
||||
|
||||
("contains? finds elem in list"
|
||||
(quote
|
||||
(contains? (1 2 3) 1)))
|
||||
|
||||
('contains? finds last elem in list'
|
||||
("contains? finds last elem in list"
|
||||
(quote
|
||||
(contains? (1 2 3) 3)))
|
||||
|
||||
('contains? doesnt find elem not in list'
|
||||
("contains? doesnt find elem not in list"
|
||||
(quote
|
||||
(not (contains? (1 2 3) 4))))
|
||||
|
||||
('get-paths properly splits path into segments'
|
||||
("get-paths properly splits path into segments"
|
||||
(quote
|
||||
(let ((PATH "/seg1:/seg2")
|
||||
(split-path (get-paths)))
|
||||
|
|
@ -107,20 +112,20 @@
|
|||
(contains? split-path "/seg1")
|
||||
(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
|
||||
(let ((PATH "/seg1:/seg2")
|
||||
(new-path "/seg3"))
|
||||
(add-path new-path)
|
||||
(contains? (get-paths) new-path))))
|
||||
|
||||
('join operates as expected'
|
||||
("join operates as expected"
|
||||
(quote
|
||||
(let ((l ("1" 2 (3))))
|
||||
(eq? (join l ".")
|
||||
"1.2.(3)"))))
|
||||
|
||||
('extend extends sets'
|
||||
("extend extends sets"
|
||||
(quote
|
||||
(let ((s1 (1 2 3))
|
||||
(s2 (4 5 6)))
|
||||
|
|
|
|||
|
|
@ -36,13 +36,13 @@
|
|||
;; this would be way faster as code in stl
|
||||
;; but stl already suffers scope creep
|
||||
(def prepend
|
||||
'takes a list and appends an element to the back of it.
|
||||
returns prepended list'
|
||||
"takes a list and appends an element to the back of it.
|
||||
returns prepended list"
|
||||
(elem list)
|
||||
(reverse (cons (reverse list) elem)))
|
||||
|
||||
(def set
|
||||
'sets an existing variable without touching its docstring.
|
||||
"sets an existing variable without touching its docstring.
|
||||
|
||||
WARNING:
|
||||
If you find yourself struggling to debug a complex error in state access,
|
||||
|
|
@ -56,89 +56,90 @@
|
|||
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.
|
||||
|
||||
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)
|
||||
(let ((doc (get-doc var)))
|
||||
(def (eval var) doc val)))
|
||||
|
||||
(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
|
||||
result is added to a new list. Returns the new list.'
|
||||
result is added to a new list. Returns the new list."
|
||||
(func list)
|
||||
(let ((list-iter (pop list))
|
||||
(result ()))
|
||||
(while (gt? (len list-iter) 1)
|
||||
(let ((current (car list-iter))
|
||||
(remaining (cdr list-iter))
|
||||
(let ((current (car list-iter))
|
||||
(remaining (cdr list-iter))
|
||||
(current-res (func current)))
|
||||
(set (q result) (cons result current-res))
|
||||
(set (q result) (cons result current-res))
|
||||
(set (q list-iter) (pop remaining))))
|
||||
result))
|
||||
|
||||
(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 current list item
|
||||
* the previous result
|
||||
Initially the function will take element1 and element2, outputting result1.
|
||||
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)
|
||||
(let ((list-iter (pop list))
|
||||
(result (car list-iter)))
|
||||
(result (car list-iter)))
|
||||
(set (q list-iter) (pop (cdr list-iter)))
|
||||
(if (lt? (len list) 2)
|
||||
(echo "list too short!")
|
||||
(while (gt? (len list-iter) 1)
|
||||
(let ((current (car list-iter))
|
||||
(let ((current (car list-iter))
|
||||
(remaining (cdr list-iter)))
|
||||
(set (q result) (func result current))
|
||||
(set (q result) (func result current))
|
||||
(set (q list-iter) (pop remaining)))))
|
||||
result))
|
||||
|
||||
(def cond
|
||||
'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 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.'
|
||||
"Takes one argument: a list of pairs consisting of a form containing 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."
|
||||
(list)
|
||||
(let ((iter list))
|
||||
(while (gt? (len iter) 0)
|
||||
(let ((current (car iter))
|
||||
(remaining (pop iter)))
|
||||
(if (eval (car current))
|
||||
(car (pop ((set (q iter) ())
|
||||
(eval (cdr current)))))
|
||||
(set (q iter) (cdr remaining)))))))
|
||||
(let ((iter list))
|
||||
(while (gt? (len iter) 0)
|
||||
(let ((current (car iter))
|
||||
(remaining (pop iter)))
|
||||
(if (eval (car current))
|
||||
(let ((res (eval (cdr current))))
|
||||
(set (q iter) ())
|
||||
res)
|
||||
(set (q iter) (cdr remaining)))))))
|
||||
|
||||
|
||||
(def contains?
|
||||
'Takes two arguments: a list and an element.
|
||||
Returns true if the list contains the element.'
|
||||
"Takes two arguments: a list and an element.
|
||||
Returns true if the list contains the element."
|
||||
(list elem)
|
||||
(let ((found false)
|
||||
(let ((found false)
|
||||
(list-iter (pop list)))
|
||||
(while (and (gt? (len list-iter) 1)
|
||||
(not found))
|
||||
(let ((current (car list-iter))
|
||||
(let ((current (car list-iter))
|
||||
(remaining (cdr list-iter)))
|
||||
(if (eq? current elem)
|
||||
(set (q found) true)
|
||||
(set (q found) true)
|
||||
(set (q list-iter) (pop remaining)))))
|
||||
found))
|
||||
|
||||
(def get-paths
|
||||
'returns each individual directory in PATH'
|
||||
() (split PATH ':'))
|
||||
"returns each individual directory in PATH"
|
||||
() (split PATH ":"))
|
||||
|
||||
(def add-path
|
||||
'Takes one argument.
|
||||
adds a directory to PATH'
|
||||
"Takes one argument.
|
||||
adds a directory to PATH"
|
||||
(path) (set (q PATH)
|
||||
(concat PATH ':' path)))
|
||||
(concat PATH ":" path)))
|
||||
|
||||
(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))))
|
||||
(while (gt? (len path-iter) 1)
|
||||
|
|
@ -148,17 +149,17 @@ adds a directory to PATH'
|
|||
(set (q path-iter) (pop rem))))))
|
||||
|
||||
(def join
|
||||
'concatenates list elements into a string, while
|
||||
interspersing a provided delimiter in between elements'
|
||||
"concatenates list elements into a string, while
|
||||
interspersing a provided delimiter in between elements"
|
||||
(list delim)
|
||||
(reduce (lambda (res elem)
|
||||
(if (eq? (strlen res) 0)
|
||||
(string elem)
|
||||
(string elem)
|
||||
(concat res delim elem)))
|
||||
list))
|
||||
|
||||
(def extend
|
||||
'adds all elements in set2 to set1'
|
||||
"adds all elements in set2 to set1"
|
||||
(set1 set2)
|
||||
(reduce (lambda (res elem) (cons res elem))
|
||||
(prepend set1 set2)))
|
||||
|
|
|
|||
|
|
@ -1,80 +0,0 @@
|
|||
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(),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,103 +0,0 @@
|
|||
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!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,229 +0,0 @@
|
|||
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(),
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue