# Part of http://akkartik.name/coroutines-in-mu # # Version 1: decompressor as caller, parser as callee # # To run: # $ git clone https://github.com/akkartik/mu1 # $ cd mu1 # $ git checkout 4a48bedcd1 # state as of this writing; later versions may be incompatible # $ wget http://akkartik.name/coroutines-in-mu/version1.mu # $ ./mu version1.mu exclusive-container token [ word:text punc:char ] container parse-state [ in-word?:bool buf:&:buffer:char ] def decompressor src:&:source:num, dest:&:sink:token -> src:&:source:num, dest:&:sink:token [ local-scope load-inputs p:parse-state <- merge 0/not-in-word, 0/null-buffer { x:num, eof?:bool, src <- read src break-if eof? c:char <- copy x $print [decompress: ] c 10/newline repeat-tag?:bool <- equal c, 255 { break-unless repeat-tag? len:num <- read src x:num <- read src c:char <- copy x $print [run: ] c [*] len 10/newline i:num <- copy 0 { done?:bool <- greater-or-equal i, len break-if done? p <- parser c, dest, p i <- add i, 1 loop } } { break-if repeat-tag? p <- parser c, dest, p } loop } parser 4/EOF, dest, p ] def parser c:char, dest:&:sink:token, p:parse-state -> p:parse-state, dest:&:sink:token [ local-scope load-inputs { eof?:bool <- equal c, 4/EOF break-unless eof? # emit final word if necessary { buf:&:buffer:char <- get p, buf:offset break-unless buf word:text <- buffer-to-array buf out:token <- merge 0/word, word dest <- write dest, out } close dest return } alpha?:bool <- isalpha c in-word?:bool <- get p, in-word?:offset buf:&:buffer:char <- get p, buf:offset { break-unless in-word? break-unless alpha? # continue pending word buf <- append buf, c p <- merge 1/in-word, buf return } { break-unless in-word? break-if alpha? # finish pending word word:text <- buffer-to-array buf out:token <- merge 0/word, word dest <- write dest, out # fall through } { break-if alpha? # emit this punctuation out <- merge 1/punc, c dest <- write dest, out p <- merge 0/not-in-word, 0/null-buffer return } { break-if in-word? break-unless alpha? # start new word buf <- new-buffer 5 buf <- append buf, c p <- merge 1/in-word, buf return } ] def isalpha c:char -> result:bool [ local-scope load-inputs # 'a' <= c <= 'z' || 'A' <= c <= 'Z' b1:bool <- greater-or-equal c, 65/A b2:bool <- lesser-or-equal c, 90/Z uppercase?:bool <- and b1, b2 return-if uppercase?, 1/true b1 <- greater-or-equal c, 97/a b2 <- lesser-or-equal c, 122/z lowercase?:bool <- and b1, b2 return lowercase? ] # test def main [ local-scope # Make channel capacities large enough to never block; we don't use # concurrency in this example. number-source:&:source:num, in:&:sink:num <- new-channel 30 out:&:source:token, token-sink:&:sink:token <- new-channel 30 # set up input ["aaaabbb,,cccdd;e"] write in, 255 write in, 4/len write in, 97/a write in, 255 write in, 3/len write in, 98/b write in, 255 write in, 2/len write in, 44/comma write in, 255 write in, 3/len write in, 99/c write in, 255 write in, 2/len write in, 100/d write in, 59/semi-colon write in, 101/e close in # run $print [run] 10/newline decompressor number-source, token-sink $print [done] 10/newline # read out result { t:token, done?:bool <- read out break-if done? { w:text, match?:bool <- maybe-convert t, word:variant break-unless match? $print [word: ] *w 10/newline } { p:char, match?:bool <- maybe-convert t, punc:variant break-unless match? $print [punc: ] p 10/newline } loop } ]