This is a Rust based language interpreter inspired by FORTH. It's main features are that it's easily extensible and that it is no_std friendly only requiering global allocator.
Push value to the stack:
12 // int
" hello " // string
12.0 // double
12.0f // float
2l // long
0x23 // byte
" hello world " // Note. This will be interpreted as " hello world ". This is the limitation of the interpreters architecture
Special characters:
<ASCII character number as byte> emit // Emits the number as a character
// For convenience purposes
nl // Writes a newline to the output
Math funcitions:
// Currently available math operations
+ - * /
// Exmaple
12 24 + . // 36 Ok.
Logic operators and comparisons:
and // logic and
or // logic or
not // logic not
== // equal
!= // not equal
> // higher
< // Lower
-1 // true
0 // false
Stack manipulation functions:
. // Pops and prints
peek // Just prints
dup // duplicates top of the stack
2dup // duplicates two highest values on the stack
drop // Drops top of the stack
swap // Swaps two highest values on the stack
rot // Rotates 3 top values on the stack example [ 3 2 1 ] -> [ 1 3 2 ]
Custom words:
: // start defining
<name>
... // here put the contents of the word
; // end defining
If statements (only in custom words):
if ... else ... then
^ execute if true
^ execute if false
^ pop top of the stack and see if true
For statements (only in custom words):
<end> <start> for ... next // increment by 1 until >= than <end>
<end> <start> for ... <increment> bynext // increment by <increment> until >= than <end>
While statement (only in custom words):
while <condition> do ... again (you can ommit "<condition> do" if you want a never ending loop)
Variables:
let <variable name> // create a variable
@<variable name> // get variable address
<variable addr> <val> push // push <val> to varable
<variable addr> pop // pop value from variable and put it on the main stack
<variable addr> <addr> get // get value from variable table at position <addr>
<variable addr> <addr> <val> set // set value of variable table at position <addr> to <val> if position exists
<varible addr> len // get lenght of the variable internal stack
Type converion:
// Convert top of the stack to the respectable value
to_int
to_long
to_float
to_double
to_byte
to_str
Input:
" This message will be displayed as a question/request for the input to the user " input // pushes user input to the stack
Silent mode:
<state either -1/0 > silent // turn the "Ok." messages on or off
Comments:
( this is a comment )
: fib
0
for
dup
rot
+
peek
next
;
0l 1l 10 fib
This can be executed with:
cargo run --example file_exec fibonacci
Add this to Cargo.toml
[dependencies]
sorth = "0.2"
use sorth::prelude::*;
fn main() {
let mut engine = Engine::new();
let std_words = Standard::new();
engine.import_word_list(std_words);
while engine.running {
let mut line = String::new();
std::io::stdin().read_line(&mut line).unwrap();
match engine.eval(line) {
Ok(ok) => println!("{}", ok.trim()),
Err(err) => println!("Error: {}", err.trim()),
}
}
}
Run with:
cargo run --examples terminal
To run the example use:
cargo run --example file_exec <Sorth program example>
Currently available Sorth program examples:
fibonacci
turing
For example
cargo run --example file_exec turing
If you would like to extend the wordset with additional functionality from your code it is very possible. It is done in exactly the same manner as including the standard wordset. You will need to create a struct that implements the WordList
trait. This trait will allow you to import the wordset into sorth engine. Here is a example of such struct
sturct standard {
words: Vec<Word>,
}
The word type is essentialy a tuple with two main elements. Element no. 1 is a function that tell the engine when will the word be executed. Whereas the second element is a function that tells the engine what to do exactly. Here is the definition of the word and a example of usage.
// Taken from word.rs
pub type WordSymbol = fn(s: &Engine) -> bool;
pub type WordDefinition = fn(s: &mut Engine) -> Result<String, String>;
pub type Word = (WordSymbol, WordDefinition);
// Taken from standard/mod.rs
(|s| s.get_curr_word() == "let" && s.mode_normal(), let_word),
// Remember to specify this ^^^^^^^^^^^^^^^ Important!!!
The normal mode is the only mode that you want to do enything in if you don't want to break the standard wordset!!!
If you want to use sorth in the no_std
ecosystem, then the only that you need to provide is a global allocator
here is a example on how to do it.
- Generic math ops
- Generic logic ops
- Generic stack ops
- Runtime word definition
- Conditional logic
- Loops
- String support
- Int, Long and Byte support
- Float and Double support
- Variables support
- Extended math ops
- File access wordset standard
- Sorth user input interface
- Rework sorth into library crate
- Share on crates.io
- Create a Book
Sorth is distributed under MIT licence. See LICENSE for details.