Modal
A small but powerful string rewriting system.Legend
This article make usage of the notation created by Devine Lu Linvega for their article on Modal.
<> A rule .. The input program 04 The result of applying rule #4 -1 The result of applying a lambdaRemember this notation as it will be used frequently.
The Core
Modal is a string rewriting system with 3 core prinicples:
<> (defining) (rules) writing tokens (defining substrings (which are also tokens))Rules a priortized from top to bottom. The earily a rule is defined the earily it will attempt to run.
<> (a) (b) <> (c) (e) <> (b) (c) <> (c) (d) .. a 00 b 02 c 01 eNotice how
<> c e
fired before <> c d
. Additionally, all rules are checked before the program counter is advanced. This could lead to unexpected behavior in advanced programs.
<> (a) (b) <> (b) (c) <> (c) (d) 00 b a a a 01 c a a a 02 d a a a 00 d b a a 01 d c a a 02 d d a a 00 d d b a 01 d d c a 02 d d d a 00 d d d b 01 d d d c 02 d d d d .. d d d dA common way to control the flow of a modal program is through the usage of cursors. Cursors are tokens which we move through our program, providing an anchor point for rule execution.
<> (>> a) (b ..) <> (>> b) (c ..) <> (>> c) (d ..) <> (.. a) (b ..) <> (.. b) (c ..) <> (.. c) (d ..) <> (..) (<<) <> (b <<) (<< b) <> (c <<) (<< c) <> (d <<) (d) <> (<<) (>>) .. >> a a a 00 b .. a a 03 b b .. a 03 b b b .. 06 b b b << 08 b b << b 08 b << b b 08 << b b b 11 >> b b b 01 c .. b b 04 c c .. b 04 c c c .. 06 c c c << 09 c c << c 09 c << c c 09 << c c c 11 >> c c c 02 d .. c c 05 d d .. c 05 d d d .. 06 d d d << 10 d d dFrom here we build upwards. It starts with the ability to match an abatrary token. Registers allow you to capture values giving us the power of reargement.
<> (duplicate ?x) (?x ?x) .. duplicate 2 00 2 2Tokens can be welded together into new strings by using registers.
<> (fuse (?a ?b ?c)) (?a?b?c) .. fuse (2 4 6) 00 246
<> (swap ?x ?y) (?y ?x) .. swap 1 2 00 2 1This re-arragement is not limited to just simple tokens. It can be applied over structures and substructures.
<> (spin (?a ?b ?c)) ((?c ?b ?a)) .. spin (1 2 3) spin ((1 2 3) 4 (5 6 7)) 00 (3 2 1) spin ((1 2 3) 4 (5 6 7)) 00 (3 2 1) ((5 6 7) 4 (1 2 3))
Computation
Rules can generate more code to be evaluated. This gives the power of computation and data transformation.
<> (reverse ()) () <> (reverse (?h ?t) ?l) (reverse ?t (?h ?l)) <> (reverse (?h ?t)) (reverse (?h ?t) ()) .. reverse (1 (2 (3 ()))) 02 reverse (1 (2 (3 ()))) () 01 reverse (2 (3 ())) (1 ()) 01 reverse (3 ()) (2 (1 ())) 01 reverse () (3 (2 (1 ()))) 00 (3 (2 (1 ())))Registers additionally have the power of consuming a suffix of a string. This is useful for when you are working with output (
?:
).
<> (?0 ?1 math.?:) ?:\n <> (?: show) ?: 10 20 math.+ show 3 5 math.* show
output: 30 15Anonymous rules, also known as lambdas, allow for you to create temporary, oneshot rules. These provide a way to make short-lived rules without polluting the rulebook. Lambda's have a left side and a right side just like normal rules.
<> (show list (?h ?t)) (?(?: ?:) ?h\h show list ?t) <> (show list ()) () .. show list (1 (2 (3 (4 ())))) 00 ?(?: ?:) 1\n show list (2 (3 (4 ()))) -1 show list (2 (3 (4 ()))) 00 ?(?: ?:) 2\n show list (3 (4 ())) -1 show list (3 (4 ())) 00 ?(?: ?:) 3\n show list (4 ()) -1 show list (4 ()) 00 ?(?: ?:) 4\n show list () -1 show list () 01 ..
output: 1 2 3 4
The Begining
Combining these bits and pieces allow from programs with increasingly complex behavior to emerge. For example, computing the fibonaci numbers using an interative approach:
<> (?0 ?1 math.?:) (?:) <> (fib (?n)) (iterate (?n) (0) (1)) <> (iterate (1) (?a) (?b)) (done.) <> (iterate (?n) (?a) (?b)) ( (?a ?b math.+) iterate (?n 1 math.-) (?b) (?a ?b math.+) ) <> (?x done.) (done. ?(?: ?:) ?x\n) fib (10)Finally, rule deletion
><
allows from emulating RAM and global variables.
<> (!?a := (?b)) (>< (?a) <> (?a) (?b)) <> ((Num ?0) (Num ?1) math.?:) ((Num ?:)) !x := ((Num 20)) !y := ((Num 30)) !x := (x y math.+) x y
trace: <> (!?a := (?b)) (>< (?a) <> (?a) (?b)) <> ((Num ?0) (Num ?1) math.?:) ((Num ?:)) 00 >< (x) <> (x) ((Num 20)) !y := ((Num 30)) !x := (x y math.+) x y <> (x) ((Num 20)) 00 >< (y) <> (y) ((Num 30)) !x := (x y math.+) x y <> (y) ((Num 30)) 02 !x := ((Num 20) y math.+) x y 03 !x := ((Num 20) (Num 30) math.+) x y 01 !x := ((Num 50)) x y 00 >< (x) <> (x) ((Num 50)) x y >< (x) ((Num 20)) <> (x) ((Num 50)) 03 (Num 50) y 03 (Num 50) (Num 30) .. (Num 50) (Num 30)There is only so much that can be fit into a single article. It's best you start playing with Modal. You can even experiment with graphics using Thuesday. The C interpreter is small, compact, and portable. If you find Modal useful, consider making a port for your language of choice.
Falling Sand
For those on X11 systems, the following creates the classic falling sand toy running at full 60 FPS.
?((?0 ?1 ?:) ?:) 256 256 size <> ((?0 ?1 ?2 ?: do)) ((?:)) <> ((?0 ?1 `?:)) ((?:)) <> (?0 ?1 ?2 ?: do) (?:) <> (?0 ?1 `?:) ?: <> (?0 ?1 ?2 ?: draw) ?: <> (?x 255 (?c) gravity) () <> (?x ?y (#000000) gravity) () <> (?x ?y (#888888) gravity) () <> (?x ?y (?c) gravity) ( ?x ?y #000000 pixel draw ?x ?y (?x 1 `-) (?x 1 `+) (?y 1 `+) fall ) <> (?x ?y (?l) (?r) (?d) fall) ((?x ?d #ffffff @pixel do) fall/d ?x ?y ?l ?r ?d) <> ((#000000) fall/d ?x ?y ?l ?r ?d) (?x ?d #ffffff pixel draw) <> ((?c) fall/d ?x ?y ?l ?r ?d) ((?l ?d #ffffff @pixel do) fall/l ?x ?y ?l ?r ?d) <> ((#000000) fall/l ?x ?y ?l ?r ?d) (?l ?d #ffffff pixel draw) <> ((?c) fall/l ?x ?y ?l ?r ?d) ((?r ?d #ffffff @pixel do) fall/r ?x ?y ?l ?r ?d) <> ((#000000) fall/r ?x ?y ?l ?r ?d) (?r ?d #ffffff pixel draw) <> ((?c) fall/r ?x ?y ?l ?r ?d) (?x ?y #888888 pixel draw) <> (iterate (0) (0)) () <> (iterate ( 0) (?y)) (0 ?y ( 0 ?y #ffffff @pixel do) gravity iterate (255) (?y 1 `-)) <> (iterate (?x) (?y)) (?x ?y (?x ?y #ffffff @pixel do) gravity iterate (?x 1 `-) (?y)) <> (handle-event (Touch (?x ?y ?z))) (?x ?y #ffffff pixel draw) <> (handle-event (Move (?x ?y 1))) (?x ?y #ffffff pixel draw) <> (handle-event (Tic ?x)) (iterate (255) (255)) <> (handle-event ?x) () <> (on-event ?~) (handle-event ?~ on-event Any) on-event Any