Skip to content

Commit 8bc595b

Browse files
authored
Add support for quoted path components (#690)
... as standardized in dhall-lang/dhall-lang#293
1 parent adf94a6 commit 8bc595b

File tree

7 files changed

+89
-22
lines changed

7 files changed

+89
-22
lines changed

dhall/dhall.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,7 @@ Library
322322
text >= 0.11.1.0 && < 1.3 ,
323323
transformers >= 0.2.0.0 && < 0.6 ,
324324
unordered-containers >= 0.1.3.0 && < 0.3 ,
325+
uri-encode < 1.6 ,
325326
vector >= 0.11.0.0 && < 0.13
326327
if flag(with-http)
327328
Build-Depends:

dhall/src/Dhall/Core.hs

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ module Dhall.Core (
5757
, reservedIdentifiers
5858
, escapeText
5959
, subExpressions
60+
, pathCharacter
6061
) where
6162

6263
#if MIN_VERSION_base(4,8,0)
@@ -75,7 +76,7 @@ import Data.String (IsString(..))
7576
import Data.Semigroup (Semigroup(..))
7677
import Data.Sequence (Seq, ViewL(..), ViewR(..))
7778
import Data.Text (Text)
78-
import Data.Text.Prettyprint.Doc (Pretty)
79+
import Data.Text.Prettyprint.Doc (Doc, Pretty)
7980
import Data.Traversable
8081
import Dhall.Map (Map)
8182
import Dhall.Set (Set)
@@ -131,10 +132,7 @@ instance Semigroup Directory where
131132
Directory (components₁ <> components₀)
132133

133134
instance Pretty Directory where
134-
pretty (Directory {..}) =
135-
foldMap prettyComponent (reverse components)
136-
where
137-
prettyComponent text = "/" <> Pretty.pretty text
135+
pretty (Directory {..}) = foldMap prettyPathComponent (reverse components)
138136

139137
{-| A `File` is a `directory` followed by one additional path component
140138
representing the `file` name
@@ -145,7 +143,9 @@ data File = File
145143
} deriving (Eq, Generic, Ord, Show)
146144

147145
instance Pretty File where
148-
pretty (File {..}) = Pretty.pretty directory <> "/" <> Pretty.pretty file
146+
pretty (File {..}) =
147+
Pretty.pretty directory
148+
<> prettyPathComponent file
149149

150150
instance Semigroup File where
151151
File directory₀ _ <> File directory₁ file =
@@ -2331,3 +2331,28 @@ subExpressions f (Project a b) = Project <$> f a <*> pure b
23312331
subExpressions f (Note a b) = Note a <$> f b
23322332
subExpressions f (ImportAlt l r) = ImportAlt <$> f l <*> f r
23332333
subExpressions _ (Embed a) = pure (Embed a)
2334+
2335+
{-| Returns `True` if the given `Char` is valid within an unquoted path
2336+
component
2337+
2338+
This is exported for reuse within the @"Dhall.Parser.Token"@ module
2339+
-}
2340+
pathCharacter :: Char -> Bool
2341+
pathCharacter c =
2342+
'\x21' == c
2343+
|| ('\x24' <= c && c <= '\x27')
2344+
|| ('\x2A' <= c && c <= '\x2B')
2345+
|| ('\x2D' <= c && c <= '\x2E')
2346+
|| ('\x30' <= c && c <= '\x3B')
2347+
|| c == '\x3D'
2348+
|| ('\x40' <= c && c <= '\x5A')
2349+
|| ('\x5E' <= c && c <= '\x7A')
2350+
|| c == '\x7C'
2351+
|| c == '\x7E'
2352+
2353+
prettyPathComponent :: Text -> Doc ann
2354+
prettyPathComponent text
2355+
| Data.Text.all pathCharacter text =
2356+
"/" <> Pretty.pretty text
2357+
| otherwise =
2358+
"/\"" <> Pretty.pretty text <> "\""

dhall/src/Dhall/Import.hs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ import qualified Dhall.Map
189189
import qualified Dhall.Parser
190190
import qualified Dhall.Pretty.Internal
191191
import qualified Dhall.TypeCheck
192+
import qualified Network.URI.Encode
192193
import qualified System.Environment
193194
import qualified System.Directory as Directory
194195
import qualified System.FilePath as FilePath
@@ -610,13 +611,22 @@ exprFromUncachedImport (Import {..}) = do
610611

611612
return (path, text)
612613

613-
Remote (URL scheme authority file query fragment maybeHeaders) -> do
614+
Remote (URL scheme authority path query fragment maybeHeaders) -> do
614615
let prefix =
615616
(case scheme of HTTP -> "http"; HTTPS -> "https")
616617
<> "://"
617618
<> authority
618619

619-
let fileText = Dhall.Pretty.Internal.prettyToStrictText file
620+
let File {..} = path
621+
let Directory {..} = directory
622+
623+
let pathComponentToText component =
624+
"/" <> Network.URI.Encode.encodeText component
625+
626+
let fileText =
627+
Text.concat
628+
(map pathComponentToText (reverse components))
629+
<> pathComponentToText file
620630

621631
let suffix =
622632
(case query of Nothing -> ""; Just q -> "?" <> q)

dhall/src/Dhall/Parser/Token.hs

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ import qualified Data.HashSet
109109
import qualified Data.List.NonEmpty
110110
import qualified Data.Text
111111
import qualified Dhall.Set
112+
import qualified Text.Megaparsec
112113
import qualified Text.Parser.Char
113114
import qualified Text.Parser.Combinators
114115

@@ -325,25 +326,25 @@ posixEnvironmentVariableCharacter =
325326
|| ('\x3E' <= c && c <= '\x5B')
326327
|| ('\x5D' <= c && c <= '\x7E')
327328

328-
pathCharacter :: Char -> Bool
329-
pathCharacter c =
330-
('\x21' <= c && c <= '\x22')
331-
|| ('\x24' <= c && c <= '\x27')
332-
|| ('\x2A' <= c && c <= '\x2B')
333-
|| ('\x2D' <= c && c <= '\x2E')
334-
|| ('\x30' <= c && c <= '\x3B')
335-
|| c == '\x3D'
336-
|| ('\x40' <= c && c <= '\x5A')
337-
|| ('\x5E' <= c && c <= '\x7A')
338-
|| c == '\x7C'
339-
|| c == '\x7E'
329+
quotedPathCharacter :: Char -> Bool
330+
quotedPathCharacter c =
331+
('\x20' <= c && c <= '\x21')
332+
|| ('\x23' <= c && c <= '\x2E')
333+
|| ('\x30' <= c && c <= '\x7E')
340334

341335
pathComponent :: Parser Text
342336
pathComponent = do
343337
_ <- "/" :: Parser Text
344-
string <- some (Text.Parser.Char.satisfy pathCharacter)
345338

346-
return (Data.Text.pack string)
339+
let pathData = Text.Megaparsec.takeWhile1P Nothing Dhall.Core.pathCharacter
340+
341+
let quotedPathData = do
342+
_ <- Text.Parser.Char.char '"'
343+
text <- Text.Megaparsec.takeWhile1P Nothing quotedPathCharacter
344+
_ <- Text.Parser.Char.char '"'
345+
return text
346+
347+
pathData <|> quotedPathData
347348

348349
file_ :: Parser File
349350
file_ = do

dhall/tests/Parser.hs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,9 @@ parserTests =
149149
, shouldParse
150150
"Sort"
151151
"./tests/parser/success/sort"
152+
, shouldParse
153+
"quoted path components"
154+
"./tests/parser/success/quotedPaths"
152155
, shouldNotParse
153156
"positive double out of bounds"
154157
"./tests/parser/failure/doubleBoundsPos.dhall"
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{ example0 = /"foo"/bar/"baz qux"
2+
, example1 = https://example.com/foo/"bar?baz"?qux
3+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
[
2+
"3.0.0",
3+
[
4+
8,
5+
{
6+
"example0": [
7+
24,
8+
2,
9+
"foo",
10+
"bar",
11+
"baz qux"
12+
],
13+
"example1": [
14+
24,
15+
1,
16+
"example.com",
17+
"foo",
18+
"bar?baz",
19+
"qux",
20+
null
21+
]
22+
}
23+
]
24+
]

0 commit comments

Comments
 (0)