Skip to content

Commit fa11f0c

Browse files
Improve robustness of :let command (#2297)
* Improve robustness of `:let` command Fixes #2296 The root cause of the above bug was that the old parser for `:let` commands was too lenient. In particular, given a command like: ``` :let x : T = e ``` … the old parser would silently ignore the `: T` part of the command. This change fixes that by adding support for type annotations and fixing the `:let` command to exactly match the standard parser in terms of what expressions it permits. * Use `NamedFieldPuns` … as suggested by @sjakobi Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
1 parent 8ab5529 commit fa11f0c

File tree

2 files changed

+67
-45
lines changed

2 files changed

+67
-45
lines changed

dhall/src/Dhall/Parser/Expression.hs

Lines changed: 31 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ importExpression embedded = importExpression_
107107
data Parsers a = Parsers
108108
{ completeExpression_ :: Parser (Expr Src a)
109109
, importExpression_ :: Parser (Expr Src a)
110+
, letBinding :: Parser (Binding Src a)
110111
}
111112

112113
{-| Parse a numeric `TimeZone`
@@ -237,7 +238,7 @@ temporalLiteral =
237238

238239
-- | Given a parser for imports,
239240
parsers :: forall a. Parser a -> Parsers a
240-
parsers embedded = Parsers {..}
241+
parsers embedded = Parsers{..}
241242
where
242243
completeExpression_ =
243244
many shebang *> whitespace *> expression <* whitespace
@@ -251,6 +252,34 @@ parsers embedded = Parsers {..}
251252

252253
endOfLine
253254

255+
letBinding = do
256+
src0 <- try (_let *> src nonemptyWhitespace)
257+
258+
c <- label
259+
260+
src1 <- src whitespace
261+
262+
d <- optional (do
263+
_colon
264+
265+
src2 <- src nonemptyWhitespace
266+
267+
e <- expression
268+
269+
whitespace
270+
271+
return (Just src2, e) )
272+
273+
_equal
274+
275+
src3 <- src whitespace
276+
277+
f <- expression
278+
279+
whitespace
280+
281+
return (Binding (Just src0) c (Just src1) d (Just src3) f)
282+
254283
expression =
255284
noted
256285
( choice
@@ -293,35 +322,7 @@ parsers embedded = Parsers {..}
293322
return (BoolIf a b c)
294323

295324
alternative2 = do
296-
let binding = do
297-
src0 <- try (_let *> src nonemptyWhitespace)
298-
299-
c <- label
300-
301-
src1 <- src whitespace
302-
303-
d <- optional (do
304-
_colon
305-
306-
src2 <- src nonemptyWhitespace
307-
308-
e <- expression
309-
310-
whitespace
311-
312-
return (Just src2, e) )
313-
314-
_equal
315-
316-
src3 <- src whitespace
317-
318-
f <- expression
319-
320-
whitespace
321-
322-
return (Binding (Just src0) c (Just src1) d (Just src3) f)
323-
324-
as <- NonEmpty.some1 binding
325+
as <- NonEmpty.some1 letBinding
325326

326327
try (_in *> nonemptyWhitespace)
327328

dhall/src/Dhall/Repl.hs

Lines changed: 36 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,10 @@ import qualified Dhall.Core as Expr (Expr (..))
6262
import qualified Dhall.Import as Dhall
6363
import qualified Dhall.Map as Map
6464
import qualified Dhall.Parser as Dhall
65-
import qualified Dhall.Parser.Token as Parser.Token
65+
import qualified Dhall.Parser.Expression as Parser.Expression
6666
import qualified Dhall.Pretty
6767
import qualified Dhall.Pretty.Internal
68+
import qualified Dhall.Syntax as Syntax
6869
import qualified Dhall.TypeCheck as Dhall
6970
import qualified Dhall.Version as Meta
7071
import qualified Prettyprinter as Pretty
@@ -234,31 +235,51 @@ parseAssignment str
234235
| otherwise
235236
= Left (trim str)
236237

237-
addBinding :: ( MonadFail m, MonadIO m, MonadState Env m ) => Either String (String, String) -> m ()
238-
addBinding (Right (k, src)) = do
239-
varName <- case Megaparsec.parse (unParser Parser.Token.label) "(input)" (Text.pack k) of
240-
Left _ -> Fail.fail "Invalid variable name"
241-
Right varName -> return varName
238+
addBinding :: ( MonadFail m, MonadIO m, MonadState Env m ) => String -> m ()
239+
addBinding string = do
240+
let parseBinding =
241+
Parser.Expression.letBinding
242+
(Parser.Expression.parsers
243+
(Megaparsec.try Parser.Expression.import_)
244+
)
242245

243-
loaded <- parseAndLoad src
246+
let input = "let " <> Text.pack string
244247

245-
t <- typeCheck loaded
248+
Syntax.Binding{ variable, annotation, value } <- case Megaparsec.parse (unParser parseBinding) "(input)" input of
249+
Left _ -> Fail.fail ":let should be of the form `:let x [: T] = y`"
250+
Right binding -> return binding
246251

247-
expr <- normalize loaded
252+
(resolved, bindingType) <- case annotation of
253+
Just (_, unresolvedType) -> do
254+
let annotated = Syntax.Annot value unresolvedType
255+
256+
resolved <- liftIO (Dhall.load annotated)
257+
258+
_ <- typeCheck resolved
259+
260+
bindingType <- liftIO (Dhall.load unresolvedType)
261+
262+
return (resolved, bindingType)
263+
_ -> do
264+
resolved <- liftIO (Dhall.load value)
265+
266+
bindingType <- typeCheck resolved
267+
268+
return (resolved, bindingType)
269+
270+
bindingExpr <- normalize resolved
248271

249272
modify
250273
( \e ->
251274
e { envBindings =
252275
Dhall.Context.insert
253-
varName
254-
Binding { bindingType = t, bindingExpr = expr }
276+
variable
277+
Binding{ bindingType, bindingExpr }
255278
( envBindings e )
256279
}
257280
)
258281

259-
output ( Expr.Annot ( Expr.Var ( Dhall.V varName 0 ) ) t )
260-
261-
addBinding _ = Fail.fail ":let should be of the form `:let x = y`"
282+
output (Expr.Annot (Expr.Var (Dhall.V variable 0)) bindingType)
262283

263284
clearBindings :: (MonadFail m, MonadState Env m) => String -> m ()
264285
clearBindings _ = modify adapt
@@ -476,7 +497,7 @@ helpOptions =
476497
"let"
477498
"IDENTIFIER = EXPRESSION"
478499
"Assign an expression to a variable"
479-
(dontCrash . addBinding . parseAssignment)
500+
(dontCrash . addBinding)
480501
, HelpOption
481502
"clear"
482503
""

0 commit comments

Comments
 (0)