keiro-dsl-0.8.0.0: src/Keiro/Dsl/Parser/Expression.hs
{-# LANGUAGE ImportQualifiedPost #-}
-- | Legacy and typed scalar expression syntax.
module Keiro.Dsl.Parser.Expression
( pExpr,
)
where
import Control.Monad.Combinators.Expr (Operator (..), makeExprParser)
import Data.Char (isUpper)
import Data.Text qualified as T
import Keiro.Dsl.Frontend.Internal (FrontendContext, frontendSupportsFeature)
import Keiro.Dsl.Grammar
import Keiro.Dsl.LanguageVersion
import Keiro.Dsl.Parser.Core
import Text.Megaparsec
-- Expr sublanguage
--------------------------------------------------------------------------------
pExpr :: FrontendContext -> P Expr
pExpr context
| frontendSupportsFeature context TypedAggregateExpressionSyntax = makeExprParser (pScalarTerm context) scalarOperatorTable
| otherwise = makeExprParser (pLegacyTerm context) legacyOperatorTable
pLegacyTerm :: FrontendContext -> P Expr
pLegacyTerm context =
choice
[ parens (pExpr context),
pUnsupportedScalarTerm context,
EAtom . ABool <$> (True <$ keyword "true" <|> False <$ keyword "false"),
EAtom . AName <$> ident
]
pUnsupportedScalarTerm :: FrontendContext -> P Expr
pUnsupportedScalarTerm context = do
locatedRoot <- withOwnedSpan (try ((keyword "reg" <|> keyword "cmd") *> symbol "."))
requireLanguageFeatureAt context TypedAggregateExpressionSyntax (spanOf locatedRoot)
fail "unreachable supported scalar term in predecessor grammar"
-- | Highest precedence first: relational comparisons bind tighter than @&&@,
-- which binds tighter than @||@.
legacyOperatorTable :: [[Operator P Expr]]
legacyOperatorTable =
[ [InfixL arithmeticUnsupported],
[ InfixN (ECmp OpLe <$ op "<="),
InfixN (ECmp OpGe <$ op ">="),
InfixN (ECmp OpEq <$ op "=="),
InfixN (ECmp OpNeq <$ op "!="),
InfixN (ECmp OpLt <$ op "<"),
InfixN (ECmp OpGt <$ op ">")
],
[InfixL (EAnd <$ op "&&")],
[InfixL (EOr <$ op "||")]
]
where
op s = symbol s
arithmeticUnsupported = do
offset <- getOffset
operator <- lexeme (oneOf ['+', '-', '*', '/'])
failAt offset ("aggregate arithmetic operator '" <> [operator] <> "' is unsupported; compare or copy whole values instead")
pScalarTerm :: FrontendContext -> P Expr
pScalarTerm context =
choice
[ parens (pExpr context),
collectionTermUnsupported,
try pIdLiteral,
do
loc <- getLoc
ELiteral loc . LiteralBool <$> (True <$ keyword "true" <|> False <$ keyword "false"),
do
loc <- getLoc
ELiteral loc . LiteralText <$> stringLit,
try $ do
loc <- getLoc
ELiteral loc . LiteralIntegral <$> integerLiteral,
pScalarPath
]
pIdLiteral :: P Expr
pIdLiteral = do
loc <- getLoc
constructor <- ident
value <- parens stringLit
pure (ELiteral loc (LiteralId constructor value))
pScalarPath :: P Expr
pScalarPath = do
loc <- getLoc
firstName <- ident
rest <- many (symbol "." *> ident)
pure $ case (firstName, rest) of
("reg", name : path) -> EPath loc RegisterRoot (name : path)
("cmd", name : path) -> EPath loc CommandRoot (name : path)
(_, [constructor]) | startsUpper firstName -> ELiteral loc (LiteralQualified firstName constructor)
_ -> EPath loc UnqualifiedRoot (firstName : rest)
where
startsUpper value = maybe False (isUpper . fst) (T.uncons value)
collectionTermUnsupported :: P Expr
collectionTermUnsupported = do
offset <- getOffset
choice
[ () <$ symbol "[",
() <$ symbol "{",
() <$ keyword "keys",
() <$ keyword "values",
() <$ keyword "any",
() <$ keyword "all"
]
failAt offset collectionExpressionMessage
collectionExpressionMessage :: String
collectionExpressionMessage = "CollectionExpressionUnsupported: collection expressions are reserved for plan 166"
scalarOperatorTable :: [[Operator P Expr]]
scalarOperatorTable =
[ [InfixL (op "*" *> located EMultiply)],
[ InfixL (op "+" *> located EAdd),
InfixL (op "-" *> located ESubtract),
InfixL scalarArithmeticUnsupported
],
[ InfixN (ECmp OpLe <$ op "<="),
InfixN (ECmp OpGe <$ op ">="),
InfixN (ECmp OpEq <$ op "=="),
InfixN (ECmp OpNeq <$ op "!="),
InfixN (ECmp OpLt <$ op "<"),
InfixN (ECmp OpGt <$ op ">"),
InfixN collectionOperatorUnsupported
],
[InfixL (EAnd <$ op "&&")],
[InfixL (EOr <$ op "||")]
]
where
op value = symbol value
located constructor = do
loc <- getLoc
pure (constructor loc)
scalarArithmeticUnsupported = do
offset <- getOffset
operator <- lexeme (oneOf ['/', '%'])
failAt offset ("aggregate arithmetic operator '" <> [operator] <> "' is unsupported")
collectionOperatorUnsupported = do
offset <- getOffset
_ <- try (keyword "not" *> keyword "in") <|> keyword "in"
failAt offset collectionExpressionMessage
--------------------------------------------------------------------------------