Feat: documentation, UI/UX
This commit is contained in:
22
crates/forth/README.md
Normal file
22
crates/forth/README.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# cagire-forth
|
||||
|
||||
Stack-based Forth VM for the Cagire sequencer. Tokenizes, compiles, and executes step scripts to produce audio and MIDI commands.
|
||||
|
||||
## Modules
|
||||
|
||||
| Module | Description |
|
||||
|--------|-------------|
|
||||
| `vm` | Interpreter loop, `Forth::evaluate()` entry point |
|
||||
| `compiler` | Tokenization (with source spans) and single-pass compilation to ops |
|
||||
| `ops` | `Op` enum (~90 variants) |
|
||||
| `types` | `Value`, `StepContext`, shared state types |
|
||||
| `words/` | Built-in word definitions: `core`, `sound`, `music`, `midi`, `effects`, `sequencing`, `compile` |
|
||||
| `theory/` | Music theory lookups: `scales` (~200 patterns), `chords` (interval arrays) |
|
||||
|
||||
## Key Types
|
||||
|
||||
- **`Forth`** — VM instance, holds stacks and compilation state
|
||||
- **`Value`** — Stack value (int, float, string, list, quotation, ...)
|
||||
- **`StepContext`** — Per-step evaluation context (step index, tempo, variables, ...)
|
||||
- **`Op`** — Compiled operation; nondeterministic variants carry `Option<SourceSpan>` for tracing
|
||||
- **`ExecutionTrace`** — Records executed/selected spans and resolved values during evaluation
|
||||
@@ -65,6 +65,7 @@ pub enum Op {
|
||||
NewCmd,
|
||||
SetParam(&'static str),
|
||||
Emit,
|
||||
Print,
|
||||
Get,
|
||||
Set,
|
||||
SetKeep,
|
||||
|
||||
@@ -328,6 +328,16 @@ impl Forth {
|
||||
Op::Drop => {
|
||||
pop(stack)?;
|
||||
}
|
||||
Op::Print => {
|
||||
let val = pop(stack)?;
|
||||
let text = match &val {
|
||||
Value::Int(n, _) => n.to_string(),
|
||||
Value::Float(f, _) => format!("{f}"),
|
||||
Value::Str(s, _) => s.to_string(),
|
||||
_ => format!("{val:?}"),
|
||||
};
|
||||
outputs.push(format!("print:{text}"));
|
||||
}
|
||||
Op::Swap => {
|
||||
ensure(stack, 2)?;
|
||||
let len = stack.len();
|
||||
@@ -558,9 +568,12 @@ impl Forth {
|
||||
|
||||
Op::NewCmd => {
|
||||
ensure(stack, 1)?;
|
||||
let values = std::mem::take(stack);
|
||||
let values = drain_skip_quotations(stack);
|
||||
if values.is_empty() {
|
||||
return Err("expected sound name".into());
|
||||
}
|
||||
let val = if values.len() == 1 {
|
||||
values.into_iter().next().expect("single value after len check")
|
||||
values.into_iter().next().unwrap()
|
||||
} else {
|
||||
Value::CycleList(Arc::from(values))
|
||||
};
|
||||
@@ -568,9 +581,12 @@ impl Forth {
|
||||
}
|
||||
Op::SetParam(param) => {
|
||||
ensure(stack, 1)?;
|
||||
let values = std::mem::take(stack);
|
||||
let values = drain_skip_quotations(stack);
|
||||
if values.is_empty() {
|
||||
return Err("expected parameter value".into());
|
||||
}
|
||||
let val = if values.len() == 1 {
|
||||
values.into_iter().next().expect("single value after len check")
|
||||
values.into_iter().next().unwrap()
|
||||
} else {
|
||||
Value::CycleList(Arc::from(values))
|
||||
};
|
||||
@@ -1866,6 +1882,21 @@ fn pop_bool(stack: &mut Vec<Value>) -> Result<bool, String> {
|
||||
Ok(pop(stack)?.is_truthy())
|
||||
}
|
||||
|
||||
/// Drain the stack, returning non-quotation values.
|
||||
/// Quotations are pushed back onto the stack (transparent).
|
||||
fn drain_skip_quotations(stack: &mut Vec<Value>) -> Vec<Value> {
|
||||
let values = std::mem::take(stack);
|
||||
let mut result = Vec::new();
|
||||
for v in values {
|
||||
if matches!(v, Value::Quotation(..)) {
|
||||
stack.push(v);
|
||||
} else {
|
||||
result.push(v);
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
fn ensure(stack: &[Value], n: usize) -> Result<(), String> {
|
||||
if stack.len() < n {
|
||||
return Err("stack underflow".into());
|
||||
|
||||
@@ -13,6 +13,7 @@ pub(super) fn simple_op(name: &str) -> Option<Op> {
|
||||
"dup" => Op::Dup,
|
||||
"dupn" => Op::Dupn,
|
||||
"drop" => Op::Drop,
|
||||
"print" => Op::Print,
|
||||
"swap" => Op::Swap,
|
||||
"over" => Op::Over,
|
||||
"rot" => Op::Rot,
|
||||
|
||||
@@ -33,6 +33,16 @@ pub(super) const WORDS: &[Word] = &[
|
||||
compile: Simple,
|
||||
varargs: false,
|
||||
},
|
||||
Word {
|
||||
name: "print",
|
||||
aliases: &[],
|
||||
category: "Stack",
|
||||
stack: "(x --)",
|
||||
desc: "Print top of stack to footer bar",
|
||||
example: "42 print",
|
||||
compile: Simple,
|
||||
varargs: false,
|
||||
},
|
||||
Word {
|
||||
name: "swap",
|
||||
aliases: &[],
|
||||
|
||||
Reference in New Issue
Block a user