<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <title>Mu - 061text.mu</title> <meta name="Generator" content="Vim/7.4"> <meta name="plugin-version" content="vim7.4_v2"> <meta name="syntax" content="none"> <meta name="settings" content="use_css,pre_wrap,no_foldcolumn,expand_tabs,prevent_copy="> <meta name="colorscheme" content="minimal"> <style type="text/css"> <!-- pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background-color: #080808; } body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 12pt; font-size: 1em; } .Delimiter { color: #800080; } .muControl { color: #c0a020; } .muData { color: #ffff00; } .Comment { color: #9090ff; } .Constant { color: #00a0a0; } .Special { color: #c00000; } .muRecipe { color: #ff8700; } .muScenario { color: #00af00; } --> </style> <script type='text/javascript'> <!-- --> </script> </head> <body> <pre id='vimCodeElement'> <span class="Comment"># Some useful helpers for dealing with text (arrays of characters)</span> <span class="muRecipe">def</span> equal a:text, b:text<span class="muRecipe"> -> </span>result:bool [ <span class="Constant">local-scope</span> <span class="Constant">load-ingredients</span> a-len:num <span class="Special"><-</span> length *a b-len:num <span class="Special"><-</span> length *b <span class="Comment"># compare lengths</span> <span class="Delimiter">{</span> trace<span class="Constant"> 99</span>, <span class="Constant">[text-equal]</span>, <span class="Constant">[comparing lengths]</span> length-equal?:bool <span class="Special"><-</span> equal a-len, b-len <span class="muControl">break-if</span> length-equal? <span class="muControl">return</span><span class="Constant"> 0</span> <span class="Delimiter">}</span> <span class="Comment"># compare each corresponding character</span> trace<span class="Constant"> 99</span>, <span class="Constant">[text-equal]</span>, <span class="Constant">[comparing characters]</span> i:num <span class="Special"><-</span> copy<span class="Constant"> 0</span> <span class="Delimiter">{</span> done?:bool <span class="Special"><-</span> greater-or-equal i, a-len <span class="muControl">break-if</span> done? a2:char <span class="Special"><-</span> index *a, i b2:char <span class="Special"><-</span> index *b, i <span class="Delimiter">{</span> chars-match?:bool <span class="Special"><-</span> equal a2, b2 <span class="muControl">break-if</span> chars-match? <span class="muControl">return</span><span class="Constant"> 0</span> <span class="Delimiter">}</span> i <span class="Special"><-</span> add i,<span class="Constant"> 1</span> <span class="muControl">loop</span> <span class="Delimiter">}</span> <span class="muControl">return</span><span class="Constant"> 1</span> ] <span class="muScenario">scenario</span> text-equal-reflexive [ <span class="Constant">local-scope</span> x:text <span class="Special"><-</span> new <span class="Constant">[abc]</span> run [ 10:bool/<span class="Special">raw</span> <span class="Special"><-</span> equal x, x ] memory-should-contain [ <span class="Constant"> 10</span> <span class="Special"><-</span><span class="Constant"> 1</span> <span class="Comment"># x == x for all x</span> ] ] <span class="muScenario">scenario</span> text-equal-identical [ <span class="Constant">local-scope</span> x:text <span class="Special"><-</span> new <span class="Constant">[abc]</span> y:text <span class="Special"><-</span> new <span class="Constant">[abc]</span> run [ 10:bool/<span class="Special">raw</span> <span class="Special"><-</span> equal x, y ] memory-should-contain [ <span class="Constant"> 10</span> <span class="Special"><-</span><span class="Constant"> 1</span> <span class="Comment"># abc == abc</span> ] ] <span class="muScenario">scenario</span> text-equal-distinct-lengths [ <span class="Constant">local-scope</span> x:text <span class="Special"><-</span> new <span class="Constant">[abc]</span> y:text <span class="Special"><-</span> new <span class="Constant">[abcd]</span> run [ 10:bool/<span class="Special">raw</span> <span class="Special"><-</span> equal x, y ] memory-should-contain [ <span class="Constant"> 10</span> <span class="Special"><-</span><span class="Constant"> 0</span> <span class="Comment"># abc != abcd</span> ] trace-should-contain [ text-equal: comparing lengths ] trace-should-not-contain [ text-equal: comparing characters ] ] <span class="muScenario">scenario</span> text-equal-with-empty [ <span class="Constant">local-scope</span> x:text <span class="Special"><-</span> new <span class="Constant">[]</span> y:text <span class="Special"><-</span> new <span class="Constant">[abcd]</span> run [ 10:bool/<span class="Special">raw</span> <span class="Special"><-</span> equal x, y ] memory-should-contain [ <span class="Constant"> 10</span> <span class="Special"><-</span><span class="Constant"> 0</span> <span class="Comment"># "" != abcd</span> ] ] <span class="muScenario">scenario</span> text-equal-common-lengths-but-distinct [ <span class="Constant">local-scope</span> x:text <span class="Special"><-</span> new <span class="Constant">[abc]</span> y:text <span class="Special"><-</span> new <span class="Constant">[abd]</span> run [ 10:bool/<span class="Special">raw</span> <span class="Special"><-</span> equal x, y ] memory-should-contain [ <span class="Constant"> 10</span> <span class="Special"><-</span><span class="Constant"> 0</span> <span class="Comment"># abc != abd</span> ] ] <span class="Comment"># A new type to help incrementally construct texts.</span> <span class="muData">container</span> buffer [ length:num data:text ] <span class="muRecipe">def</span> new-buffer capacity:num<span class="muRecipe"> -> </span>result:&:buffer [ <span class="Constant">local-scope</span> <span class="Constant">load-ingredients</span> result <span class="Special"><-</span> new <span class="Constant">buffer:type</span> *result <span class="Special"><-</span> put *result, <span class="Constant">length:offset</span>,<span class="Constant"> 0</span> <span class="Delimiter">{</span> <span class="muControl">break-if</span> capacity <span class="Comment"># capacity not provided</span> capacity <span class="Special"><-</span> copy<span class="Constant"> 10</span> <span class="Delimiter">}</span> data:text <span class="Special"><-</span> new <span class="Constant">character:type</span>, capacity *result <span class="Special"><-</span> put *result, <span class="Constant">data:offset</span>, data <span class="muControl">return</span> result ] <span class="muRecipe">def</span> grow-buffer buf:&:buffer<span class="muRecipe"> -> </span>buf:&:buffer [ <span class="Constant">local-scope</span> <span class="Constant">load-ingredients</span> <span class="Comment"># double buffer size</span> olddata:text <span class="Special"><-</span> get *buf, <span class="Constant">data:offset</span> oldlen:num <span class="Special"><-</span> length *olddata newlen:num <span class="Special"><-</span> multiply oldlen,<span class="Constant"> 2</span> newdata:text <span class="Special"><-</span> new <span class="Constant">character:type</span>, newlen *buf <span class="Special"><-</span> put *buf, <span class="Constant">data:offset</span>, newdata <span class="Comment"># copy old contents</span> i:num <span class="Special"><-</span> copy<span class="Constant"> 0</span> <span class="Delimiter">{</span> done?:bool <span class="Special"><-</span> greater-or-equal i, oldlen <span class="muControl">break-if</span> done? src:char <span class="Special"><-</span> index *olddata, i *newdata <span class="Special"><-</span> put-index *newdata, i, src i <span class="Special"><-</span> add i,<span class="Constant"> 1</span> <span class="muControl">loop</span> <span class="Delimiter">}</span> ] <span class="muRecipe">def</span> buffer-full? in:&:buffer<span class="muRecipe"> -> </span>result:bool [ <span class="Constant">local-scope</span> <span class="Constant">load-ingredients</span> len:num <span class="Special"><-</span> get *in, <span class="Constant">length:offset</span> s:text <span class="Special"><-</span> get *in, <span class="Constant">data:offset</span> capacity:num <span class="Special"><-</span> length *s result <span class="Special"><-</span> greater-or-equal len, capacity ] <span class="Comment"># most broadly applicable definition of append to a buffer: just call to-text</span> <span class="muRecipe">def</span> append buf:&:buffer, x:_elem<span class="muRecipe"> -> </span>buf:&:buffer [ <span class="Constant">local-scope</span> <span class="Constant">load-ingredients</span> text:text <span class="Special"><-</span> to-text x len:num <span class="Special"><-</span> length *text i:num <span class="Special"><-</span> copy<span class="Constant"> 0</span> <span class="Delimiter">{</span> done?:bool <span class="Special"><-</span> greater-or-equal i, len <span class="muControl">break-if</span> done? c:char <span class="Special"><-</span> index *text, i buf <span class="Special"><-</span> append buf, c i <span class="Special"><-</span> add i,<span class="Constant"> 1</span> <span class="muControl">loop</span> <span class="Delimiter">}</span> ] <span class="muRecipe">def</span> append buf:&:buffer, c:char<span class="muRecipe"> -> </span>buf:&:buffer [ <span class="Constant">local-scope</span> <span class="Constant">load-ingredients</span> len:num <span class="Special"><-</span> get *buf, <span class="Constant">length:offset</span> <span class="Delimiter">{</span> <span class="Comment"># backspace? just drop last character if it exists and return</span> backspace?:bool <span class="Special"><-</span> equal c, <span class="Constant">8/backspace</span> <span class="muControl">break-unless</span> backspace? empty?:bool <span class="Special"><-</span> lesser-or-equal len,<span class="Constant"> 0</span> <span class="muControl">return-if</span> empty? len <span class="Special"><-</span> subtract len,<span class="Constant"> 1</span> *buf <span class="Special"><-</span> put *buf, <span class="Constant">length:offset</span>, len <span class="muControl">return</span> <span class="Delimiter">}</span> <span class="Delimiter">{</span> <span class="Comment"># grow buffer if necessary</span> full?:bool <span class="Special"><-</span> buffer-full? buf <span class="muControl">break-unless</span> full? buf <span class="Special"><-</span> grow-buffer buf <span class="Delimiter">}</span> s:text <span class="Special"><-</span> get *buf, <span class="Constant">data:offset</span> *s <span class="Special"><-</span> put-index *s, len, c len <span class="Special"><-</span> add len,<span class="Constant"> 1</span> *buf <span class="Special"><-</span> put *buf, <span class="Constant">length:offset</span>, len ] <span class="muRecipe">def</span> append buf:&:buffer, t:text<span class="muRecipe"> -> </span>buf:&:buffer [ <span class="Constant">local-scope</span> <span class="Constant">load-ingredients</span> len:num <span class="Special"><-</span> length *t i:num <span class="Special"><-</span> copy<span class="Constant"> 0</span> <span class="Delimiter">{</span> done?:bool <span class="Special"><-</span> greater-or-equal i, len <span class="muControl">break-if</span> done? c:char <span class="Special"><-</span> index *t, i buf <span class="Special"><-</span> append buf, c i <span class="Special"><-</span> add i,<span class="Constant"> 1</span> <span class="muControl">loop</span> <span class="Delimiter">}</span> ] <span class="muScenario">scenario</span> append-to-empty-buffer [ <span class="Constant">local-scope</span> x:&:buffer <span class="Special"><-</span> new-buffer run [ c:char <span class="Special"><-</span> copy <span class="Constant">97/a</span> x <span class="Special"><-</span> append x, c 10:num/<span class="Special">raw</span> <span class="Special"><-</span> get *x, <span class="Constant">length:offset</span> s:text <span class="Special"><-</span> get *x, <span class="Constant">data:offset</span> 11:char/<span class="Special">raw</span> <span class="Special"><-</span> index *s,<span class="Constant"> 0</span> 12:char/<span class="Special">raw</span> <span class="Special"><-</span> index *s,<span class="Constant"> 1</span> ] memory-should-contain [ <span class="Constant"> 10</span> <span class="Special"><-</span><span class="Constant"> 1</span> <span class="Comment"># buffer length</span> <span class="Constant"> 11</span> <span class="Special"><-</span><span class="Constant"> 97</span> <span class="Comment"># a</span> <span class="Constant"> 12</span> <span class="Special"><-</span><span class="Constant"> 0</span> <span class="Comment"># rest of buffer is empty</span> ] ] <span class="muScenario">scenario</span> append-to-buffer [ <span class="Constant">local-scope</span> x:&:buffer <span class="Special"><-</span> new-buffer c:char <span class="Special"><-</span> copy <span class="Constant">97/a</span> x <span class="Special"><-</span> append x, c run [ c <span class="Special"><-</span> copy <span class="Constant">98/b</span> x <span class="Special"><-</span> append x, c 10:num/<span class="Special">raw</span> <span class="Special"><-</span> get *x, <span class="Constant">length:offset</span> s:text <span class="Special"><-</span> get *x, <span class="Constant">data:offset</span> 11:char/<span class="Special">raw</span> <span class="Special"><-</span> index *s,<span class="Constant"> 0</span> 12:char/<span class="Special">raw</span> <span class="Special"><-</span> index *s,<span class="Constant"> 1</span> 13:char/<span class="Special">raw</span> <span class="Special"><-</span> index *s,<span class="Constant"> 2</span> ] memory-should-contain [ <span class="Constant"> 10</span> <span class="Special"><-</span><span class="Constant"> 2</span> <span class="Comment"># buffer length</span> <span class="Constant"> 11</span> <span class="Special"><-</span><span class="Constant"> 97</span> <span class="Comment"># a</span> <span class="Constant"> 12</span> <span class="Special"><-</span><span class="Constant"> 98</span> <span class="Comment"># b</span> <span class="Constant"> 13</span> <span class="Special"><-</span><span class="Constant"> 0</span> <span class="Comment"># rest of buffer is empty</span> ] ] <span class="muScenario">scenario</span> append-grows-buffer [ <span class="Constant">local-scope</span> x:&:buffer <span class="Special"><-</span> new-buffer<span class="Constant"> 3</span> s1:text <span class="Special"><-</span> get *x, <span class="Constant">data:offset</span> x <span class="Special"><-</span> append x, <span class="Constant">[abc]</span> <span class="Comment"># buffer is now full</span> s2:text <span class="Special"><-</span> get *x, <span class="Constant">data:offset</span> run [ 10:bool/<span class="Special">raw</span> <span class="Special"><-</span> equal s1, s2 11:@:char/<span class="Special">raw</span> <span class="Special"><-</span> copy *s2 <span class="Constant"> +buffer-filled</span> c:char <span class="Special"><-</span> copy <span class="Constant">100/d</span> x <span class="Special"><-</span> append x, c s3:text <span class="Special"><-</span> get *x, <span class="Constant">data:offset</span> 20:bool/<span class="Special">raw</span> <span class="Special"><-</span> equal s1, s3 21:num/<span class="Special">raw</span> <span class="Special"><-</span> get *x, <span class="Constant">length:offset</span> 30:@:char/<span class="Special">raw</span> <span class="Special"><-</span> copy *s3 ] memory-should-contain [ <span class="Comment"># before +buffer-filled</span> <span class="Constant"> 10</span> <span class="Special"><-</span><span class="Constant"> 1</span> <span class="Comment"># no change in data pointer after original append</span> <span class="Constant"> 11</span> <span class="Special"><-</span><span class="Constant"> 3</span> <span class="Comment"># size of data</span> <span class="Constant"> 12</span> <span class="Special"><-</span><span class="Constant"> 97</span> <span class="Comment"># data</span> <span class="Constant"> 13</span> <span class="Special"><-</span><span class="Constant"> 98</span> <span class="Constant"> 14</span> <span class="Special"><-</span><span class="Constant"> 99</span> <span class="Comment"># in the end</span> <span class="Constant"> 20</span> <span class="Special"><-</span><span class="Constant"> 0</span> <span class="Comment"># data pointer has grown after second append</span> <span class="Constant"> 21</span> <span class="Special"><-</span><span class="Constant"> 4</span> <span class="Comment"># final length</span> <span class="Constant"> 30</span> <span class="Special"><-</span><span class="Constant"> 6</span> <span class="Comment"># but data's capacity has doubled</span> <span class="Constant"> 31</span> <span class="Special"><-</span><span class="Constant"> 97</span> <span class="Comment"># data</span> <span class="Constant"> 32</span> <span class="Special"><-</span><span class="Constant"> 98</span> <span class="Constant"> 33</span> <span class="Special"><-</span><span class="Constant"> 99</span> <span class="Constant"> 34</span> <span class="Special"><-</span><span class="Constant"> 100</span> <span class="Constant"> 35</span> <span class="Special"><-</span><span class="Constant"> 0</span> <span class="Constant"> 36</span> <span class="Special"><-</span><span class="Constant"> 0</span> ] ] <span class="muScenario">scenario</span> buffer-append-handles-backspace [ <span class="Constant">local-scope</span> x:&:buffer <span class="Special"><-</span> new-buffer x <span class="Special"><-</span> append x, <span class="Constant">[ab]</span> run [ c:char <span class="Special"><-</span> copy <span class="Constant">8/backspace</span> x <span class="Special"><-</span> append x, c s:text <span class="Special"><-</span> buffer-to-array x 10:@:char/<span class="Special">raw</span> <span class="Special"><-</span> copy *s ] memory-should-contain [ <span class="Constant"> 10</span> <span class="Special"><-</span><span class="Constant"> 1</span> <span class="Comment"># length</span> <span class="Constant"> 11</span> <span class="Special"><-</span><span class="Constant"> 97</span> <span class="Comment"># contents</span> <span class="Constant"> 12</span> <span class="Special"><-</span><span class="Constant"> 0</span> ] ] <span class="muRecipe">def</span> buffer-to-array in:&:buffer<span class="muRecipe"> -> </span>result:text [ <span class="Constant">local-scope</span> <span class="Constant">load-ingredients</span> <span class="Delimiter">{</span> <span class="Comment"># propagate null buffer</span> <span class="muControl">break-if</span> in <span class="muControl">return</span><span class="Constant"> 0</span> <span class="Delimiter">}</span> len:num <span class="Special"><-</span> get *in, <span class="Constant">length:offset</span> s:text <span class="Special"><-</span> get *in, <span class="Constant">data:offset</span> <span class="Comment"># we can't just return s because it is usually the wrong length</span> result <span class="Special"><-</span> new <span class="Constant">character:type</span>, len i:num <span class="Special"><-</span> copy<span class="Constant"> 0</span> <span class="Delimiter">{</span> done?:bool <span class="Special"><-</span> greater-or-equal i, len <span class="muControl">break-if</span> done? src:char <span class="Special"><-</span> index *s, i *result <span class="Special"><-</span> put-index *result, i, src i <span class="Special"><-</span> add i,<span class="Constant"> 1</span> <span class="muControl">loop</span> <span class="Delimiter">}</span> ] <span class="Comment"># Append any number of texts together.</span> <span class="Comment"># A later layer also translates calls to this to implicitly call to-text, so</span> <span class="Comment"># append to string becomes effectively dynamically typed.</span> <span class="Comment">#</span> <span class="Comment"># Beware though: this hack restricts how much 'append' can be overridden. Any</span> <span class="Comment"># new variants that match:</span> <span class="Comment"># append _:text, ___</span> <span class="Comment"># will never ever get used.</span> <span class="muRecipe">def</span> append first:text<span class="muRecipe"> -> </span>result:text [ <span class="Constant">local-scope</span> <span class="Constant">load-ingredients</span> buf:&:buffer <span class="Special"><-</span> new-buffer<span class="Constant"> 30</span> <span class="Comment"># append first ingredient</span> <span class="Delimiter">{</span> <span class="muControl">break-unless</span> first buf <span class="Special"><-</span> append buf, first <span class="Delimiter">}</span> <span class="Comment"># append remaining ingredients</span> <span class="Delimiter">{</span> arg:text, arg-found?:bool <span class="Special"><-</span> <span class="Constant">next-ingredient</span> <span class="muControl">break-unless</span> arg-found? <span class="muControl">loop-unless</span> arg buf <span class="Special"><-</span> append buf, arg <span class="muControl">loop</span> <span class="Delimiter">}</span> result <span class="Special"><-</span> buffer-to-array buf ] <span class="muScenario">scenario</span> text-append-1 [ <span class="Constant">local-scope</span> x:text <span class="Special"><-</span> new <span class="Constant">[hello,]</span> y:text <span class="Special"><-</span> new <span class="Constant">[ world!]</span> run [ z:text <span class="Special"><-</span> append x, y 10:@:char/<span class="Special">raw</span> <span class="Special"><-</span> copy *z ] memory-should-contain [ 10:array:character <span class="Special"><-</span> <span class="Constant">[hello, world!]</span> ] ] <span class="muScenario">scenario</span> text-append-null [ <span class="Constant">local-scope</span> x:text <span class="Special"><-</span> copy<span class="Constant"> 0</span> y:text <span class="Special"><-</span> new <span class="Constant">[ world!]</span> run [ z:text <span class="Special"><-</span> append x, y 10:@:char/<span class="Special">raw</span> <span class="Special"><-</span> copy *z ] memory-should-contain [ 10:array:character <span class="Special"><-</span> <span class="Constant">[ world!]</span> ] ] <span class="muScenario">scenario</span> text-append-null-2 [ <span class="Constant">local-scope</span> x:text <span class="Special"><-</span> new <span class="Constant">[hello,]</span> y:text <span class="Special"><-</span> copy<span class="Constant"> 0</span> run [ z:text <span class="Special"><-</span> append x, y 10:@:char/<span class="Special">raw</span> <span class="Special"><-</span> copy *z ] memory-should-contain [ 10:array:character <span class="Special"><-</span> <span class="Constant">[hello,]</span> ] ] <span class="muScenario">scenario</span> text-append-multiary [ <span class="Constant">local-scope</span> x:text <span class="Special"><-</span> new <span class="Constant">[hello, ]</span> y:text <span class="Special"><-</span> new <span class="Constant">[world]</span> z:text <span class="Special"><-</span> new <span class="Constant">[!]</span> run [ z:text <span class="Special"><-</span> append x, y, z 10:@:char/<span class="Special">raw</span> <span class="Special"><-</span> copy *z ] memory-should-contain [ 10:array:character <span class="Special"><-</span> <span class="Constant">[hello, world!]</span> ] ] <span class="muScenario">scenario</span> replace-character-in-text [ <span class="Constant">local-scope</span> x:text <span class="Special"><-</span> new <span class="Constant">[abc]</span> run [ x <span class="Special"><-</span> replace x, <span class="Constant">98/b</span>, <span class="Constant">122/z</span> 10:@:char/<span class="Special">raw</span> <span class="Special"><-</span> copy *x ] memory-should-contain [ 10:array:character <span class="Special"><-</span> <span class="Constant">[azc]</span> ] ] <span class="muRecipe">def</span> replace s:text, oldc:char, newc:char, from:num/optional<span class="muRecipe"> -> </span>s:text [ <span class="Constant">local-scope</span> <span class="Constant">load-ingredients</span> len:num <span class="Special"><-</span> length *s i:num <span class="Special"><-</span> find-next s, oldc, from done?:bool <span class="Special"><-</span> greater-or-equal i, len <span class="muControl">return-if</span> done?, s/same-as-ingredient:0 *s <span class="Special"><-</span> put-index *s, i, newc i <span class="Special"><-</span> add i,<span class="Constant"> 1</span> s <span class="Special"><-</span> replace s, oldc, newc, i ] <span class="muScenario">scenario</span> replace-character-at-start [ <span class="Constant">local-scope</span> x:text <span class="Special"><-</span> new <span class="Constant">[abc]</span> run [ x <span class="Special"><-</span> replace x, <span class="Constant">97/a</span>, <span class="Constant">122/z</span> 10:@:char/<span class="Special">raw</span> <span class="Special"><-</span> copy *x ] memory-should-contain [ 10:array:character <span class="Special"><-</span> <span class="Constant">[zbc]</span> ] ] <span class="muScenario">scenario</span> replace-character-at-end [ <span class="Constant">local-scope</span> x:text <span class="Special"><-</span> new <span class="Constant">[abc]</span> run [ x <span class="Special"><-</span> replace x, <span class="Constant">99/c</span>, <span class="Constant">122/z</span> 10:@:char/<span class="Special">raw</span> <span class="Special"><-</span> copy *x ] memory-should-contain [ 10:array:character <span class="Special"><-</span> <span class="Constant">[abz]</span> ] ] <span class="muScenario">scenario</span> replace-character-missing [ <span class="Constant">local-scope</span> x:text <span class="Special"><-</span> new <span class="Constant">[abc]</span> run [ x <span class="Special"><-</span> replace x, <span class="Constant">100/d</span>, <span class="Constant">122/z</span> 10:@:char/<span class="Special">raw</span> <span class="Special"><-</span> copy *x ] memory-should-contain [ 10:array:character <span class="Special"><-</span> <span class="Constant">[abc]</span> ] ] <span class="muScenario">scenario</span> replace-all-characters [ <span class="Constant">local-scope</span> x:text <span class="Special"><-</span> new <span class="Constant">[banana]</span> run [ x <span class="Special"><-</span> replace x, <span class="Constant">97/a</span>, <span class="Constant">122/z</span> 10:@:char/<span class="Special">raw</span> <span class="Special"><-</span> copy *x ] memory-should-contain [ 10:array:character <span class="Special"><-</span> <span class="Constant">[bznznz]</span> ] ] <span class="Comment"># replace underscores in first with remaining args</span> <span class="muRecipe">def</span> interpolate template:text<span class="muRecipe"> -> </span>result:text [ <span class="Constant">local-scope</span> <span class="Constant">load-ingredients</span> <span class="Comment"># consume just the template</span> <span class="Comment"># compute result-len, space to allocate for result</span> tem-len:num <span class="Special"><-</span> length *template result-len:num <span class="Special"><-</span> copy tem-len <span class="Delimiter">{</span> <span class="Comment"># while ingredients remain</span> a:text, arg-received?:bool <span class="Special"><-</span> <span class="Constant">next-ingredient</span> <span class="muControl">break-unless</span> arg-received? <span class="Comment"># result-len = result-len + arg.length - 1 (for the 'underscore' being replaced)</span> a-len:num <span class="Special"><-</span> length *a result-len <span class="Special"><-</span> add result-len, a-len result-len <span class="Special"><-</span> subtract result-len,<span class="Constant"> 1</span> <span class="muControl">loop</span> <span class="Delimiter">}</span> <span class="Constant">rewind-ingredients</span> _ <span class="Special"><-</span> <span class="Constant">next-ingredient</span> <span class="Comment"># skip template</span> result <span class="Special"><-</span> new <span class="Constant">character:type</span>, result-len <span class="Comment"># repeatedly copy sections of template and 'holes' into result</span> result-idx:num <span class="Special"><-</span> copy<span class="Constant"> 0</span> i:num <span class="Special"><-</span> copy<span class="Constant"> 0</span> <span class="Delimiter">{</span> <span class="Comment"># while arg received</span> a:text, arg-received?:bool <span class="Special"><-</span> <span class="Constant">next-ingredient</span> <span class="muControl">break-unless</span> arg-received? <span class="Comment"># copy template into result until '_'</span> <span class="Delimiter">{</span> <span class="Comment"># while i < template.length</span> tem-done?:bool <span class="Special"><-</span> greater-or-equal i, tem-len <span class="muControl">break-if</span> tem-done?, <span class="Constant">+done</span> <span class="Comment"># while template[i] != '_'</span> in:char <span class="Special"><-</span> index *template, i underscore?:bool <span class="Special"><-</span> equal in, <span class="Constant">95/_</span> <span class="muControl">break-if</span> underscore? <span class="Comment"># result[result-idx] = template[i]</span> *result <span class="Special"><-</span> put-index *result, result-idx, in i <span class="Special"><-</span> add i,<span class="Constant"> 1</span> result-idx <span class="Special"><-</span> add result-idx,<span class="Constant"> 1</span> <span class="muControl">loop</span> <span class="Delimiter">}</span> <span class="Comment"># copy 'a' into result</span> j:num <span class="Special"><-</span> copy<span class="Constant"> 0</span> <span class="Delimiter">{</span> <span class="Comment"># while j < a.length</span> arg-done?:bool <span class="Special"><-</span> greater-or-equal j, a-len <span class="muControl">break-if</span> arg-done? <span class="Comment"># result[result-idx] = a[j]</span> in:char <span class="Special"><-</span> index *a, j *result <span class="Special"><-</span> put-index *result, result-idx, in j <span class="Special"><-</span> add j,<span class="Constant"> 1</span> result-idx <span class="Special"><-</span> add result-idx,<span class="Constant"> 1</span> <span class="muControl">loop</span> <span class="Delimiter">}</span> <span class="Comment"># skip '_' in template</span> i <span class="Special"><-</span> add i,<span class="Constant"> 1</span> <span class="muControl">loop</span> <span class="Comment"># interpolate next arg</span> <span class="Delimiter">}</span> <span class="Constant"> +done</span> <span class="Comment"># done with holes; copy rest of template directly into result</span> <span class="Delimiter">{</span> <span class="Comment"># while i < template.length</span> tem-done?:bool <span class="Special"><-</span> greater-or-equal i, tem-len <span class="muControl">break-if</span> tem-done? <span class="Comment"># result[result-idx] = template[i]</span> in:char <span class="Special"><-</span> index *template, i *result <span class="Special"><-</span> put-index *result, result-idx, in i <span class="Special"><-</span> add i,<span class="Constant"> 1</span> result-idx <span class="Special"><-</span> add result-idx,<span class="Constant"> 1</span> <span class="muControl">loop</span> <span class="Delimiter">}</span> ] <span class="muScenario">scenario</span> interpolate-works [ <span class="Constant">local-scope</span> x:text <span class="Special"><-</span> new <span class="Constant">[abc_ghi]</span> y:text <span class="Special"><-</span> new <span class="Constant">[def]</span> run [ z:text <span class="Special"><-</span> interpolate x, y 10:@:char/<span class="Special">raw</span> <span class="Special"><-</span> copy *z ] memory-should-contain [ 10:array:character <span class="Special"><-</span> <span class="Constant">[abcdefghi]</span> ] ] <span class="muScenario">scenario</span> interpolate-at-start [ <span class="Constant">local-scope</span> x:text <span class="Special"><-</span> new <span class="Constant">[_, hello!]</span> y:text <span class="Special"><-</span> new <span class="Constant">[abc]</span> run [ z:text <span class="Special"><-</span> interpolate x, y 10:@:char/<span class="Special">raw</span> <span class="Special"><-</span> copy *z ] memory-should-contain [ 10:array:character <span class="Special"><-</span> <span class="Constant">[abc, hello!]</span> <span class="Constant"> 22</span> <span class="Special"><-</span><span class="Constant"> 0</span> <span class="Comment"># out of bounds</span> ] ] <span class="muScenario">scenario</span> interpolate-at-end [ <span class="Constant">local-scope</span> x:text <span class="Special"><-</span> new <span class="Constant">[hello, _]</span> y:text <span class="Special"><-</span> new <span class="Constant">[abc]</span> run [ z:text <span class="Special"><-</span> interpolate x, y 10:@:char/<span class="Special">raw</span> <span class="Special"><-</span> copy *z ] memory-should-contain [ 10:array:character <span class="Special"><-</span> <span class="Constant">[hello, abc]</span> ] ] <span class="Comment"># result:bool <- space? c:char</span> <span class="muRecipe">def</span> space? c:char<span class="muRecipe"> -> </span>result:bool [ <span class="Constant">local-scope</span> <span class="Constant">load-ingredients</span> <span class="Comment"># most common case first</span> result <span class="Special"><-</span> equal c, <span class="Constant">32/space</span> <span class="muControl">return-if</span> result result <span class="Special"><-</span> equal c, <span class="Constant">10/newline</span> <span class="muControl">return-if</span> result result <span class="Special"><-</span> equal c, <span class="Constant">9/tab</span> <span class="muControl">return-if</span> result result <span class="Special"><-</span> equal c, <span class="Constant">13/carriage-return</span> <span class="muControl">return-if</span> result <span class="Comment"># remaining uncommon cases in sorted order</span> <span class="Comment"># <a href="http://unicode.org">http://unicode.org</a> code-points in unicode-set Z and Pattern_White_Space</span> result <span class="Special"><-</span> equal c, <span class="Constant">11/ctrl-k</span> <span class="muControl">return-if</span> result result <span class="Special"><-</span> equal c, <span class="Constant">12/ctrl-l</span> <span class="muControl">return-if</span> result result <span class="Special"><-</span> equal c, <span class="Constant">133/ctrl-0085</span> <span class="muControl">return-if</span> result result <span class="Special"><-</span> equal c, <span class="Constant">160/no-break-space</span> <span class="muControl">return-if</span> result result <span class="Special"><-</span> equal c, <span class="Constant">5760/ogham-space-mark</span> <span class="muControl">return-if</span> result result <span class="Special"><-</span> equal c, <span class="Constant">8192/en-quad</span> <span class="muControl">return-if</span> result result <span class="Special"><-</span> equal c, <span class="Constant">8193/em-quad</span> <span class="muControl">return-if</span> result result <span class="Special"><-</span> equal c, <span class="Constant">8194/en-space</span> <span class="muControl">return-if</span> result result <span class="Special"><-</span> equal c, <span class="Constant">8195/em-space</span> <span class="muControl">return-if</span> result result <span class="Special"><-</span> equal c, <span class="Constant">8196/three-per-em-space</span> <span class="muControl">return-if</span> result result <span class="Special"><-</span> equal c, <span class="Constant">8197/four-per-em-space</span> <span class="muControl">return-if</span> result result <span class="Special"><-</span> equal c, <span class="Constant">8198/six-per-em-space</span> <span class="muControl">return-if</span> result result <span class="Special"><-</span> equal c, <span class="Constant">8199/figure-space</span> <span class="muControl">return-if</span> result result <span class="Special"><-</span> equal c, <span class="Constant">8200/punctuation-space</span> <span class="muControl">return-if</span> result result <span class="Special"><-</span> equal c, <span class="Constant">8201/thin-space</span> <span class="muControl">return-if</span> result result <span class="Special"><-</span> equal c, <span class="Constant">8202/hair-space</span> <span class="muControl">return-if</span> result result <span class="Special"><-</span> equal c, <span class="Constant">8206/left-to-right</span> <span class="muControl">return-if</span> result result <span class="Special"><-</span> equal c, <span class="Constant">8207/right-to-left</span> <span class="muControl">return-if</span> result result <span class="Special"><-</span> equal c, <span class="Constant">8232/line-separator</span> <span class="muControl">return-if</span> result result <span class="Special"><-</span> equal c, <span class="Constant">8233/paragraph-separator</span> <span class="muControl">return-if</span> result result <span class="Special"><-</span> equal c, <span class="Constant">8239/narrow-no-break-space</span> <span class="muControl">return-if</span> result result <span class="Special"><-</span> equal c, <span class="Constant">8287/medium-mathematical-space</span> <span class="muControl">return-if</span> result result <span class="Special"><-</span> equal c, <span class="Constant">12288/ideographic-space</span> ] <span class="muRecipe">def</span> trim s:text<span class="muRecipe"> -> </span>result:text [ <span class="Constant">local-scope</span> <span class="Constant">load-ingredients</span> len:num <span class="Special"><-</span> length *s <span class="Comment"># left trim: compute start</span> start:num <span class="Special"><-</span> copy<span class="Constant"> 0</span> <span class="Delimiter">{</span> <span class="Delimiter">{</span> at-end?:bool <span class="Special"><-</span> greater-or-equal start, len <span class="muControl">break-unless</span> at-end? result <span class="Special"><-</span> new <span class="Constant">character:type</span>,<span class="Constant"> 0</span> <span class="muControl">return</span> <span class="Delimiter">}</span> curr:char <span class="Special"><-</span> index *s, start whitespace?:bool <span class="Special"><-</span> space? curr <span class="muControl">break-unless</span> whitespace? start <span class="Special"><-</span> add start,<span class="Constant"> 1</span> <span class="muControl">loop</span> <span class="Delimiter">}</span> <span class="Comment"># right trim: compute end</span> end:num <span class="Special"><-</span> subtract len,<span class="Constant"> 1</span> <span class="Delimiter">{</span> not-at-start?:bool <span class="Special"><-</span> greater-than end, start assert not-at-start?, <span class="Constant">[end ran up against start]</span> curr:char <span class="Special"><-</span> index *s, end whitespace?:bool <span class="Special"><-</span> space? curr <span class="muControl">break-unless</span> whitespace? end <span class="Special"><-</span> subtract end,<span class="Constant"> 1</span> <span class="muControl">loop</span> <span class="Delimiter">}</span> <span class="Comment"># result = new character[end+1 - start]</span> new-len:num <span class="Special"><-</span> subtract end, start,<span class="Constant"> -1</span> result:text <span class="Special"><-</span> new <span class="Constant">character:type</span>, new-len <span class="Comment"># copy the untrimmed parts between start and end</span> i:num <span class="Special"><-</span> copy start j:num <span class="Special"><-</span> copy<span class="Constant"> 0</span> <span class="Delimiter">{</span> <span class="Comment"># while i <= end</span> done?:bool <span class="Special"><-</span> greater-than i, end <span class="muControl">break-if</span> done? <span class="Comment"># result[j] = s[i]</span> src:char <span class="Special"><-</span> index *s, i *result <span class="Special"><-</span> put-index *result, j, src i <span class="Special"><-</span> add i,<span class="Constant"> 1</span> j <span class="Special"><-</span> add j,<span class="Constant"> 1</span> <span class="muControl">loop</span> <span class="Delimiter">}</span> ] <span class="muScenario">scenario</span> trim-unmodified [ <span class="Constant">local-scope</span> x:text <span class="Special"><-</span> new <span class="Constant">[abc]</span> run [ y:text <span class="Special"><-</span> trim x 1:@:char/<span class="Special">raw</span> <span class="Special"><-</span> copy *y ] memory-should-contain [ 1:array:character <span class="Special"><-</span> <span class="Constant">[abc]</span> ] ] <span class="muScenario">scenario</span> trim-left [ <span class="Constant">local-scope</span> x:text <span class="Special"><-</span> new <span class="Constant">[ abc]</span> run [ y:text <span class="Special"><-</span> trim x 1:@:char/<span class="Special">raw</span> <span class="Special"><-</span> copy *y ] memory-should-contain [ 1:array:character <span class="Special"><-</span> <span class="Constant">[abc]</span> ] ] <span class="muScenario">scenario</span> trim-right [ <span class="Constant">local-scope</span> x:text <span class="Special"><-</span> new <span class="Constant">[abc ]</span> run [ y:text <span class="Special"><-</span> trim x 1:@:char/<span class="Special">raw</span> <span class="Special"><-</span> copy *y ] memory-should-contain [ 1:array:character <span class="Special"><-</span> <span class="Constant">[abc]</span> ] ] <span class="muScenario">scenario</span> trim-left-right [ <span class="Constant">local-scope</span> x:text <span class="Special"><-</span> new <span class="Constant">[ abc ]</span> run [ y:text <span class="Special"><-</span> trim x 1:@:char/<span class="Special">raw</span> <span class="Special"><-</span> copy *y ] memory-should-contain [ 1:array:character <span class="Special"><-</span> <span class="Constant">[abc]</span> ] ] <span class="muScenario">scenario</span> trim-newline-tab [ <span class="Constant">local-scope</span> x:text <span class="Special"><-</span> new <span class="Constant">[ abc</span> <span class="Constant">]</span> run [ y:text <span class="Special"><-</span> trim x 1:@:char/<span class="Special">raw</span> <span class="Special"><-</span> copy *y ] memory-should-contain [ 1:array:character <span class="Special"><-</span> <span class="Constant">[abc]</span> ] ] <span class="muRecipe">def</span> find-next text:text, pattern:char, idx:num<span class="muRecipe"> -> </span>next-index:num [ <span class="Constant">local-scope</span> <span class="Constant">load-ingredients</span> len:num <span class="Special"><-</span> length *text <span class="Delimiter">{</span> eof?:bool <span class="Special"><-</span> greater-or-equal idx, len <span class="muControl">break-if</span> eof? curr:char <span class="Special"><-</span> index *text, idx found?:bool <span class="Special"><-</span> equal curr, pattern <span class="muControl">break-if</span> found? idx <span class="Special"><-</span> add idx,<span class="Constant"> 1</span> <span class="muControl">loop</span> <span class="Delimiter">}</span> <span class="muControl">return</span> idx ] <span class="muScenario">scenario</span> text-find-next [ <span class="Constant">local-scope</span> x:text <span class="Special"><-</span> new <span class="Constant">[a/b]</span> run [ 10:num/<span class="Special">raw</span> <span class="Special"><-</span> find-next x, <span class="Constant">47/slash</span>, <span class="Constant">0/start-index</span> ] memory-should-contain [ <span class="Constant"> 10</span> <span class="Special"><-</span><span class="Constant"> 1</span> ] ] <span class="muScenario">scenario</span> text-find-next-empty [ <span class="Constant">local-scope</span> x:text <span class="Special"><-</span> new <span class="Constant">[]</span> run [ 10:num/<span class="Special">raw</span> <span class="Special"><-</span> find-next x, <span class="Constant">47/slash</span>, <span class="Constant">0/start-index</span> ] memory-should-contain [ <span class="Constant"> 10</span> <span class="Special"><-</span><span class="Constant"> 0</span> ] ] <span class="muScenario">scenario</span> text-find-next-initial [ <span class="Constant">local-scope</span> x:text <span class="Special"><-</span> new <span class="Constant">[/abc]</span> run [ 10:num/<span class="Special">raw</span> <span class="Special"><-</span> find-next x, <span class="Constant">47/slash</span>, <span class="Constant">0/start-index</span> ] memory-should-contain [ <span class="Constant"> 10</span> <span class="Special"><-</span><span class="Constant"> 0</span> <span class="Comment"># prefix match</span> ] ] <span class="muScenario">scenario</span> text-find-next-final [ <span class="Constant">local-scope</span> x:text <span class="Special"><-</span> new <span class="Constant">[abc/]</span> run [ 10:num/<span class="Special">raw</span> <span class="Special"><-</span> find-next x, <span class="Constant">47/slash</span>, <span class="Constant">0/start-index</span> ] memory-should-contain [ <span class="Constant"> 10</span> <span class="Special"><-</span><span class="Constant"> 3</span> <span class="Comment"># suffix match</span> ] ] <span class="muScenario">scenario</span> text-find-next-missing [ <span class="Constant">local-scope</span> x:text <span class="Special"><-</span> new <span class="Constant">[abcd]</span> run [ 10:num/<span class="Special">raw</span> <span class="Special"><-</span> find-next x, <span class="Constant">47/slash</span>, <span class="Constant">0/start-index</span> ] memory-should-contain [ <span class="Constant"> 10</span> <span class="Special"><-</span><span class="Constant"> 4</span> <span class="Comment"># no match</span> ] ] <span class="muScenario">scenario</span> text-find-next-invalid-index [ <span class="Constant">local-scope</span> x:text <span class="Special"><-</span> new <span class="Constant">[abc]</span> run [ 10:num/<span class="Special">raw</span> <span class="Special"><-</span> find-next x, <span class="Constant">47/slash</span>, <span class="Constant">4/start-index</span> ] memory-should-contain [ <span class="Constant"> 10</span> <span class="Special"><-</span><span class="Constant"> 4</span> <span class="Comment"># no change</span> ] ] <span class="muScenario">scenario</span> text-find-next-first [ <span class="Constant">local-scope</span> x:text <span class="Special"><-</span> new <span class="Constant">[ab/c/]</span> run [ 10:num/<span class="Special">raw</span> <span class="Special"><-</span> find-next x, <span class="Constant">47/slash</span>, <span class="Constant">0/start-index</span> ] memory-should-contain [ <span class="Constant"> 10</span> <span class="Special"><-</span><span class="Constant"> 2</span> <span class="Comment"># first '/' of multiple</span> ] ] <span class="muScenario">scenario</span> text-find-next-second [ <span class="Constant">local-scope</span> x:text <span class="Special"><-</span> new <span class="Constant">[ab/c/]</span> run [ 10:num/<span class="Special">raw</span> <span class="Special"><-</span> find-next x, <span class="Constant">47/slash</span>, <span class="Constant">3/start-index</span> ] memory-should-contain [ <span class="Constant"> 10</span> <span class="Special"><-</span><span class="Constant"> 4</span> <span class="Comment"># second '/' of multiple</span> ] ] <span class="Comment"># search for a pattern of multiple characters</span> <span class="Comment"># fairly dumb algorithm</span> <span class="muRecipe">def</span> find-next text:text, pattern:text, idx:num<span class="muRecipe"> -> </span>next-index:num [ <span class="Constant">local-scope</span> <span class="Constant">load-ingredients</span> first:char <span class="Special"><-</span> index *pattern,<span class="Constant"> 0</span> <span class="Comment"># repeatedly check for match at current idx</span> len:num <span class="Special"><-</span> length *text <span class="Delimiter">{</span> <span class="Comment"># does some unnecessary work checking even when there isn't enough of text left</span> done?:bool <span class="Special"><-</span> greater-or-equal idx, len <span class="muControl">break-if</span> done? found?:bool <span class="Special"><-</span> match-at text, pattern, idx <span class="muControl">break-if</span> found? idx <span class="Special"><-</span> add idx,<span class="Constant"> 1</span> <span class="Comment"># optimization: skip past indices that definitely won't match</span> idx <span class="Special"><-</span> find-next text, first, idx <span class="muControl">loop</span> <span class="Delimiter">}</span> <span class="muControl">return</span> idx ] <span class="muScenario">scenario</span> find-next-text-1 [ <span class="Constant">local-scope</span> x:text <span class="Special"><-</span> new <span class="Constant">[abc]</span> y:text <span class="Special"><-</span> new <span class="Constant">[bc]</span> run [ 10:num/<span class="Special">raw</span> <span class="Special"><-</span> find-next x, y,<span class="Constant"> 0</span> ] memory-should-contain [ <span class="Constant"> 10</span> <span class="Special"><-</span><span class="Constant"> 1</span> ] ] <span class="muScenario">scenario</span> find-next-text-2 [ <span class="Constant">local-scope</span> x:text <span class="Special"><-</span> new <span class="Constant">[abcd]</span> y:text <span class="Special"><-</span> new <span class="Constant">[bc]</span> run [ 10:num/<span class="Special">raw</span> <span class="Special"><-</span> find-next x, y,<span class="Constant"> 1</span> ] memory-should-contain [ <span class="Constant"> 10</span> <span class="Special"><-</span><span class="Constant"> 1</span> ] ] <span class="muScenario">scenario</span> find-next-no-match [ <span class="Constant">local-scope</span> x:text <span class="Special"><-</span> new <span class="Constant">[abc]</span> y:text <span class="Special"><-</span> new <span class="Constant">[bd]</span> run [ 10:num/<span class="Special">raw</span> <span class="Special"><-</span> find-next x, y,<span class="Constant"> 0</span> ] memory-should-contain [ <span class="Constant"> 10</span> <span class="Special"><-</span><span class="Constant"> 3</span> <span class="Comment"># not found</span> ] ] <span class="muScenario">scenario</span> find-next-suffix-match [ <span class="Constant">local-scope</span> x:text <span class="Special"><-</span> new <span class="Constant">[abcd]</span> y:text <span class="Special"><-</span> new <span class="Constant">[cd]</span> run [ 10:num/<span class="Special">raw</span> <span class="Special"><-</span> find-next x, y,<span class="Constant"> 0</span> ] memory-should-contain [ <span class="Constant"> 10</span> <span class="Special"><-</span><span class="Constant"> 2</span> ] ] <span class="muScenario">scenario</span> find-next-suffix-match-2 [ <span class="Constant">local-scope</span> x:text <span class="Special"><-</span> new <span class="Constant">[abcd]</span> y:text <span class="Special"><-</span> new <span class="Constant">[cde]</span> run [ 10:num/<span class="Special">raw</span> <span class="Special"><-</span> find-next x, y,<span class="Constant"> 0</span> ] memory-should-contain [ <span class="Constant"> 10</span> <span class="Special"><-</span><span class="Constant"> 4</span> <span class="Comment"># not found</span> ] ] <span class="Comment"># checks if pattern matches at index 'idx'</span> <span class="muRecipe">def</span> match-at text:text, pattern:text, idx:num<span class="muRecipe"> -> </span>result:bool [ <span class="Constant">local-scope</span> <span class="Constant">load-ingredients</span> pattern-len:num <span class="Special"><-</span> length *pattern <span class="Comment"># check that there's space left for the pattern</span> <span class="Delimiter">{</span> x:num <span class="Special"><-</span> length *text x <span class="Special"><-</span> subtract x, pattern-len enough-room?:bool <span class="Special"><-</span> lesser-or-equal idx, x <span class="muControl">break-if</span> enough-room? <span class="muControl">return</span> <span class="Constant">0/not-found</span> <span class="Delimiter">}</span> <span class="Comment"># check each character of pattern</span> pattern-idx:num <span class="Special"><-</span> copy<span class="Constant"> 0</span> <span class="Delimiter">{</span> done?:bool <span class="Special"><-</span> greater-or-equal pattern-idx, pattern-len <span class="muControl">break-if</span> done? c:char <span class="Special"><-</span> index *text, idx exp:char <span class="Special"><-</span> index *pattern, pattern-idx <span class="Delimiter">{</span> match?:bool <span class="Special"><-</span> equal c, exp <span class="muControl">break-if</span> match? <span class="muControl">return</span> <span class="Constant">0/not-found</span> <span class="Delimiter">}</span> idx <span class="Special"><-</span> add idx,<span class="Constant"> 1</span> pattern-idx <span class="Special"><-</span> add pattern-idx,<span class="Constant"> 1</span> <span class="muControl">loop</span> <span class="Delimiter">}</span> <span class="muControl">return</span> <span class="Constant">1/found</span> ] <span class="muScenario">scenario</span> match-at-checks-pattern-at-index [ <span class="Constant">local-scope</span> x:text <span class="Special"><-</span> new <span class="Constant">[abc]</span> y:text <span class="Special"><-</span> new <span class="Constant">[ab]</span> run [ 10:bool/<span class="Special">raw</span> <span class="Special"><-</span> match-at x, y,<span class="Constant"> 0</span> ] memory-should-contain [ <span class="Constant"> 10</span> <span class="Special"><-</span><span class="Constant"> 1</span> <span class="Comment"># match found</span> ] ] <span class="muScenario">scenario</span> match-at-reflexive [ <span class="Constant">local-scope</span> x:text <span class="Special"><-</span> new <span class="Constant">[abc]</span> run [ 10:bool/<span class="Special">raw</span> <span class="Special"><-</span> match-at x, x,<span class="Constant"> 0</span> ] memory-should-contain [ <span class="Constant"> 10</span> <span class="Special"><-</span><span class="Constant"> 1</span> <span class="Comment"># match found</span> ] ] <span class="muScenario">scenario</span> match-at-outside-bounds [ <span class="Constant">local-scope</span> x:text <span class="Special"><-</span> new <span class="Constant">[abc]</span> y:text <span class="Special"><-</span> new <span class="Constant">[a]</span> run [ 10:bool/<span class="Special">raw</span> <span class="Special"><-</span> match-at x, y,<span class="Constant"> 4</span> ] memory-should-contain [ <span class="Constant"> 10</span> <span class="Special"><-</span><span class="Constant"> 0</span> <span class="Comment"># never matches</span> ] ] <span class="muScenario">scenario</span> match-at-empty-pattern [ <span class="Constant">local-scope</span> x:text <span class="Special"><-</span> new <span class="Constant">[abc]</span> y:text <span class="Special"><-</span> new <span class="Constant">[]</span> run [ 10:bool/<span class="Special">raw</span> <span class="Special"><-</span> match-at x, y,<span class="Constant"> 0</span> ] memory-should-contain [ <span class="Constant"> 10</span> <span class="Special"><-</span><span class="Constant"> 1</span> <span class="Comment"># always matches empty pattern given a valid index</span> ] ] <span class="muScenario">scenario</span> match-at-empty-pattern-outside-bound [ <span class="Constant">local-scope</span> x:text <span class="Special"><-</span> new <span class="Constant">[abc]</span> y:text <span class="Special"><-</span> new <span class="Constant">[]</span> run [ 10:bool/<span class="Special">raw</span> <span class="Special"><-</span> match-at x, y,<span class="Constant"> 4</span> ] memory-should-contain [ <span class="Constant"> 10</span> <span class="Special"><-</span><span class="Constant"> 0</span> <span class="Comment"># no match</span> ] ] <span class="muScenario">scenario</span> match-at-empty-text [ <span class="Constant">local-scope</span> x:text <span class="Special"><-</span> new <span class="Constant">[]</span> y:text <span class="Special"><-</span> new <span class="Constant">[abc]</span> run [ 10:bool/<span class="Special">raw</span> <span class="Special"><-</span> match-at x, y,<span class="Constant"> 0</span> ] memory-should-contain [ <span class="Constant"> 10</span> <span class="Special"><-</span><span class="Constant"> 0</span> <span class="Comment"># no match</span> ] ] <span class="muScenario">scenario</span> match-at-empty-against-empty [ <span class="Constant">local-scope</span> x:text <span class="Special"><-</span> new <span class="Constant">[]</span> run [ 10:bool/<span class="Special">raw</span> <span class="Special"><-</span> match-at x, x,<span class="Constant"> 0</span> ] memory-should-contain [ <span class="Constant"> 10</span> <span class="Special"><-</span><span class="Constant"> 1</span> <span class="Comment"># matches because pattern is also empty</span> ] ] <span class="muScenario">scenario</span> match-at-inside-bounds [ <span class="Constant">local-scope</span> x:text <span class="Special"><-</span> new <span class="Constant">[abc]</span> y:text <span class="Special"><-</span> new <span class="Constant">[bc]</span> run [ 10:bool/<span class="Special">raw</span> <span class="Special"><-</span> match-at x, y,<span class="Constant"> 1</span> ] memory-should-contain [ <span class="Constant"> 10</span> <span class="Special"><-</span><span class="Constant"> 1</span> <span class="Comment"># match</span> ] ] <span class="muScenario">scenario</span> match-at-inside-bounds-2 [ <span class="Constant">local-scope</span> x:text <span class="Special"><-</span> new <span class="Constant">[abc]</span> y:text <span class="Special"><-</span> new <span class="Constant">[bc]</span> run [ 10:bool/<span class="Special">raw</span> <span class="Special"><-</span> match-at x, y,<span class="Constant"> 0</span> ] memory-should-contain [ <span class="Constant"> 10</span> <span class="Special"><-</span><span class="Constant"> 0</span> <span class="Comment"># no match</span> ] ] <span class="muRecipe">def</span> split s:text, delim:char<span class="muRecipe"> -> </span>result:&:@:text [ <span class="Constant">local-scope</span> <span class="Constant">load-ingredients</span> <span class="Comment"># empty text? return empty array</span> len:num <span class="Special"><-</span> length *s <span class="Delimiter">{</span> empty?:bool <span class="Special"><-</span> equal len,<span class="Constant"> 0</span> <span class="muControl">break-unless</span> empty? result <span class="Special"><-</span> new <span class="Delimiter">{</span>(address array character): type<span class="Delimiter">}</span>,<span class="Constant"> 0</span> <span class="muControl">return</span> <span class="Delimiter">}</span> <span class="Comment"># count #pieces we need room for</span> count:num <span class="Special"><-</span> copy<span class="Constant"> 1</span> <span class="Comment"># n delimiters = n+1 pieces</span> idx:num <span class="Special"><-</span> copy<span class="Constant"> 0</span> <span class="Delimiter">{</span> idx <span class="Special"><-</span> find-next s, delim, idx done?:bool <span class="Special"><-</span> greater-or-equal idx, len <span class="muControl">break-if</span> done? idx <span class="Special"><-</span> add idx,<span class="Constant"> 1</span> count <span class="Special"><-</span> add count,<span class="Constant"> 1</span> <span class="muControl">loop</span> <span class="Delimiter">}</span> <span class="Comment"># allocate space</span> result <span class="Special"><-</span> new <span class="Delimiter">{</span>(address array character): type<span class="Delimiter">}</span>, count <span class="Comment"># repeatedly copy slices start..end until delimiter into result[curr-result]</span> curr-result:num <span class="Special"><-</span> copy<span class="Constant"> 0</span> start:num <span class="Special"><-</span> copy<span class="Constant"> 0</span> <span class="Delimiter">{</span> <span class="Comment"># while next delim exists</span> done?:bool <span class="Special"><-</span> greater-or-equal start, len <span class="muControl">break-if</span> done? end:num <span class="Special"><-</span> find-next s, delim, start <span class="Comment"># copy start..end into result[curr-result]</span> dest:text <span class="Special"><-</span> copy-range s, start, end *result <span class="Special"><-</span> put-index *result, curr-result, dest <span class="Comment"># slide over to next slice</span> start <span class="Special"><-</span> add end,<span class="Constant"> 1</span> curr-result <span class="Special"><-</span> add curr-result,<span class="Constant"> 1</span> <span class="muControl">loop</span> <span class="Delimiter">}</span> ] <span class="muScenario">scenario</span> text-split-1 [ <span class="Constant">local-scope</span> x:text <span class="Special"><-</span> new <span class="Constant">[a/b]</span> run [ y:&:@:text <span class="Special"><-</span> split x, <span class="Constant">47/slash</span> 10:num/<span class="Special">raw</span> <span class="Special"><-</span> length *y a:text <span class="Special"><-</span> index *y,<span class="Constant"> 0</span> b:text <span class="Special"><-</span> index *y,<span class="Constant"> 1</span> 20:@:char/<span class="Special">raw</span> <span class="Special"><-</span> copy *a 30:@:char/<span class="Special">raw</span> <span class="Special"><-</span> copy *b ] memory-should-contain [ <span class="Constant"> 10</span> <span class="Special"><-</span><span class="Constant"> 2</span> <span class="Comment"># length of result</span> 20:array:character <span class="Special"><-</span> <span class="Constant">[a]</span> 30:array:character <span class="Special"><-</span> <span class="Constant">[b]</span> ] ] <span class="muScenario">scenario</span> text-split-2 [ <span class="Constant">local-scope</span> x:text <span class="Special"><-</span> new <span class="Constant">[a/b/c]</span> run [ y:&:@:text <span class="Special"><-</span> split x, <span class="Constant">47/slash</span> 10:num/<span class="Special">raw</span> <span class="Special"><-</span> length *y a:text <span class="Special"><-</span> index *y,<span class="Constant"> 0</span> b:text <span class="Special"><-</span> index *y,<span class="Constant"> 1</span> c:text <span class="Special"><-</span> index *y,<span class="Constant"> 2</span> 20:@:char/<span class="Special">raw</span> <span class="Special"><-</span> copy *a 30:@:char/<span class="Special">raw</span> <span class="Special"><-</span> copy *b 40:@:char/<span class="Special">raw</span> <span class="Special"><-</span> copy *c ] memory-should-contain [ <span class="Constant"> 10</span> <span class="Special"><-</span><span class="Constant"> 3</span> <span class="Comment"># length of result</span> 20:array:character <span class="Special"><-</span> <span class="Constant">[a]</span> 30:array:character <span class="Special"><-</span> <span class="Constant">[b]</span> 40:array:character <span class="Special"><-</span> <span class="Constant">[c]</span> ] ] <span class="muScenario">scenario</span> text-split-missing [ <span class="Constant">local-scope</span> x:text <span class="Special"><-</span> new <span class="Constant">[abc]</span> run [ y:&:@:text <span class="Special"><-</span> split x, <span class="Constant">47/slash</span> 10:num/<span class="Special">raw</span> <span class="Special"><-</span> length *y a:text <span class="Special"><-</span> index *y,<span class="Constant"> 0</span> 20:@:char/<span class="Special">raw</span> <span class="Special"><-</span> copy *a ] memory-should-contain [ <span class="Constant"> 10</span> <span class="Special"><-</span><span class="Constant"> 1</span> <span class="Comment"># length of result</span> 20:array:character <span class="Special"><-</span> <span class="Constant">[abc]</span> ] ] <span class="muScenario">scenario</span> text-split-empty [ <span class="Constant">local-scope</span> x:text <span class="Special"><-</span> new <span class="Constant">[]</span> run [ y:&:@:text <span class="Special"><-</span> split x, <span class="Constant">47/slash</span> 10:num/<span class="Special">raw</span> <span class="Special"><-</span> length *y ] memory-should-contain [ <span class="Constant"> 10</span> <span class="Special"><-</span><span class="Constant"> 0</span> <span class="Comment"># empty result</span> ] ] <span class="muScenario">scenario</span> text-split-empty-piece [ <span class="Constant">local-scope</span> x:text <span class="Special"><-</span> new <span class="Constant">[a/b//c]</span> run [ y:&:@:text <span class="Special"><-</span> split x:text, <span class="Constant">47/slash</span> 10:num/<span class="Special">raw</span> <span class="Special"><-</span> length *y a:text <span class="Special"><-</span> index *y,<span class="Constant"> 0</span> b:text <span class="Special"><-</span> index *y,<span class="Constant"> 1</span> c:text <span class="Special"><-</span> index *y,<span class="Constant"> 2</span> d:text <span class="Special"><-</span> index *y,<span class="Constant"> 3</span> 20:@:char/<span class="Special">raw</span> <span class="Special"><-</span> copy *a 30:@:char/<span class="Special">raw</span> <span class="Special"><-</span> copy *b 40:@:char/<span class="Special">raw</span> <span class="Special"><-</span> copy *c 50:@:char/<span class="Special">raw</span> <span class="Special"><-</span> copy *d ] memory-should-contain [ <span class="Constant"> 10</span> <span class="Special"><-</span><span class="Constant"> 4</span> <span class="Comment"># length of result</span> 20:array:character <span class="Special"><-</span> <span class="Constant">[a]</span> 30:array:character <span class="Special"><-</span> <span class="Constant">[b]</span> 40:array:character <span class="Special"><-</span> <span class="Constant">[]</span> 50:array:character <span class="Special"><-</span> <span class="Constant">[c]</span> ] ] <span class="muRecipe">def</span> split-first text:text, delim:char<span class="muRecipe"> -> </span>x:text, y:text [ <span class="Constant">local-scope</span> <span class="Constant">load-ingredients</span> <span class="Comment"># empty text? return empty texts</span> len:num <span class="Special"><-</span> length *text <span class="Delimiter">{</span> empty?:bool <span class="Special"><-</span> equal len,<span class="Constant"> 0</span> <span class="muControl">break-unless</span> empty? x:text <span class="Special"><-</span> new <span class="Constant">[]</span> y:text <span class="Special"><-</span> new <span class="Constant">[]</span> <span class="muControl">return</span> <span class="Delimiter">}</span> idx:num <span class="Special"><-</span> find-next text, delim,<span class="Constant"> 0</span> x:text <span class="Special"><-</span> copy-range text,<span class="Constant"> 0</span>, idx idx <span class="Special"><-</span> add idx,<span class="Constant"> 1</span> y:text <span class="Special"><-</span> copy-range text, idx, len ] <span class="muScenario">scenario</span> text-split-first [ <span class="Constant">local-scope</span> x:text <span class="Special"><-</span> new <span class="Constant">[a/b]</span> run [ y:text, z:text <span class="Special"><-</span> split-first x, <span class="Constant">47/slash</span> 10:@:char/<span class="Special">raw</span> <span class="Special"><-</span> copy *y 20:@:char/<span class="Special">raw</span> <span class="Special"><-</span> copy *z ] memory-should-contain [ 10:array:character <span class="Special"><-</span> <span class="Constant">[a]</span> 20:array:character <span class="Special"><-</span> <span class="Constant">[b]</span> ] ] <span class="muRecipe">def</span> copy-range buf:text, start:num, end:num<span class="muRecipe"> -> </span>result:text [ <span class="Constant">local-scope</span> <span class="Constant">load-ingredients</span> <span class="Comment"># if end is out of bounds, trim it</span> len:num <span class="Special"><-</span> length *buf end:num <span class="Special"><-</span> min len, end <span class="Comment"># allocate space for result</span> len <span class="Special"><-</span> subtract end, start result:text <span class="Special"><-</span> new <span class="Constant">character:type</span>, len <span class="Comment"># copy start..end into result[curr-result]</span> src-idx:num <span class="Special"><-</span> copy start dest-idx:num <span class="Special"><-</span> copy<span class="Constant"> 0</span> <span class="Delimiter">{</span> done?:bool <span class="Special"><-</span> greater-or-equal src-idx, end <span class="muControl">break-if</span> done? src:char <span class="Special"><-</span> index *buf, src-idx *result <span class="Special"><-</span> put-index *result, dest-idx, src src-idx <span class="Special"><-</span> add src-idx,<span class="Constant"> 1</span> dest-idx <span class="Special"><-</span> add dest-idx,<span class="Constant"> 1</span> <span class="muControl">loop</span> <span class="Delimiter">}</span> ] <span class="muScenario">scenario</span> copy-range-works [ <span class="Constant">local-scope</span> x:text <span class="Special"><-</span> new <span class="Constant">[abc]</span> run [ y:text <span class="Special"><-</span> copy-range x,<span class="Constant"> 1</span>,<span class="Constant"> 3</span> 1:@:char/<span class="Special">raw</span> <span class="Special"><-</span> copy *y ] memory-should-contain [ 1:array:character <span class="Special"><-</span> <span class="Constant">[bc]</span> ] ] <span class="muScenario">scenario</span> copy-range-out-of-bounds [ <span class="Constant">local-scope</span> x:text <span class="Special"><-</span> new <span class="Constant">[abc]</span> run [ y:text <span class="Special"><-</span> copy-range x,<span class="Constant"> 2</span>,<span class="Constant"> 4</span> 1:@:char/<span class="Special">raw</span> <span class="Special"><-</span> copy *y ] memory-should-contain [ 1:array:character <span class="Special"><-</span> <span class="Constant">[c]</span> ] ] <span class="muScenario">scenario</span> copy-range-out-of-bounds-2 [ <span class="Constant">local-scope</span> x:text <span class="Special"><-</span> new <span class="Constant">[abc]</span> run [ y:text <span class="Special"><-</span> copy-range x,<span class="Constant"> 3</span>,<span class="Constant"> 3</span> 1:@:char/<span class="Special">raw</span> <span class="Special"><-</span> copy *y ] memory-should-contain [ 1:array:character <span class="Special"><-</span> <span class="Constant">[]</span> ] ] </pre> </body> </html> <!-- vim: set foldmethod=manual : -->