<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  </head>
  <body text="#000000" bgcolor="#FFFFFF">
    <div class="markdown-here-wrapper" data-md-url="Thunderbird"
      style="font-family: Open Sans, Cantarell, Carlito, Calibri,
      Verdana, DejaVu Sans, Trebuchet MS, sans-serif;">
      <p style="font-family: Open Sans, Cantarell, Carlito, Calibri,
        Verdana, DejaVu Sans, Trebuchet MS, sans-serif;margin: 0px 0px
        0.75em !important;">I’m a fan of <code style="font-family: Consolas, Inconsolata, Andale Mono, DejaVu Sans Mono, Courier, monospace; font-size: 10pt;margin: 0px 0.15em; padding: 0px 0.3em; white-space: pre-wrap; border: 1px solid rgb(234, 234, 234); background-color: rgb(248, 248, 248); border-radius: 3px; display: inline;">regex-applicative</code>.
        It’s a combinator library modelled on the parsec family but
        because it parses only regular languages rather than
        context-free ones, it’s a bit simpler to use and is a better
        match for some tasks. It uses <code style="font-family: Consolas, Inconsolata, Andale Mono, DejaVu Sans Mono, Courier, monospace; font-size: 10pt;margin: 0px 0.15em; padding: 0px 0.3em; white-space: pre-wrap; border: 1px solid rgb(234, 234, 234); background-color: rgb(248, 248, 248); border-radius: 3px; display: inline;">OverloadedStrings</code>
        to make it easier to include literal string matches and provides
        a non-greedy repeating combinator called <code style="font-family: Consolas, Inconsolata, Andale Mono, DejaVu Sans Mono, Courier, monospace; font-size: 10pt;margin: 0px 0.15em; padding: 0px 0.3em; white-space: pre-wrap; border: 1px solid rgb(234, 234, 234); background-color: rgb(248, 248, 248); border-radius: 3px; display: inline;">few</code>
        that avoids having to specify exclusive matches. I think it
        solves this problem quite nicely:</p>
      <pre style="font-family: Consolas, Inconsolata, Andale Mono, DejaVu Sans Mono, Courier, monospace; font-size: 10pt;font-size: 1em; line-height: 1.2em;margin: 1.2em 0px;"><code class="hljs language-haskell" style="font-family: Consolas, Inconsolata, Andale Mono, DejaVu Sans Mono, Courier, monospace; font-size: 10pt;margin: 0px 0.15em; padding: 0px 0.3em; white-space: pre-wrap; border: 1px solid rgb(234, 234, 234); background-color: rgb(248, 248, 248); border-radius: 3px; display: inline;white-space: pre; overflow: auto; border-radius: 3px; border: 1px solid rgb(0, 0, 0); background-color: rgb(68, 68, 68); color: rgb(0, 204, 0); display: block !important;display: block; overflow-x: auto; padding: 0.5em; background: rgb(35, 36, 31) none repeat scroll 0% 0%; -moz-text-size-adjust: none;color: rgb(248, 248, 242);"><span class="hljs-pragma" style="color: rgb(248, 248, 242);">{-# LANGUAGE OverloadedStrings #-}</span>

<span class="hljs-import"><span class="hljs-keyword" style="color: rgb(249, 38, 114);">import</span> Text.Regex.Applicative</span>

<span class="hljs-comment" style="color: rgb(117, 113, 94);">-- This is just <*> with right associativity</span>
(<&>) :: <span class="hljs-type" style="color: rgb(230, 219, 116);">Applicative</span> f => f (a -> b) -> f a -> f b
(<&>) = (<*>)
<span class="hljs-infix"><span class="hljs-keyword" style="color: rgb(249, 38, 114);">infixr</span> <span class="hljs-number" style="color: rgb(174, 129, 255);">3</span> <&></span>

<span class="hljs-typedef"><span class="hljs-keyword" style="color: rgb(249, 38, 114);">type</span> <span class="hljs-type" style="color: rgb(230, 219, 116);">Item</span> = <span class="hljs-container">(<span class="hljs-type" style="color: rgb(230, 219, 116);">String</span>, <span class="hljs-type" style="color: rgb(230, 219, 116);">String</span>)</span></span>

<span class="hljs-title" style="color: rgb(166, 226, 46);">item</span> :: <span class="hljs-type" style="color: rgb(230, 219, 116);">String</span> -> <span class="hljs-type" style="color: rgb(230, 219, 116);">RE</span> <span class="hljs-type" style="color: rgb(230, 219, 116);">Char</span> ([<span class="hljs-type" style="color: rgb(230, 219, 116);">Item</span>] -> [<span class="hljs-type" style="color: rgb(230, 219, 116);">Item</span>])
<span class="hljs-title" style="color: rgb(166, 226, 46);">item</span> key = (:) . (,) key <$> few anySym

<span class="hljs-comment" style="color: rgb(117, 113, 94);">-- ${year}/${month}/${day} ${hour}:${minute} User ${username} runs command ${command}.</span>
<span class="hljs-title" style="color: rgb(166, 226, 46);">pattern</span> :: <span class="hljs-type" style="color: rgb(230, 219, 116);">RE</span> <span class="hljs-type" style="color: rgb(230, 219, 116);">Char</span> [<span class="hljs-type" style="color: rgb(230, 219, 116);">Item</span>]
<span class="hljs-title" style="color: rgb(166, 226, 46);">pattern</span> =
    item <span class="hljs-string" style="color: rgb(230, 219, 116);">"year"</span> <* <span class="hljs-string" style="color: rgb(230, 219, 116);">"/"</span> <&> item <span class="hljs-string" style="color: rgb(230, 219, 116);">"month"</span> <* <span class="hljs-string" style="color: rgb(230, 219, 116);">"/"</span> <&> item <span class="hljs-string" style="color: rgb(230, 219, 116);">"day"</span> <* <span class="hljs-string" style="color: rgb(230, 219, 116);">" "</span> <&>
    item <span class="hljs-string" style="color: rgb(230, 219, 116);">"hour"</span> <* <span class="hljs-string" style="color: rgb(230, 219, 116);">":"</span> <&> item <span class="hljs-string" style="color: rgb(230, 219, 116);">"minute"</span> <* <span class="hljs-string" style="color: rgb(230, 219, 116);">" User "</span> <&>
    item <span class="hljs-string" style="color: rgb(230, 219, 116);">"username"</span> <* <span class="hljs-string" style="color: rgb(230, 219, 116);">" runs command "</span> <&> item <span class="hljs-string" style="color: rgb(230, 219, 116);">"command"</span> <* <span class="hljs-string" style="color: rgb(230, 219, 116);">"."</span> <&>
    pure []

<span class="hljs-title" style="color: rgb(166, 226, 46);">input</span> :: <span class="hljs-type" style="color: rgb(230, 219, 116);">String</span>
<span class="hljs-title" style="color: rgb(166, 226, 46);">input</span> = <span class="hljs-string" style="color: rgb(230, 219, 116);">"2019/04/17 17:27 User magicloud runs command ls."</span>

<span class="hljs-title" style="color: rgb(166, 226, 46);">output</span> :: <span class="hljs-type" style="color: rgb(230, 219, 116);">Maybe</span> [<span class="hljs-type" style="color: rgb(230, 219, 116);">Item</span>]
<span class="hljs-title" style="color: rgb(166, 226, 46);">output</span> = match pattern input
<span class="hljs-comment" style="color: rgb(117, 113, 94);">-- Just [("year","2019"),("month","04"),("day","17"),("hour","17"),("minute","27"),("username","magicloud"),("command","ls")]</span>
</code></pre>
      <p style="font-family: Open Sans, Cantarell, Carlito, Calibri,
        Verdana, DejaVu Sans, Trebuchet MS, sans-serif;margin: 0px 0px
        0.75em !important;">(Also available as a <a
href="https://gist.github.com/neilmayhew/e4fc90b7eaeb7bbcfeb6d6938544ecc9">gist</a>.)</p>
      <p style="font-family: Open Sans, Cantarell, Carlito, Calibri,
        Verdana, DejaVu Sans, Trebuchet MS, sans-serif;margin: 0px 0px
        0.75em !important;">Obviously the combinator version is less
        compact and therefore could be considered less readable, but the
        implementation details could probably be tweaked a bit. It would
        also be relatively easy to write a quasi-quoter that turns the
        original input syntax (with <code style="font-family: Consolas, Inconsolata, Andale Mono, DejaVu Sans Mono, Courier, monospace; font-size: 10pt;margin: 0px 0.15em; padding: 0px 0.3em; white-space: pre-wrap; border: 1px solid rgb(234, 234, 234); background-color: rgb(248, 248, 248); border-radius: 3px; display: inline;">${variable}</code>)
        into the equivalent I’ve shown here.</p>
      <div
title="MDH:SSdtIGEgZmFuIG9mIGByZWdleC1hcHBsaWNhdGl2ZWAuIEl0J3MgYSBjb21iaW5hdG9yIGxpYnJhcnkgbW9kZWxsZWQgb24gdGhlIHBhcnNlYyBmYW1pbHkgYnV0IGJlY2F1c2UgaXQgcGFyc2VzIG9u
bHkgcmVndWxhciBsYW5ndWFnZXMgcmF0aGVyIHRoYW4gY29udGV4dC1mcmVlIG9uZXMsIGl0J3Mg
YSBiaXQgc2ltcGxlciB0byB1c2UgYW5kIGlzIGEgYmV0dGVyIG1hdGNoIGZvciBzb21lIHRhc2tz
LiBJdCB1c2VzIGBPdmVybG9hZGVkU3RyaW5nc2AgdG8gbWFrZSBpdCBlYXNpZXIgdG8gaW5jbHVk
ZSBsaXRlcmFsIHN0cmluZyBtYXRjaGVzIGFuZCBwcm92aWRlcyBhIG5vbi1ncmVlZHkgcmVwZWF0
aW5nIGNvbWJpbmF0b3IgY2FsbGVkIGBmZXdgIHRoYXQgYXZvaWRzIGhhdmluZyB0byBzcGVjaWZ5
IGV4Y2x1c2l2ZSBtYXRjaGVzLiBJIHRoaW5rIGl0IHNvbHZlcyB0aGlzIHByb2JsZW0gcXVpdGUg
bmljZWx5Ojxicj48YnI+YGBgaGFza2VsbDxicj57LSMgTEFOR1VBR0UgT3ZlcmxvYWRlZFN0cmlu
Z3MgIy19PGJyPjxicj5pbXBvcnQgVGV4dC5SZWdleC5BcHBsaWNhdGl2ZTxicj48YnI+LS0gVGhp
cyBpcyBqdXN0ICZsdDsqJmd0OyB3aXRoIHJpZ2h0IGFzc29jaWF0aXZpdHk8YnI+KCZsdDsmYW1w
OyZndDspIDo6IEFwcGxpY2F0aXZlIGYgPSZndDsgZiAoYSAtJmd0OyBiKSAtJmd0OyBmIGEgLSZn
dDsgZiBiPGJyPigmbHQ7JmFtcDsmZ3Q7KSA9ICgmbHQ7KiZndDspPGJyPmluZml4ciAzICZsdDsm
YW1wOyZndDs8YnI+PGJyPnR5cGUgSXRlbSA9IChTdHJpbmcsIFN0cmluZyk8YnI+PGJyPml0ZW0g
OjogU3RyaW5nIC0mZ3Q7IFJFIENoYXIgKFtJdGVtXSAtJmd0OyBbSXRlbV0pPGJyPml0ZW0ga2V5
ID0gKDopIC4gKCwpIGtleSAmbHQ7JCZndDsgZmV3IGFueVN5bTxicj48YnI+LS0gJHt5ZWFyfS8k
e21vbnRofS8ke2RheX0gJHtob3VyfToke21pbnV0ZX0gVXNlciAke3VzZXJuYW1lfSBydW5zIGNv
bW1hbmQgJHtjb21tYW5kfS48YnI+cGF0dGVybiA6OiBSRSBDaGFyIFtJdGVtXTxicj5wYXR0ZXJu
ID08YnI+wqDCoMKgIGl0ZW0gInllYXIiICZsdDsqICIvIiAmbHQ7JmFtcDsmZ3Q7IGl0ZW0gIm1v
bnRoIiAmbHQ7KiAiLyIgJmx0OyZhbXA7Jmd0OyBpdGVtICJkYXkiICZsdDsqICIgIiAmbHQ7JmFt
cDsmZ3Q7PGJyPsKgwqDCoCBpdGVtICJob3VyIiAmbHQ7KiAiOiIgJmx0OyZhbXA7Jmd0OyBpdGVt
ICJtaW51dGUiICZsdDsqICIgVXNlciAiICZsdDsmYW1wOyZndDs8YnI+wqDCoMKgIGl0ZW0gInVz
ZXJuYW1lIiAmbHQ7KiAiIHJ1bnMgY29tbWFuZCAiICZsdDsmYW1wOyZndDsgaXRlbSAiY29tbWFu
ZCIgJmx0OyogIi4iICZsdDsmYW1wOyZndDs8YnI+wqDCoMKgIHB1cmUgW108YnI+PGJyPmlucHV0
IDo6IFN0cmluZzxicj5pbnB1dCA9ICIyMDE5LzA0LzE3IDE3OjI3IFVzZXIgbWFnaWNsb3VkIHJ1
bnMgY29tbWFuZCBscy4iPGJyPjxicj5vdXRwdXQgOjogTWF5YmUgW0l0ZW1dPGJyPm91dHB1dCA9
IG1hdGNoIHBhdHRlcm4gaW5wdXQ8YnI+LS0gSnVzdCBbKCJ5ZWFyIiwiMjAxOSIpLCgibW9udGgi
LCIwNCIpLCgiZGF5IiwiMTciKSwoImhvdXIiLCIxNyIpLCgibWludXRlIiwiMjciKSwoInVzZXJu
YW1lIiwibWFnaWNsb3VkIiksKCJjb21tYW5kIiwibHMiKV08YnI+YGBgPGJyPjxicj4oQWxzbyBh
dmFpbGFibGUgYXMgYSBbZ2lzdF0oaHR0cHM6Ly9naXN0LmdpdGh1Yi5jb20vbmVpbG1heWhldy9l
NGZjOTBiN2VhZWI3YmJjZmViNmQ2OTM4NTQ0ZWNjOSkuKTxicj48YnI+T2J2aW91c2x5IHRoZSBj
b21iaW5hdG9yIHZlcnNpb24gaXMgbGVzcyBjb21wYWN0IGFuZCB0aGVyZWZvcmUgY291bGQgYmUg
Y29uc2lkZXJlZCBsZXNzIHJlYWRhYmxlLCBidXQgdGhlIGltcGxlbWVudGF0aW9uIGRldGFpbHMg
Y291bGQgcHJvYmFibHkgYmUgdHdlYWtlZCBhIGJpdC4gSXQgd291bGQgYWxzbyBiZSByZWxhdGl2
ZWx5IGVhc3kgdG8gd3JpdGUgYSBxdWFzaS1xdW90ZXIgdGhhdCB0dXJucyB0aGUgb3JpZ2luYWwg
aW5wdXQgc3ludGF4ICh3aXRoIGAke3ZhcmlhYmxlfWApIGludG8gdGhlIGVxdWl2YWxlbnQgSSd2
        ZSBzaG93biBoZXJlLjxicj4="
style="height:0;width:0;max-height:0;max-width:0;overflow:hidden;font-size:0em;padding:0;margin:0;">​</div>
    </div>
  </body>
</html>