about summary refs log blame commit diff stats
path: root/README.md
blob: f18d320e6c69e1a45f44ed78b57819d1f56d996c (plain) (tree)
7dc8fef8 ^pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
<!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 - 060string.mu</title>
<meta name="Generator" content="Vim/7.4">
<meta name="plugin-version" content="vim7.4_v1">
<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-family: monospace; color: #eeeeee; background-color: #080808; }
* { font-size: 1.05em; }
.muRecipe { color: #ff8700; }
.Delimiter { color: #a04060; }
.muScenario { color: #00af00; }
.Comment { color: #9090ff; }
.Constant { color: #00a0a0; }
.Special { color: #ff6060; }
.CommentedCode { color: #6c6c6c; }
.muControl { color: #c0a020; }
-->
</style>

<script type='text/javascript'>
<!--

-->
</script>
</head>
<body>
<pre id='vimCodeElement'>
<span class="Comment"># Some useful helpers for dealing with strings.</span>

<span class="muRecipe">recipe</span> string-equal [
  <span class="Constant">local-scope</span>
  a:address:array:character<span class="Special"> &lt;- </span><span class="Constant">next-ingredient</span>
  a-len:number<span class="Special"> &lt;- </span>length *a
  b:address:array:character<span class="Special"> &lt;- </span><span class="Constant">next-ingredient</span>
  b-len:number<span class="Special"> &lt;- </span>length *b
  <span class="Comment"># compare lengths</span>
  <span class="Delimiter">{</span>
    trace <span class="Constant">[string-equal]</span>, <span class="Constant">[comparing lengths]</span>
    length-equal?:boolean<span class="Special"> &lt;- </span>equal a-len, b-len
    <span class="muControl">break-if</span> length-equal?
    <span class="muControl">reply</span> <span class="Constant">0</span>
  <span class="Delimiter">}</span>
  <span class="Comment"># compare each corresponding character</span>
  trace <span class="Constant">[string-equal]</span>, <span class="Constant">[comparing characters]</span>
  i:number<span class="Special"> &lt;- </span>copy <span class="Constant">0</span>
  <span class="Delimiter">{</span>
    done?:boolean<span class="Special"> &lt;- </span>greater-or-equal i, a-len
    <span class="muControl">break-if</span> done?
    a2:character<span class="Special"> &lt;- </span>index *a, i
    b2:character<span class="Special"> &lt;- </span>index *b, i
    <span class="Delimiter">{</span>
      chars-match?:boolean<span class="Special"> &lt;- </span>equal a2, b2
      <span class="muControl">break-if</span> chars-match?
      <span class="muControl">reply</span> <span class="Constant">0</span>
    <span class="Delimiter">}</span>
    i<span class="Special"> &lt;- </span>add i, <span class="Constant">1</span>
    <span class="muControl">loop</span>
  <span class="Delimiter">}</span>
  <span class="muControl">reply</span> <span class="Constant">1</span>
]

<span class="muScenario">scenario</span> string-equal-reflexive [
  run [
    <span class="Constant"pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
ranger 1.9.3
============

<img src="https://ranger.github.io/ranger_logo.png" width="150">

[![Build Status](https://travis-ci.org/ranger/ranger.svg?branch=master)](https://travis-ci.org/ranger/ranger)
<a href="https://repology.org/metapackage/ranger/versions">
  <img src="https://repology.org/badge/latest-versions/ranger.svg" alt="latest packaged version(s)">
</a>

ranger is a console file manager with VI key bindings.  It provides a
minimalistic and nice curses interface with a view on the directory hierarchy.
It ships with `rifle`, a file launcher that is good at automatically finding
out which program to use for what file type.

![screenshot](https://raw.githubusercontent.com/ranger/ranger-assets/master/screenshots/screenshot.png)

For `mc` aficionados there's also the multi-pane viewmode.

<p>
<img src="https://raw.githubusercontent.com/ranger/ranger-assets/master/screenshots/twopane.png" alt="two panes" width="49%" />
<img src="https://raw.githubusercontent.com/ranger/ranger-assets/master/screenshots/multipane.png" alt="multiple panes" width="49%" />
</p>

This file describes ranger and how to get it to run.  For instructions on the
usage, please read the man page (`man ranger` in a terminal).  See `HACKING.md`
for development-specific information.

For configuration, check the files in `ranger/config/` or copy the
default config to `~/.config/ranger` with `ranger --copy-config`
(see [instructions](#getting-started)).

The `examples/` directory contains several scripts and plugins that demonstrate how
ranger can be extended or combined with other programs.  These files can be
found in the git repository or in `/usr/share/doc/ranger`.

A note to packagers: Versions meant for packaging are listed in the changelog
on the website.


About
-----
* Authors:     see `AUTHORS` file
* License:     GNU General Public License Version 3
* Website:     https://ranger.github.io/
* Download:    https://ranger.github.io/ranger-stable.tar.gz
* Bug reports: https://github.com/ranger/ranger/issues
* git clone    https://github.com/ranger/ranger.git


Design Goals
------------
* An easily maintainable file manager in a high level language
* A quick way to switch directories and browse the file system
* Keep it small but useful, do one thing and do it well
* Console-based, with smooth integration into the unix shell


Features
--------
* UTF-8 Support  (if your Python copy supports it)
* Multi-column display
* Preview of the selected file/directory
* Common file operations (create/chmod/copy/delete/...)
* Renaming multiple files at once
* VIM-like console and hotkeys
* Automatically determine file types and run them with correct programs
* Change the directory of your shell after exiting ranger
* Tabs, bookmarks, mouse support...


Dependencies
------------
* Python (`>=2.6` or `>=3.1`) with the `curses` module
  and (optionally) wide-unicode support
* A pager (`less` by default)

### Optional dependencies

For general usage:

* `file` for determining file types
* `chardet` (Python package) for improved encoding detection of text files
* `sudo` to use the "run as root" feature
* `python-bidi` (Python package) to display right-to-left file names correctly
  (Hebrew, Arabic)

For enhanced file previews (with `scope.sh`):

* `img2txt` (from `caca-utils`) for ASCII-art image previews
* `w3mimgdisplay`, `ueberzug`, `mpv`, `iTerm2`, `kitty`, `terminology` or `urxvt` for image previews
* `convert` (from `imagemagick`) to auto-rotate images and for SVG previews
* `ffmpegthumbnailer` for video thumbnails
* `highlight`, `bat` or `pygmentize` for syntax highlighting of code
* `atool`, `bsdtar`, `unrar` and/or `7z` to preview archives
* `bsdtar`, `tar`, `unrar`, `unzip` and/or `zipinfo` (and `sed`) to preview
  archives as their first image
* `lynx`, `w3m` or `elinks` to preview html pages
* `pdftotext` or `mutool` (and `fmt`) for textual `pdf` previews, `pdftoppm` to
  preview as image
* `djvutxt` for textual DjVu previews, `ddjvu` to preview as image
* `calibre` or `epub-thumbnailer` for image previews of ebooks
* `transmission-show` for viewing BitTorrent information
* `mediainfo` or `exiftool` for viewing information about media files
* `odt2txt` for OpenDocument text files (`odt`, `ods`, `odp` and `sxw`)
* `python` or `jq` for JSON files
* `fontimage` for font previews
* `openscad` for 3D model previews (`stl`, `off`, `dxf`, `scad`, `csg`)

Installing
----------
Use the package manager of your operating system to install ranger.
You can also install ranger through PyPI: ```pip install ranger-fm```.

<details>
  <summary>
    Check current version:
    <sub>
      <a href="https://repology.org/metapackage/ranger/versions">
        <img src="https://repology.org/badge/tiny-repos/ranger.svg" alt="Packaging status">
      </a>
    </sub>
  </summary>
  <a href="https://repology.org/metapackage/ranger/versions">
    <img src="https://repology.org/badge/vertical-allrepos/ranger.svg" alt="Packaging status">
  </a>
</details>

### Installing from a clone
Note that you don't *have* to install ranger; you can simply run `ranger.py`.

To install ranger manually:
```
sudo make install
```

This translates roughly to:
```
sudo python setup.py install --optimize=1 --record=install_log.txt
```

This also saves a list of all installed files to `install_log.txt`, which you can
use to uninstall ranger.


Getting Started
---------------
After starting ranger, you can use the Arrow Keys or `h` `j` `k` `l` to
navigate, `Enter` to open a file or `q` to quit.  The third column shows a
preview of the current file.  The second is the main column and the first shows
the parent directory.

Ranger can automatically copy default configuration files to `~/.config/ranger`
if you run it with the switch `--copy-config=( rc | scope | ... | all )`.
See `ranger --help` for a description of that switch.  Also check
`ranger/config/` for the default configuration.


Going Further
---------------
* To get the most out of ranger, read the [Official User Guide](https://github.com/ranger/ranger/wiki/Official-user-guide).
* For frequently asked questions, see the [FAQ](https://github.com/ranger/ranger/wiki/FAQ%3A-Frequently-Asked-Questions).
* For more information on customization, see the [wiki](https://github.com/ranger/ranger/wiki).


Community
---------------
For help, support, or if you just want to hang out with us, you can find us here:
* **IRC**: channel **#ranger** on [freenode](https://freenode.net/kb/answer/chat)
* **Reddit**: [r/ranger](https://www.reddit.com/r/ranger/)
ass="na">class="Constant">3</span>:boolean/<span class="Special">raw &lt;- </span>string-equal x, y ] memory-should-contain [ <span class="Constant">3</span><span class="Special"> &lt;- </span><span class="Constant">0</span> <span class="Comment"># abc != abd</span> ] ] <span class="Comment"># A new type to help incrementally construct strings.</span> container buffer [ length:number data:address:array:character ] <span class="muRecipe">recipe</span> new-buffer [ <span class="Constant">local-scope</span> <span class="CommentedCode">#? $print default-space:address:array:location, 10/newline</span> result:address:buffer<span class="Special"> &lt;- </span>new buffer:type len:address:number<span class="Special"> &lt;- </span>get-address *result, length:offset *len:address:number<span class="Special"> &lt;- </span>copy <span class="Constant">0</span> s:address:address:array:character<span class="Special"> &lt;- </span>get-address *result, data:offset capacity:number, found?:boolean<span class="Special"> &lt;- </span><span class="Constant">next-ingredient</span> assert found?, <span class="Constant">[new-buffer must get a capacity argument]</span> *s<span class="Special"> &lt;- </span>new character:type, capacity <span class="CommentedCode">#? $print *s, 10/newline</span> <span class="muControl">reply</span> result ] <span class="muRecipe">recipe</span> grow-buffer [ <span class="Constant">local-scope</span> in:address:buffer<span class="Special"> &lt;- </span><span class="Constant">next-ingredient</span> <span class="Comment"># double buffer size</span> x:address:address:array:character<span class="Special"> &lt;- </span>get-address *in, data:offset oldlen:number<span class="Special"> &lt;- </span>length **x newlen:number<span class="Special"> &lt;- </span>multiply oldlen, <span class="Constant">2</span> olddata:address:array:character<span class="Special"> &lt;- </span>copy *x *x<span class="Special"> &lt;- </span>new character:type, newlen <span class="Comment"># copy old contents</span> i:number<span class="Special"> &lt;- </span>copy <span class="Constant">0</span> <span class="Delimiter">{</span> done?:boolean<span class="Special"> &lt;- </span>greater-or-equal i, oldlen <span class="muControl">break-if</span> done? src:character<span class="Special"> &lt;- </span>index *olddata, i dest:address:character<span class="Special"> &lt;- </span>index-address **x, i *dest<span class="Special"> &lt;- </span>copy src i<span class="Special"> &lt;- </span>add i, <span class="Constant">1</span> <span class="muControl">loop</span> <span class="Delimiter">}</span> <span class="muControl">reply</span> in ] <span class="muRecipe">recipe</span> buffer-full? [ <span class="Constant">local-scope</span> in:address:buffer<span class="Special"> &lt;- </span><span class="Constant">next-ingredient</span> len:number<span class="Special"> &lt;- </span>get *in, length:offset s:address:array:character<span class="Special"> &lt;- </span>get *in, data:offset capacity:number<span class="Special"> &lt;- </span>length *s result:boolean<span class="Special"> &lt;- </span>greater-or-equal len, capacity <span class="muControl">reply</span> result ] <span class="Comment"># in:address:buffer &lt;- buffer-append in:address:buffer, c:character</span> <span class="muRecipe">recipe</span> buffer-append [ <span class="Constant">local-scope</span> in:address:buffer<span class="Special"> &lt;- </span><span class="Constant">next-ingredient</span> c:character<span class="Special"> &lt;- </span><span class="Constant">next-ingredient</span> len:address:number<span class="Special"> &lt;- </span>get-address *in, length:offset <span class="Delimiter">{</span> <span class="Comment"># backspace? just drop last character if it exists and return</span> backspace?:boolean<span class="Special"> &lt;- </span>equal c, <span class="Constant">8/backspace</span> <span class="muControl">break-unless</span> backspace? empty?:boolean<span class="Special"> &lt;- </span>lesser-or-equal *len, <span class="Constant">0</span> <span class="muControl">reply-if</span> empty?, in/same-as-ingredient:<span class="Constant">0</span> *len<span class="Special"> &lt;- </span>subtract *len, <span class="Constant">1</span> <span class="muControl">reply</span> in/same-as-ingredient:<span class="Constant">0</span> <span class="Delimiter">}</span> <span class="Delimiter">{</span> <span class="Comment"># grow buffer if necessary</span> full?:boolean<span class="Special"> &lt;- </span>buffer-full? in <span class="muControl">break-unless</span> full? in<span class="Special"> &lt;- </span>grow-buffer in <span class="Delimiter">}</span> s:address:array:character<span class="Special"> &lt;- </span>get *in, data:offset <span class="CommentedCode">#? $print [array underlying buf: ], s, 10/newline</span> <span class="CommentedCode">#? $print [index: ], *len, 10/newline</span> dest:address:character<span class="Special"> &lt;- </span>index-address *s, *len <span class="CommentedCode">#? $print [storing ], c, [ in ], dest, 10/newline</span> *dest<span class="Special"> &lt;- </span>copy c *len<span class="Special"> &lt;- </span>add *len, <span class="Constant">1</span> <span class="muControl">reply</span> in/same-as-ingredient:<span class="Constant">0</span> ] <span class="muScenario">scenario</span> buffer-append-works [ run [ <span class="Constant">local-scope</span> x:address:buffer<span class="Special"> &lt;- </span>new-buffer <span class="Constant">3</span> s1:address:array:character<span class="Special"> &lt;- </span>get *x:address:buffer, data:offset x:address:buffer<span class="Special"> &lt;- </span>buffer-append x:address:buffer, <span class="Constant">97</span> <span class="Comment"># 'a'</span> x:address:buffer<span class="Special"> &lt;- </span>buffer-append x:address:buffer, <span class="Constant">98</span> <span class="Comment"># 'b'</span> x:address:buffer<span class="Special"> &lt;- </span>buffer-append x:address:buffer, <span class="Constant">99</span> <span class="Comment"># 'c'</span> s2:address:array:character<span class="Special"> &lt;- </span>get *x:address:buffer, data:offset <span class="Constant">1</span>:boolean/<span class="Special">raw &lt;- </span>equal s1:address:array:character, s2:address:array:character <span class="Constant">2</span>:array:character/<span class="Special">raw &lt;- </span>copy *s2:address:array:character <span class="Constant"> +buffer-filled</span> x:address:buffer<span class="Special"> &lt;- </span>buffer-append x:address:buffer, <span class="Constant">100</span> <span class="Comment"># 'd'</span> s3:address:array:character<span class="Special"> &lt;- </span>get *x:address:buffer, data:offset <span class="Constant">10</span>:boolean/<span class="Special">raw &lt;- </span>equal s1:address:array:character, s3:address:array:character <span class="Constant">11</span>:number/<span class="Special">raw &lt;- </span>get *x:address:buffer, length:offset <span class="Constant">12</span>:array:character/<span class="Special">raw &lt;- </span>copy *s3:address:array:character ] memory-should-contain [ <span class="Comment"># before +buffer-filled</span> <span class="Constant">1</span><span class="Special"> &lt;- </span><span class="Constant">1</span> <span class="Comment"># no change in data pointer</span> <span class="Constant">2</span><span class="Special"> &lt;- </span><span class="Constant">3</span> <span class="Comment"># size of data</span> <span class="Constant">3</span><span class="Special"> &lt;- </span><span class="Constant">97</span> <span class="Comment"># data</span> <span class="Constant">4</span><span class="Special"> &lt;- </span><span class="Constant">98</span> <span class="Constant">5</span><span class="Special"> &lt;- </span><span class="Constant">99</span> <span class="Comment"># in the end</span> <span class="Constant">10</span><span class="Special"> &lt;- </span><span class="Constant">0</span> <span class="Comment"># data pointer has grown</span> <span class="Constant">11</span><span class="Special"> &lt;- </span><span class="Constant">4</span> <span class="Comment"># final length</span> <span class="Constant">12</span><span class="Special"> &lt;- </span><span class="Constant">6</span> <span class="Comment"># but data's capacity has doubled</span> <span class="Constant">13</span><span class="Special"> &lt;- </span><span class="Constant">97</span> <span class="Comment"># data</span> <span class="Constant">14</span><span class="Special"> &lt;- </span><span class="Constant">98</span> <span class="Constant">15</span><span class="Special"> &lt;- </span><span class="Constant">99</span> <span class="Constant">16</span><span class="Special"> &lt;- </span><span class="Constant">100</span> <span class="Constant">17</span><span class="Special"> &lt;- </span><span class="Constant">0</span> <span class="Constant">18</span><span class="Special"> &lt;- </span><span class="Constant">0</span> ] ] <span class="muScenario">scenario</span> buffer-append-handles-backspace [ run [ <span class="Constant">local-scope</span> x:address:buffer<span class="Special"> &lt;- </span>new-buffer <span class="Constant">3</span> x:address:buffer<span class="Special"> &lt;- </span>buffer-append x:address:buffer, <span class="Constant">97</span> <span class="Comment"># 'a'</span> x:address:buffer<span class="Special"> &lt;- </span>buffer-append x:address:buffer, <span class="Constant">98</span> <span class="Comment"># 'b'</span> x:address:buffer<span class="Special"> &lt;- </span>buffer-append x:address:buffer, <span class="Constant">8/backspace</span> s:address:array:character<span class="Special"> &lt;- </span>buffer-to-array x:address:buffer <span class="Constant">1</span>:array:character/<span class="Special">raw &lt;- </span>copy *s:address:array:character ] memory-should-contain [ <span class="Constant">1</span><span class="Special"> &lt;- </span><span class="Constant">1</span> <span class="Comment"># length</span> <span class="Constant">2</span><span class="Special"> &lt;- </span><span class="Constant">97</span> <span class="Comment"># contents</span> <span class="Constant">3</span><span class="Special"> &lt;- </span><span class="Constant">0</span> ] ] <span class="Comment"># result:address:array:character &lt;- integer-to-decimal-string n:number</span> <span class="muRecipe">recipe</span> integer-to-decimal-string [ <span class="Constant">local-scope</span> n:number<span class="Special"> &lt;- </span><span class="Constant">next-ingredient</span> <span class="Comment"># is it zero?</span> <span class="Delimiter">{</span> <span class="muControl">break-if</span> n result:address:array:character<span class="Special"> &lt;- </span>new <span class="Constant">[0]</span> <span class="muControl">reply</span> result <span class="Delimiter">}</span> <span class="Comment"># save sign</span> negate-result:boolean<span class="Special"> &lt;- </span>copy <span class="Constant">0</span> <span class="Delimiter">{</span> negative?:boolean<span class="Special"> &lt;- </span>lesser-than n, <span class="Constant">0</span> <span class="muControl">break-unless</span> negative? negate-result<span class="Special"> &lt;- </span>copy <span class="Constant">1</span> n<span class="Special"> &lt;- </span>multiply n, -1 <span class="Delimiter">}</span> <span class="Comment"># add digits from right to left into intermediate buffer</span> tmp:address:buffer<span class="Special"> &lt;- </span>new-buffer <span class="Constant">30</span> digit-base:number<span class="Special"> &lt;- </span>copy <span class="Constant">48</span> <span class="Comment"># '0'</span> <span class="Delimiter">{</span> done?:boolean<span class="Special"> &lt;- </span>equal n, <span class="Constant">0</span> <span class="muControl">break-if</span> done? n, digit:number<span class="Special"> &lt;- </span>divide-with-remainder n, <span class="Constant">10</span> c:character<span class="Special"> &lt;- </span>add digit-base, digit tmp:address:buffer<span class="Special"> &lt;- </span>buffer-append tmp, c <span class="muControl">loop</span> <span class="Delimiter">}</span> <span class="Comment"># add sign</span> <span class="Delimiter">{</span> <span class="muControl">break-unless</span> negate-result:boolean tmp<span class="Special"> &lt;- </span>buffer-append tmp, <span class="Constant">45</span> <span class="Comment"># '-'</span> <span class="Delimiter">}</span> <span class="Comment"># reverse buffer into string result</span> len:number<span class="Special"> &lt;- </span>get *tmp, length:offset buf:address:array:character<span class="Special"> &lt;- </span>get *tmp, data:offset result:address:array:character<span class="Special"> &lt;- </span>new character:type, len i:number<span class="Special"> &lt;- </span>subtract len, <span class="Constant">1</span> <span class="Comment"># source index, decreasing</span> j:number<span class="Special"> &lt;- </span>copy <span class="Constant">0</span> <span class="Comment"># destination index, increasing</span> <span class="Delimiter">{</span> <span class="Comment"># while i &gt;= 0</span> done?:boolean<span class="Special"> &lt;- </span>lesser-than i, <span class="Constant">0</span> <span class="muControl">break-if</span> done? <span class="Comment"># result[j] = tmp[i]</span> src:character<span class="Special"> &lt;- </span>index *buf, i dest:address:character<span class="Special"> &lt;- </span>index-address *result, j *dest<span class="Special"> &lt;- </span>copy src i<span class="Special"> &lt;- </span>subtract i, <span class="Constant">1</span> j<span class="Special"> &lt;- </span>add j, <span class="Constant">1</span> <span class="muControl">loop</span> <span class="Delimiter">}</span> <span class="muControl">reply</span> result ] <span class="muRecipe">recipe</span> buffer-to-array [ <span class="Constant">local-scope</span> in:address:buffer<span class="Special"> &lt;- </span><span class="Constant">next-ingredient</span> <span class="Delimiter">{</span> <span class="Comment"># propagate null buffer</span> <span class="muControl">break-if</span> in <span class="muControl">reply</span> <span class="Constant">0</span> <span class="Delimiter">}</span> len:number<span class="Special"> &lt;- </span>get *in, length:offset <span class="CommentedCode">#? $print [size ], len, 10/newline</span> s:address:array:character<span class="Special"> &lt;- </span>get *in, data:offset <span class="Comment"># we can't just return s because it is usually the wrong length</span> result:address:array:character<span class="Special"> &lt;- </span>new character:type, len i:number<span class="Special"> &lt;- </span>copy <span class="Constant">0</span> <span class="Delimiter">{</span> <span class="CommentedCode">#? $print i #? 1</span> done?:boolean<span class="Special"> &lt;- </span>greater-or-equal i, len <span class="muControl">break-if</span> done? src:character<span class="Special"> &lt;- </span>index *s, i dest:address:character<span class="Special"> &lt;- </span>index-address *result, i *dest<span class="Special"> &lt;- </span>copy src i<span class="Special"> &lt;- </span>add i, <span class="Constant">1</span> <span class="muControl">loop</span> <span class="Delimiter">}</span> <span class="muControl">reply</span> result ] <span class="muScenario">scenario</span> integer-to-decimal-digit-zero [ run [ <span class="Constant">1</span>:address:array:character/<span class="Special">raw &lt;- </span>integer-to-decimal-string <span class="Constant">0</span> <span class="Constant">2</span>:array:character/<span class="Special">raw &lt;- </span>copy *<span class="Constant">1</span>:address:array:character/<span class="Special">raw</span> ] memory-should-contain [ <span class="Constant">2</span>:string<span class="Special"> &lt;- </span><span class="Constant">[0]</span> ] ] <span class="muScenario">scenario</span> integer-to-decimal-digit-positive [ run [ <span class="Constant">1</span>:address:array:character/<span class="Special">raw &lt;- </span>integer-to-decimal-string <span class="Constant">234</span> <span class="Constant">2</span>:array:character/<span class="Special">raw &lt;- </span>copy *<span class="Constant">1</span>:address:array:character/<span class="Special">raw</span> ] memory-should-contain [ <span class="Constant">2</span>:string<span class="Special"> &lt;- </span><span class="Constant">[234]</span> ] ] <span class="muScenario">scenario</span> integer-to-decimal-digit-negative [ run [ <span class="Constant">1</span>:address:array:character/<span class="Special">raw &lt;- </span>integer-to-decimal-string -1 <span class="Constant">2</span>:array:character/<span class="Special">raw &lt;- </span>copy *<span class="Constant">1</span>:address:array:character/<span class="Special">raw</span> ] memory-should-contain [ <span class="Constant">2</span><span class="Special"> &lt;- </span><span class="Constant">2</span> <span class="Constant">3</span><span class="Special"> &lt;- </span><span class="Constant">45</span> <span class="Comment"># '-'</span> <span class="Constant">4</span><span class="Special"> &lt;- </span><span class="Constant">49</span> <span class="Comment"># '1'</span> ] ] <span class="Comment"># result:address:array:character &lt;- string-append a:address:array:character, b:address:array:character</span> <span class="muRecipe">recipe</span> string-append [ <span class="Constant">local-scope</span> <span class="Comment"># result = new character[a.length + b.length]</span> a:address:array:character<span class="Special"> &lt;- </span><span class="Constant">next-ingredient</span> a-len:number<span class="Special"> &lt;- </span>length *a b:address:array:character<span class="Special"> &lt;- </span><span class="Constant">next-ingredient</span> b-len:number<span class="Special"> &lt;- </span>length *b result-len:number<span class="Special"> &lt;- </span>add a-len, b-len result:address:array:character<span class="Special"> &lt;- </span>new character:type, result-len <span class="Comment"># copy a into result</span> result-idx:number<span class="Special"> &lt;- </span>copy <span class="Constant">0</span> i:number<span class="Special"> &lt;- </span>copy <span class="Constant">0</span> <span class="Delimiter">{</span> <span class="Comment"># while i &lt; a.length</span> a-done?:boolean<span class="Special"> &lt;- </span>greater-or-equal i, a-len <span class="muControl">break-if</span> a-done? <span class="Comment"># result[result-idx] = a[i]</span> out:address:character<span class="Special"> &lt;- </span>index-address *result, result-idx in:character<span class="Special"> &lt;- </span>index *a, i *out<span class="Special"> &lt;- </span>copy in i<span class="Special"> &lt;- </span>add i, <span class="Constant">1</span> result-idx<span class="Special"> &lt;- </span>add result-idx, <span class="Constant">1</span> <span class="muControl">loop</span> <span class="Delimiter">}</span> <span class="Comment"># copy b into result</span> i<span class="Special"> &lt;- </span>copy <span class="Constant">0</span> <span class="Delimiter">{</span> <span class="Comment"># while i &lt; b.length</span> b-done?:boolean<span class="Special"> &lt;- </span>greater-or-equal i, b-len <span class="muControl">break-if</span> b-done? <span class="Comment"># result[result-idx] = a[i]</span> out:address:character<span class="Special"> &lt;- </span>index-address *result, result-idx in:character<span class="Special"> &lt;- </span>index *b, i *out<span class="Special"> &lt;- </span>copy in i<span class="Special"> &lt;- </span>add i, <span class="Constant">1</span> result-idx<span class="Special"> &lt;- </span>add result-idx, <span class="Constant">1</span> <span class="muControl">loop</span> <span class="Delimiter">}</span> <span class="muControl">reply</span> result ] <span class="muScenario">scenario</span> string-append-1 [ run [ <span class="Constant">1</span>:address:array:character/<span class="Special">raw &lt;- </span>new <span class="Constant">[hello,]</span> <span class="Constant">2</span>:address:array:character/<span class="Special">raw &lt;- </span>new <span class="Constant">[ world!]</span> <span class="Constant">3</span>:address:array:character/<span class="Special">raw &lt;- </span>string-append <span class="Constant">1</span>:address:array:character/<span class="Special">raw</span>, <span class="Constant">2</span>:address:array:character/<span class="Special">raw</span> <span class="Constant">4</span>:array:character/<span class="Special">raw &lt;- </span>copy *<span class="Constant">3</span>:address:array:character/<span class="Special">raw</span> ] memory-should-contain [ <span class="Constant">4</span>:string<span class="Special"> &lt;- </span><span class="Constant">[hello, world!]</span> ] ] <span class="Comment"># replace underscores in first with remaining args</span> <span class="Comment"># result:address:array:character &lt;- interpolate template:address:array:character, ...</span> <span class="muRecipe">recipe</span> interpolate [ <span class="Constant">local-scope</span> template:address:array:character<span class="Special"> &lt;- </span><span class="Constant">next-ingredient</span> <span class="Comment"># compute result-len, space to allocate for result</span> tem-len:number<span class="Special"> &lt;- </span>length *template result-len:number<span class="Special"> &lt;- </span>copy tem-len <span class="Delimiter">{</span> <span class="Comment"># while arg received</span> a:address:array:character, arg-received?:boolean<span class="Special"> &lt;- </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:number<span class="Special"> &lt;- </span>length *a result-len<span class="Special"> &lt;- </span>add result-len, a-len result-len<span class="Special"> &lt;- </span>subtract result-len, <span class="Constant">1</span> <span class="muControl">loop</span> <span class="Delimiter">}</span> <span class="CommentedCode">#? $print tem-len, [ ], $result-len, 10/newline</span> rewind-ingredients _<span class="Special"> &lt;- </span><span class="Constant">next-ingredient</span> <span class="Comment"># skip template</span> result:address:array:character<span class="Special"> &lt;- </span>new character:type, result-len <span class="Comment"># repeatedly copy sections of template and 'holes' into result</span> result-idx:number<span class="Special"> &lt;- </span>copy <span class="Constant">0</span> i:number<span class="Special"> &lt;- </span>copy <span class="Constant">0</span> <span class="Delimiter">{</span> <span class="Comment"># while arg received</span> a:address:array:character, arg-received?:boolean<span class="Special"> &lt;- </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 &lt; template.length</span> tem-done?:boolean<span class="Special"> &lt;- </span>greater-or-equal i, tem-len <span class="muControl">break-if</span> tem-done?, <span class="Constant">+done:label</span> <span class="Comment"># while template[i] != '_'</span> in:character<span class="Special"> &lt;- </span>index *template, i underscore?:boolean<span class="Special"> &lt;- </span>equal in, <span class="Constant">95/_</span> <span class="muControl">break-if</span> underscore? <span class="Comment"># result[result-idx] = template[i]</span> out:address:character<span class="Special"> &lt;- </span>index-address *result, result-idx *out<span class="Special"> &lt;- </span>copy in i<span class="Special"> &lt;- </span>add i, <span class="Constant">1</span> result-idx<span class="Special"> &lt;- </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:number<span class="Special"> &lt;- </span>copy <span class="Constant">0</span> <span class="Delimiter">{</span> <span class="Comment"># while j &lt; a.length</span> arg-done?:boolean<span class="Special"> &lt;- </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:character<span class="Special"> &lt;- </span>index *a, j out:address:character<span class="Special"> &lt;- </span>index-address *result, result-idx *out<span class="Special"> &lt;- </span>copy in j<span class="Special"> &lt;- </span>add j, <span class="Constant">1</span> result-idx<span class="Special"> &lt;- </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"> &lt;- </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 &lt; template.length</span> tem-done?:boolean<span class="Special"> &lt;- </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:character<span class="Special"> &lt;- </span>index *template, i out:address:character<span class="Special"> &lt;- </span>index-address *result, result-idx:number *out<span class="Special"> &lt;- </span>copy in i<span class="Special"> &lt;- </span>add i, <span class="Constant">1</span> result-idx<span class="Special"> &lt;- </span>add result-idx, <span class="Constant">1</span> <span class="muControl">loop</span> <span class="Delimiter">}</span> <span class="muControl">reply</span> result ] <span class="muScenario">scenario</span> interpolate-works [ <span class="CommentedCode">#? dump run #? 1</span> run [ <span class="Constant">1</span>:address:array:character/<span class="Special">raw &lt;- </span>new <span class="Constant">[abc _]</span> <span class="Constant">2</span>:address:array:character/<span class="Special">raw &lt;- </span>new <span class="Constant">[def]</span> <span class="Constant">3</span>:address:array:character/<span class="Special">raw &lt;- </span>interpolate <span class="Constant">1</span>:address:array:character/<span class="Special">raw</span>, <span class="Constant">2</span>:address:array:character/<span class="Special">raw</span> <span class="Constant">4</span>:array:character/<span class="Special">raw &lt;- </span>copy *<span class="Constant">3</span>:address:array:character/<span class="Special">raw</span> ] memory-should-contain [ <span class="Constant">4</span>:string<span class="Special"> &lt;- </span><span class="Constant">[abc def]</span> ] ] <span class="muScenario">scenario</span> interpolate-at-start [ run [ <span class="Constant">1</span>:address:array:character/<span class="Special">raw &lt;- </span>new <span class="Constant">[_, hello!]</span> <span class="Constant">2</span>:address:array:character/<span class="Special">raw &lt;- </span>new <span class="Constant">[abc]</span> <span class="Constant">3</span>:address:array:character/<span class="Special">raw &lt;- </span>interpolate <span class="Constant">1</span>:address:array:character/<span class="Special">raw</span>, <span class="Constant">2</span>:address:array:character/<span class="Special">raw</span> <span class="Constant">4</span>:array:character/<span class="Special">raw &lt;- </span>copy *<span class="Constant">3</span>:address:array:character/<span class="Special">raw</span> ] memory-should-contain [ <span class="Constant">4</span>:string<span class="Special"> &lt;- </span><span class="Constant">[abc, hello!]</span> <span class="Constant">16</span><span class="Special"> &lt;- </span><span class="Constant">0</span> <span class="Comment"># out of bounds</span> ] ] <span class="muScenario">scenario</span> interpolate-at-end [ run [ <span class="Constant">1</span>:address:array:character/<span class="Special">raw &lt;- </span>new <span class="Constant">[hello, _]</span> <span class="Constant">2</span>:address:array:character/<span class="Special">raw &lt;- </span>new <span class="Constant">[abc]</span> <span class="Constant">3</span>:address:array:character/<span class="Special">raw &lt;- </span>interpolate <span class="Constant">1</span>:address:array:character/<span class="Special">raw</span>, <span class="Constant">2</span>:address:array:character/<span class="Special">raw</span> <span class="Constant">4</span>:array:character/<span class="Special">raw &lt;- </span>copy *<span class="Constant">3</span>:address:array:character/<span class="Special">raw</span> ] memory-should-contain [ <span class="Constant">4</span>:string<span class="Special"> &lt;- </span><span class="Constant">[hello, abc]</span> ] ] <span class="Comment"># result:boolean &lt;- space? c:character</span> <span class="muRecipe">recipe</span> space? [ <span class="Constant">local-scope</span> c:character<span class="Special"> &lt;- </span><span class="Constant">next-ingredient</span> <span class="Comment"># most common case first</span> result:boolean<span class="Special"> &lt;- </span>equal c, <span class="Constant">32/space</span> <span class="muControl">jump-if</span> result <span class="Constant">+reply:label</span> result<span class="Special"> &lt;- </span>equal c, <span class="Constant">10/newline</span> <span class="muControl">jump-if</span> result, <span class="Constant">+reply:label</span> result<span class="Special"> &lt;- </span>equal c, <span class="Constant">9/tab</span> <span class="muControl">jump-if</span> result, <span class="Constant">+reply:label</span> result<span class="Special"> &lt;- </span>equal c, <span class="Constant">13/carriage-return</span> <span class="muControl">jump-if</span> result, <span class="Constant">+reply:label</span> <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"> &lt;- </span>equal c, <span class="Constant">11/ctrl-k</span> <span class="muControl">jump-if</span> result, <span class="Constant">+reply:label</span> result<span class="Special"> &lt;- </span>equal c, <span class="Constant">12/ctrl-l</span> <span class="muControl">jump-if</span> result, <span class="Constant">+reply:label</span> result<span class="Special"> &lt;- </span>equal c, <span class="Constant">133/ctrl-0085</span> <span class="muControl">jump-if</span> result, <span class="Constant">+reply:label</span> result<span class="Special"> &lt;- </span>equal c, <span class="Constant">160/no-break-space</span> <span class="muControl">jump-if</span> result, <span class="Constant">+reply:label</span> result<span class="Special"> &lt;- </span>equal c, <span class="Constant">5760/ogham-space-mark</span> <span class="muControl">jump-if</span> result, <span class="Constant">+reply:label</span> result<span class="Special"> &lt;- </span>equal c, <span class="Constant">8192/en-quad</span> <span class="muControl">jump-if</span> result, <span class="Constant">+reply:label</span> result<span class="Special"> &lt;- </span>equal c, <span class="Constant">8193/em-quad</span> <span class="muControl">jump-if</span> result, <span class="Constant">+reply:label</span> result<span class="Special"> &lt;- </span>equal c, <span class="Constant">8194/en-space</span> <span class="muControl">jump-if</span> result, <span class="Constant">+reply:label</span> result<span class="Special"> &lt;- </span>equal c, <span class="Constant">8195/em-space</span> <span class="muControl">jump-if</span> result, <span class="Constant">+reply:label</span> result<span class="Special"> &lt;- </span>equal c, <span class="Constant">8196/three-per-em-space</span> <span class="muControl">jump-if</span> result, <span class="Constant">+reply:label</span> result<span class="Special"> &lt;- </span>equal c, <span class="Constant">8197/four-per-em-space</span> <span class="muControl">jump-if</span> result, <span class="Constant">+reply:label</span> result<span class="Special"> &lt;- </span>equal c, <span class="Constant">8198/six-per-em-space</span> <span class="muControl">jump-if</span> result, <span class="Constant">+reply:label</span> result<span class="Special"> &lt;- </span>equal c, <span class="Constant">8199/figure-space</span> <span class="muControl">jump-if</span> result, <span class="Constant">+reply:label</span> result<span class="Special"> &lt;- </span>equal c, <span class="Constant">8200/punctuation-space</span> <span class="muControl">jump-if</span> result, <span class="Constant">+reply:label</span> result<span class="Special"> &lt;- </span>equal c, <span class="Constant">8201/thin-space</span> <span class="muControl">jump-if</span> result, <span class="Constant">+reply:label</span> result<span class="Special"> &lt;- </span>equal c, <span class="Constant">8202/hair-space</span> <span class="muControl">jump-if</span> result, <span class="Constant">+reply:label</span> result<span class="Special"> &lt;- </span>equal c, <span class="Constant">8206/left-to-right</span> <span class="muControl">jump-if</span> result, <span class="Constant">+reply:label</span> result<span class="Special"> &lt;- </span>equal c, <span class="Constant">8207/right-to-left</span> <span class="muControl">jump-if</span> result, <span class="Constant">+reply:label</span> result<span class="Special"> &lt;- </span>equal c, <span class="Constant">8232/line-separator</span> <span class="muControl">jump-if</span> result, <span class="Constant">+reply:label</span> result<span class="Special"> &lt;- </span>equal c, <span class="Constant">8233/paragraph-separator</span> <span class="muControl">jump-if</span> result, <span class="Constant">+reply:label</span> result<span class="Special"> &lt;- </span>equal c, <span class="Constant">8239/narrow-no-break-space</span> <span class="muControl">jump-if</span> result, <span class="Constant">+reply:label</span> result<span class="Special"> &lt;- </span>equal c, <span class="Constant">8287/medium-mathematical-space</span> <span class="muControl">jump-if</span> result, <span class="Constant">+reply:label</span> result<span class="Special"> &lt;- </span>equal c, <span class="Constant">12288/ideographic-space</span> <span class="muControl">jump-if</span> result, <span class="Constant">+reply:label</span> <span class="Constant"> +reply</span> <span class="muControl">reply</span> result ] <span class="Comment"># result:address:array:character &lt;- trim s:address:array:character</span> <span class="muRecipe">recipe</span> trim [ <span class="Constant">local-scope</span> s:address:array:character<span class="Special"> &lt;- </span><span class="Constant">next-ingredient</span> len:number<span class="Special"> &lt;- </span>length *s <span class="Comment"># left trim: compute start</span> start:number<span class="Special"> &lt;- </span>copy <span class="Constant">0</span> <span class="Delimiter">{</span> <span class="Delimiter">{</span> at-end?:boolean<span class="Special"> &lt;- </span>greater-or-equal start, len <span class="muControl">break-unless</span> at-end? result:address:array:character<span class="Special"> &lt;- </span>new character:type, <span class="Constant">0</span> <span class="muControl">reply</span> result <span class="Delimiter">}</span> curr:character<span class="Special"> &lt;- </span>index *s, start whitespace?:boolean<span class="Special"> &lt;- </span>space? curr <span class="muControl">break-unless</span> whitespace? start<span class="Special"> &lt;- </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:number<span class="Special"> &lt;- </span>subtract len, <span class="Constant">1</span> <span class="Delimiter">{</span> not-at-start?:boolean<span class="Special"> &lt;- </span>greater-than end, start assert not-at-start?, <span class="Constant">[end ran up against start]</span> curr:character<span class="Special"> &lt;- </span>index *s, end whitespace?:boolean<span class="Special"> &lt;- </span>space? curr <span class="muControl">break-unless</span> whitespace? end<span class="Special"> &lt;- </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:number<span class="Special"> &lt;- </span>subtract end, start, -1 result:address:array:character<span class="Special"> &lt;- </span>new character:type, new-len <span class="Comment"># i = start, j = 0</span> i:number<span class="Special"> &lt;- </span>copy start j:number<span class="Special"> &lt;- </span>copy <span class="Constant">0</span> <span class="Delimiter">{</span> <span class="Comment"># while i &lt;= end</span> done?:boolean<span class="Special"> &lt;- </span>greater-than i, end <span class="muControl">break-if</span> done? <span class="Comment"># result[j] = s[i]</span> src:character<span class="Special"> &lt;- </span>index *s, i dest:address:character<span class="Special"> &lt;- </span>index-address *result, j *dest<span class="Special"> &lt;- </span>copy src i<span class="Special"> &lt;- </span>add i, <span class="Constant">1</span> j<span class="Special"> &lt;- </span>add j, <span class="Constant">1</span> <span class="muControl">loop</span> <span class="Delimiter">}</span> <span class="muControl">reply</span> result ] <span class="muScenario">scenario</span> trim-unmodified [ run [ <span class="Constant">1</span>:address:array:character<span class="Special"> &lt;- </span>new <span class="Constant">[abc]</span> <span class="Constant">2</span>:address:array:character<span class="Special"> &lt;- </span>trim <span class="Constant">1</span>:address:array:character <span class="Constant">3</span>:array:character<span class="Special"> &lt;- </span>copy *<span class="Constant">2</span>:address:array:character ] memory-should-contain [ <span class="Constant">3</span>:string<span class="Special"> &lt;- </span><span class="Constant">[abc]</span> ] ] <span class="muScenario">scenario</span> trim-left [ run [ <span class="Constant">1</span>:address:array:character<span class="Special"> &lt;- </span>new <span class="Constant">[ abc]</span> <span class="Constant">2</span>:address:array:character<span class="Special"> &lt;- </span>trim <span class="Constant">1</span>:address:array:character <span class="Constant">3</span>:array:character<span class="Special"> &lt;- </span>copy *<span class="Constant">2</span>:address:array:character ] memory-should-contain [ <span class="Constant">3</span>:string<span class="Special"> &lt;- </span><span class="Constant">[abc]</span> ] ] <span class="muScenario">scenario</span> trim-right [ run [ <span class="Constant">1</span>:address:array:character<span class="Special"> &lt;- </span>new <span class="Constant">[abc ]</span> <span class="Constant">2</span>:address:array:character<span class="Special"> &lt;- </span>trim <span class="Constant">1</span>:address:array:character <span class="Constant">3</span>:array:character<span class="Special"> &lt;- </span>copy *<span class="Constant">2</span>:address:array:character ] memory-should-contain [ <span class="Constant">3</span>:string<span class="Special"> &lt;- </span><span class="Constant">[abc]</span> ] ] <span class="muScenario">scenario</span> trim-left-right [ run [ <span class="Constant">1</span>:address:array:character<span class="Special"> &lt;- </span>new <span class="Constant">[ abc ]</span> <span class="Constant">2</span>:address:array:character<span class="Special"> &lt;- </span>trim <span class="Constant">1</span>:address:array:character <span class="Constant">3</span>:array:character<span class="Special"> &lt;- </span>copy *<span class="Constant">2</span>:address:array:character ] memory-should-contain [ <span class="Constant">3</span>:string<span class="Special"> &lt;- </span><span class="Constant">[abc]</span> ] ] <span class="muScenario">scenario</span> trim-newline-tab [ run [ <span class="Constant">1</span>:address:array:character<span class="Special"> &lt;- </span>new <span class="Constant">[ abc</span> <span class="Constant">]</span> <span class="Constant">2</span>:address:array:character<span class="Special"> &lt;- </span>trim <span class="Constant">1</span>:address:array:character <span class="Constant">3</span>:array:character<span class="Special"> &lt;- </span>copy *<span class="Constant">2</span>:address:array:character ] memory-should-contain [ <span class="Constant">3</span>:string<span class="Special"> &lt;- </span><span class="Constant">[abc]</span> ] ] <span class="Comment"># next-index:number &lt;- find-next text:address:array:character, pattern:character, idx:number</span> <span class="muRecipe">recipe</span> find-next [ <span class="Constant">local-scope</span> text:address:array:character<span class="Special"> &lt;- </span><span class="Constant">next-ingredient</span> pattern:character<span class="Special"> &lt;- </span><span class="Constant">next-ingredient</span> idx:number<span class="Special"> &lt;- </span><span class="Constant">next-ingredient</span> len:number<span class="Special"> &lt;- </span>length *text <span class="Delimiter">{</span> eof?:boolean<span class="Special"> &lt;- </span>greater-or-equal idx, len <span class="muControl">break-if</span> eof? curr:character<span class="Special"> &lt;- </span>index *text, idx found?:boolean<span class="Special"> &lt;- </span>equal curr, pattern <span class="muControl">break-if</span> found? idx<span class="Special"> &lt;- </span>add idx, <span class="Constant">1</span> <span class="muControl">loop</span> <span class="Delimiter">}</span> <span class="muControl">reply</span> idx ] <span class="muScenario">scenario</span> string-find-next [ run [ <span class="Constant">1</span>:address:array:character<span class="Special"> &lt;- </span>new <span class="Constant">[a/b]</span> <span class="Constant">2</span>:number<span class="Special"> &lt;- </span>find-next <span class="Constant">1</span>:address:array:character, <span class="Constant">47/slash</span>, <span class="Constant">0/start-index</span> ] memory-should-contain [ <span class="Constant">2</span><span class="Special"> &lt;- </span><span class="Constant">1</span> ] ] <span class="muScenario">scenario</span> string-find-next-empty [ run [ <span class="Constant">1</span>:address:array:character<span class="Special"> &lt;- </span>new <span class="Constant">[]</span> <span class="Constant">2</span>:number<span class="Special"> &lt;- </span>find-next <span class="Constant">1</span>:address:array:character, <span class="Constant">47/slash</span>, <span class="Constant">0/start-index</span> ] memory-should-contain [ <span class="Constant">2</span><span class="Special"> &lt;- </span><span class="Constant">0</span> ] ] <span class="muScenario">scenario</span> string-find-next-initial [ run [ <span class="Constant">1</span>:address:array:character<span class="Special"> &lt;- </span>new <span class="Constant">[/abc]</span> <span class="Constant">2</span>:number<span class="Special"> &lt;- </span>find-next <span class="Constant">1</span>:address:array:character, <span class="Constant">47/slash</span>, <span class="Constant">0/start-index</span> ] memory-should-contain [ <span class="Constant">2</span><span class="Special"> &lt;- </span><span class="Constant">0</span> <span class="Comment"># prefix match</span> ] ] <span class="muScenario">scenario</span> string-find-next-final [ run [ <span class="Constant">1</span>:address:array:character<span class="Special"> &lt;- </span>new <span class="Constant">[abc/]</span> <span class="Constant">2</span>:number<span class="Special"> &lt;- </span>find-next <span class="Constant">1</span>:address:array:character, <span class="Constant">47/slash</span>, <span class="Constant">0/start-index</span> ] memory-should-contain [ <span class="Constant">2</span><span class="Special"> &lt;- </span><span class="Constant">3</span> <span class="Comment"># suffix match</span> ] ] <span class="muScenario">scenario</span> string-find-next-missing [ run [ <span class="Constant">1</span>:address:array:character<span class="Special"> &lt;- </span>new <span class="Constant">[abc]</span> <span class="Constant">2</span>:number<span class="Special"> &lt;- </span>find-next <span class="Constant">1</span>:address:array:character, <span class="Constant">47/slash</span>, <span class="Constant">0/start-index</span> ] memory-should-contain [ <span class="Constant">2</span><span class="Special"> &lt;- </span><span class="Constant">3</span> <span class="Comment"># no match</span> ] ] <span class="muScenario">scenario</span> string-find-next-invalid-index [ run [ <span class="Constant">1</span>:address:array:character<span class="Special"> &lt;- </span>new <span class="Constant">[abc]</span> <span class="Constant">2</span>:number<span class="Special"> &lt;- </span>find-next <span class="Constant">1</span>:address:array:character, <span class="Constant">47/slash</span>, <span class="Constant">4/start-index</span> ] memory-should-contain [ <span class="Constant">2</span><span class="Special"> &lt;- </span><span class="Constant">4</span> <span class="Comment"># no change</span> ] ] <span class="muScenario">scenario</span> string-find-next-first [ run [ <span class="Constant">1</span>:address:array:character<span class="Special"> &lt;- </span>new <span class="Constant">[ab/c/]</span> <span class="Constant">2</span>:number<span class="Special"> &lt;- </span>find-next <span class="Constant">1</span>:address:array:character, <span class="Constant">47/slash</span>, <span class="Constant">0/start-index</span> ] memory-should-contain [ <span class="Constant">2</span><span class="Special"> &lt;- </span><span class="Constant">2</span> <span class="Comment"># first '/' of multiple</span> ] ] <span class="muScenario">scenario</span> string-find-next-second [ run [ <span class="Constant">1</span>:address:array:character<span class="Special"> &lt;- </span>new <span class="Constant">[ab/c/]</span> <span class="Constant">2</span>:number<span class="Special"> &lt;- </span>find-next <span class="Constant">1</span>:address:array:character, <span class="Constant">47/slash</span>, <span class="Constant">3/start-index</span> ] memory-should-contain [ <span class="Constant">2</span><span class="Special"> &lt;- </span><span class="Constant">4</span> <span class="Comment"># second '/' of multiple</span> ] ] <span class="Comment"># idx:number &lt;- find-substring text:address:array:character, pattern:address:array:character, idx:number</span> <span class="Comment"># like find-next, but searches for multiple characters</span> <span class="Comment"># fairly dumb algorithm</span> <span class="muRecipe">recipe</span> find-substring [ <span class="Constant">local-scope</span> text:address:array:character<span class="Special"> &lt;- </span><span class="Constant">next-ingredient</span> pattern:address:array:character<span class="Special"> &lt;- </span><span class="Constant">next-ingredient</span> idx:number<span class="Special"> &lt;- </span><span class="Constant">next-ingredient</span> first:character<span class="Special"> &lt;- </span>index *pattern, <span class="Constant">0</span> <span class="Comment"># repeatedly check for match at current idx</span> len:number<span class="Special"> &lt;- </span>length *text <span class="Delimiter">{</span> <span class="Comment"># does some unnecessary work checking for substrings even when there isn't enough of text left</span> done?:boolean<span class="Special"> &lt;- </span>greater-or-equal idx, len <span class="muControl">break-if</span> done? found?:boolean<span class="Special"> &lt;- </span>match-at text, pattern, idx <span class="muControl">break-if</span> found? idx<span class="Special"> &lt;- </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"> &lt;- </span>find-next text, first, idx <span class="muControl">loop</span> <span class="Delimiter">}</span> <span class="muControl">reply</span> idx ] <span class="muScenario">scenario</span> find-substring-1 [ run [ <span class="Constant">1</span>:address:array:character<span class="Special"> &lt;- </span>new <span class="Constant">[abc]</span> <span class="Constant">2</span>:address:array:character<span class="Special"> &lt;- </span>new <span class="Constant">[bc]</span> <span class="Constant">3</span>:number<span class="Special"> &lt;- </span>find-substring <span class="Constant">1</span>:address:array:character, <span class="Constant">2</span>:address:array:character, <span class="Constant">0</span> ] memory-should-contain [ <span class="Constant">3</span><span class="Special"> &lt;- </span><span class="Constant">1</span> ] ] <span class="muScenario">scenario</span> find-substring-2 [ run [ <span class="Constant">1</span>:address:array:character<span class="Special"> &lt;- </span>new <span class="Constant">[abcd]</span> <span class="Constant">2</span>:address:array:character<span class="Special"> &lt;- </span>new <span class="Constant">[bc]</span> <span class="Constant">3</span>:number<span class="Special"> &lt;- </span>find-substring <span class="Constant">1</span>:address:array:character, <span class="Constant">2</span>:address:array:character, <span class="Constant">1</span> ] memory-should-contain [ <span class="Constant">3</span><span class="Special"> &lt;- </span><span class="Constant">1</span> ] ] <span class="muScenario">scenario</span> find-substring-no-match [ run [ <span class="Constant">1</span>:address:array:character<span class="Special"> &lt;- </span>new <span class="Constant">[abc]</span> <span class="Constant">2</span>:address:array:character<span class="Special"> &lt;- </span>new <span class="Constant">[bd]</span> <span class="Constant">3</span>:number<span class="Special"> &lt;- </span>find-substring <span class="Constant">1</span>:address:array:character, <span class="Constant">2</span>:address:array:character, <span class="Constant">0</span> ] memory-should-contain [ <span class="Constant">3</span><span class="Special"> &lt;- </span><span class="Constant">3</span> <span class="Comment"># not found</span> ] ] <span class="muScenario">scenario</span> find-substring-suffix-match [ run [ <span class="Constant">1</span>:address:array:character<span class="Special"> &lt;- </span>new <span class="Constant">[abcd]</span> <span class="Constant">2</span>:address:array:character<span class="Special"> &lt;- </span>new <span class="Constant">[cd]</span> <span class="Constant">3</span>:number<span class="Special"> &lt;- </span>find-substring <span class="Constant">1</span>:address:array:character, <span class="Constant">2</span>:address:array:character, <span class="Constant">0</span> ] memory-should-contain [ <span class="Constant">3</span><span class="Special"> &lt;- </span><span class="Constant">2</span> ] ] <span class="muScenario">scenario</span> find-substring-suffix-match-2 [ run [ <span class="Constant">1</span>:address:array:character<span class="Special"> &lt;- </span>new <span class="Constant">[abcd]</span> <span class="Constant">2</span>:address:array:character<span class="Special"> &lt;- </span>new <span class="Constant">[cde]</span> <span class="Constant">3</span>:number<span class="Special"> &lt;- </span>find-substring <span class="Constant">1</span>:address:array:character, <span class="Constant">2</span>:address:array:character, <span class="Constant">0</span> ] memory-should-contain [ <span class="Constant">3</span><span class="Special"> &lt;- </span><span class="Constant">4</span> <span class="Comment"># not found</span> ] ] <span class="Comment"># result:boolean &lt;- match-at text:address:array:character, pattern:address:array:character, idx:number</span> <span class="Comment"># checks if substring matches at index 'idx'</span> <span class="muRecipe">recipe</span> match-at [ <span class="Constant">local-scope</span> text:address:array:character<span class="Special"> &lt;- </span><span class="Constant">next-ingredient</span> pattern:address:array:character<span class="Special"> &lt;- </span><span class="Constant">next-ingredient</span> idx:number<span class="Special"> &lt;- </span><span class="Constant">next-ingredient</span> pattern-len:number<span class="Special"> &lt;- </span>length *pattern <span class="Comment"># check that there's space left for the pattern</span> <span class="Delimiter">{</span> x:number<span class="Special"> &lt;- </span>length *text x<span class="Special"> &lt;- </span>subtract x, pattern-len enough-room?:boolean<span class="Special"> &lt;- </span>lesser-or-equal idx, x <span class="muControl">break-if</span> enough-room? <span class="muControl">reply</span> <span class="Constant">0/not-found</span> <span class="Delimiter">}</span> <span class="Comment"># check each character of pattern</span> pattern-idx:number<span class="Special"> &lt;- </span>copy <span class="Constant">0</span> <span class="Delimiter">{</span> done?:boolean<span class="Special"> &lt;- </span>greater-or-equal pattern-idx, pattern-len <span class="muControl">break-if</span> done? c:character<span class="Special"> &lt;- </span>index *text, idx exp:character<span class="Special"> &lt;- </span>index *pattern, pattern-idx <span class="Delimiter">{</span> match?:boolean<span class="Special"> &lt;- </span>equal c, exp <span class="muControl">break-if</span> match? <span class="muControl">reply</span> <span class="Constant">0/not-found</span> <span class="Delimiter">}</span> idx<span class="Special"> &lt;- </span>add idx, <span class="Constant">1</span> pattern-idx<span class="Special"> &lt;- </span>add pattern-idx, <span class="Constant">1</span> <span class="muControl">loop</span> <span class="Delimiter">}</span> <span class="muControl">reply</span> <span class="Constant">1/found</span> ] <span class="muScenario">scenario</span> match-at-checks-substring-at-index [ run [ <span class="Constant">1</span>:address:array:character<span class="Special"> &lt;- </span>new <span class="Constant">[abc]</span> <span class="Constant">2</span>:address:array:character<span class="Special"> &lt;- </span>new <span class="Constant">[ab]</span> <span class="Constant">3</span>:boolean<span class="Special"> &lt;- </span>match-at <span class="Constant">1</span>:address:array:character, <span class="Constant">2</span>:address:array:character, <span class="Constant">0</span> ] memory-should-contain [ <span class="Constant">3</span><span class="Special"> &lt;- </span><span class="Constant">1</span> <span class="Comment"># match found</span> ] ] <span class="muScenario">scenario</span> match-at-reflexive [ run [ <span class="Constant">1</span>:address:array:character<span class="Special"> &lt;- </span>new <span class="Constant">[abc]</span> <span class="Constant">3</span>:boolean<span class="Special"> &lt;- </span>match-at <span class="Constant">1</span>:address:array:character, <span class="Constant">1</span>:address:array:character, <span class="Constant">0</span> ] memory-should-contain [ <span class="Constant">3</span><span class="Special"> &lt;- </span><span class="Constant">1</span> <span class="Comment"># match found</span> ] ] <span class="muScenario">scenario</span> match-at-outside-bounds [ run [ <span class="Constant">1</span>:address:array:character<span class="Special"> &lt;- </span>new <span class="Constant">[abc]</span> <span class="Constant">2</span>:address:array:character<span class="Special"> &lt;- </span>new <span class="Constant">[a]</span> <span class="Constant">3</span>:boolean<span class="Special"> &lt;- </span>match-at <span class="Constant">1</span>:address:array:character, <span class="Constant">2</span>:address:array:character, <span class="Constant">4</span> ] memory-should-contain [ <span class="Constant">3</span><span class="Special"> &lt;- </span><span class="Constant">0</span> <span class="Comment"># never matches</span> ] ] <span class="muScenario">scenario</span> match-at-empty-pattern [ run [ <span class="Constant">1</span>:address:array:character<span class="Special"> &lt;- </span>new <span class="Constant">[abc]</span> <span class="Constant">2</span>:address:array:character<span class="Special"> &lt;- </span>new <span class="Constant">[]</span> <span class="Constant">3</span>:boolean<span class="Special"> &lt;- </span>match-at <span class="Constant">1</span>:address:array:character, <span class="Constant">2</span>:address:array:character, <span class="Constant">0</span> ] memory-should-contain [ <span class="Constant">3</span><span class="Special"> &lt;- </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 [ run [ <span class="Constant">1</span>:address:array:character<span class="Special"> &lt;- </span>new <span class="Constant">[abc]</span> <span class="Constant">2</span>:address:array:character<span class="Special"> &lt;- </span>new <span class="Constant">[]</span> <span class="Constant">3</span>:boolean<span class="Special"> &lt;- </span>match-at <span class="Constant">1</span>:address:array:character, <span class="Constant">2</span>:address:array:character, <span class="Constant">4</span> ] memory-should-contain [ <span class="Constant">3</span><span class="Special"> &lt;- </span><span class="Constant">0</span> <span class="Comment"># no match</span> ] ] <span class="muScenario">scenario</span> match-at-empty-text [ run [ <span class="Constant">1</span>:address:array:character<span class="Special"> &lt;- </span>new <span class="Constant">[]</span> <span class="Constant">2</span>:address:array:character<span class="Special"> &lt;- </span>new <span class="Constant">[abc]</span> <span class="Constant">3</span>:boolean<span class="Special"> &lt;- </span>match-at <span class="Constant">1</span>:address:array:character, <span class="Constant">2</span>:address:array:character, <span class="Constant">0</span> ] memory-should-contain [ <span class="Constant">3</span><span class="Special"> &lt;- </span><span class="Constant">0</span> <span class="Comment"># no match</span> ] ] <span class="muScenario">scenario</span> match-at-empty-against-empty [ run [ <span class="Constant">1</span>:address:array:character<span class="Special"> &lt;- </span>new <span class="Constant">[]</span> <span class="Constant">3</span>:boolean<span class="Special"> &lt;- </span>match-at <span class="Constant">1</span>:address:array:character, <span class="Constant">1</span>:address:array:character, <span class="Constant">0</span> ] memory-should-contain [ <span class="Constant">3</span><span class="Special"> &lt;- </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 [ run [ <span class="Constant">1</span>:address:array:character<span class="Special"> &lt;- </span>new <span class="Constant">[abc]</span> <span class="Constant">2</span>:address:array:character<span class="Special"> &lt;- </span>new <span class="Constant">[bc]</span> <span class="Constant">3</span>:boolean<span class="Special"> &lt;- </span>match-at <span class="Constant">1</span>:address:array:character, <span class="Constant">2</span>:address:array:character, <span class="Constant">1</span> ] memory-should-contain [ <span class="Constant">3</span><span class="Special"> &lt;- </span><span class="Constant">1</span> <span class="Comment"># matches inner substring</span> ] ] <span class="muScenario">scenario</span> match-at-inside-bounds-2 [ run [ <span class="Constant">1</span>:address:array:character<span class="Special"> &lt;- </span>new <span class="Constant">[abc]</span> <span class="Constant">2</span>:address:array:character<span class="Special"> &lt;- </span>new <span class="Constant">[bc]</span> <span class="Constant">3</span>:boolean<span class="Special"> &lt;- </span>match-at <span class="Constant">1</span>:address:array:character, <span class="Constant">2</span>:address:array:character, <span class="Constant">0</span> ] memory-should-contain [ <span class="Constant">3</span><span class="Special"> &lt;- </span><span class="Constant">0</span> <span class="Comment"># no match</span> ] ] <span class="Comment"># result:address:array:address:array:character &lt;- split s:address:array:character, delim:character</span> <span class="muRecipe">recipe</span> split [ <span class="Constant">local-scope</span> s:address:array:character<span class="Special"> &lt;- </span><span class="Constant">next-ingredient</span> delim:character<span class="Special"> &lt;- </span><span class="Constant">next-ingredient</span> <span class="Comment"># empty string? return empty array</span> len:number<span class="Special"> &lt;- </span>length *s <span class="Delimiter">{</span> empty?:boolean<span class="Special"> &lt;- </span>equal len, <span class="Constant">0</span> <span class="muControl">break-unless</span> empty? result:address:array:address:array:character<span class="Special"> &lt;- </span>new location:type, <span class="Constant">0</span> <span class="muControl">reply</span> result <span class="Delimiter">}</span> <span class="Comment"># count #pieces we need room for</span> count:number<span class="Special"> &lt;- </span>copy <span class="Constant">1</span> <span class="Comment"># n delimiters = n+1 pieces</span> idx:number<span class="Special"> &lt;- </span>copy <span class="Constant">0</span> <span class="Delimiter">{</span> idx<span class="Special"> &lt;- </span>find-next s, delim, idx done?:boolean<span class="Special"> &lt;- </span>greater-or-equal idx, len <span class="muControl">break-if</span> done? idx<span class="Special"> &lt;- </span>add idx, <span class="Constant">1</span> count<span class="Special"> &lt;- </span>add count, <span class="Constant">1</span> <span class="muControl">loop</span> <span class="Delimiter">}</span> <span class="Comment"># allocate space</span> result:address:array:address:array:character<span class="Special"> &lt;- </span>new location:type, count <span class="Comment"># repeatedly copy slices start..end until delimiter into result[curr-result]</span> curr-result:number<span class="Special"> &lt;- </span>copy <span class="Constant">0</span> start:number<span class="Special"> &lt;- </span>copy <span class="Constant">0</span> <span class="Delimiter">{</span> <span class="Comment"># while next delim exists</span> done?:boolean<span class="Special"> &lt;- </span>greater-or-equal start, len <span class="muControl">break-if</span> done? end:number<span class="Special"> &lt;- </span>find-next s, delim, start <span class="Comment"># copy start..end into result[curr-result]</span> dest:address:address:array:character<span class="Special"> &lt;- </span>index-address *result, curr-result *dest<span class="Special"> &lt;- </span>string-copy s, start, end <span class="Comment"># slide over to next slice</span> start<span class="Special"> &lt;- </span>add end, <span class="Constant">1</span> curr-result<span class="Special"> &lt;- </span>add curr-result, <span class="Constant">1</span> <span class="muControl">loop</span> <span class="Delimiter">}</span> <span class="muControl">reply</span> result ] <span class="muScenario">scenario</span> string-split-1 [ run [ <span class="Constant">1</span>:address:array:character<span class="Special"> &lt;- </span>new <span class="Constant">[a/b]</span> <span class="Constant">2</span>:address:array:address:array:character<span class="Special"> &lt;- </span>split <span class="Constant">1</span>:address:array:character, <span class="Constant">47/slash</span> <span class="Constant">3</span>:number<span class="Special"> &lt;- </span>length *<span class="Constant">2</span>:address:array:address:array:character <span class="Constant">4</span>:address:array:character<span class="Special"> &lt;- </span>index *<span class="Constant">2</span>:address:array:address:array:character, <span class="Constant">0</span> <span class="Constant">5</span>:address:array:character<span class="Special"> &lt;- </span>index *<span class="Constant">2</span>:address:array:address:array:character, <span class="Constant">1</span> <span class="Constant">10</span>:array:character<span class="Special"> &lt;- </span>copy *<span class="Constant">4</span>:address:array:character <span class="Constant">20</span>:array:character<span class="Special"> &lt;- </span>copy *<span class="Constant">5</span>:address:array:character ] memory-should-contain [ <span class="Constant">3</span><span class="Special"> &lt;- </span><span class="Constant">2</span> <span class="Comment"># length of result</span> <span class="Constant">10</span>:string<span class="Special"> &lt;- </span><span class="Constant">[a]</span> <span class="Constant">20</span>:string<span class="Special"> &lt;- </span><span class="Constant">[b]</span> ] ] <span class="muScenario">scenario</span> string-split-2 [ run [ <span class="Constant">1</span>:address:array:character<span class="Special"> &lt;- </span>new <span class="Constant">[a/b/c]</span> <span class="Constant">2</span>:address:array:address:array:character<span class="Special"> &lt;- </span>split <span class="Constant">1</span>:address:array:character, <span class="Constant">47/slash</span> <span class="Constant">3</span>:number<span class="Special"> &lt;- </span>length *<span class="Constant">2</span>:address:array:address:array:character <span class="Constant">4</span>:address:array:character<span class="Special"> &lt;- </span>index *<span class="Constant">2</span>:address:array:address:array:character, <span class="Constant">0</span> <span class="Constant">5</span>:address:array:character<span class="Special"> &lt;- </span>index *<span class="Constant">2</span>:address:array:address:array:character, <span class="Constant">1</span> <span class="Constant">6</span>:address:array:character<span class="Special"> &lt;- </span>index *<span class="Constant">2</span>:address:array:address:array:character, <span class="Constant">2</span> <span class="Constant">10</span>:array:character<span class="Special"> &lt;- </span>copy *<span class="Constant">4</span>:address:array:character <span class="Constant">20</span>:array:character<span class="Special"> &lt;- </span>copy *<span class="Constant">5</span>:address:array:character <span class="Constant">30</span>:array:character<span class="Special"> &lt;- </span>copy *<span class="Constant">6</span>:address:array:character ] memory-should-contain [ <span class="Constant">3</span><span class="Special"> &lt;- </span><span class="Constant">3</span> <span class="Comment"># length of result</span> <span class="Constant">10</span>:string<span class="Special"> &lt;- </span><span class="Constant">[a]</span> <span class="Constant">20</span>:string<span class="Special"> &lt;- </span><span class="Constant">[b]</span> <span class="Constant">30</span>:string<span class="Special"> &lt;- </span><span class="Constant">[c]</span> ] ] <span class="muScenario">scenario</span> string-split-missing [ run [ <span class="Constant">1</span>:address:array:character<span class="Special"> &lt;- </span>new <span class="Constant">[abc]</span> <span class="Constant">2</span>:address:array:address:array:character<span class="Special"> &lt;- </span>split <span class="Constant">1</span>:address:array:character, <span class="Constant">47/slash</span> <span class="Constant">3</span>:number<span class="Special"> &lt;- </span>length *<span class="Constant">2</span>:address:array:address:array:character <span class="Constant">4</span>:address:array:character<span class="Special"> &lt;- </span>index *<span class="Constant">2</span>:address:array:address:array:character, <span class="Constant">0</span> <span class="Constant">10</span>:array:character<span class="Special"> &lt;- </span>copy *<span class="Constant">4</span>:address:array:character ] memory-should-contain [ <span class="Constant">3</span><span class="Special"> &lt;- </span><span class="Constant">1</span> <span class="Comment"># length of result</span> <span class="Constant">10</span>:string<span class="Special"> &lt;- </span><span class="Constant">[abc]</span> ] ] <span class="muScenario">scenario</span> string-split-empty [ run [ <span class="Constant">1</span>:address:array:character<span class="Special"> &lt;- </span>new <span class="Constant">[]</span> <span class="Constant">2</span>:address:array:address:array:character<span class="Special"> &lt;- </span>split <span class="Constant">1</span>:address:array:character, <span class="Constant">47/slash</span> <span class="Constant">3</span>:number<span class="Special"> &lt;- </span>length *<span class="Constant">2</span>:address:array:address:array:character ] memory-should-contain [ <span class="Constant">3</span><span class="Special"> &lt;- </span><span class="Constant">0</span> <span class="Comment"># empty result</span> ] ] <span class="muScenario">scenario</span> string-split-empty-piece [ run [ <span class="Constant">1</span>:address:array:character<span class="Special"> &lt;- </span>new <span class="Constant">[a/b//c]</span> <span class="Constant">2</span>:address:array:address:array:character<span class="Special"> &lt;- </span>split <span class="Constant">1</span>:address:array:character, <span class="Constant">47/slash</span> <span class="Constant">3</span>:number<span class="Special"> &lt;- </span>length *<span class="Constant">2</span>:address:array:address:array:character <span class="Constant">4</span>:address:array:character<span class="Special"> &lt;- </span>index *<span class="Constant">2</span>:address:array:address:array:character, <span class="Constant">0</span> <span class="Constant">5</span>:address:array:character<span class="Special"> &lt;- </span>index *<span class="Constant">2</span>:address:array:address:array:character, <span class="Constant">1</span> <span class="Constant">6</span>:address:array:character<span class="Special"> &lt;- </span>index *<span class="Constant">2</span>:address:array:address:array:character, <span class="Constant">2</span> <span class="Constant">7</span>:address:array:character<span class="Special"> &lt;- </span>index *<span class="Constant">2</span>:address:array:address:array:character, <span class="Constant">3</span> <span class="Constant">10</span>:array:character<span class="Special"> &lt;- </span>copy *<span class="Constant">4</span>:address:array:character <span class="Constant">20</span>:array:character<span class="Special"> &lt;- </span>copy *<span class="Constant">5</span>:address:array:character <span class="Constant">30</span>:array:character<span class="Special"> &lt;- </span>copy *<span class="Constant">6</span>:address:array:character <span class="Constant">40</span>:array:character<span class="Special"> &lt;- </span>copy *<span class="Constant">7</span>:address:array:character ] memory-should-contain [ <span class="Constant">3</span><span class="Special"> &lt;- </span><span class="Constant">4</span> <span class="Comment"># length of result</span> <span class="Constant">10</span>:string<span class="Special"> &lt;- </span><span class="Constant">[a]</span> <span class="Constant">20</span>:string<span class="Special"> &lt;- </span><span class="Constant">[b]</span> <span class="Constant">30</span>:string<span class="Special"> &lt;- </span><span class="Constant">[]</span> <span class="Constant">40</span>:string<span class="Special"> &lt;- </span><span class="Constant">[c]</span> ] ] <span class="Comment"># x:address:array:character, y:address:array:character &lt;- split-first text:address:array:character, delim:character</span> <span class="muRecipe">recipe</span> split-first [ <span class="Constant">local-scope</span> text:address:array:character<span class="Special"> &lt;- </span><span class="Constant">next-ingredient</span> delim:character<span class="Special"> &lt;- </span><span class="Constant">next-ingredient</span> <span class="Comment"># empty string? return empty strings</span> len:number<span class="Special"> &lt;- </span>length *text <span class="Delimiter">{</span> empty?:boolean<span class="Special"> &lt;- </span>equal len, <span class="Constant">0</span> <span class="muControl">break-unless</span> empty? x:address:array:character<span class="Special"> &lt;- </span>new <span class="Constant">[]</span> y:address:array:character<span class="Special"> &lt;- </span>new <span class="Constant">[]</span> <span class="muControl">reply</span> x, y <span class="Delimiter">}</span> idx:number<span class="Special"> &lt;- </span>find-next text, delim, <span class="Constant">0</span> x:address:array:character<span class="Special"> &lt;- </span>string-copy text, <span class="Constant">0</span>, idx idx<span class="Special"> &lt;- </span>add idx, <span class="Constant">1</span> y:address:array:character<span class="Special"> &lt;- </span>string-copy text, idx, len <span class="muControl">reply</span> x, y ] <span class="muScenario">scenario</span> string-split-first [ run [ <span class="Constant">1</span>:address:array:character<span class="Special"> &lt;- </span>new <span class="Constant">[a/b]</span> <span class="Constant">2</span>:address:array:character, <span class="Constant">3</span>:address:array:character<span class="Special"> &lt;- </span>split-first <span class="Constant">1</span>:address:array:character, <span class="Constant">47/slash</span> <span class="Constant">10</span>:array:character<span class="Special"> &lt;- </span>copy *<span class="Constant">2</span>:address:array:character <span class="Constant">20</span>:array:character<span class="Special"> &lt;- </span>copy *<span class="Constant">3</span>:address:array:character ] memory-should-contain [ <span class="Constant">10</span>:string<span class="Special"> &lt;- </span><span class="Constant">[a]</span> <span class="Constant">20</span>:string<span class="Special"> &lt;- </span><span class="Constant">[b]</span> ] ] <span class="Comment"># result:address:array:character &lt;- string-copy buf:address:array:character, start:number, end:number</span> <span class="Comment"># todo: make this generic</span> <span class="muRecipe">recipe</span> string-copy [ <span class="Constant">local-scope</span> buf:address:array:character<span class="Special"> &lt;- </span><span class="Constant">next-ingredient</span> start:number<span class="Special"> &lt;- </span><span class="Constant">next-ingredient</span> end:number<span class="Special"> &lt;- </span><span class="Constant">next-ingredient</span> <span class="Comment"># if end is out of bounds, trim it</span> len:number<span class="Special"> &lt;- </span>length *buf end:number<span class="Special"> &lt;- </span>min len, end <span class="Comment"># allocate space for result</span> len<span class="Special"> &lt;- </span>subtract end, start result:address:array:character<span class="Special"> &lt;- </span>new character:type, len <span class="Comment"># copy start..end into result[curr-result]</span> src-idx:number<span class="Special"> &lt;- </span>copy start dest-idx:number<span class="Special"> &lt;- </span>copy <span class="Constant">0</span> <span class="Delimiter">{</span> done?:boolean<span class="Special"> &lt;- </span>greater-or-equal src-idx, end <span class="muControl">break-if</span> done? src:character<span class="Special"> &lt;- </span>index *buf, src-idx dest:address:character<span class="Special"> &lt;- </span>index-address *result, dest-idx *dest<span class="Special"> &lt;- </span>copy src src-idx<span class="Special"> &lt;- </span>add src-idx, <span class="Constant">1</span> dest-idx<span class="Special"> &lt;- </span>add dest-idx, <span class="Constant">1</span> <span class="muControl">loop</span> <span class="Delimiter">}</span> <span class="muControl">reply</span> result ] <span class="muScenario">scenario</span> string-copy-copies-substring [ run [ <span class="Constant">1</span>:address:array:character<span class="Special"> &lt;- </span>new <span class="Constant">[abc]</span> <span class="Constant">2</span>:address:array:character<span class="Special"> &lt;- </span>string-copy <span class="Constant">1</span>:address:array:character, <span class="Constant">1</span>, <span class="Constant">3</span> <span class="Constant">3</span>:array:character<span class="Special"> &lt;- </span>copy *<span class="Constant">2</span>:address:array:character ] memory-should-contain [ <span class="Constant">3</span>:string<span class="Special"> &lt;- </span><span class="Constant">[bc]</span> ] ] <span class="muScenario">scenario</span> string-copy-out-of-bounds [ run [ <span class="Constant">1</span>:address:array:character<span class="Special"> &lt;- </span>new <span class="Constant">[abc]</span> <span class="Constant">2</span>:address:array:character<span class="Special"> &lt;- </span>string-copy <span class="Constant">1</span>:address:array:character, <span class="Constant">2</span>, <span class="Constant">4</span> <span class="Constant">3</span>:array:character<span class="Special"> &lt;- </span>copy *<span class="Constant">2</span>:address:array:character ] memory-should-contain [ <span class="Constant">3</span>:string<span class="Special"> &lt;- </span><span class="Constant">[c]</span> ] ] <span class="muScenario">scenario</span> string-copy-out-of-bounds-2 [ run [ <span class="Constant">1</span>:address:array:character<span class="Special"> &lt;- </span>new <span class="Constant">[abc]</span> <span class="Constant">2</span>:address:array:character<span class="Special"> &lt;- </span>string-copy <span class="Constant">1</span>:address:array:character, <span class="Constant">3</span>, <span class="Constant">3</span> <span class="Constant">3</span>:array:character<span class="Special"> &lt;- </span>copy *<span class="Constant">2</span>:address:array:character ] memory-should-contain [ <span class="Constant">3</span>:string<span class="Special"> &lt;- </span><span class="Constant">[]</span> ] ] <span class="muRecipe">recipe</span> min [ <span class="Constant">local-scope</span> x:number<span class="Special"> &lt;- </span><span class="Constant">next-ingredient</span> y:number<span class="Special"> &lt;- </span><span class="Constant">next-ingredient</span> <span class="Delimiter">{</span> return-x?:boolean<span class="Special"> &lt;- </span>lesser-than x, y <span class="muControl">break-if</span> return-x? <span class="muControl">reply</span> y <span class="Delimiter">}</span> <span class="muControl">reply</span> x ] <span class="muRecipe">recipe</span> max [ <span class="Constant">local-scope</span> x:number<span class="Special"> &lt;- </span><span class="Constant">next-ingredient</span> y:number<span class="Special"> &lt;- </span><span class="Constant">next-ingredient</span> <span class="Delimiter">{</span> return-x?:boolean<span class="Special"> &lt;- </span>greater-than x, y <span class="muControl">break-if</span> return-x? <span class="muControl">reply</span> y <span class="Delimiter">}</span> <span class="muControl">reply</span> x ] </pre> </body> </html> <!-- vim: set foldmethod=manual : -->