Feat: refactoring codebase
This commit is contained in:
250
docs/forth/oddities.md
Normal file
250
docs/forth/oddities.md
Normal file
@@ -0,0 +1,250 @@
|
||||
# Oddities
|
||||
|
||||
Cagire's Forth is not a classic Forth. It borrows the core ideas (stack-based evaluation, postfix notation, word definitions) but adds modern features and domain-specific extensions. If you know traditional Forth, here are the differences.
|
||||
|
||||
## Comments
|
||||
|
||||
Classic Forth uses parentheses for comments:
|
||||
|
||||
```forth
|
||||
( this is a comment )
|
||||
```
|
||||
|
||||
Cagire uses double semicolons:
|
||||
|
||||
```forth
|
||||
;; this is a comment
|
||||
```
|
||||
|
||||
Everything after `;;` until the end of the line is ignored.
|
||||
|
||||
## Quotations
|
||||
|
||||
Classic Forth has no quotations. Code is not a value you can pass around.
|
||||
|
||||
Cagire has first-class quotations using curly braces:
|
||||
|
||||
```forth
|
||||
{ dup + }
|
||||
```
|
||||
|
||||
This pushes a block of code onto the stack. You can store it, pass it to other words, and execute it later. Quotations enable conditionals, probability, and cycling.
|
||||
|
||||
## Conditionals
|
||||
|
||||
Classic Forth uses `IF ... ELSE ... THEN`:
|
||||
|
||||
```forth
|
||||
x 0 > IF 1 ELSE -1 THEN
|
||||
```
|
||||
|
||||
Cagire supports this syntax but also provides quotation-based conditionals:
|
||||
|
||||
```forth
|
||||
{ 1 } { -1 } x 0 > ifelse
|
||||
```
|
||||
|
||||
The words `?` and `!?` execute a quotation based on a condition:
|
||||
|
||||
```forth
|
||||
{ "kick" s . } coin ? ;; execute if coin is 1
|
||||
{ "snare" s . } coin !? ;; execute if coin is 0
|
||||
```
|
||||
|
||||
## Strings
|
||||
|
||||
Classic Forth has limited string support. You print strings with `."`:
|
||||
|
||||
```forth
|
||||
." Hello World"
|
||||
```
|
||||
|
||||
Cagire has first-class strings:
|
||||
|
||||
```forth
|
||||
"hello"
|
||||
```
|
||||
|
||||
This pushes a string value onto the stack. Strings are used for sound names, sample names, and variable keys. You often do not need quotes at all. Any unrecognized word becomes a string automatically:
|
||||
|
||||
```forth
|
||||
kick s . ;; "kick" is not a word, so it becomes the string "kick"
|
||||
myweirdname ;; pushes "myweirdname" onto the stack
|
||||
```
|
||||
|
||||
This makes scripts cleaner. You only need quotes when the string contains spaces or conflicts with a real word.
|
||||
|
||||
## Variables
|
||||
|
||||
Classic Forth declares variables explicitly:
|
||||
|
||||
```forth
|
||||
VARIABLE x
|
||||
10 x !
|
||||
x @
|
||||
```
|
||||
|
||||
Cagire uses prefix syntax:
|
||||
|
||||
```forth
|
||||
10 !x ;; store 10 in x
|
||||
@x ;; fetch x (returns 0 if undefined)
|
||||
10 ,x ;; store 10 in x, keep on stack
|
||||
```
|
||||
|
||||
No declaration needed. Variables spring into existence when you store to them. `,x` stores and keeps the value on the stack.
|
||||
|
||||
## Floating Point
|
||||
|
||||
Classic Forth (in its original form) has no floating point. Numbers are integers. Floating point was added later as an optional extension with separate words. Cagire has native floating point:
|
||||
|
||||
```forth
|
||||
3.14159
|
||||
0.5 0.3 + ;; 0.8
|
||||
```
|
||||
|
||||
Integers and floats mix freely. Division always produces a float.
|
||||
|
||||
## Loops
|
||||
|
||||
Classic Forth has `DO ... LOOP`:
|
||||
|
||||
```forth
|
||||
10 0 DO I . LOOP
|
||||
```
|
||||
|
||||
Cagire uses a quotation-based loop with `times`:
|
||||
|
||||
```forth
|
||||
4 { @i . } times ;; prints 0 1 2 3
|
||||
```
|
||||
|
||||
The loop counter is stored in the variable `i`, accessed with `@i`. This fits Cagire's style where control flow uses quotations.
|
||||
|
||||
```forth
|
||||
4 { @i 4 / at hat s . } times ;; hat at 0, 0.25, 0.5, 0.75
|
||||
4 { c4 @i + note sine s . } times ;; ascending notes
|
||||
```
|
||||
|
||||
For generating sequences without side effects, use `..` or `gen`:
|
||||
|
||||
```forth
|
||||
1 5 .. ;; pushes 1 2 3 4 5
|
||||
{ dup * } 4 gen ;; pushes 0 1 4 9 (squares)
|
||||
```
|
||||
|
||||
## The Command Register
|
||||
|
||||
This is completely unique to Cagire. Traditional Forth programs print text. Cagire programs build sound commands.
|
||||
|
||||
The command register accumulates a sound name and parameters:
|
||||
|
||||
```forth
|
||||
"sine" sound ;; set sound
|
||||
440 freq ;; add parameter
|
||||
0.5 gain ;; add parameter
|
||||
. ;; emit and clear
|
||||
```
|
||||
|
||||
Nothing is sent to the audio engine until you emit with `.`. This is unlike any classic Forth.
|
||||
|
||||
## Context Words
|
||||
|
||||
Cagire provides words that read the current sequencer state:
|
||||
|
||||
```forth
|
||||
step ;; current step index (0-127)
|
||||
beat ;; current beat position
|
||||
iter ;; pattern iteration count
|
||||
tempo ;; current BPM
|
||||
phase ;; position in bar (0-1)
|
||||
```
|
||||
|
||||
These have no equivalent in classic Forth. They connect your script to the sequencer's timeline.
|
||||
|
||||
## Probability
|
||||
|
||||
Classic Forth is deterministic. Cagire has built-in randomness:
|
||||
|
||||
```forth
|
||||
{ "snare" s . } 50 prob ;; 50% chance
|
||||
{ "clap" s . } 0.25 chance ;; 25% chance
|
||||
{ "hat" s . } often ;; 75% chance
|
||||
{ "rim" s . } sometimes ;; 50% chance
|
||||
{ "tom" s . } rarely ;; 25% chance
|
||||
```
|
||||
|
||||
These words take a quotation and execute it probabilistically.
|
||||
|
||||
## Periodic Execution
|
||||
|
||||
Execute a quotation on specific iterations:
|
||||
|
||||
```forth
|
||||
{ "snare" s . } 4 every ;; every 4th pattern iteration
|
||||
{ "hat" s . } 3 8 bjork ;; Euclidean: 3 hits across 8 step runs
|
||||
{ "hat" s . } 5 8 pbjork ;; Euclidean: 5 hits across 8 pattern iterations
|
||||
```
|
||||
|
||||
`every` checks the pattern iteration count. On iteration 0, 4, 8, 12... the quotation runs. On all other iterations it is skipped.
|
||||
|
||||
`bjork` and `pbjork` use Bjorklund's algorithm to distribute k hits as evenly as possible across n positions. `bjork` counts by step runs, `pbjork` counts by pattern iterations. Classic Euclidean rhythms: tresillo (3,8), cinquillo (5,8), son clave (5,16).
|
||||
|
||||
## Cycling
|
||||
|
||||
Cagire has built-in support for cycling through values. Push values onto the stack, then select one based on pattern state:
|
||||
|
||||
```forth
|
||||
60 64 67 3 cycle note
|
||||
```
|
||||
|
||||
Each time the step runs, a different note is selected. The `3` tells `cycle` how many values to pick from.
|
||||
|
||||
You can also use quotations if you need to execute code:
|
||||
|
||||
```forth
|
||||
{ c4 note } { e4 note } { g4 note } 3 cycle
|
||||
```
|
||||
|
||||
When the selected value is a quotation, it gets executed. When it is a plain value, it gets pushed onto the stack.
|
||||
|
||||
Two cycling words exist:
|
||||
|
||||
- `cycle` - selects based on `runs` (how many times this step has played)
|
||||
- `pcycle` - selects based on `iter` (how many times the pattern has looped)
|
||||
|
||||
The difference between `cycle` and `pcycle` matters when patterns have different lengths. `cycle` counts per-step, `pcycle` counts per-pattern.
|
||||
|
||||
## Polyphonic Parameters
|
||||
|
||||
Parameter words like `note`, `freq`, and `gain` consume the entire stack. If you push multiple values before a param word, you get polyphony:
|
||||
|
||||
```forth
|
||||
60 64 67 note sine s . ;; emits 3 voices with notes 60, 64, 67
|
||||
```
|
||||
|
||||
This works for any param and for the sound word itself:
|
||||
|
||||
```forth
|
||||
440 880 freq sine tri s . ;; 2 voices: sine at 440, tri at 880
|
||||
```
|
||||
|
||||
When params have different lengths, shorter lists cycle:
|
||||
|
||||
```forth
|
||||
60 64 67 note ;; 3 notes
|
||||
0.5 1.0 gain ;; 2 gains (cycles: 0.5, 1.0, 0.5)
|
||||
sine s . ;; emits 3 voices
|
||||
```
|
||||
|
||||
Polyphony multiplies with `at` deltas:
|
||||
|
||||
```forth
|
||||
0 0.5 at ;; 2 time points
|
||||
60 64 note ;; 2 notes
|
||||
sine s . ;; emits 4 voices (2 notes × 2 times)
|
||||
```
|
||||
|
||||
## Summary
|
||||
|
||||
Cagire's Forth is a domain-specific language for music. It keeps Forth's elegance (stack, postfix, definitions) but adapts it for live coding.
|
||||
Reference in New Issue
Block a user