Skip to content

Commit 6ffffdf

Browse files
Implement v2 parser (#16)
* Add kdl-test tests * Implement v2 parser * Update unit tests
1 parent 74a949d commit 6ffffdf

24 files changed

Lines changed: 1731 additions & 890 deletions

.github/workflows/ci.yml

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,16 @@ jobs:
5959
restore-keys: |
6060
${{ runner.os }}-cabal-cache-${{ env.CURR_MONTH }}-${{ matrix.ghc_version }}-
6161
-
62-
name: Build + Test
63-
run: cabal test
62+
name: Build
63+
run: cabal build
64+
-
65+
name: Install dotslash
66+
run: >
67+
curl -fsSL https://github.com/facebook/dotslash/releases/latest/download/dotslash-ubuntu-22.04.$(uname -m).tar.gz
68+
| tar xzf - -C /usr/local/bin/
69+
-
70+
name: Test
71+
run: cabal exec cabal test
6472

6573
lint:
6674
runs-on: ubuntu-latest

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
.DS_Store
22
dist-newstyle/
3+
cabal.project.local*

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
## Unreleased
22

3+
* Implement KDL v2 parser
34
* Implement `KDL.render`, which is format-preserving
45
* Improve rendering parse errors
56
* Include filepath in error messages when `decodeFileWith` fails

kdl-hs.cabal

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ build-type: Simple
1515
extra-source-files:
1616
README.md
1717
CHANGELOG.md
18+
test/KDL/__snapshots__/DecoderSpec.snap.md
19+
test/KDL/__snapshots__/ParserSpec.snap.md
1820

1921
source-repository head
2022
type: git
@@ -34,14 +36,9 @@ library
3436
KDL.Decoder.Monad
3537
KDL.Decoder.Schema
3638
KDL.Parser
39+
KDL.Parser.Internal
3740
KDL.Render
3841
KDL.Types
39-
other-modules:
40-
KDL.Parser.Hustle
41-
KDL.Parser.Hustle.Formatter
42-
KDL.Parser.Hustle.Internal
43-
KDL.Parser.Hustle.Parser
44-
KDL.Parser.Hustle.Types
4542
build-depends:
4643
base < 5
4744
, containers
@@ -53,10 +50,25 @@ library
5350
default-language: GHC2021
5451
ghc-options: -Wall -Wcompat
5552

53+
executable kdl-hs-test-decoder
54+
main-is: test/kdl-hs-test-decoder.hs
55+
build-depends:
56+
base < 5
57+
, aeson
58+
, bytestring
59+
, containers
60+
, kdl-hs
61+
, scientific
62+
, text
63+
default-language: GHC2021
64+
ghc-options: -Wall -Wcompat
65+
5666
test-suite kdl-tests
5767
type: exitcode-stdio-1.0
5868
ghc-options: -F -pgmF=skeletest-preprocessor
59-
build-tool-depends: skeletest:skeletest-preprocessor
69+
build-tool-depends:
70+
, skeletest:skeletest-preprocessor
71+
, kdl-hs:kdl-hs-test-decoder
6072
hs-source-dirs: test
6173
main-is: Main.hs
6274
other-modules:
@@ -65,12 +77,17 @@ test-suite kdl-tests
6577
KDL.Decoder.ArrowSpec
6678
KDL.Decoder.MonadSpec
6779
KDL.ParserSpec
80+
KDL.TestUtils.AST
6881
KDL.TestUtils.Error
6982
build-depends:
7083
base
7184
, containers
85+
, directory
7286
, filepath
7387
, kdl-hs
88+
, pretty-show
89+
, process
90+
, scientific
7491
, skeletest
7592
, temporary
7693
, text

scripts/kdl-test

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#!/usr/bin/env dotslash
2+
{
3+
"name": "kdl-test-0.2.0",
4+
"platforms": {
5+
"linux-x86_64": {
6+
"size": 583741,
7+
"hash": "sha256",
8+
"digest": "1627ea50594c7c322ed5c7fb0d0063b2ac20a7a9460acc9363fa1777c92364c3",
9+
"format": "tar.gz",
10+
"path": "kdl-test",
11+
"providers": [
12+
{
13+
"url": "https://github.com/brandonchinn178/kdl-test/releases/download/v0.2.0/kdl-test-0.2.0-linux-x86_64.tar.gz"
14+
}
15+
]
16+
},
17+
"linux-aarch64": {
18+
"size": 568646,
19+
"hash": "sha256",
20+
"digest": "f3ecbdb2225b6abc0d69a451be76eca4d095d835ee2e7830a880b4bfa051e6ee",
21+
"format": "tar.gz",
22+
"path": "kdl-test",
23+
"providers": [
24+
{
25+
"url": "https://github.com/brandonchinn178/kdl-test/releases/download/v0.2.0/kdl-test-0.2.0-linux-arm64.tar.gz"
26+
}
27+
]
28+
},
29+
"macos-x86_64": {
30+
"size": 555773,
31+
"hash": "sha256",
32+
"digest": "2d66b5409889992554dbd9bf04c2817552b2c0416a2793204e2e4eea06bc4da4",
33+
"format": "tar.gz",
34+
"path": "kdl-test",
35+
"providers": [
36+
{
37+
"url": "https://github.com/brandonchinn178/kdl-test/releases/download/v0.2.0/kdl-test-0.2.0-darwin-x86_64.tar.gz"
38+
}
39+
]
40+
},
41+
"macos-aarch64": {
42+
"size": 529715,
43+
"hash": "sha256",
44+
"digest": "abfcade1d1ce02810dc155b95ef0720e9c679d62cf50c7ab5400741909978eb2",
45+
"format": "tar.gz",
46+
"path": "kdl-test",
47+
"providers": [
48+
{
49+
"url": "https://github.com/brandonchinn178/kdl-test/releases/download/v0.2.0/kdl-test-0.2.0-darwin-arm64.tar.gz"
50+
}
51+
]
52+
}
53+
}
54+
}

src/KDL/Decoder/Internal/Error.hs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -67,22 +67,22 @@ data BaseDecodeError
6767
renderDecodeError :: DecodeError -> Text
6868
renderDecodeError decodeError =
6969
Text.intercalate "\n"
70-
. addPath decodeError.filepath
71-
. map renderCtxErrors
70+
. concatMap renderCtxErrors
7271
. groupCtxErrors
7372
$ decodeError.errors
7473
where
7574
-- Group errors with the same contexts together
7675
groupCtxErrors es = Map.toAscList $ Map.fromListWith (<>) [(ctx, [e]) | (ctx, e) <- es]
7776

78-
addPath = \case
79-
Nothing -> id
80-
Just fp -> let msg = "Failed to decode " <> Text.pack fp <> ":" in (msg :)
77+
addPath =
78+
case decodeError.filepath of
79+
Nothing -> id
80+
Just fp -> let msg = "Failed to decode " <> Text.pack fp <> ":" in (msg :)
8181

8282
renderCtxErrors = \case
8383
-- Special case parse errors, which shouldn't have a context
84-
(_, [DecodeError_ParseError msg]) -> msg
85-
(ctx, errs) -> Text.intercalate "\n" $ ("At: " <> renderCtxItems ctx) : renderErrors errs
84+
(_, [DecodeError_ParseError msg]) -> [msg]
85+
(ctx, errs) -> addPath $ ("At: " <> renderCtxItems ctx) : renderErrors errs
8686

8787
renderCtxItems items
8888
| null items = "<root>"

src/KDL/Parser.hs

Lines changed: 13 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,91 +1,28 @@
11
{-# LANGUAGE OverloadedStrings #-}
2-
{-# LANGUAGE RecordWildCards #-}
32

3+
{-|
4+
Implement the v2 parser specified at: https://kdl.dev/spec/#name-full-grammar
5+
-}
46
module KDL.Parser (
57
parse,
68
parseFile,
79
) where
810

9-
import Data.Map qualified as Map
11+
import Data.Bifunctor (first)
1012
import Data.Text (Text)
1113
import Data.Text qualified as Text
1214
import Data.Text.IO qualified as Text
13-
import KDL.Parser.Hustle qualified as Hustle
14-
import KDL.Types (
15-
Ann (..),
16-
Document,
17-
Entry (..),
18-
Identifier (..),
19-
Node (..),
20-
NodeList (..),
21-
Value (..),
22-
ValueData (..),
23-
ValueFormat (..),
24-
)
15+
import KDL.Parser.Internal (p_document)
16+
import KDL.Types (Document)
17+
import Text.Megaparsec qualified as Megaparsec
2518

26-
-- TODO: Implement our own parser that implements the v2.0.0 spec + preserves formatting and comments
2719
parse :: Text -> Either Text Document
28-
parse input =
29-
case Hustle.parse Hustle.document "" input of
30-
Left e -> Left . Text.strip . Text.pack . Hustle.errorBundlePretty $ e
31-
Right (Hustle.Document nodes) -> Right $ fromNodes nodes
32-
where
33-
fromNodes nodes =
34-
NodeList
35-
{ nodes = map fromNode nodes
36-
, format = Nothing
37-
}
20+
parse = parse' ""
3821

39-
fromAnn identifier =
40-
Ann
41-
{ identifier = fromIdentifier identifier
42-
, format = Nothing
43-
}
44-
45-
fromNode Hustle.Node{..} =
46-
Node
47-
{ ann = fromAnn <$> nodeAnn
48-
, name = fromIdentifier nodeName
49-
, entries = map fromArgEntry nodeArgs <> map fromPropEntry (Map.toList nodeProps)
50-
, children = Just $ fromNodes nodeChildren
51-
, format = Nothing
52-
}
53-
54-
fromArgEntry v =
55-
Entry
56-
{ name = Nothing
57-
, value = fromValue v
58-
, format = Nothing
59-
}
60-
61-
fromPropEntry (name, v) =
62-
Entry
63-
{ name = Just $ fromIdentifier name
64-
, value = fromValue v
65-
, format = Nothing
66-
}
67-
68-
fromValue Hustle.Value{..} =
69-
Value
70-
{ ann = fromAnn <$> valueAnn
71-
, data_ =
72-
case valueExp of
73-
Hustle.StringValue s -> Text s
74-
Hustle.IntegerValue x -> Number (fromInteger x)
75-
Hustle.SciValue x -> Number x
76-
Hustle.BooleanValue x -> Bool x
77-
Hustle.NullValue -> Null
78-
, format =
79-
case valueExp of
80-
Hustle.IntegerValue x -> Just ValueFormat{repr = Text.pack $ show x}
81-
_ -> Nothing
82-
}
83-
84-
fromIdentifier (Hustle.Identifier s) =
85-
Identifier
86-
{ value = s
87-
, format = Nothing
88-
}
22+
parse' :: FilePath -> Text -> Either Text Document
23+
parse' fp input =
24+
first (Text.strip . Text.pack . Megaparsec.errorBundlePretty) $
25+
Megaparsec.parse p_document fp input
8926

9027
parseFile :: FilePath -> IO (Either Text Document)
91-
parseFile = fmap parse . Text.readFile
28+
parseFile fp = parse' fp <$> Text.readFile fp

src/KDL/Parser/Hustle.hs

Lines changed: 0 additions & 28 deletions
This file was deleted.

0 commit comments

Comments
 (0)