flesh/Readme.org

268 lines
12 KiB
Org Mode
Raw Normal View History

#+Title: Relish: Rusty Expressive LIsp SHell
#+Author: Ava Hahn
2023-03-07 14:29:31 -08:00
Note: this document is best read using a dedicated ORG mode editor
* Purpose statement
2023-03-07 14:29:31 -08:00
The purpose of Relish is to create a highly portable, easy to integrate language that can be used in many environments.
* Goals
- Iterate on the ideas and designs that were tested with SHS
https://gitlab.com/whom/shs
2023-03-07 14:29:31 -08:00
- Create a usable POSIX shell
- Create usable applications/scripts
- To have quality code coverage
- No unsafe code
** Stretch Goals
- Create an interpreter that can be booted on one or more SOCs
- Create an interpreter that can be embedded in another application
- Create UI bindings
* Contact
- Matrix chat: #vomitorium:matrix.sunnypup.io
https://matrix.to/#/#vomitorium:matrix.sunnypup.io
* How to use
** TODO Syntax
*** TODO Basic data types
*** TODO S-Expressions
**** TODO calling a function
*** TODO Control flow
**** TODO if
**** TODO while
**** TODO let
**** TODO circuit
*** TODO Defining variables and functions
**** TODO Anatomy
**** TODO Naming conventions
**** TODO Undefining variables and functions
** Easy patterns
This section can serve as a sort of cookbook for a user who is new to leveraging LISP languages or unsure of where to start with ~relish~.
More ideas may be explored in the file:snippets directory of this project.
The author encourages any users to contribute their own personal favorites not already in this section either by adding them to the file:snippets folder, or to extend the documentation here.
*** while-let combo
#+BEGIN_SRC lisp
;; myiter = (1 (2 3 4 5 6))
(def myiter 'iterator over a list' (head (1 2 3 4 5 6)))
;; iterate over each element in mylist
(while (gt? (len (cdr myiter)) 0) ;; while there are more elements to consume
(let ((elem (car myiter)) ;; elem = consumed element from myiter
(remaining (cdr myiter))) ;; remaining = rest of elements
(echo elem) ;; do a thing with the element, could be any operation
(def myiter (head remaining)))) ;; consume next element, loop
#+END_SRC
The while-let pattern can be used for many purposes. Above it is used to iterate over elements in a list. It can also be used to receive connections to a socket and write data to them.
*** TODO main loop application
- state switch (while-toggle)
- state calculation
*** TODO short-circuit guard
- circuit example
- while-not-circuit-do-more-work
*** let destructuring
~let~ is very useful for destructuring complex return types. If you have a function that may return a whole list of values you can then call it from ~let~ to consume the result data.
In this example a let form is used to destructure a call to ~head~. ~head~ returns a list consisting of ~(first-element rest-of-list)~ (for more information see ~(help head)~).
The ~let~ form starts with the output of ~head~ stored in ~head-struct~ (short for head-structured). The next variables defined are ~first~ and ~rest~ which contain individual elements from the return of the call to ~head~.
Finally, the bodies evaluated in the ~let~ form are able to operate on the head and the rest.
#+BEGIN_SRC lisp
;; individually access the top of a list
(let ((head-struct (head (1 2 3))
(first (car head-struct))
(rest (cdr head-struct)))
(echo "this is 1: " first)
(echo "this is 2, 3: " rest))
#+END_SRC
*** if-set?
One common pattern seen in bash scripts and makefiles is the set-variable-if-not-set pattern.
#+BEGIN_SRC shell
MYVAR ?= MY_SPECIAL_VALUE
#+END_SRC
Translated, can be seen below
#+BEGIN_SRC lisp
(if (set? myvar)
() ;; no need to do anything... or add a call here
(def myvar "MY_SPECIAL_VALUE"))
#+END_SRC
Alternatively this combination can be used to process flags in a script or application:
#+BEGIN_SRC lisp
(if (set? myflag)
(process-flag myflag)
())
#+END_SRC
** TODO Builtin functions
*** TODO Env function
** TODO Documentation
*** TODO Tests
*** TODO Help function
*** TODO Snippets directory
** Configuration
By default Relish will read from ~/.relishrc for configuration, but the default shell will also accept a filename from the RELISH_CFG_FILE environment variable.
See file:snippets/basic_minimal_configuration.rls for an example of a basic minimal configuration file.
*** The configuration file
The configuration file is a script containing arbitrary Relish code.
On start, any shell which leverages the configuration code in the config module (file:src/config.rs) will create a clean seperate context, including default configuration values, within which the standard library will be initialized.
The configuration file is evaluated and run as a standalone script and may include arbitrary executable code. Afterwards, configuration values found in the variable map will be used to configure the standard library function mappings that the shell will use.
Errors during configuration are non-terminal. In such a case any defaults which have not been overwritten will remain present.
**** Important points to note
- When the configuration file is run, it will be run with default configuration values.
- The user/script interpreter will be run with the standard library configured to use the previously defined configuration variables.
- The standard library will then be re-processed and re-added to the symbol table with new configuration.
- Variables and functions defined during configuration will carry over to the user/script interpreter, allowing the user to load any number of custom functions and variables.
*** Configuration Values
- CFG_RELISH_POSIX (default 0): when on, enables POSIX style job control.
- CFG_RELISH_ENV (default 1): when on, interpreter's variable table and environment variable table are kept in sync.
- CFG_RELISH_PROMPT (default (echo "λ ")): A *function* definition which is called in order to output the prompt for each loop of the REPL.
This function will be reloaded each REPL loop and will be called by the interpreter with no arguments.
** TODO Further configuration
** Compilation
#+BEGIN_SRC sh
cargo build
#+END_SRC
** Testing
#+BEGIN_SRC sh
cargo test
#+END_SRC
** Running (the main shell)
#+BEGIN_SRC sh
cargo run src/bin/main.rs
#+END_SRC
* Guide to codebase
** file:tests directory
Start here if you are new.
Most of these files have unimplemented tests commented out in them.
Contributions that help fill out all of these tests
*** Eval tests: file:tests/test_eval.rs
These are particularly easy to read and write tests.
*** Func tests: file:tests/test_func.rs
You can consider these to extend the eval tests to cover the co-recursive nature between eval and func calls.
*** Lex tests: file:tests/test_lex.rs
These tests verify the handling of syntax.
*** Lib tests: (tests/test_lib*)
These tests are unique per stdlib module.
** file:src directory
This directory contains all of the user facing code in relish.
Just a few entries of note:
*** segment: file:src/segment.rs
This file lays out the +spiritual+ +theological+ +ideological+ +theoretical+ mechanical underpinnings of the entire interpreter.
The entire LISP machine centers around a singlet or pairing of datum.
The ~Ctr~ datatype provides an abstraction for which any type of data, including a ~Seg~ can be a datum.
The ~Seg~ datatype provides a mechanism to hold a single datum or a pair of datum. It is implemented as two ~Ctr~s: ~car~ and ~cdr~.
A primitive type system is provided through the Rust Enum datatype. A number of utility functions follow.
*** lib: file:src/lib.rs
This defines a library that can be included to provide an interpreter interface within any Rust project.
It includes the core interpreter mechanisms, full stdlib, and the configuration system.
Your project can use or not use any number of these components. They can certainly be used to support language development for other LISP machines,
or even other languages.
*** config: file:src/config.rs
This file contains default configuration values as well as functions which load and run the configuration file script.
For more information see the configuraiton section above in this Readme.
*** stl: file:src/stl.rs
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 ~dyanmic_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.
For more information see file:src/config.
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 ~get_stdlib~ function).
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 small minimal subet of what it is.
You can view the code for standard library functions in file:src/stl/.
*** bin: file:src/bin/
This contains any executable target of this project. Notably the main shell file:src/bin/main.rs.
* 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.
*** TODO Eval function
*** TODO Input function
*** TODO Load (load a script) function
Pull/Refactor the logic out of the configure functions.
Optionally return a list of new variables and/or functions?
Will need a concatenate function for tables
*** TODO Main shell calls Load function on arg and exits
*** TODO Can enter multiple lines of text, with formatting in repl
*** TODO arithmetic operations
2023-03-07 13:43:51 -08:00
**** DONE typecast (int)
**** DONE typecast (float)
**** DONE add
**** DONE sub
**** DONE div
**** DONE mul
**** DONE exp
**** DONE mod
2023-03-07 21:31:54 -08:00
***** DONE Test for Let Destructuring
**** TODO inc
**** TODO dec
**** TODO gt?
**** TODO lt?
**** TODO snippets for gte and lte
*** TODO string operations
**** TODO typecast (string)
**** TODO contains
**** TODO len
**** TODO concat
**** TODO substr by index
**** TODO split (on delimiter)
**** TODO strcons (sprintf but its all string tokens under the hood)
*** TODO Shell module
**** TODO Support for single quoting string literals
**** TODO Args parsing helper
**** TODO Process launching with environment variables
**** TODO Optional form of process which allows fd redirecting
**** TODO Foreground process TTY
**** TODO Background processes
**** TODO Update config env var docstring with functions loaded or unloaded
*** TODO list operations
**** DONE append
**** TODO head (returns (head rest))
2023-03-07 14:29:31 -08:00
***** TODO let destructuring test
**** TODO tail (returns (rest tail))
2023-03-07 14:29:31 -08:00
***** TODO let destructuring test
**** TODO queue (append to front)
**** TODO snippet for dequeue
**** TODO snippet for pop
2023-03-07 14:29:31 -08:00
**** TODO front (returns copy of first elem)
***** TODO normal positive test
***** TODO test for err case on empty list
**** TODO back (returns copy of last elem)
***** TODO normal positive test
***** TODO test for err case on empty list
**** TODO list len
*** TODO lambda
2023-03-07 14:29:31 -08:00
*** TODO bool cast
*** TODO file operations
**** TODO read-to-string
**** TODO write-to-file
*** TODO Rename to Flesh
*** TODO Pick through core/ast and make sure no unnessesary moves are happening
*** TODO Create a dedicated community channel on matrix.sunnypup.io
*** TODO Post to relevant channels
*** TODO Network library
**** TODO HTTP Client
**** TODO TCP Stream client
**** TODO UDP Client
**** TODO TCP Listener
**** TODO HTTP Listener
**** TODO UDP Listener
*** TODO Custom ast pretty print