1 + if true then 1 else 1 should also not parse

Operators are also not allowed to have an `if`, `let`, `with` or `assert`
expression as their argument (just like functions).
This commit is contained in:
Benno Fünfstück 2014-08-19 19:20:17 +02:00
parent 82c19b650c
commit fe596a3e3a
3 changed files with 42 additions and 30 deletions

View file

@ -14,10 +14,10 @@ import Prelude hiding (elem)
-- | The lexer for this parser is defined in 'Nix.Parser.Library'.
nixExpr :: Parser NExpr
nixExpr = whiteSpace *> foldl' makeParser nixTerm nixOperators where
nixExpr = whiteSpace *> (nixToplevelForm <|> foldl' makeParser nixOpArg nixOperators)
where
makeParser term (Left NSelectOp) = nixSelect term
makeParser term (Left NAppOp)
= foldl' (fmap Fix . NApp) <$> term <*> many nixFunArg
makeParser term (Left NAppOp) = chainl1 term $ pure $ \a b -> Fix (NApp a b)
makeParser term (Left NHasAttrOp) = nixHasAttr term
makeParser term (Right ops) = buildExpressionParser [map buildOp ops] term
@ -55,25 +55,13 @@ nixHasAttr term = build <$> term <*> optional (reservedOp "?" *> nixSelector) wh
build t Nothing = t
build t (Just s) = Fix $ NHasAttr t s
nixFunArgUnamb :: Parser NExpr
nixFunArgUnamb = choice
nixOpArg :: Parser NExpr
nixOpArg = nixSelect $ choice
[ nixInt, nixBool, nixNull, nixParens, nixList, nixPath, nixSPath, nixUri
, nixStringExpr ]
, nixStringExpr, nixSet, nixSym ]
nixFunArg :: Parser NExpr
nixFunArg = nixSelect $ nixFunArgUnamb <|> nixSet <|> nixSym
nixTerm :: Parser NExpr
nixTerm = nixSelect $ choice
[ nixFunArgUnamb
, nixLambda
, nixSym
, nixSet
, nixLet
, nixIf
, nixAssert
, nixWith
]
nixToplevelForm :: Parser NExpr
nixToplevelForm = choice [nixLambda, nixLet, nixIf, nixAssert, nixWith]
nixSym :: Parser NExpr
nixSym = mkSym <$> identifier
@ -93,7 +81,7 @@ nixParens :: Parser NExpr
nixParens = parens nixExpr <?> "parens"
nixList :: Parser NExpr
nixList = brackets (Fix . NList <$> many nixFunArg) <?> "list"
nixList = brackets (Fix . NList <$> many nixOpArg) <?> "list"
pathChars :: String
pathChars = ['A'..'Z'] ++ ['a'..'z'] ++ "._-+" ++ ['0'..'9']
@ -103,9 +91,9 @@ nixSPath = mkPath True <$> try (char '<' *> some (oneOf ('/':pathChars)) <* symb
<?> "spath"
nixPath :: Parser NExpr
nixPath = token $ fmap (mkPath False) $ (++)
nixPath = token $ notFollowedBy (try (string "//")) *> fmap (mkPath False) ((++)
<$> (try ((++) <$> many (oneOf pathChars) <*> string "/") <?> "path")
<*> some (oneOf ('/':pathChars))
<*> some (oneOf ('/':pathChars)))
<?> "path"
nixLet :: Parser NExpr
@ -137,13 +125,15 @@ nixLambda = Fix <$> (NAbs <$> (try argExpr <?> "lambda arguments") <*> nixExpr)
nixStringExpr :: Parser NExpr
nixStringExpr = Fix . NStr <$> nixString
uriAfterColonC :: Parser Char
uriAfterColonC = alphaNum <|> oneOf "%/?:@&=+$,-_.!~*'"
nixUri :: Parser NExpr
nixUri = token $ fmap (mkUri . pack) $ (++)
<$> try ((++) <$> (scheme <* char ':') <*> fmap (\x -> [':',x]) afterColonC)
<*> many afterColonC
<$> try ((++) <$> (scheme <* char ':') <*> fmap (\x -> [':',x]) uriAfterColonC)
<*> many uriAfterColonC
where
scheme = (:) <$> letter <*> many (alphaNum <|> oneOf "+-.")
afterColonC = alphaNum <|> oneOf "%/?:@&=+$,-_.!~*'"
nixString :: Parser (NString NExpr)
nixString = doubleQuoted <|> indented <?> "string"
@ -177,7 +167,7 @@ nixString = doubleQuoted <|> indented <?> "string"
argExpr :: Parser (Formals NExpr)
argExpr = choice
[ idOrAtPattern <$> identifier <*> optional (symbolic '@' *> paramSet)
[ idOrAtPattern <$> identifierNotUri <*> optional (symbolic '@' *> paramSet)
, setOrAtPattern <$> paramSet <*> optional (symbolic '@' *> identifier)
] <* symbolic ':'
where
@ -187,6 +177,9 @@ argExpr = choice
argList :: Parser [(Text, Maybe NExpr)]
argList = braces (argName `sepBy` symbolic ',') <?> "arglist"
identifierNotUri :: Parser Text
identifierNotUri = notFollowedBy nixUri *> identifier
argName :: Parser (Text, Maybe NExpr)
argName = (,) <$> identifier
<*> optional (symbolic '?' *> nixExpr)

View file

@ -354,6 +354,12 @@ mkBool = Fix . NConstant . NBool
mkNull :: NExpr
mkNull = Fix (NConstant NNull)
mkOper :: NUnaryOp -> NExpr -> NExpr
mkOper op = Fix . NOper . NUnary op
mkOper2 :: NBinaryOp -> NExpr -> NExpr -> NExpr
mkOper2 op a = Fix . NOper . NBinary op a
-- | An 'NValue' is the most reduced form of an 'NExpr' after evaluation
-- is completed.
data NValueF r

View file

@ -116,6 +116,8 @@ case_lambda_or_uri = do
assertParseString "a :b" $ Fix $ NAbs (FormalName "a") (mkSym "b")
assertParseString "a c:def" $ Fix $ NApp (mkSym "a") (mkUri "c:def")
assertParseString "c:def: c" $ Fix $ NApp (mkUri "c:def:") (mkSym "c")
assertParseString "a:{}" $ Fix $ NAbs (FormalName "a") $ Fix $ NSet NonRec []
assertParseString "a:[a]" $ Fix $ NAbs (FormalName "a") $ Fix $ NList [mkSym "a"]
assertParseFail "def:"
case_lambda_pattern :: Assertion
@ -266,9 +268,20 @@ case_indented_string_escape = assertParseString
case_operator_fun_app :: Assertion
case_operator_fun_app = do
assertParseString "a ++ b" $ Fix $ NOper (NBinary NConcat (mkSym "a") (mkSym "b"))
assertParseString "a ++ f b" $ Fix $ NOper (NBinary NConcat (mkSym "a") (Fix
(NApp (mkSym "f") (mkSym "b"))))
assertParseString "a ++ b" $ mkOper2 NConcat (mkSym "a") (mkSym "b")
assertParseString "a ++ f b" $ mkOper2 NConcat (mkSym "a") $ Fix $ NApp
(mkSym "f") (mkSym "b")
case_operators :: Assertion
case_operators = do
assertParseString "1 + 2 - 3" $ mkOper2 NMinus
(mkOper2 NPlus (mkInt 1) (mkInt 2)) (mkInt 3)
assertParseFail "1 + if true then 1 else 2"
assertParseString "1 + (if true then 2 else 3)" $ mkOper2 NPlus (mkInt 1) $ Fix $ NIf
(mkBool True) (mkInt 2) (mkInt 3)
assertParseString "{ a = 3; } // rec { b = 4; }" $ mkOper2 NUpdate
(Fix $ NSet NonRec [NamedVar (mkSelector "a") (mkInt 3)])
(Fix $ NSet Rec [NamedVar (mkSelector "b") (mkInt 4)])
tests :: TestTree
tests = $testGroupGenerator