Skip to content

Commit

Permalink
Merge pull request #8 from MaybeJustJames/add-decoder-tests
Browse files Browse the repository at this point in the history
Add decoder tests and fix some parser and decoder bugs
  • Loading branch information
MaybeJustJames authored Jul 16, 2020
2 parents 95a57bd + 74da8e6 commit 45d564c
Show file tree
Hide file tree
Showing 7 changed files with 556 additions and 44 deletions.
3 changes: 2 additions & 1 deletion elm.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"elm-version": "0.19.0 <= v < 0.20.0",
"dependencies": {
"elm/core": "1.0.0 <= v < 2.0.0",
"elm/parser": "1.1.0 <= v < 2.0.0"
"elm/parser": "1.1.0 <= v < 2.0.0",
"elm/regex": "1.0.0 <= v < 2.0.0"
},
"test-dependencies": {
"elm-explorations/test": "1.2.2 <= v < 2.0.0"
Expand Down
4 changes: 3 additions & 1 deletion src/Yaml/Decode.elm
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ string =
Decoder <| \v ->
case v of
Ast.String_ string_ -> Ok string_
Ast.Null_ -> Ok ""
_ -> Err (Decoding "Expected string")


Expand Down Expand Up @@ -121,6 +122,7 @@ float =
Decoder <| \v ->
case v of
Ast.Float_ float_ -> Ok float_
Ast.Int_ int_ -> Ok (toFloat int_)
_ -> Err (Decoding "Expected float")

{-| Decode a null value.
Expand Down Expand Up @@ -155,7 +157,7 @@ list decoder =
{-| Decode a YAML object, requiring a particular field.
The object can have other fields. Lots of them! The only thing this decoder
cares about is if x is present and that the value there is an Int.
cares about is if x is present and that the value there is an `a`.
Check out [map2](#map2) to see how to decode multiple fields!
Expand Down
98 changes: 76 additions & 22 deletions src/Yaml/Parser.elm
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Yaml.Parser.Util as U
import Yaml.Parser.Document
import Yaml.Parser.String
import Dict
import Regex exposing (Regex)


{-| -}
Expand All @@ -19,15 +20,60 @@ toString =
Ast.toString


-- ERROR REPORTING

deadEndsToString : List P.DeadEnd -> String
deadEndsToString deadends =
String.join "\n"
<| List.map deadEndToString deadends

deadEndToString : P.DeadEnd -> String
deadEndToString deadend =
"Line " ++ String.fromInt deadend.row
++ ", column " ++ String.fromInt deadend.col
++ ": " ++ problemToString deadend.problem

problemToString : P.Problem -> String
problemToString p =
case p of
P.Expecting msg ->
"Expected " ++ msg
P.ExpectingInt ->
"Expected an integer"
P.ExpectingHex ->
"Expected a hexadecimal value"
P.ExpectingOctal ->
"Expected an octal value"
P.ExpectingBinary ->
"Expected a binary value"
P.ExpectingFloat ->
"Expected a float"
P.ExpectingNumber ->
"Expected a number"
P.ExpectingVariable ->
"Expected a variable"
P.ExpectingSymbol name ->
"Expected symbol '" ++ name ++ "'"
P.ExpectingKeyword name ->
"Expected keyword '" ++ name ++ "'"
P.ExpectingEnd ->
"Expected end of input"
P.UnexpectedChar ->
"Encountered an unexpected character"
P.Problem msg ->
"Problem: " ++ msg
P.BadRepeat ->
"Bad repeat"



-- PARSER


{-| -}
fromString : String -> Result String Ast.Value
fromString =
P.run parser >> Result.mapError P.deadEndsToString

P.run parser >> Result.mapError deadEndsToString

{-| -}
parser : P.Parser Ast.Value
Expand Down Expand Up @@ -161,6 +207,7 @@ listInlineValue =
P.oneOf
[ listInline
, recordInline
, quotedString 0
, listInlineString
]

Expand All @@ -170,7 +217,7 @@ listInlineString =
P.succeed ()
|. P.chompWhile (U.neither U.isComma U.isListEnd)
|> P.getChompedString
|> P.map Ast.fromString
|> P.map (Ast.fromString << (String.replace "\\" "\\\\"))


listInlineNext : List Ast.Value -> Ast.Value -> P.Parser (P.Step (List Ast.Value) (List Ast.Value))
Expand Down Expand Up @@ -204,28 +251,17 @@ listInlineOnDone elements element =
recordOrString : Int -> Int -> P.Parser Ast.Value
recordOrString indent indent_ =
let
withQuote qoute =
P.oneOf
[ property qoute
, P.succeed (Ast.String_ qoute)
]

withString string =
P.oneOf
[ P.succeed (Ast.fromString string)
|. P.end
, property string
, recordProperty indent_ string
, P.succeed (addRemaining string)
|= if indent == 0 then U.remaining else U.multiline indent
]

property name =
P.succeed (record indent_ name)
|. P.chompIf U.isColon
|> P.andThen identity

addRemaining string remaining =
Ast.fromString (removeComment string ++ remaining)
Ast.fromString <| U.postProcessString (removeComment string ++ remaining)

removeComment string =
string
Expand All @@ -234,19 +270,37 @@ recordOrString indent indent_ =
|> Maybe.withDefault ""
in
P.oneOf
[ P.succeed (Ast.String_ ":")
|. P.chompIf U.isColon
, P.succeed identity
|= P.oneOf [ U.singleQuotes, U.doubleQuotes ]
|. U.spaces
|> P.andThen withQuote
[ quotedString indent_
, P.succeed identity
|. P.chompIf (U.neither U.isColon U.isNewLine)
|. P.chompWhile (U.neither U.isColon U.isNewLine)
|> P.getChompedString
|> P.andThen withString
, P.succeed identity
|. P.chompWhile U.isColon
|> P.getChompedString
|> P.andThen withString
]

quotedString : Int -> P.Parser Ast.Value
quotedString indent =
let
withQuote quote =
P.oneOf
[ recordProperty indent quote
, P.succeed (Ast.String_ <| U.postProcessString quote)
]
in
P.succeed identity
|= P.oneOf [ U.singleQuotes, U.doubleQuotes ]
|. U.spaces
|> P.andThen withQuote

recordProperty : Int -> String -> P.Parser Ast.Value
recordProperty indent name =
P.succeed (record indent name)
|. P.chompIf U.isColon
|> P.andThen identity

record : Int -> String -> P.Parser Ast.Value
record indent property =
Expand Down
2 changes: 1 addition & 1 deletion src/Yaml/Parser/Document.elm
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module Yaml.Parser.Document exposing (begins, ends)


import Parser as P exposing ((|=), (|.))
import Parser as P exposing ((|.))
import Yaml.Parser.Util as U


Expand Down
46 changes: 34 additions & 12 deletions src/Yaml/Parser/Util.elm
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ module Yaml.Parser.Util exposing
( isColon, isComma, isDot, isDash, isHash, isSpace, isNewLine, isListStart, isListEnd, isRecordStart, isRecordEnd, either, neither, neither3
, threeDashes, threeDots, spaces, whitespace, multiline
, singleQuotes, doubleQuotes, remaining
, indented
, indented, postProcessString
)

import Parser as P exposing ((|=), (|.))
import Yaml.Parser.Ast as Ast
import Regex exposing (Regex)



Expand Down Expand Up @@ -174,17 +174,24 @@ multiline indent =
multilineStep : Int -> List String -> P.Parser (P.Step (List String) String)
multilineStep indent lines =
let
conclusion line indent_ =
if indent_ > indent then
P.Loop (line :: lines)
else
P.Done (String.join "\n" (List.reverse (line :: lines)))
multilineString : List String -> String
multilineString lines_ =
String.join " " (List.reverse lines_)

conclusion line indent_ =
if indent_ > indent then
P.Loop (line :: lines)
else
P.Done (multilineString (line :: lines))
in
P.succeed conclusion
|= characters (not << isNewLine)
|. P.chompIf isNewLine
|. spaces
|= P.getCol
P.oneOf
[ P.succeed conclusion
|= characters (not << isNewLine)
|. P.chompIf isNewLine
|. spaces
|= P.getCol
, P.succeed (P.Done <| multilineString lines)
]


{-| -}
Expand Down Expand Up @@ -250,6 +257,21 @@ remaining =
|. P.chompUntilEndOr "\n...\n"
|> P.getChompedString

postProcessString : String -> String
postProcessString str =
let
regexFromString : String -> Regex
regexFromString =
Regex.fromString >> Maybe.withDefault Regex.never
in
str
|> Regex.replace (regexFromString "\\s\\s+")
(\match ->
if String.contains "\n\n" match.match then
"\n"
else
" "
)


-- INDENT
Expand Down
Loading

0 comments on commit 45d564c

Please sign in to comment.