Files
Cagire/docs/forth/control_flow.md

3.2 KiB

Control Flow

Control flow in Cagire's Forth comes in two families. The first is compiled syntax — if/then and case — which the compiler handles directly as branch instructions. The second is quotation words — ?, !?, ifelse, select, apply — which pop ( ... ) quotations from the stack and decide whether to run them. Probability and periodic execution (chance, every, bjork) are covered in the Randomness tutorial.

Branching with if / else / then

Push a condition, then if. Everything between if and then runs only when the condition is truthy:

;; degrade sound if true
coin if
  7 crush
then
sine sound
c4 note
1 decay
.

The crush is applied on half the hits. The sound always plays. Add else for a two-way split:

coin if
  c5 note
else
  c3 note
then
saw sound
0.3 verb
0.5 decay
0.6 gain
.

These are compiled directly into branch instructions — they will not appear in the dictionary. This is a "low level" way to use conditionals in Cagire.

Matching with case

For matching a value against several options. Cleaner than a chain of ifs when you have more than two branches:

1 8 rand 4 mod case
  0 of c3 note endof
  1 of e3 note endof
  2 of g3 note endof
  3 of a3 note endof
endcase
tri s
2 fm 0.99 fmh
0.6 gain 0.2 chorus
1 decay
800 lpf
.

A different root note each time the pattern loops. The last line before endcase is the default — it runs when no of matched:

iter 3 mod case
  0 of 0.9 gain endof
  0.4 gain
endcase
saw s
.5 decay
c4 note
.

Like if/then, case is compiled syntax and does not appear in the dictionary.

Quotation Words

The remaining control flow words operate on quotations — ( ... ) blocks sitting on the stack. Each word pops one or more quotations and decides whether or how to execute them.

? and !?

? executes a quotation if the condition is truthy:

( 0.4 verb 6 crush ) coin ?
tri sound 2 fm 0.5 fmh
c3 note 0.5 gain 2 decay
.

Reverb on half the hits. !? is the opposite — executes when falsy:

( 0.5 delay 0.9 delayfeedback ) coin !?
saw sound
c4 note
500 lpf
0.5 decay
0.5 gain
.

Quiet on half the hits. These pair well with chance and fill from the Randomness tutorial.

ifelse

Two quotations, one condition. The true branch comes first:

( c3 note ) ( c5 note ) coin ifelse
saw sound 0.3 verb
0.5 decay 0.6 gain
.

Reads naturally: "c3 or c5, depending on the coin."

( 0.8 gain ) ( 0.3 gain ) fill ifelse
tri snd c4 note 0.2 decay .

Loud during fills, quiet otherwise.

select

Choose the nth quotation from a list. The index is 0-based:

( c4 ) ( e4 ) ( g4 ) ( b4 ) 0 3 rand select
note sine snd 0.5 decay .

Four notes of a major seventh chord picked randomly. Note that this is unnecessarily complex :)

apply

When you have a quotation and want to execute it unconditionally:

( dup + ) apply

Pops the quotation and runs it. Simpler than ? when there is no condition to check.

More!

For probability gates, periodic execution, and euclidean rhythms, see the Randomness tutorial. For generators and ranges, see the Generators tutorial.