Created
March 16, 2025 13:10
-
-
Save postspectacular/7d0ae7d90e0eafb69e60fb97e2838221 to your computer and use it in GitHub Desktop.
L-System based FizzBuzz implementations (using https://thi.ng/lsys)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import type { Fn2 } from "@thi.ng/api"; | |
import { | |
DEFAULT, | |
expand, | |
interpret, | |
type LSysSymbol, | |
type RuleImplementations, | |
} from "@thi.ng/lsys"; | |
// iterative rule expansion | |
const fizzbuzz = expand( | |
{ | |
start: ["_1"], | |
_1: ["1", "_2"], | |
_2: ["2", "_3"], | |
_3: ["fizz", "_4"], | |
_4: ["4", "_5"], | |
_5: ["buzz", "_6"], | |
_6: ["fizz", "_7"], | |
_7: ["7", "_8"], | |
_8: ["8", "_9"], | |
_9: ["fizz", "_10"], | |
_10: ["buzz", "_11"], | |
_11: ["11", "_12"], | |
_12: ["fizz", "_13"], | |
_13: ["13", "_14"], | |
_14: ["14", "_15"], | |
_15: ["fizz buzz", "end"], | |
}, | |
"start", | |
100 | |
); | |
console.log([...fizzbuzz].join(" ")); | |
// 1 2 fizz 4 buzz fizz 7 8 fizz 10 11 fizz 13 14 fizz buzz end | |
// alternative approach: | |
// attaching a L-System iterpreter implementing a simple stack machine VM | |
// for Forth-like execution (incl. quotations) | |
// VM state is a 2-tuple of `[current stack, scopes]` | |
type VmCtx = [any[], any[]]; | |
// VM operators (i.e. mapping L-System symbols to actions) | |
const VM: RuleImplementations<VmCtx> = { | |
// any unmatches symbol will be pushed on stack | |
[DEFAULT]: ([stack]: any[], x: any) => stack.push(x), | |
// quoation handling | |
"[": (ctx) => (ctx[1].push(ctx[0]), (ctx[0] = [])), | |
"]": (ctx) => { | |
const parent = ctx[1].pop(); | |
parent.push(ctx[0]); | |
ctx[0] = parent; | |
}, | |
}; | |
// all other VM ops are deferrable (i.e. will only execute in the root scope, | |
// otherwise the symbol is quoted and just pushed on current stack...) | |
const DEFERED_OPS: RuleImplementations<VmCtx> = { | |
// print TOS: ( x -- ) | |
".": (ctx) => console.log(ctx[0].pop()!), | |
// 2-branch conditional: ( test truthy falsy -- ) | |
ifelse: (ctx) => { | |
const [falsy, truthy] = [ctx[0].pop(), ctx[0].pop()]; | |
interpret(ctx, VM, ctx[0].pop() ? truthy : falsy); | |
}, | |
// duplicate TOS: ( x -- x x ) | |
dup: ([stack]) => stack.push(stack[stack.length - 1]), | |
// remove TOS: ( x -- ) | |
drop: ([stack]) => stack.pop(), | |
// swap two topmost values: ( x y -- y x ) | |
swap: ([stack]) => stack.splice(stack.length - 2, 0, stack.pop()), | |
// compute modulo: ( x y -- x%y ) | |
mod: ([stack]) => { | |
const x = stack.pop()!; | |
stack.push(stack.pop()! % x); | |
}, | |
// concatenate strings: ( x y -- x+y ) | |
concat: ([stack]) => { | |
const [b, a] = [stack.pop(), stack.pop()]; | |
stack.push(a + b); | |
}, | |
}; | |
// helper function to allow op be quotable | |
const defer = | |
(id: string, fn: Fn2<VmCtx, LSysSymbol, void>) => | |
(ctx: VmCtx, sym: LSysSymbol) => | |
!ctx[1].length ? fn(ctx, sym) : ctx[0].push(id); | |
// inject all deferred ops | |
for (let [id, op] of Object.entries(DEFERED_OPS)) { | |
VM[id] = defer(id, op); | |
} | |
// L-System ruleset (aka source code) for VM | |
const fizzbuzzVM = [ | |
...expand( | |
{ | |
start: ["_1"], | |
empty: ["[", "", "]"], | |
_fizz: ["[", "fizz", "]"], | |
_buzz: ["[", "buzz", "]"], | |
label: ["[", ".", "drop", "]"], | |
counter: ["[", "drop", ".", "]"], | |
"fizz?": [3, "mod", "empty", "_fizz", "ifelse"], | |
"buzz?": [5, "mod", "empty", "_buzz", "ifelse"], | |
fizzbuzz: [ | |
"dup", | |
"dup", | |
"fizz?", | |
"swap", | |
"buzz?", | |
"concat", | |
"dup", | |
"label", | |
"counter", | |
"ifelse", | |
], | |
_1: [1, "fizzbuzz", "_2"], | |
_2: [2, "fizzbuzz", "_3"], | |
_3: [3, "fizzbuzz", "_4"], | |
_4: [4, "fizzbuzz", "_5"], | |
_5: [5, "fizzbuzz", "_6"], | |
_6: [6, "fizzbuzz", "_7"], | |
_7: [7, "fizzbuzz", "_8"], | |
_8: [8, "fizzbuzz", "_9"], | |
_9: [9, "fizzbuzz", "_10"], | |
_10: [10, "fizzbuzz", "_11"], | |
_11: [11, "fizzbuzz", "_12"], | |
_12: [12, "fizzbuzz", "_13"], | |
_13: [13, "fizzbuzz", "_14"], | |
_14: [14, "fizzbuzz", "_15"], | |
_15: [15, "fizzbuzz", "end"], | |
}, | |
"start", | |
100 | |
), | |
]; | |
// output expanded rules | |
console.log(fizzbuzzVM.join(" ")); | |
// 1 dup dup 3 mod [ ] [ fizz ] ifelse swap 5 mod [ ] [ buzz ] ifelse concat dup [ . drop ] [ drop . ] ifelse 2 dup dup 3 mod [ ] [ fizz ] ifelse swap 5 mod [ ] [ buzz ] ifelse concat dup [ . drop ] [ drop . ] ifelse 3 dup dup 3 mod [ ] [ fizz ] ifelse swap 5 mod [ ] [ buzz ] ifelse concat dup [ . drop ] [ drop . ] ifelse 4 dup dup 3 mod [ ] [ fizz ] ifelse swap 5 mod [ ] [ buzz ] ifelse concat dup [ . drop ] [ drop . ] ifelse 5 dup dup 3 mod [ ] [ fizz ] ifelse swap 5 mod [ ] [ buzz ] ifelse concat dup [ . drop ] [ drop . ] ifelse 6 dup dup 3 mod [ ] [ fizz ] ifelse swap 5 mod [ ] [ buzz ] ifelse concat dup [ . drop ] [ drop . ] ifelse 7 dup dup 3 mod [ ] [ fizz ] ifelse swap 5 mod [ ] [ buzz ] ifelse concat dup [ . drop ] [ drop . ] ifelse 8 dup dup 3 mod [ ] [ fizz ] ifelse swap 5 mod [ ] [ buzz ] ifelse concat dup [ . drop ] [ drop . ] ifelse 9 dup dup 3 mod [ ] [ fizz ] ifelse swap 5 mod [ ] [ buzz ] ifelse concat dup [ . drop ] [ drop . ] ifelse 10 dup dup 3 mod [ ] [ fizz ] ifelse swap 5 mod [ ] [ buzz ] ifelse concat dup [ . drop ] [ drop . ] ifelse 11 dup dup 3 mod [ ] [ fizz ] ifelse swap 5 mod [ ] [ buzz ] ifelse concat dup [ . drop ] [ drop . ] ifelse 12 dup dup 3 mod [ ] [ fizz ] ifelse swap 5 mod [ ] [ buzz ] ifelse concat dup [ . drop ] [ drop . ] ifelse 13 dup dup 3 mod [ ] [ fizz ] ifelse swap 5 mod [ ] [ buzz ] ifelse concat dup [ . drop ] [ drop . ] ifelse 14 dup dup 3 mod [ ] [ fizz ] ifelse swap 5 mod [ ] [ buzz ] ifelse concat dup [ . drop ] [ drop . ] ifelse 15 dup dup 3 mod [ ] [ fizz ] ifelse swap 5 mod [ ] [ buzz ] ifelse concat dup [ . drop ] [ drop . ] ifelse end | |
// execute | |
interpret([[], []], VM, fizzbuzzVM); | |
// Output: | |
// 1 | |
// 2 | |
// fizz | |
// 4 | |
// buzz | |
// fizz | |
// 7 | |
// 8 | |
// fizz | |
// buzz | |
// 11 | |
// fizz | |
// 13 | |
// 14 | |
// fizzbuzz |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment