diff --git a/finished/elm-package.json b/finished/elm-package.json
index 7c979f1..bc1d227 100644
--- a/finished/elm-package.json
+++ b/finished/elm-package.json
@@ -15,6 +15,7 @@
"elm-lang/html": "2.0.0 <= v < 3.0.0",
"elm-lang/http": "1.0.0 <= v < 2.0.0",
"elm-lang/navigation": "2.1.0 <= v < 3.0.0",
+ "elm-tools/parser": "2.0.1 <= v < 3.0.0",
"evancz/elm-markdown": "3.0.2 <= v < 4.0.0",
"evancz/url-parser": "2.0.1 <= v < 3.0.0",
"lukewestby/elm-http-builder": "5.1.0 <= v < 6.0.0",
diff --git a/finished/elm-stuff/exact-dependencies.json b/finished/elm-stuff/exact-dependencies.json
index 77b48b4..2e36c57 100644
--- a/finished/elm-stuff/exact-dependencies.json
+++ b/finished/elm-stuff/exact-dependencies.json
@@ -1,6 +1,7 @@
{
"rtfeldman/elm-validate": "3.0.0",
"rtfeldman/selectlist": "1.0.0",
+ "elm-tools/parser-primitives": "1.0.0",
"elm-lang/navigation": "2.1.0",
"elm-lang/virtual-dom": "2.0.4",
"evancz/url-parser": "2.0.1",
@@ -8,6 +9,7 @@
"evancz/elm-markdown": "3.0.2",
"elm-lang/dom": "1.1.1",
"elm-lang/html": "2.0.0",
+ "elm-tools/parser": "2.0.1",
"elm-community/json-extra": "2.7.0",
"elm-lang/http": "1.0.0",
"lukewestby/elm-http-builder": "5.2.0",
diff --git a/finished/elm-stuff/packages/elm-tools/parser-primitives/1.0.0/.gitignore b/finished/elm-stuff/packages/elm-tools/parser-primitives/1.0.0/.gitignore
new file mode 100644
index 0000000..e185314
--- /dev/null
+++ b/finished/elm-stuff/packages/elm-tools/parser-primitives/1.0.0/.gitignore
@@ -0,0 +1 @@
+elm-stuff
\ No newline at end of file
diff --git a/finished/elm-stuff/packages/elm-tools/parser-primitives/1.0.0/LICENSE b/finished/elm-stuff/packages/elm-tools/parser-primitives/1.0.0/LICENSE
new file mode 100644
index 0000000..81f65b3
--- /dev/null
+++ b/finished/elm-stuff/packages/elm-tools/parser-primitives/1.0.0/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2017-present, Evan Czaplicki
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+* Neither the name of the {organization} nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/finished/elm-stuff/packages/elm-tools/parser-primitives/1.0.0/README.md b/finished/elm-stuff/packages/elm-tools/parser-primitives/1.0.0/README.md
new file mode 100644
index 0000000..647a900
--- /dev/null
+++ b/finished/elm-stuff/packages/elm-tools/parser-primitives/1.0.0/README.md
@@ -0,0 +1,7 @@
+# Parser Primitives
+
+**In 99.9999% of cases, you do not want this.**
+
+When creating a parser combinator library like [`elm-tools/parser`](https://github.com/elm-tools/parser), you want lower-level access to strings to get better performance.
+
+This package exposes these low-level functions so that `elm-tools/parser` does not have an unfair performance advantage.
diff --git a/finished/elm-stuff/packages/elm-tools/parser-primitives/1.0.0/elm-package.json b/finished/elm-stuff/packages/elm-tools/parser-primitives/1.0.0/elm-package.json
new file mode 100644
index 0000000..c673a2d
--- /dev/null
+++ b/finished/elm-stuff/packages/elm-tools/parser-primitives/1.0.0/elm-package.json
@@ -0,0 +1,17 @@
+{
+ "version": "1.0.0",
+ "summary": "Fast (but safe) primitives for creating parsing packages",
+ "repository": "https://github.com/elm-tools/parser-primitives.git",
+ "license": "BSD-3-Clause",
+ "source-directories": [
+ "src"
+ ],
+ "exposed-modules": [
+ "ParserPrimitives"
+ ],
+ "dependencies": {
+ "elm-lang/core": "5.0.0 <= v < 6.0.0"
+ },
+ "native-modules": true,
+ "elm-version": "0.18.0 <= v < 0.19.0"
+}
diff --git a/finished/elm-stuff/packages/elm-tools/parser-primitives/1.0.0/src/Native/ParserPrimitives.js b/finished/elm-stuff/packages/elm-tools/parser-primitives/1.0.0/src/Native/ParserPrimitives.js
new file mode 100644
index 0000000..c7a6578
--- /dev/null
+++ b/finished/elm-stuff/packages/elm-tools/parser-primitives/1.0.0/src/Native/ParserPrimitives.js
@@ -0,0 +1,130 @@
+var _elm_tools$parser_primitives$Native_ParserPrimitives = function() {
+
+
+// STRINGS
+
+function isSubString(smallString, offset, row, col, bigString)
+{
+ var smallLength = smallString.length;
+ var bigLength = bigString.length - offset;
+
+ if (bigLength < smallLength)
+ {
+ return tuple3(-1, row, col);
+ }
+
+ for (var i = 0; i < smallLength; i++)
+ {
+ var char = smallString[i];
+
+ if (char !== bigString[offset + i])
+ {
+ return tuple3(-1, row, col);
+ }
+
+ // if it is a two word character
+ if ((bigString.charCodeAt(offset) & 0xF800) === 0xD800)
+ {
+ i++
+ if (smallString[i] !== bigString[offset + i])
+ {
+ return tuple3(-1, row, col);
+ }
+ col++;
+ continue;
+ }
+
+ // if it is a newline
+ if (char === '\n')
+ {
+ row++;
+ col = 1;
+ continue;
+ }
+
+ // if it is a one word character
+ col++
+ }
+
+ return tuple3(offset + smallLength, row, col);
+}
+
+function tuple3(a, b, c)
+{
+ return { ctor: '_Tuple3', _0: a, _1: b, _2: c };
+}
+
+
+// CHARS
+
+var mkChar = _elm_lang$core$Native_Utils.chr;
+
+function isSubChar(predicate, offset, string)
+{
+ if (offset >= string.length)
+ {
+ return -1;
+ }
+
+ if ((string.charCodeAt(offset) & 0xF800) === 0xD800)
+ {
+ return predicate(mkChar(string.substr(offset, 2)))
+ ? offset + 2
+ : -1;
+ }
+
+ var char = string[offset];
+
+ return predicate(mkChar(char))
+ ? ((char === '\n') ? -2 : (offset + 1))
+ : -1;
+}
+
+
+// FIND STRING
+
+function findSubString(before, smallString, offset, row, col, bigString)
+{
+ var newOffset = bigString.indexOf(smallString, offset);
+
+ if (newOffset === -1)
+ {
+ return tuple3(-1, row, col);
+ }
+
+ var scanTarget = before ? newOffset : newOffset + smallString.length;
+
+ while (offset < scanTarget)
+ {
+ var char = bigString[offset];
+
+ if (char === '\n')
+ {
+ offset++;
+ row++;
+ col = 1;
+ continue;
+ }
+
+ if ((bigString.charCodeAt(offset) & 0xF800) === 0xD800)
+ {
+ offset += 2;
+ col++;
+ continue;
+ }
+
+ offset++;
+ col++;
+ }
+
+ return tuple3(offset, row, col);
+}
+
+
+return {
+ isSubString: F5(isSubString),
+ isSubChar: F3(isSubChar),
+ findSubString: F6(findSubString)
+};
+
+}();
diff --git a/finished/elm-stuff/packages/elm-tools/parser-primitives/1.0.0/src/ParserPrimitives.elm b/finished/elm-stuff/packages/elm-tools/parser-primitives/1.0.0/src/ParserPrimitives.elm
new file mode 100644
index 0000000..e93d647
--- /dev/null
+++ b/finished/elm-stuff/packages/elm-tools/parser-primitives/1.0.0/src/ParserPrimitives.elm
@@ -0,0 +1,109 @@
+module ParserPrimitives exposing
+ ( isSubString
+ , isSubChar
+ , findSubString
+ )
+
+{-| Low-level functions for creating parser combinator libraries.
+
+@docs isSubString, isSubChar, findSubString
+-}
+
+import Native.ParserPrimitives
+
+
+
+-- STRINGS
+
+
+{-| When making a fast parser, you want to avoid allocation as much as
+possible. That means you never want to mess with the source string, only
+keep track of an offset into that string.
+
+You use `isSubString` like this:
+
+ isSubString "let" offset row col "let x = 4 in x"
+ --==> ( newOffset, newRow, newCol )
+
+You are looking for `"let"` at a given `offset`. On failure, the
+`newOffset` is `-1`. On success, the `newOffset` is the new offset. With
+our `"let"` example, it would be `offset + 3`.
+
+You also provide the current `row` and `col` which do not align with
+`offset` in a clean way. For example, when you see a `\n` you are at
+`row = row + 1` and `col = 1`. Furthermore, some UTF16 characters are
+two words wide, so even if there are no newlines, `offset` and `col`
+may not be equal.
+-}
+isSubString : String -> Int -> Int -> Int -> String -> (Int, Int, Int)
+isSubString =
+ Native.ParserPrimitives.isSubString
+
+
+
+-- CHARACTERS
+
+
+{-| Again, when parsing, you want to allocate as little as possible.
+So this function lets you say:
+
+ isSubChar isSpace offset "this is the source string"
+ --==> newOffset
+
+The `(Char -> Bool)` argument is called a predicate.
+The `newOffset` value can be a few different things:
+
+ - `-1` means that the predicate failed
+ - `-2` means the predicate succeeded with a `\n`
+ - otherwise you will get `offset + 1` or `offset + 2`
+ depending on whether the UTF16 character is one or two
+ words wide.
+
+It is better to use union types in general, but it is worth the
+danger *within* parsing libraries to get the benefit *outside*.
+
+So you can write a `chomp` function like this:
+
+ chomp : (Char -> Bool) -> Int -> Int -> Int -> String -> (Int, Int, Int)
+ chomp isGood offset row col source =
+ let
+ newOffset =
+ Prim.isSubChar isGood offset source
+ in
+ -- no match
+ if newOffset == -1 then
+ (offset, row, col)
+
+ -- newline match
+ else if newOffset == -2 then
+ chomp isGood (offset + 1) (row + 1) 1 source
+
+ -- normal match
+ else
+ chomp isGood newOffset row (col + 1) source
+
+Notice that `chomp` can be tail-call optimized, so this turns into a
+`while` loop under the hood.
+-}
+isSubChar : (Char -> Bool) -> Int -> String -> Int
+isSubChar =
+ Native.ParserPrimitives.isSubChar
+
+
+
+-- INDEX
+
+
+{-| Find a substring after a given offset.
+
+ findSubString before "42" offset row col "Is 42 the answer?"
+ --==> (newOffset, newRow, newCol)
+
+If `offset = 0` and `before = True` we would get `(3, 1, 4)`
+If `offset = 0` and `before = False` we would get `(5, 1, 6)`
+
+If `offset = 7` we would get `(-1, 1, 18)`
+-}
+findSubString : Bool -> String -> Int -> Int -> Int -> String -> (Int, Int, Int)
+findSubString =
+ Native.ParserPrimitives.findSubString
diff --git a/finished/elm-stuff/packages/elm-tools/parser/2.0.1/.gitignore b/finished/elm-stuff/packages/elm-tools/parser/2.0.1/.gitignore
new file mode 100644
index 0000000..e185314
--- /dev/null
+++ b/finished/elm-stuff/packages/elm-tools/parser/2.0.1/.gitignore
@@ -0,0 +1 @@
+elm-stuff
\ No newline at end of file
diff --git a/finished/elm-stuff/packages/elm-tools/parser/2.0.1/LICENSE b/finished/elm-stuff/packages/elm-tools/parser/2.0.1/LICENSE
new file mode 100644
index 0000000..81f65b3
--- /dev/null
+++ b/finished/elm-stuff/packages/elm-tools/parser/2.0.1/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2017-present, Evan Czaplicki
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+* Neither the name of the {organization} nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/finished/elm-stuff/packages/elm-tools/parser/2.0.1/README.md b/finished/elm-stuff/packages/elm-tools/parser/2.0.1/README.md
new file mode 100644
index 0000000..50289ed
--- /dev/null
+++ b/finished/elm-stuff/packages/elm-tools/parser/2.0.1/README.md
@@ -0,0 +1,170 @@
+# Parser + Nice Error Messages
+
+Goals:
+
+ - Make writing parsers as simple and fun as possible.
+ - Produce excellent error messages.
+ - Go pretty fast.
+
+This is achieved with a couple concepts that I have not seen in any other parser libraries: [parser pipelines](#parser-pipelines), [tracking context](#tracking-context), and [delayed commits](#delayed-commits).
+
+
+## Parser Pipelines
+
+To parse a 2D point like `( 3, 4 )`, you might create a `point` parser like this:
+
+```elm
+import Parser exposing (Parser, (|.), (|=), succeed, symbol, float, ignore, zeroOrMore)
+
+
+type alias Point =
+ { x : Float
+ , y : Float
+ }
+
+
+point : Parser Point
+point =
+ succeed Point
+ |. symbol "("
+ |. spaces
+ |= float
+ |. spaces
+ |. symbol ","
+ |. spaces
+ |= float
+ |. spaces
+ |. symbol ")"
+
+
+spaces : Parser ()
+spaces =
+ ignore zeroOrMore (\c -> c == ' ')
+```
+
+All the interesting stuff is happening in `point`. It uses two operators:
+
+ - [`(|.)`][ignore] means “parse this, but **ignore** the result”
+ - [`(|=)`][keep] means “parse this, and **keep** the result”
+
+So the `Point` function only gets the result of the two `float` parsers.
+
+[ignore]: http://package.elm-lang.org/packages/elm-tools/parser/latest/Parser#|.
+[keep]: http://package.elm-lang.org/packages/elm-tools/parser/latest/Parser#|=
+
+The theory is that `|=` introduces more “visual noise” than `|.`, making it pretty easy to pick out which lines in the pipeline are important.
+
+I recommend having one line per operator in your parser pipeline. If you need multiple lines for some reason, use a `let` or make a helper function.
+
+
+## Tracking Context
+
+Most parsers tell you the row and column of the problem:
+
+ Something went wrong at (4:17)
+
+That may be true, but it is not how humans think. It is how text editors think! It would be better to say:
+
+ I found a problem with this list:
+
+ [ 1, 23zm5, 3 ]
+ ^
+ I wanted an integer, like 6 or 90219.
+
+Notice that the error messages says `this list`. That is context! That is the language my brain speaks, not rows and columns.
+
+This parser package lets you annotate context with the [`inContext`][inContext] function. You can let the parser know “I am trying to parse a `"list"` right now” so if an error happens anywhere in that context, you get the hand annotation!
+
+[inContext]: http://package.elm-lang.org/packages/elm-tools/parser/latest/Parser#inContext
+
+> **Note:** This technique is used by the parser in the Elm compiler to give more helpful error messages.
+
+
+## Delayed Commits
+
+To make fast parsers with precise error messages, this package lets you control when a parser **commits** to a certain path.
+
+For example, you are trying to parse the following list:
+
+```elm
+[ 1, 23zm5, 3 ]
+```
+
+Ideally, you want the error at the `z`, but the libraries I have seen make this difficult to achieve efficiently. You often end up with an error at `[` because “something went wrong”.
+
+**This package introduces [`delayedCommit`][delayedCommit] to resolve this.**
+
+Say we want to create `intList`, a parser for comma separated lists of integers like `[1, 2, 3]`. We would say something like this:
+
+[delayedCommit]: http://package.elm-lang.org/packages/elm-tools/parser/latest/Parser#delayedCommit
+
+```elm
+import Parser exposing (..)
+
+
+{-| We start by ignoring the opening square brace and some spaces.
+We only really care about the numbers, so we parse an `int` and
+then use `intListHelp` to start chomping other list entries.
+-}
+intList : Parser (List Int)
+intList =
+ succeed identity
+ |. symbol "["
+ |. spaces
+ |= andThen (\n -> intListHelp [n]) int
+ |. spaces
+ |. symbol "]"
+
+
+{-| `intListHelp` checks if there is a `nextInt`. If so, it
+continues trying to find more list items. If not, it gives
+back the list of integers we have accumulated so far.
+-}
+intListHelp : List Int -> Parser (List Int)
+intListHelp revInts =
+ oneOf
+ [ nextInt
+ |> andThen (\n -> intListHelp (n :: revInts))
+ , succeed (List.reverse revInts)
+ ]
+```
+
+Now we get to the tricky part! How do we define `nextInt`? Here are two approaches, but only the second one actually works!
+
+
+```elm
+-- BAD
+badNextInt : Parser Int
+badNextInt =
+ succeed identity
+ |. spaces
+ |. symbol ","
+ |. spaces
+ |= int
+
+-- GOOD
+nextInt : Parser Int
+nextInt =
+ delayedCommit spaces <|
+ succeed identity
+ |. symbol ","
+ |. spaces
+ |= int
+```
+
+The `badNextInt` looks pretty normal, but it will not work. It commits as soon as the first `spaces` parser succeeds. It fails in the following situation:
+
+```elm
+[ 1, 2, 3 ]
+ ^
+```
+
+When we get to the closing `]` we have already successfully parsed some spaces. That means we are commited to `badNextInt` and need a comma. That fails, so the whole parse fails!
+
+With `nextInt`, the [`delayedCommit`][delayedCommit] function is saying to parse `spaces` but only commit if progress is made *after* that. So we are only commited to this parser if we see a comma.
+
+
+
+
+
+## [Comparison with Prior Work](https://github.com/elm-tools/parser/blob/master/comparison.md)
diff --git a/finished/elm-stuff/packages/elm-tools/parser/2.0.1/comparison.md b/finished/elm-stuff/packages/elm-tools/parser/2.0.1/comparison.md
new file mode 100644
index 0000000..81ef545
--- /dev/null
+++ b/finished/elm-stuff/packages/elm-tools/parser/2.0.1/comparison.md
@@ -0,0 +1,69 @@
+## Comparison with Prior Work
+
+I have not seen the [parser pipeline][1] or the [context stack][2] ideas in other libraries, but [delayed commits][3] relate to prior work.
+
+[1]: README.md#parser-pipelines
+[2]: README.md#tracking-context
+[3]: README.md#delayed-commits
+
+Most parser combinator libraries I have seen are based on Haskell’s Parsec library, which has primitives named `try` and `lookAhead`. I believe [`delayedCommitMap`][delayedCommitMap] is a better primitive for two reasons.
+
+[delayedCommitMap]: http://package.elm-lang.org/packages/elm-tools/parser/latest/Parser#delayedCommitMap
+
+
+### Performance and Composition
+
+Say we want to create a precise error message for `length [1,,3]`. The naive approach with Haskell’s Parsec library produces very bad error messages:
+
+```haskell
+spaceThenArg :: Parser Expr
+spaceThenArg =
+ try (spaces >> term)
+```
+
+This means we get a precise error from `term`, but then throw it away and say something went wrong at the space before the `[`. Very confusing! To improve quality, we must write something like this:
+
+```haskell
+spaceThenArg :: Parser Expr
+spaceThenArg =
+ choice
+ [ do lookAhead (spaces >> char '[')
+ spaces
+ term
+ , try (spaces >> term)
+ ]
+```
+
+Notice that we parse `spaces` twice no matter what.
+
+Notice that we also had to hardcode `[` in the `lookAhead`. What if we update `term` to parse records that start with `{` as well? To get good commits on records, we must remember to update `lookAhead` to look for `oneOf "[{"`. Implementation details are leaking out of `term`!
+
+With `delayedCommit` in this Elm library, you can just say:
+
+```elm
+spaceThenArg : Parser Expr
+spaceThenArg =
+ delayedCommit spaces term
+```
+
+It does less work, and is more reliable as `term` evolves. I believe `delayedCommit` makes `lookAhead` pointless.
+
+
+### Expressiveness
+
+You can define `try` in terms of [`delayedCommitMap`][delayedCommitMap] like this:
+
+```elm
+try : Parser a -> Parser a
+try parser =
+ delayedCommitMap always parser (succeed ())
+```
+
+No expressiveness is lost!
+
+While it is possible to define `try`, I left it out of this package. In practice, `try` often leads to “bad commits” where your parser fails in a very specific way, but you then backtrack to a less specific error message. I considered naming it `allOrNothing` to better explain how it changes commit behavior, but ultimately, I thought it was best to encourage users to express their parsers with `delayedCommit` directly.
+
+
+### Summary
+
+Compared to previous work, `delayedCommit` lets you produce precise error messages **more efficiently**. By thinking about “commit behavior” directly, you also end up with **cleaner composition** of parsers. And these benefits come **without any loss of expressiveness**.
diff --git a/finished/elm-stuff/packages/elm-tools/parser/2.0.1/elm-package.json b/finished/elm-stuff/packages/elm-tools/parser/2.0.1/elm-package.json
new file mode 100644
index 0000000..7c82383
--- /dev/null
+++ b/finished/elm-stuff/packages/elm-tools/parser/2.0.1/elm-package.json
@@ -0,0 +1,19 @@
+{
+ "version": "2.0.1",
+ "summary": "a parsing library, focused on simplicity and great error messages",
+ "repository": "https://github.com/elm-tools/parser.git",
+ "license": "BSD-3-Clause",
+ "source-directories": [
+ "src"
+ ],
+ "exposed-modules": [
+ "Parser",
+ "Parser.LanguageKit",
+ "Parser.LowLevel"
+ ],
+ "dependencies": {
+ "elm-lang/core": "5.1.0 <= v < 6.0.0",
+ "elm-tools/parser-primitives": "1.0.0 <= v < 2.0.0"
+ },
+ "elm-version": "0.18.0 <= v < 0.19.0"
+}
diff --git a/finished/elm-stuff/packages/elm-tools/parser/2.0.1/src/Parser.elm b/finished/elm-stuff/packages/elm-tools/parser/2.0.1/src/Parser.elm
new file mode 100644
index 0000000..163d26b
--- /dev/null
+++ b/finished/elm-stuff/packages/elm-tools/parser/2.0.1/src/Parser.elm
@@ -0,0 +1,1116 @@
+module Parser exposing
+ ( Parser
+ , run
+ , int, float, symbol, keyword, end
+ , Count(..), zeroOrMore, oneOrMore, keep, ignore, repeat
+ , succeed, fail, map, oneOf, (|=), (|.), map2, lazy, andThen
+ , delayedCommit, delayedCommitMap
+ , source, sourceMap, ignoreUntil
+ , Error, Problem(..), Context, inContext
+ )
+
+{-|
+
+# Parsers
+@docs Parser, run
+
+# Numbers and Keywords
+@docs int, float, symbol, keyword, end
+
+# Repeat Parsers
+@docs Count, zeroOrMore, oneOrMore, keep, ignore, repeat
+
+# Combining Parsers
+@docs succeed, fail, map, oneOf, (|=), (|.), map2, lazy, andThen
+
+# Delayed Commits
+@docs delayedCommit, delayedCommitMap
+
+# Efficiency Tricks
+@docs source, sourceMap, ignoreUntil
+
+# Errors
+@docs Error, Problem, Context, inContext
+-}
+
+import Char
+import Parser.Internal as Internal exposing (Parser(..), Step(..))
+import ParserPrimitives as Prim
+
+
+
+-- PARSER
+
+
+{-| A parser! If you have a `Parser Int`, it is a parser that turns
+strings into integers.
+-}
+type alias Parser a =
+ Internal.Parser Context Problem a
+
+
+type alias Step a =
+ Internal.Step Context Problem a
+
+
+type alias State =
+ Internal.State Context
+
+
+{-| Actually run a parser.
+
+ run (keyword "true") "true" == Ok ()
+ run (keyword "true") "True" == Err ...
+ run (keyword "true") "false" == Err ...
+-}
+run : Parser a -> String -> Result Error a
+run (Parser parse) source =
+ let
+ initialState =
+ { source = source
+ , offset = 0
+ , indent = 1
+ , context = []
+ , row = 1
+ , col = 1
+ }
+ in
+ case parse initialState of
+ Good a _ ->
+ Ok a
+
+ Bad problem { row, col, context } ->
+ Err
+ { row = row
+ , col = col
+ , source = source
+ , problem = problem
+ , context = context
+ }
+
+
+-- ERRORS
+
+
+{-| Parse errors as data. You can format it however makes the most
+sense for your application. Maybe that is all text, or maybe it is fancy
+interactive HTML. Up to you!
+
+You get:
+
+ - The `row` and `col` of the error.
+ - The full `source` provided to the [`run`](#run) function.
+ - The actual `problem` you ran into.
+ - A stack of `context` that describes where the error is *conceptually*.
+
+**Note:** `context` is a stack. That means [`inContext`](#inContext)
+adds to the *front* of this list, not the back. So if you want the
+[`Context`](#Context) closest to the error, you want the first element
+of the `context` stack.
+-}
+type alias Error =
+ { row : Int
+ , col : Int
+ , source : String
+ , problem : Problem
+ , context : List Context
+ }
+
+
+{-| The particular problem you ran into.
+
+The tricky one here is `BadRepeat`. That means that you are running
+`zeroOrMore parser` where `parser` can succeed without consuming any
+input. That means it will just loop forever, consuming no input until
+the program crashes.
+-}
+type Problem
+ = BadOneOf (List Problem)
+ | BadInt
+ | BadFloat
+ | BadRepeat
+ | ExpectingEnd
+ | ExpectingSymbol String
+ | ExpectingKeyword String
+ | ExpectingVariable
+ | ExpectingClosing String
+ | Fail String
+
+
+{-| Most parsers only let you know the row and column where the error
+occurred. But what if you could *also* say “the error occured **while
+parsing a list**” and let folks know what the *parser* thinks it is
+doing?!
+
+The error messages would be a lot nicer! That is what Elm compiler does,
+and it is what `Context` helps you do in this library! **See the
+[`inContext`](#inContext) docs for a nice example!**
+
+About the actual fields:
+
+ - `description` is set by [`inContext`](#inContext)
+ - `row` and `col` are where [`inContext`](#inContext) began
+
+Say you use `inContext` in your list parser. And say get an error trying
+to parse `[ 1, 23zm5, 3 ]`. In addition to error information about `23zm5`,
+you would have `Context` with the row and column of the starting `[` symbol.
+-}
+type alias Context =
+ { row : Int
+ , col : Int
+ , description : String
+ }
+
+
+
+-- PRIMITIVES
+
+
+{-| A parser that succeeds without consuming any text.
+
+ run (succeed 90210 ) "mississippi" == Ok 90210
+ run (succeed 3.141 ) "mississippi" == Ok 3.141
+ run (succeed () ) "mississippi" == Ok ()
+ run (succeed Nothing) "mississippi" == Ok Nothing
+
+Seems weird, but it is often useful in combination with
+[`oneOf`](#oneOf) or [`andThen`](#andThen).
+-}
+succeed : a -> Parser a
+succeed a =
+ Parser <| \state -> Good a state
+
+
+{-| A parser always fails.
+
+ run (fail "bad list") "[1,2,3]" == Err ..
+
+Seems weird, but it is often useful in combination with
+[`oneOf`](#oneOf) or [`andThen`](#andThen).
+-}
+fail : String -> Parser a
+fail message =
+ Parser <| \state -> Bad (Fail message) state
+
+
+
+-- MAPPING
+
+
+{-| Transform the result of a parser. Maybe you have a value that is
+an integer or `null`:
+
+ nullOrInt : Parser (Maybe Int)
+ nullOrInt =
+ oneOf
+ [ map Just int
+ , map (\_ -> Nothing) (keyword "null")
+ ]
+
+ -- run nullOrInt "0" == Ok (Just 0)
+ -- run nullOrInt "13" == Ok (Just 13)
+ -- run nullOrInt "null" == Ok Nothing
+ -- run nullOrInt "zero" == Err ...
+
+-}
+map : (a -> b) -> Parser a -> Parser b
+map func (Parser parse) =
+ Parser <| \state1 ->
+ case parse state1 of
+ Good a state2 ->
+ Good (func a) state2
+
+ Bad x state2 ->
+ Bad x state2
+
+
+{-| **This function is not used much in practice.** It is nicer to use
+the [parser pipeline][pp] operators [`(|.)`](#|.) and [`(|=)`](#|=)
+instead.
+
+[pp]: https://github.com/elm-tools/parser/blob/master/README.md#parser-pipeline
+
+That said, this function can combine two parsers. Maybe you
+want to parse some spaces followed by an integer:
+
+ spacesThenInt : Parser Int
+ spacesThenInt =
+ map2 (\_ n -> n) spaces int
+
+ spaces : Parser ()
+ spaces =
+ ignore zeroOrMore (\char -> char == ' ')
+
+We can also use `map2` to define `(|.)` and `(|=)` like this:
+
+ (|.) : Parser keep -> Parser ignore -> Parser keep
+ (|.) keepParser ignoreParser =
+ map2 (\keep _ -> keep) keepParser ignoreParser
+
+ (|=) : Parser (a -> b) -> Parser a -> Parser b
+ (|=) funcParser argParser =
+ map2 (\func arg -> func arg) funcParser argParser
+-}
+map2 : (a -> b -> value) -> Parser a -> Parser b -> Parser value
+map2 func (Parser parseA) (Parser parseB) =
+ Parser <| \state1 ->
+ case parseA state1 of
+ Bad x state2 ->
+ Bad x state2
+
+ Good a state2 ->
+ case parseB state2 of
+ Bad x state3 ->
+ Bad x state3
+
+ Good b state3 ->
+ Good (func a b) state3
+
+
+{-| **Keep** a value in a parser pipeline.
+
+Read about parser pipelines **[here][]**. They are really nice!
+
+[here]: https://github.com/elm-tools/parser/blob/master/README.md#parser-pipeline
+-}
+(|=) : Parser (a -> b) -> Parser a -> Parser b
+(|=) parseFunc parseArg =
+ map2 apply parseFunc parseArg
+
+
+apply : (a -> b) -> a -> b
+apply f a =
+ f a
+
+
+{-| **Ignore** a value in a parser pipeline.
+
+Read about parser pipelines **[here][]**. They are really nice!
+
+[here]: https://github.com/elm-tools/parser/blob/master/README.md#parser-pipeline
+-}
+(|.) : Parser keep -> Parser ignore -> Parser keep
+(|.) keepParser ignoreParser =
+ map2 always keepParser ignoreParser
+
+
+infixl 5 |.
+infixl 5 |=
+
+
+
+-- AND THEN
+
+
+{-| Run a parser *and then* run another parser!
+-}
+andThen : (a -> Parser b) -> Parser a -> Parser b
+andThen callback (Parser parseA) =
+ Parser <| \state1 ->
+ case parseA state1 of
+ Bad x state2 ->
+ Bad x state2
+
+ Good a state2 ->
+ let
+ (Parser parseB) =
+ callback a
+ in
+ parseB state2
+
+
+
+-- LAZY
+
+
+{-| Helper to define recursive parsers. Say we want a parser for simple
+boolean expressions:
+
+ true
+ false
+ (true || false)
+ (true || (true || false))
+
+Notice that a boolean expression might contain *other* boolean expressions.
+That means we will want to define our parser in terms of itself:
+
+ type Boolean
+ = MyTrue
+ | MyFalse
+ | MyOr Boolean Boolean
+
+ boolean : Parser Boolean
+ boolean =
+ oneOf
+ [ succeed MyTrue
+ |. keyword "true"
+ , succeed MyFalse
+ |. keyword "false"
+ , succeed MyOr
+ |. symbol "("
+ |. spaces
+ |= lazy (\_ -> boolean)
+ |. spaces
+ |. symbol "||"
+ |. spaces
+ |= lazy (\_ -> boolean)
+ |. spaces
+ |. symbol ")"
+ ]
+
+ spaces : Parser ()
+ spaces =
+ ignore zeroOrMore (\char -> char == ' ')
+
+**Notice that `boolean` uses `boolean` in its definition!** In Elm, you can
+only define a value in terms of itself it is behind a function call. So
+`lazy` helps us define these self-referential parsers.
+
+**Note:** In some cases, it may be more natural or efficient to use
+`andThen` to hide a self-reference behind a function.
+-}
+lazy : (() -> Parser a) -> Parser a
+lazy thunk =
+ Parser <| \state ->
+ let
+ (Parser parse) =
+ thunk ()
+ in
+ parse state
+
+
+
+-- ONE OF
+
+
+{-| Try a bunch of different parsers. If a parser does not commit, we
+move on and try the next one. If a parser *does* commit, we give up on any
+remaining parsers.
+
+The idea is: if you make progress and commit to a parser, you want to
+get error messages from *that path*. If you bactrack and keep trying stuff
+you will get a much less precise error.
+
+So say we are parsing “language terms” that include integers and lists
+of integers:
+
+ term : Parser Expr
+ term =
+ oneOf
+ [ listOf int
+ , int
+ ]
+
+ listOf : Parser a -> Parser (List a)
+ listOf parser =
+ succeed identity
+ |. symbol "["
+ |. spaces
+ ...
+
+When we get to `oneOf`, we first try the `listOf int` parser. If we see a
+`[` we *commit* to that parser. That means if something goes wrong, we do
+not backtrack. Instead the parse fails! If we do not see a `[` we move on
+to the second option and just try the `int` parser.
+-}
+oneOf : List (Parser a) -> Parser a
+oneOf parsers =
+ Parser <| \state -> oneOfHelp state [] parsers
+
+
+oneOfHelp : State -> List Problem -> List (Parser a) -> Step a
+oneOfHelp state problems parsers =
+ case parsers of
+ [] ->
+ Bad (BadOneOf (List.reverse problems)) state
+
+ Parser parse :: remainingParsers ->
+ case parse state of
+ Good _ _ as step ->
+ step
+
+ Bad problem { row, col } as step ->
+ if state.row == row && state.col == col then
+ oneOfHelp state (problem :: problems) remainingParsers
+
+ else
+ step
+
+
+
+-- REPEAT
+
+
+{-| Try to use the parser as many times as possible. Say we want to parse
+`NaN` a bunch of times:
+
+ batman : Parser Int
+ batman =
+ map List.length (repeat zeroOrMore (keyword "NaN"))
+
+ -- run batman "whatever" == Ok 0
+ -- run batman "" == Ok 0
+ -- run batman "NaN" == Ok 1
+ -- run batman "NaNNaN" == Ok 2
+ -- run batman "NaNNaNNaN" == Ok 3
+ -- run batman "NaNNaN batman!" == Ok 2
+
+**Note:** If you are trying to parse things like `[1,2,3]` or `{ x = 3 }`
+check out the [`list`](Parser-LanguageKit#list) and
+[`record`](Parser-LanguageKit#record) functions in the
+[`Parser.LanguageKit`](Parser-LanguageKit) module.
+-}
+repeat : Count -> Parser a -> Parser (List a)
+repeat count (Parser parse) =
+ case count of
+ Exactly n ->
+ Parser <| \state ->
+ repeatExactly n parse [] state
+
+ AtLeast n ->
+ Parser <| \state ->
+ repeatAtLeast n parse [] state
+
+
+repeatExactly : Int -> (State -> Step a) -> List a -> State -> Step (List a)
+repeatExactly n parse revList state1 =
+ if n <= 0 then
+ Good (List.reverse revList) state1
+
+ else
+ case parse state1 of
+ Good a state2 ->
+ if state1.row == state2.row && state1.col == state2.col then
+ Bad BadRepeat state2
+ else
+ repeatExactly (n - 1) parse (a :: revList) state2
+
+ Bad x state2 ->
+ Bad x state2
+
+
+repeatAtLeast : Int -> (State -> Step a) -> List a -> State -> Step (List a)
+repeatAtLeast n parse revList state1 =
+ case parse state1 of
+ Good a state2 ->
+ if state1.row == state2.row && state1.col == state2.col then
+ Bad BadRepeat state2
+ else
+ repeatAtLeast (n - 1) parse (a :: revList) state2
+
+ Bad x state2 ->
+ if state1.row == state2.row && state1.col == state2.col && n <= 0 then
+ Good (List.reverse revList) state1
+
+ else
+ Bad x state2
+
+
+
+-- DELAYED COMMIT
+
+
+{-| Only commit if `Parser a` succeeds and `Parser value` makes some progress.
+
+This is very important for generating high quality error messages! Read more
+about this [here][1] and [here][2].
+
+[1]: https://github.com/elm-tools/parser/blob/master/README.md#delayed-commits
+[2]: https://github.com/elm-tools/parser/blob/master/comparison.md
+-}
+delayedCommit : Parser a -> Parser value -> Parser value
+delayedCommit filler realStuff =
+ delayedCommitMap (\_ v -> v) filler realStuff
+
+
+{-| Like [`delayedCommit`](#delayedCommit), but lets you extract values from
+both parsers. Read more about it [here][1] and [here][2].
+
+[1]: https://github.com/elm-tools/parser/blob/master/README.md#delayed-commits
+[2]: https://github.com/elm-tools/parser/blob/master/comparison.md
+-}
+delayedCommitMap : (a -> b -> value) -> Parser a -> Parser b -> Parser value
+delayedCommitMap func (Parser parseA) (Parser parseB) =
+ Parser <| \state1 ->
+ case parseA state1 of
+ Bad x _ ->
+ Bad x state1
+
+ Good a state2 ->
+ case parseB state2 of
+ Good b state3 ->
+ Good (func a b) state3
+
+ Bad x state3 ->
+ if state2.row == state3.row && state2.col == state3.col then
+ Bad x state1
+ else
+ Bad x state3
+
+
+
+-- SYMBOLS and KEYWORDS
+
+
+{-| Parse symbols like `,`, `(`, and `&&`.
+
+ run (symbol "[") "[" == Ok ()
+ run (symbol "[") "4" == Err ... (ExpectingSymbol "[") ...
+-}
+symbol : String -> Parser ()
+symbol str =
+ token ExpectingSymbol str
+
+
+{-| Parse keywords like `let`, `case`, and `type`.
+
+ run (keyword "let") "let" == Ok ()
+ run (keyword "let") "var" == Err ... (ExpectingKeyword "let") ...
+-}
+keyword : String -> Parser ()
+keyword str =
+ token ExpectingKeyword str
+
+
+token : (String -> Problem) -> String -> Parser ()
+token makeProblem str =
+ Parser <| \({ source, offset, indent, context, row, col } as state) ->
+ let
+ (newOffset, newRow, newCol) =
+ Prim.isSubString str offset row col source
+ in
+ if newOffset == -1 then
+ Bad (makeProblem str) state
+
+ else
+ Good ()
+ { source = source
+ , offset = newOffset
+ , indent = indent
+ , context = context
+ , row = newRow
+ , col = newCol
+ }
+
+
+-- INT
+
+
+{-| Parse integers. It accepts decimal and hexidecimal formats.
+
+ -- decimal
+ run int "1234" == Ok 1234
+ run int "1.34" == Err ...
+ run int "1e31" == Err ...
+ run int "123a" == Err ...
+ run int "0123" == Err ...
+
+ -- hexidecimal
+ run int "0x001A" == Ok 26
+ run int "0x001a" == Ok 26
+ run int "0xBEEF" == Ok 48879
+ run int "0x12.0" == Err ...
+ run int "0x12an" == Err ...
+
+**Note:** If you want a parser for both `Int` and `Float` literals,
+check out [`Parser.LanguageKit.number`](Parser-LanguageKit#number).
+It does not backtrack, so it should be faster and give better error
+messages than using `oneOf` and combining `int` and `float` yourself.
+
+**Note:** If you want to enable octal or binary `Int` literals,
+check out [`Parser.LanguageKit.int`](Parser-LanguageKit#int).
+-}
+int : Parser Int
+int =
+ Parser <| \{ source, offset, indent, context, row, col } ->
+ case intHelp offset (Prim.isSubChar isZero offset source) source of
+ Err badOffset ->
+ Bad BadInt
+ { source = source
+ , offset = badOffset
+ , indent = indent
+ , context = context
+ , row = row
+ , col = col + (badOffset - offset)
+ }
+
+ Ok goodOffset ->
+ case String.toInt (String.slice offset goodOffset source) of
+ Err _ ->
+ Debug.crash badIntMsg
+
+ Ok n ->
+ Good n
+ { source = source
+ , offset = goodOffset
+ , indent = indent
+ , context = context
+ , row = row
+ , col = col + (goodOffset - offset)
+ }
+
+
+intHelp : Int -> Int -> String -> Result Int Int
+intHelp offset zeroOffset source =
+ if zeroOffset == -1 then
+ Internal.chompDigits Char.isDigit offset source
+
+ else if Prim.isSubChar isX zeroOffset source /= -1 then
+ Internal.chompDigits Char.isHexDigit (offset + 2) source
+
+-- else if Prim.isSubChar isO zeroOffset source /= -1 then
+-- Internal.chompDigits Char.isOctDigit (offset + 2) source
+
+ else if Prim.isSubChar Internal.isBadIntEnd zeroOffset source == -1 then
+ Ok zeroOffset
+
+ else
+ Err zeroOffset
+
+
+isZero : Char -> Bool
+isZero char =
+ char == '0'
+
+
+isO : Char -> Bool
+isO char =
+ char == 'o'
+
+
+isX : Char -> Bool
+isX char =
+ char == 'x'
+
+
+badIntMsg : String
+badIntMsg =
+ """The `Parser.int` parser seems to have a bug.
+Please report an SSCCE to ."""
+
+
+
+-- FLOAT
+
+
+{-| Parse floats.
+
+ run float "123" == Ok 123
+ run float "3.1415" == Ok 3.1415
+ run float "0.1234" == Ok 0.1234
+ run float ".1234" == Ok 0.1234
+ run float "1e-42" == Ok 1e-42
+ run float "6.022e23" == Ok 6.022e23
+ run float "6.022E23" == Ok 6.022e23
+ run float "6.022e+23" == Ok 6.022e23
+ run float "6.022e" == Err ..
+ run float "6.022n" == Err ..
+ run float "6.022.31" == Err ..
+
+**Note:** If you want a parser for both `Int` and `Float` literals,
+check out [`Parser.LanguageKit.number`](Parser-LanguageKit#number).
+It does not backtrack, so it should be faster and give better error
+messages than using `oneOf` and combining `int` and `float` yourself.
+
+**Note:** If you want to disable literals like `.123` like Elm,
+check out [`Parser.LanguageKit.float`](Parser-LanguageKit#float).
+-}
+float : Parser Float
+float =
+ Parser <| \{ source, offset, indent, context, row, col } ->
+ case floatHelp offset (Prim.isSubChar isZero offset source) source of
+ Err badOffset ->
+ Bad BadFloat
+ { source = source
+ , offset = badOffset
+ , indent = indent
+ , context = context
+ , row = row
+ , col = col + (badOffset - offset)
+ }
+
+ Ok goodOffset ->
+ case String.toFloat (String.slice offset goodOffset source) of
+ Err _ ->
+ Debug.crash badFloatMsg
+
+ Ok n ->
+ Good n
+ { source = source
+ , offset = goodOffset
+ , indent = indent
+ , context = context
+ , row = row
+ , col = col + (goodOffset - offset)
+ }
+
+
+floatHelp : Int -> Int -> String -> Result Int Int
+floatHelp offset zeroOffset source =
+ if zeroOffset >= 0 then
+ Internal.chompDotAndExp zeroOffset source
+
+ else
+ let
+ dotOffset =
+ Internal.chomp Char.isDigit offset source
+
+ result =
+ Internal.chompDotAndExp dotOffset source
+ in
+ case result of
+ Err _ ->
+ result
+
+ Ok n ->
+ if n == offset then Err n else result
+
+
+badFloatMsg : String
+badFloatMsg =
+ """The `Parser.float` parser seems to have a bug.
+Please report an SSCCE to ."""
+
+
+
+-- END
+
+
+{-| Check if you have reached the end of the string you are parsing.
+
+ justAnInt : Parser Int
+ justAnInt =
+ succeed identity
+ |= int
+ |. end
+
+ -- run justAnInt "90210" == Ok 90210
+ -- run justAnInt "1 + 2" == Err ...
+ -- run int "1 + 2" == Ok 1
+
+Parsers can succeed without parsing the whole string. Ending your parser
+with `end` guarantees that you have successfully parsed the whole string.
+-}
+end : Parser ()
+end =
+ Parser <| \state ->
+ if String.length state.source == state.offset then
+ Good () state
+
+ else
+ Bad ExpectingEnd state
+
+
+
+-- SOURCE
+
+
+{-| Run a parser, but return the underlying source code that actually
+got parsed.
+
+ -- run (source (ignore oneOrMore Char.isLower)) "abc" == Ok "abc"
+ -- keep count isOk = source (ignore count isOk)
+
+This becomes a useful optimization when you need to [`keep`](#keep)
+something very specific. For example, say we want to parse capitalized
+words:
+
+ import Char
+
+ variable : Parser String
+ variable =
+ succeed (++)
+ |= keep (Exactly 1) Char.isUpper
+ |= keep zeroOrMore Char.isLower
+
+In this case, each `keep` allocates a string. Then we use `(++)` to create the
+final string. That means *three* strings are allocated.
+
+In contrast, using `source` with `ignore` lets you grab the final string
+directly. It tracks where the parser starts and ends, so it can use
+`String.slice` to grab that part directly.
+
+ variable : Parser String
+ variable =
+ source <|
+ ignore (Exactly 1) Char.isUpper
+ |. ignore zeroOrMore Char.isLower
+
+This version only allocates *one* string.
+-}
+source : Parser a -> Parser String
+source parser =
+ sourceMap always parser
+
+
+{-| Like `source`, but it allows you to combine the source string
+with the value that is produced by the parser. So maybe you want
+a float, but you also want to know exactly how it looked.
+
+ number : Parser (String, Float)
+ number =
+ sourceMap (,) float
+
+ -- run number "100" == Ok ("100", 100)
+ -- run number "1e2" == Ok ("1e2", 100)
+-}
+sourceMap : (String -> a -> b) -> Parser a -> Parser b
+sourceMap func (Parser parse) =
+ Parser <| \({source, offset} as state1) ->
+ case parse state1 of
+ Bad x state2 ->
+ Bad x state2
+
+ Good a state2 ->
+ let
+ subString =
+ String.slice offset state2.offset source
+ in
+ Good (func subString a) state2
+
+
+
+-- REPEAT
+
+
+{-| How many characters to [`keep`](#keep) or [`ignore`](#ignore).
+-}
+type Count = AtLeast Int | Exactly Int
+
+
+{-| A simple alias for `AtLeast 0` so your code reads nicer:
+
+ import Char
+
+ spaces : Parser String
+ spaces =
+ keep zeroOrMore (\c -> c == ' ')
+
+ -- same as: keep (AtLeast 0) (\c -> c == ' ')
+-}
+zeroOrMore : Count
+zeroOrMore =
+ AtLeast 0
+
+
+{-| A simple alias for `AtLeast 1` so your code reads nicer:
+
+ import Char
+
+ lows : Parser String
+ lows =
+ keep oneOrMore Char.isLower
+
+ -- same as: keep (AtLeast 1) Char.isLower
+-}
+oneOrMore : Count
+oneOrMore =
+ AtLeast 1
+
+
+{-| Keep some characters. If you want a capital letter followed by
+zero or more lower case letters, you could say:
+
+ import Char
+
+ capitalized : Parser String
+ capitalized =
+ succeed (++)
+ |= keep (Exactly 1) Char.isUpper
+ |= keep zeroOrMore Char.isLower
+
+ -- good: Cat, Tom, Sally
+ -- bad: cat, tom, TOM, tOm
+
+**Note:** Check out [`source`](#source) for a more efficient
+way to grab the underlying source of a complex parser.
+-}
+keep : Count -> (Char -> Bool) -> Parser String
+keep count predicate =
+ source (ignore count predicate)
+
+
+{-| Ignore some characters. If you want to ignore one or more
+spaces, you might say:
+
+ spaces : Parser ()
+ spaces =
+ ignore oneOrMore (\c -> c == ' ')
+
+-}
+ignore : Count -> (Char -> Bool) -> Parser ()
+ignore count predicate =
+ case count of
+ Exactly n ->
+ Parser <| \{ source, offset, indent, context, row, col } ->
+ ignoreExactly n predicate source offset indent context row col
+
+ AtLeast n ->
+ Parser <| \{ source, offset, indent, context, row, col } ->
+ ignoreAtLeast n predicate source offset indent context row col
+
+
+ignoreExactly : Int -> (Char -> Bool) -> String -> Int -> Int -> List Context -> Int -> Int -> Step ()
+ignoreExactly n predicate source offset indent context row col =
+ if n <= 0 then
+ Good ()
+ { source = source
+ , offset = offset
+ , indent = indent
+ , context = context
+ , row = row
+ , col = col
+ }
+
+ else
+ let
+ newOffset =
+ Prim.isSubChar predicate offset source
+ in
+ if newOffset == -1 then
+ Bad BadRepeat
+ { source = source
+ , offset = offset
+ , indent = indent
+ , context = context
+ , row = row
+ , col = col
+ }
+
+ else if newOffset == -2 then
+ ignoreExactly (n - 1) predicate source (offset + 1) indent context (row + 1) 1
+
+ else
+ ignoreExactly (n - 1) predicate source newOffset indent context row (col + 1)
+
+
+ignoreAtLeast : Int -> (Char -> Bool) -> String -> Int -> Int -> List Context -> Int -> Int -> Step ()
+ignoreAtLeast n predicate source offset indent context row col =
+ let
+ newOffset =
+ Prim.isSubChar predicate offset source
+ in
+ -- no match
+ if newOffset == -1 then
+ let
+ state =
+ { source = source
+ , offset = offset
+ , indent = indent
+ , context = context
+ , row = row
+ , col = col
+ }
+ in
+ if n <= 0 then Good () state else Bad BadRepeat state
+
+ -- matched a newline
+ else if newOffset == -2 then
+ ignoreAtLeast (n - 1) predicate source (offset + 1) indent context (row + 1) 1
+
+ -- normal match
+ else
+ ignoreAtLeast (n - 1) predicate source newOffset indent context row (col + 1)
+
+
+
+-- IGNORE UNTIL
+
+
+{-| Ignore characters until *after* the given string.
+So maybe we want to parse Elm-style single-line comments:
+
+ elmComment : Parser ()
+ elmComment =
+ symbol "--"
+ |. ignoreUntil "\n"
+
+Or maybe you want to parse JS-style multi-line comments:
+
+ jsComment : Parser ()
+ jsComment =
+ symbol "/*"
+ |. ignoreUntil "*/"
+
+**Note:** You must take more care when parsing Elm-style multi-line
+comments. Elm can recognize nested comments, but the `jsComment` parser
+cannot. See [`Parser.LanguageKit.whitespace`](Parser-LanguageKit#whitespace)
+for help with this.
+-}
+ignoreUntil : String -> Parser ()
+ignoreUntil str =
+ Parser <| \({ source, offset, indent, context, row, col } as state) ->
+ let
+ (newOffset, newRow, newCol) =
+ Prim.findSubString False str offset row col source
+ in
+ if newOffset == -1 then
+ Bad (ExpectingClosing str) state
+
+ else
+ Good ()
+ { source = source
+ , offset = newOffset
+ , indent = indent
+ , context = context
+ , row = newRow
+ , col = newCol
+ }
+
+
+
+-- CONTEXT
+
+
+{-| Specify what you are parsing right now. So if you have a parser
+for lists like `[ 1, 2, 3 ]` you could say:
+
+ list : Parser (List Int)
+ list =
+ inContext "list" <|
+ succeed identity
+ |. symbol "["
+ |. spaces
+ |= commaSep int
+ |. spaces
+ |. symbol "]"
+
+ -- spaces : Parser ()
+ -- commaSep : Parser a -> Parser (List a)
+
+Now you get that extra context information if there is a parse error anywhere
+in the list. For example, if you have `[ 1, 23zm5, 3 ]` you could generate an
+error message like this:
+
+ I ran into a problem while parsing this list:
+
+ [ 1, 23zm5, 3 ]
+ ^
+ Looking for a valid integer, like 6 or 90210.
+
+Notice that the error message knows you are parsing a list right now!
+-}
+inContext : String -> Parser a -> Parser a
+inContext ctx (Parser parse) =
+ Parser <| \({ context, row, col } as initialState) ->
+ let
+ state1 =
+ changeContext (Context row col ctx :: context) initialState
+ in
+ case parse state1 of
+ Good a state2 ->
+ Good a (changeContext context state2)
+
+ Bad _ _ as step ->
+ step
+
+
+changeContext : List Context -> State -> State
+changeContext newContext { source, offset, indent, row, col } =
+ { source = source
+ , offset = offset
+ , indent = indent
+ , context = newContext
+ , row = row
+ , col = col
+ }
diff --git a/finished/elm-stuff/packages/elm-tools/parser/2.0.1/src/Parser/Internal.elm b/finished/elm-stuff/packages/elm-tools/parser/2.0.1/src/Parser/Internal.elm
new file mode 100644
index 0000000..cd07186
--- /dev/null
+++ b/finished/elm-stuff/packages/elm-tools/parser/2.0.1/src/Parser/Internal.elm
@@ -0,0 +1,149 @@
+module Parser.Internal exposing
+ ( Parser(..)
+ , Step(..)
+ , State
+ , chomp
+ , chompDigits
+ , chompDotAndExp
+ , isBadIntEnd
+ )
+
+
+import Char
+import ParserPrimitives as Prim
+
+
+
+-- PARSERS
+
+
+type Parser ctx x a =
+ Parser (State ctx -> Step ctx x a)
+
+
+type Step ctx x a
+ = Good a (State ctx)
+ | Bad x (State ctx)
+
+
+type alias State ctx =
+ { source : String
+ , offset : Int
+ , indent : Int
+ , context : List ctx
+ , row : Int
+ , col : Int
+ }
+
+
+
+-- CHOMPERS
+
+
+chomp : (Char -> Bool) -> Int -> String -> Int
+chomp isGood offset source =
+ let
+ newOffset =
+ Prim.isSubChar isGood offset source
+ in
+ if newOffset < 0 then
+ offset
+
+ else
+ chomp isGood newOffset source
+
+
+
+-- CHOMP DIGITS
+
+
+chompDigits : (Char -> Bool) -> Int -> String -> Result Int Int
+chompDigits isValidDigit offset source =
+ let
+ newOffset =
+ chomp isValidDigit offset source
+ in
+ -- no digits
+ if newOffset == offset then
+ Err newOffset
+
+ -- ends with non-digit characters
+ else if Prim.isSubChar isBadIntEnd newOffset source /= -1 then
+ Err newOffset
+
+ -- all valid digits!
+ else
+ Ok newOffset
+
+
+isBadIntEnd : Char -> Bool
+isBadIntEnd char =
+ Char.isDigit char
+ || Char.isUpper char
+ || Char.isLower char
+ || char == '.'
+
+
+
+-- CHOMP FLOAT STUFF
+
+
+chompDotAndExp : Int -> String -> Result Int Int
+chompDotAndExp offset source =
+ let
+ dotOffset =
+ Prim.isSubChar isDot offset source
+ in
+ if dotOffset == -1 then
+ chompExp offset source
+
+ else
+ chompExp (chomp Char.isDigit dotOffset source) source
+
+
+isDot : Char -> Bool
+isDot char =
+ char == '.'
+
+
+chompExp : Int -> String -> Result Int Int
+chompExp offset source =
+ let
+ eOffset =
+ Prim.isSubChar isE offset source
+ in
+ if eOffset == -1 then
+ Ok offset
+
+ else
+ let
+ opOffset =
+ Prim.isSubChar isPlusOrMinus eOffset source
+
+ expOffset =
+ if opOffset == -1 then eOffset else opOffset
+ in
+ if Prim.isSubChar isZero expOffset source /= -1 then
+ Err expOffset
+
+ else if Prim.isSubChar Char.isDigit expOffset source == -1 then
+ Err expOffset
+
+ else
+ chompDigits Char.isDigit expOffset source
+
+
+isE : Char -> Bool
+isE char =
+ char == 'e' || char == 'E'
+
+
+isZero : Char -> Bool
+isZero char =
+ char == '0'
+
+
+isPlusOrMinus : Char -> Bool
+isPlusOrMinus char =
+ char == '+' || char == '-'
+
diff --git a/finished/elm-stuff/packages/elm-tools/parser/2.0.1/src/Parser/LanguageKit.elm b/finished/elm-stuff/packages/elm-tools/parser/2.0.1/src/Parser/LanguageKit.elm
new file mode 100644
index 0000000..4b6e4ea
--- /dev/null
+++ b/finished/elm-stuff/packages/elm-tools/parser/2.0.1/src/Parser/LanguageKit.elm
@@ -0,0 +1,509 @@
+module Parser.LanguageKit exposing
+ ( variable
+ , list, record, tuple, sequence, Trailing(..)
+ , whitespace, LineComment(..), MultiComment(..)
+ )
+
+
+{-|
+
+# Variables
+@docs variable
+
+# Lists, records, and that sort of thing
+@docs list, record, tuple, sequence, Trailing
+
+# Whitespace
+@docs whitespace, LineComment, MultiComment
+
+-}
+
+
+import Set exposing (Set)
+import Parser exposing (..)
+import Parser.Internal as I exposing (Step(..), State)
+import ParserPrimitives as Prim
+
+
+
+-- VARIABLES
+
+
+{-| Create a parser for variables. It takes two `Char` checkers. The
+first one is for the first character. The second one is for all the
+other characters.
+
+In Elm, we distinguish between upper and lower case variables, so we
+can do something like this:
+
+ import Char
+ import Parser exposing (..)
+ import Parser.LanguageKit exposing (variable)
+ import Set
+
+ lowVar : Parser String
+ lowVar =
+ variable Char.isLower isVarChar keywords
+
+ capVar : Parser String
+ capVar =
+ variable Char.isUpper isVarChar keywords
+
+ isVarChar : Char -> Bool
+ isVarChar char =
+ Char.isLower char
+ || Char.isUpper char
+ || Char.isDigit char
+ || char == '_'
+
+ keywords : Set.Set String
+ keywords =
+ Set.fromList [ "let", "in", "case", "of" ]
+-}
+variable : (Char -> Bool) -> (Char -> Bool) -> Set String -> Parser String
+variable isFirst isOther keywords =
+ I.Parser <| \({ source, offset, indent, context, row, col } as state1) ->
+ let
+ firstOffset =
+ Prim.isSubChar isFirst offset source
+ in
+ if firstOffset == -1 then
+ Bad ExpectingVariable state1
+
+ else
+ let
+ state2 =
+ if firstOffset == -2 then
+ varHelp isOther (offset + 1) (row + 1) 1 source indent context
+ else
+ varHelp isOther firstOffset row (col + 1) source indent context
+
+ name =
+ String.slice offset state2.offset source
+ in
+ if Set.member name keywords then
+ Bad ExpectingVariable state1
+
+ else
+ Good name state2
+
+
+varHelp : (Char -> Bool) -> Int -> Int -> Int -> String -> Int -> List ctx -> State ctx
+varHelp isGood offset row col source indent context =
+ let
+ newOffset =
+ Prim.isSubChar isGood offset source
+ in
+ if newOffset == -1 then
+ { source = source
+ , offset = offset
+ , indent = indent
+ , context = context
+ , row = row
+ , col = col
+ }
+
+ else if newOffset == -2 then
+ varHelp isGood (offset + 1) (row + 1) 1 source indent context
+
+ else
+ varHelp isGood newOffset row (col + 1) source indent context
+
+
+
+-- SEQUENCES
+
+
+{-| Parse a comma-separated list like `[ 1, 2, 3 ]`. You provide
+a parser for the spaces and for the list items. So if you want
+to parse a list of integers, you would say:
+
+ import Parser exposing (Parser)
+ import Parser.LanguageKit as Parser
+
+ intList : Parser (List Int)
+ intList =
+ Parser.list spaces Parser.int
+
+ spaces : Parser ()
+ spaces =
+ Parser.ignore zeroOrMore (\char -> char == ' ')
+
+ -- run intList "[]" == Ok []
+ -- run intList "[ ]" == Ok []
+ -- run intList "[1,2,3]" == Ok [1,2,3]
+ -- run intList "[ 1, 2, 3 ]" == Ok [1,2,3]
+ -- run intList "[ 1 , 2 , 3 ]" == Ok [1,2,3]
+ -- run intList "[ 1, 2, 3, ]" == Err ...
+ -- run intList "[, 1, 2, 3 ]" == Err ...
+
+**Note:** If you want trailing commas, check out the
+[`sequence`](#sequence) function.
+-}
+list : Parser () -> Parser a -> Parser (List a)
+list spaces item =
+ sequence
+ { start = "["
+ , separator = ","
+ , end = "]"
+ , spaces = spaces
+ , item = item
+ , trailing = Forbidden
+ }
+
+
+{-| Help parse records like `{ a = 2, b = 2 }`. You provide
+a parser for the spaces and for the list items, you might say:
+
+ import Parser exposing ( Parser, (|.), (|=), zeroOrMore )
+ import Parser.LanguageKit as Parser
+
+ record : Parser (List (String, Int))
+ record =
+ Parser.record spaces field
+
+ field : Parser (String, Int)
+ field =
+ Parser.succeed (,)
+ |= lowVar
+ |. spaces
+ |. Parser.symbol "="
+ |. spaces
+ |= int
+
+ spaces : Parser ()
+ spaces =
+ Parser.ignore zeroOrMore (\char -> char == ' ')
+
+ -- run record "{}" == Ok []
+ -- run record "{ }" == Ok []
+ -- run record "{ x = 3 }" == Ok [ ("x",3) ]
+ -- run record "{ x = 3, }" == Err ...
+ -- run record "{ x = 3, y = 4 }" == Ok [ ("x",3), ("y",4) ]
+ -- run record "{ x = 3, y = }" == Err ...
+
+**Note:** If you want trailing commas, check out the
+[`sequence`](#sequence) function.
+-}
+record : Parser () -> Parser a -> Parser (List a)
+record spaces item =
+ sequence
+ { start = "{"
+ , separator = ","
+ , end = "}"
+ , spaces = spaces
+ , item = item
+ , trailing = Forbidden
+ }
+
+
+{-| Help parse tuples like `(3, 4)`. Works just like [`list`](#list)
+and [`record`](#record). And if you need something custom, check out
+the [`sequence`](#sequence) function.
+-}
+tuple : Parser () -> Parser a -> Parser (List a)
+tuple spaces item =
+ sequence
+ { start = "("
+ , separator = ","
+ , end = ")"
+ , spaces = spaces
+ , item = item
+ , trailing = Forbidden
+ }
+
+
+{-| Handle things *like* lists and records, but you can customize the
+details however you need. Say you want to parse C-style code blocks:
+
+ import Parser exposing (Parser)
+ import Parser.LanguageKit as Parser exposing (Trailing(..))
+
+ block : Parser (List Stmt)
+ block =
+ Parser.sequence
+ { start = "{"
+ , separator = ";"
+ , end = "}"
+ , spaces = spaces
+ , item = statement
+ , trailing = Mandatory -- demand a trailing semi-colon
+ }
+
+ -- spaces : Parser ()
+ -- statement : Parser Stmt
+
+**Note:** If you need something more custom, do not be afraid to check
+out the implementation and customize it for your case. It is better to
+get nice error messages with a lower-level implementation than to try
+to hack high-level parsers to do things they are not made for.
+-}
+sequence
+ : { start : String
+ , separator : String
+ , end : String
+ , spaces : Parser ()
+ , item : Parser a
+ , trailing : Trailing
+ }
+ -> Parser (List a)
+sequence { start, end, spaces, item, separator, trailing } =
+ symbol start
+ |- spaces
+ |- sequenceEnd end spaces item separator trailing
+
+
+{-| What’s the deal with trailing commas? Are they `Forbidden`?
+Are they `Optional`? Are they `Mandatory`? Welcome to [shapes
+club](http://poorlydrawnlines.com/comic/shapes-club/)!
+-}
+type Trailing = Forbidden | Optional | Mandatory
+
+
+ignore : Parser ignore -> Parser keep -> Parser keep
+ignore ignoreParser keepParser =
+ map2 revAlways ignoreParser keepParser
+
+
+(|-) : Parser ignore -> Parser keep -> Parser keep
+(|-) =
+ ignore
+
+
+revAlways : ignore -> keep -> keep
+revAlways _ keep =
+ keep
+
+
+sequenceEnd : String -> Parser () -> Parser a -> String -> Trailing -> Parser (List a)
+sequenceEnd end spaces parseItem sep trailing =
+ let
+ chompRest item =
+ case trailing of
+ Forbidden ->
+ sequenceEndForbidden end spaces parseItem sep [item]
+
+ Optional ->
+ sequenceEndOptional end spaces parseItem sep [item]
+
+ Mandatory ->
+ spaces
+ |- symbol sep
+ |- spaces
+ |- sequenceEndMandatory end spaces parseItem sep [item]
+ in
+ oneOf
+ [ parseItem
+ |> andThen chompRest
+ , symbol end
+ |- succeed []
+ ]
+
+
+sequenceEndForbidden : String -> Parser () -> Parser a -> String -> List a -> Parser (List a)
+sequenceEndForbidden end spaces parseItem sep revItems =
+ let
+ chompRest item =
+ sequenceEndForbidden end spaces parseItem sep (item :: revItems)
+ in
+ ignore spaces <|
+ oneOf
+ [ symbol sep
+ |- spaces
+ |- andThen chompRest parseItem
+ , symbol end
+ |- succeed (List.reverse revItems)
+ ]
+
+
+sequenceEndOptional : String -> Parser () -> Parser a -> String -> List a -> Parser (List a)
+sequenceEndOptional end spaces parseItem sep revItems =
+ let
+ parseEnd =
+ andThen (\_ -> succeed (List.reverse revItems)) (symbol end)
+
+ chompRest item =
+ sequenceEndOptional end spaces parseItem sep (item :: revItems)
+ in
+ ignore spaces <|
+ oneOf
+ [ symbol sep
+ |- spaces
+ |- oneOf [ andThen chompRest parseItem, parseEnd ]
+ , parseEnd
+ ]
+
+
+sequenceEndMandatory : String -> Parser () -> Parser a -> String -> List a -> Parser (List a)
+sequenceEndMandatory end spaces parseItem sep revItems =
+ let
+ chompRest item =
+ sequenceEndMandatory end spaces parseItem sep (item :: revItems)
+ in
+ oneOf
+ [ andThen chompRest <|
+ parseItem
+ |. spaces
+ |. symbol sep
+ |. spaces
+ , symbol end
+ |- succeed (List.reverse revItems)
+ ]
+
+
+
+-- WHITESPACE
+
+
+{-| Create a custom whitespace parser. It will always chomp the
+`' '`, `'\r'`, and `'\n'` characters, but you can customize some
+other things. Here are some examples:
+
+ elm : Parser ()
+ elm =
+ whitespace
+ { allowTabs = False
+ , lineComment = LineComment "--"
+ , multiComment = NestableComment "{-" "-}"
+ }
+
+ js : Parser ()
+ js =
+ whitespace
+ { allowTabs = True
+ , lineComment = LineComment "//"
+ , multiComment = UnnestableComment "/*" "*/"
+ }
+
+If you need further customization, please open an issue describing your
+scenario or check out the source code and write it yourself. This is all
+built using stuff from the root `Parser` module.
+-}
+whitespace
+ : { allowTabs : Bool
+ , lineComment : LineComment
+ , multiComment : MultiComment
+ }
+ -> Parser ()
+whitespace { allowTabs, lineComment, multiComment } =
+ let
+ tabParser =
+ if allowTabs then
+ [ Parser.ignore zeroOrMore isTab ]
+ else
+ []
+
+ lineParser =
+ case lineComment of
+ NoLineComment ->
+ []
+
+ LineComment start ->
+ [ symbol start
+ |. ignoreUntil "\n"
+ ]
+
+ multiParser =
+ case multiComment of
+ NoMultiComment ->
+ []
+
+ UnnestableComment start end ->
+ [ symbol start
+ |. ignoreUntil end
+ ]
+
+ NestableComment start end ->
+ [ nestableComment start end
+ ]
+ in
+ whitespaceHelp <|
+ oneOf (tabParser ++ lineParser ++ multiParser)
+
+
+chompSpaces : Parser ()
+chompSpaces =
+ Parser.ignore zeroOrMore isSpace
+
+
+isSpace : Char -> Bool
+isSpace char =
+ char == ' ' || char == '\n' || char == '\r'
+
+
+isTab : Char -> Bool
+isTab char =
+ char == '\t'
+
+
+whitespaceHelp : Parser a -> Parser ()
+whitespaceHelp parser =
+ ignore chompSpaces <|
+ oneOf [ andThen (\_ -> whitespaceHelp parser) parser, succeed () ]
+
+
+{-| Are line comments allowed? If so, what symbol do they start with?
+
+ LineComment "--" -- Elm
+ LineComment "//" -- JS
+ LineComment "#" -- Python
+ NoLineComment -- OCaml
+-}
+type LineComment = NoLineComment | LineComment String
+
+
+{-| Are multi-line comments allowed? If so, what symbols do they start
+and end with?
+
+ NestableComment "{-" "-}" -- Elm
+ UnnestableComment "/*" "*/" -- JS
+ NoMultiComment -- Python
+
+In Elm, you can nest multi-line comments. In C-like languages, like JS,
+this is not allowed. As soon as you see a `*/` the comment is over no
+matter what.
+-}
+type MultiComment
+ = NoMultiComment
+ | NestableComment String String
+ | UnnestableComment String String
+
+
+nestableComment : String -> String -> Parser ()
+nestableComment start end =
+ case (String.uncons start, String.uncons end) of
+ (Nothing, _) ->
+ fail "Trying to parse a multi-line comment, but the start token cannot be the empty string!"
+
+ (_, Nothing) ->
+ fail "Trying to parse a multi-line comment, but the end token cannot be the empty string!"
+
+ ( Just (startChar, _), Just (endChar, _) ) ->
+ let
+ isNotRelevant char =
+ char /= startChar && char /= endChar
+ in
+ symbol start
+ |. nestableCommentHelp isNotRelevant start end 1
+
+
+nestableCommentHelp : (Char -> Bool) -> String -> String -> Int -> Parser ()
+nestableCommentHelp isNotRelevant start end nestLevel =
+ lazy <| \_ ->
+ ignore (Parser.ignore zeroOrMore isNotRelevant) <|
+ oneOf
+ [ ignore (symbol end) <|
+ if nestLevel == 1 then
+ succeed ()
+ else
+ nestableCommentHelp isNotRelevant start end (nestLevel - 1)
+ , ignore (symbol start) <|
+ nestableCommentHelp isNotRelevant start end (nestLevel + 1)
+ , ignore (Parser.ignore (Exactly 1) isChar) <|
+ nestableCommentHelp isNotRelevant start end nestLevel
+ ]
+
+
+isChar : Char -> Bool
+isChar char =
+ True
diff --git a/finished/elm-stuff/packages/elm-tools/parser/2.0.1/src/Parser/LowLevel.elm b/finished/elm-stuff/packages/elm-tools/parser/2.0.1/src/Parser/LowLevel.elm
new file mode 100644
index 0000000..653c347
--- /dev/null
+++ b/finished/elm-stuff/packages/elm-tools/parser/2.0.1/src/Parser/LowLevel.elm
@@ -0,0 +1,118 @@
+module Parser.LowLevel exposing
+ ( getIndentLevel
+ , withIndentLevel
+
+ , getPosition
+ , getRow
+ , getCol
+
+ , getOffset
+ , getSource
+ )
+
+{-| You are unlikely to need any of this under normal circumstances.
+
+# Indentation
+@docs getIndentLevel, withIndentLevel
+
+# Row, Column, Offset, and Source
+@docs getPosition, getRow, getCol, getOffset, getSource
+
+-}
+
+import Parser exposing (Parser)
+import Parser.Internal as I exposing (State)
+
+
+
+-- INDENTATION
+
+
+{-| This parser tracks “indentation level” so you can parse indentation
+sensitive languages. Indentation levels correspond to column numbers, so
+it starts at 1.
+-}
+getIndentLevel : Parser Int
+getIndentLevel =
+ I.Parser <| \state -> I.Good state.indent state
+
+
+{-| Run a parser with a given indentation level. So you will likely
+use `getCol` to get the current column, `andThen` give that to
+`withIndentLevel`.
+-}
+withIndentLevel : Int -> Parser a -> Parser a
+withIndentLevel newIndent (I.Parser parse) =
+ I.Parser <| \state1 ->
+ case parse (changeIndent newIndent state1) of
+ I.Good a state2 ->
+ I.Good a (changeIndent state1.indent state2)
+
+ I.Bad x state2 ->
+ I.Bad x (changeIndent state1.indent state2)
+
+
+changeIndent : Int -> State ctx -> State ctx
+changeIndent newIndent { source, offset, context, row, col } =
+ { source = source
+ , offset = offset
+ , indent = newIndent
+ , context = context
+ , row = row
+ , col = col
+ }
+
+
+
+-- POSITION
+
+
+{-| Code editors treat code like a grid. There are rows and columns.
+In most editors, rows and colums are 1-indexed. You move to a new row
+whenever you see a `\n` character.
+
+The `getPosition` parser succeeds with your current row and column
+within the string you are parsing.
+-}
+getPosition : Parser (Int, Int)
+getPosition =
+ I.Parser <| \state -> I.Good (state.row, state.col) state
+
+
+{-| The `getRow` parser succeeds with your current row within
+the string you are parsing.
+-}
+getRow : Parser Int
+getRow =
+ I.Parser <| \state -> I.Good state.row state
+
+
+{-| The `getCol` parser succeeds with your current column within
+the string you are parsing.
+-}
+getCol : Parser Int
+getCol =
+ I.Parser <| \state -> I.Good state.col state
+
+
+{-| Editors think of code as a grid, but behind the scenes it is just
+a flat array of UTF16 characters. `getOffset` tells you your index in
+that flat array. So if you have read `"\n\n\n\n"` you are on row 5,
+column 1, and offset 4.
+
+**Note:** browsers use UTF16 strings, so characters may be one or two 16-bit
+words. This means you can read 4 characters, but your offset will move by 8.
+-}
+getOffset : Parser Int
+getOffset =
+ I.Parser <| \state -> I.Good state.offset state
+
+
+{-| Get the entire string you are parsing right now. Paired with
+`getOffset` this can let you use `String.slice` to grab substrings
+with very little intermediate allocation.
+-}
+getSource : Parser String
+getSource =
+ I.Parser <| \state -> I.Good state.source state
+
diff --git a/finished/src/Data/Article.elm b/finished/src/Data/Article.elm
index 0a4b8ec..c921d4b 100644
--- a/finished/src/Data/Article.elm
+++ b/finished/src/Data/Article.elm
@@ -3,15 +3,12 @@ module Data.Article
( Article
, Body
, Slug
- , Tag
, bodyToHtml
, bodyToMarkdownString
, decoder
, decoderWithBody
, slugParser
, slugToString
- , tagDecoder
- , tagToString
)
import Data.Article.Author as Author exposing (Author)
@@ -110,24 +107,6 @@ slugToString (Slug slug) =
--- TAGS --
-
-
-type Tag
- = Tag String
-
-
-tagToString : Tag -> String
-tagToString (Tag slug) =
- slug
-
-
-tagDecoder : Decoder Tag
-tagDecoder =
- Decode.map Tag Decode.string
-
-
-
-- BODY --
diff --git a/finished/src/Data/Article/Tag.elm b/finished/src/Data/Article/Tag.elm
new file mode 100644
index 0000000..9f0ee90
--- /dev/null
+++ b/finished/src/Data/Article/Tag.elm
@@ -0,0 +1,55 @@
+module Data.Article.Tag
+ exposing
+ ( Tag
+ , decoder
+ , encode
+ , listParser
+ , toString
+ )
+
+import Json.Decode as Decode exposing (Decoder)
+import Json.Encode as Encode exposing (Value)
+import Parser exposing ((|.), (|=), Parser, end, ignore, keep, oneOrMore, repeat, zeroOrMore)
+
+
+type Tag
+ = Tag String
+
+
+toString : Tag -> String
+toString (Tag str) =
+ str
+
+
+encode : Tag -> Value
+encode (Tag str) =
+ Encode.string str
+
+
+decoder : Decoder Tag
+decoder =
+ Decode.map Tag Decode.string
+
+
+listParser : Parser (List Tag)
+listParser =
+ Parser.succeed (List.map Tag)
+ |. ignore zeroOrMore isWhitespace
+ |= repeat zeroOrMore tag
+ |. end
+
+
+
+-- INTERNAL --
+
+
+tag : Parser String
+tag =
+ keep oneOrMore (\char -> not (isWhitespace char))
+ |. ignore zeroOrMore isWhitespace
+
+
+isWhitespace : Char -> Bool
+isWhitespace char =
+ -- Treat hashtags and commas as effectively whitespace; ignore them.
+ char == '#' || char == ',' || char == ' '
diff --git a/finished/src/Page/Article/Editor.elm b/finished/src/Page/Article/Editor.elm
index 7e6d802..2f9274b 100644
--- a/finished/src/Page/Article/Editor.elm
+++ b/finished/src/Page/Article/Editor.elm
@@ -1,6 +1,7 @@
module Page.Article.Editor exposing (Model, Msg, initEdit, initNew, update, view)
import Data.Article as Article exposing (Article, Body)
+import Data.Article.Tag as Tag exposing (Tag)
import Data.Session exposing (Session)
import Data.User exposing (User)
import Html exposing (..)
@@ -8,6 +9,7 @@ import Html.Attributes exposing (attribute, class, defaultValue, disabled, href,
import Html.Events exposing (onInput, onSubmit)
import Http
import Page.Errored exposing (PageLoadError, pageLoadError)
+import Parser
import Request.Article
import Route
import Task exposing (Task)
@@ -26,7 +28,7 @@ type alias Model =
, title : String
, body : String
, description : String
- , tags : List String
+ , tags : String
, isSaving : Bool
}
@@ -38,7 +40,7 @@ initNew =
, title = ""
, body = ""
, description = ""
- , tags = []
+ , tags = ""
, isSaving = False
}
@@ -60,7 +62,7 @@ initEdit session slug =
, title = article.title
, body = Article.bodyToMarkdownString article.body
, description = article.description
- , tags = article.tags
+ , tags = String.join " " article.tags
, isSaving = False
}
)
@@ -121,7 +123,7 @@ viewForm model =
, Form.input
[ placeholder "Enter tags"
, onInput SetTags
- , defaultValue (String.join " " model.tags)
+ , defaultValue model.tags
]
[]
, button [ class "btn btn-lg pull-xs-right btn-primary", disabled model.isSaving ]
@@ -152,10 +154,24 @@ update user msg model =
[] ->
case model.editingArticle of
Nothing ->
- user.token
- |> Request.Article.create model
- |> Http.send CreateCompleted
- |> pair { model | errors = [], isSaving = True }
+ case Parser.run Tag.listParser model.tags of
+ Ok tags ->
+ let
+ request =
+ Request.Article.create
+ { tags = tags
+ , title = model.title
+ , body = model.body
+ , description = model.description
+ }
+ user.token
+ in
+ request
+ |> Http.send CreateCompleted
+ |> pair { model | errors = [], isSaving = True }
+
+ Err _ ->
+ ( { model | errors = [ ( Tags, "Invalid tags." ) ] }, Cmd.none )
Just slug ->
user.token
@@ -173,7 +189,7 @@ update user msg model =
( { model | description = description }, Cmd.none )
SetTags tags ->
- ( { model | tags = tagsFromString tags }, Cmd.none )
+ ( { model | tags = tags }, Cmd.none )
SetBody body ->
( { model | body = body }, Cmd.none )
@@ -217,6 +233,7 @@ type Field
= Form
| Title
| Body
+ | Tags
type alias Error =
@@ -235,14 +252,6 @@ modelValidator =
-- INTERNAL --
-tagsFromString : String -> List String
-tagsFromString str =
- str
- |> String.split " "
- |> List.map String.trim
- |> List.filter (not << String.isEmpty)
-
-
redirectToArticle : Article.Slug -> Cmd msg
redirectToArticle =
Route.modifyUrl << Route.Article
diff --git a/finished/src/Page/Home.elm b/finished/src/Page/Home.elm
index ffc4995..2b04a6c 100644
--- a/finished/src/Page/Home.elm
+++ b/finished/src/Page/Home.elm
@@ -3,7 +3,7 @@ module Page.Home exposing (Model, Msg, init, update, view)
{-| The homepage. You can get here via either the / or /#/ routes.
-}
-import Data.Article as Article exposing (Tag)
+import Data.Article.Tag as Tag exposing (Tag)
import Data.Session exposing (Session)
import Html exposing (..)
import Html.Attributes exposing (attribute, class, classList, href, id, placeholder)
@@ -100,7 +100,7 @@ viewTag tagName =
, href "javascript:void(0)"
, onClick (SelectTag tagName)
]
- [ text (Article.tagToString tagName) ]
+ [ text (Tag.toString tagName) ]
diff --git a/finished/src/Request/Article.elm b/finished/src/Request/Article.elm
index 25d8aaf..8f6fea2 100644
--- a/finished/src/Request/Article.elm
+++ b/finished/src/Request/Article.elm
@@ -14,8 +14,9 @@ module Request.Article
, update
)
-import Data.Article as Article exposing (Article, Body, Tag, slugToString)
+import Data.Article as Article exposing (Article, Body, slugToString)
import Data.Article.Feed as Feed exposing (Feed)
+import Data.Article.Tag as Tag exposing (Tag)
import Data.AuthToken exposing (AuthToken, withAuthorization)
import Data.User as User exposing (Username)
import Http
@@ -68,7 +69,7 @@ defaultListConfig =
list : ListConfig -> Maybe AuthToken -> Http.Request Feed
list config maybeToken =
- [ ( "tag", Maybe.map Article.tagToString config.tag )
+ [ ( "tag", Maybe.map Tag.toString config.tag )
, ( "author", Maybe.map User.usernameToString config.author )
, ( "favorited", Maybe.map User.usernameToString config.favorited )
, ( "limit", Just (toString config.limit) )
@@ -114,7 +115,7 @@ feed config token =
tags : Http.Request (List Tag)
tags =
- Decode.field "tags" (Decode.list Article.tagDecoder)
+ Decode.field "tags" (Decode.list Tag.decoder)
|> Http.get (apiUrl "/tags")
@@ -169,7 +170,7 @@ type alias CreateConfig record =
| title : String
, description : String
, body : String
- , tags : List String
+ , tags : List Tag
}
@@ -194,7 +195,7 @@ create config token =
[ ( "title", Encode.string config.title )
, ( "description", Encode.string config.description )
, ( "body", Encode.string config.body )
- , ( "tagList", Encode.list (List.map Encode.string config.tags) )
+ , ( "tagList", Encode.list (List.map Tag.encode config.tags) )
]
body =
diff --git a/finished/src/Request/Article/Comments.elm b/finished/src/Request/Article/Comments.elm
index 87501c5..0f7343b 100644
--- a/finished/src/Request/Article/Comments.elm
+++ b/finished/src/Request/Article/Comments.elm
@@ -1,6 +1,6 @@
module Request.Article.Comments exposing (delete, list, post)
-import Data.Article as Article exposing (Article, Tag, slugToString)
+import Data.Article as Article exposing (Article, slugToString)
import Data.Article.Comment as Comment exposing (Comment, CommentId)
import Data.AuthToken exposing (AuthToken, withAuthorization)
import Http
diff --git a/finished/src/Views/Article/Feed.elm b/finished/src/Views/Article/Feed.elm
index ce3fd8c..6936c88 100644
--- a/finished/src/Views/Article/Feed.elm
+++ b/finished/src/Views/Article/Feed.elm
@@ -16,8 +16,9 @@ overkill, so we use simpler APIs instead.
-}
-import Data.Article as Article exposing (Article, Tag)
+import Data.Article as Article exposing (Article)
import Data.Article.Feed exposing (Feed)
+import Data.Article.Tag as Tag exposing (Tag)
import Data.AuthToken exposing (AuthToken)
import Data.Session exposing (Session)
import Data.User exposing (Username)
@@ -126,7 +127,7 @@ sourceName source =
"Global Feed"
TagFeed tagName ->
- "#" ++ Article.tagToString tagName
+ "#" ++ Tag.toString tagName
FavoritedFeed username ->
"Favorited Articles"