187 lines
4.3 KiB
Haskell
187 lines
4.3 KiB
Haskell
{-# Language ConstraintKinds #-}
|
|
module Data.Config.Suckless.Parse.Megaparsec
|
|
( parseSyntax
|
|
, parseTop
|
|
, MegaParsec
|
|
, MegaContext
|
|
)
|
|
where
|
|
|
|
import Data.Config.Suckless.Syntax
|
|
|
|
import Control.Applicative()
|
|
|
|
import Data.Char (showLitChar)
|
|
import Data.Text qualified as Text
|
|
|
|
import Control.Monad
|
|
import Text.Megaparsec
|
|
import Text.Megaparsec.Char (hspace1,space1,char,letterChar,digitChar,eol)
|
|
import Text.Megaparsec.Char.Lexer qualified as L
|
|
import Text.Megaparsec.Char.Lexer ( signed )
|
|
import Data.String(IsString(..))
|
|
import GHC.Generics
|
|
import Data.Data
|
|
|
|
data MegaParsec =
|
|
MegaParsec
|
|
deriving (Data,Typeable,Generic)
|
|
|
|
newtype instance Context MegaParsec = MegaContext (Maybe Int)
|
|
|
|
instance IsContext MegaParsec where
|
|
noContext = MegaContext Nothing
|
|
|
|
type MegaContext = Context MegaParsec
|
|
|
|
type MegaConstraints c = ( c ~ MegaParsec, IsContext c )
|
|
|
|
type Parser r = Parsec () [Char] r
|
|
|
|
type ParseFail = ParseErrorBundle [Char] ()
|
|
|
|
sc :: Parser ()
|
|
sc = do
|
|
L.space space1 lineComment empty
|
|
where
|
|
lineComment = L.skipLineComment ";"
|
|
|
|
scTop :: Parser ()
|
|
scTop = do
|
|
L.space hspace1 lineComment empty
|
|
where
|
|
lineComment = L.skipLineComment ";"
|
|
|
|
dquot :: Parser Char
|
|
dquot = char '"'
|
|
|
|
-- FIXME: position!
|
|
stringLit :: forall c . MegaConstraints c
|
|
=> Parser () -> Parser (Syntax c)
|
|
|
|
stringLit sp = L.lexeme sp $ do
|
|
co <- MegaContext . Just <$> getOffset
|
|
Literal co . LitStr <$> str
|
|
where
|
|
str = do
|
|
s <- dquot >> manyTill L.charLiteral dquot
|
|
pure $ Text.pack (mconcat [ showLitChar c "" | c <- s ])
|
|
|
|
-- FIXME: position!
|
|
intLit :: forall c . MegaConstraints c
|
|
=> Parser () -> Parser (Syntax c)
|
|
|
|
intLit sp = L.lexeme sp $ do
|
|
co <- MegaContext . Just <$> getOffset
|
|
Literal co . LitInt <$> choice [hex, oct, bin, dec, dec']
|
|
where
|
|
hex = L.symbol sc "0x" >> L.hexadecimal
|
|
oct = L.symbol sc "0o" >> L.octal
|
|
bin = L.symbol sc "0b" >> L.binary
|
|
dec = L.decimal
|
|
dec'= signed sc L.decimal
|
|
|
|
symbolChars :: [Char]
|
|
symbolChars = "!$%&|*+-/:<=>?@^_~#.'"
|
|
|
|
symbolChar :: Parser Char
|
|
symbolChar = oneOf symbolChars
|
|
|
|
-- FIXME: position!
|
|
symbol :: forall c . MegaConstraints c
|
|
=> Parser () -> Parser (Syntax c)
|
|
symbol sp = L.lexeme sp $ do
|
|
co <- MegaContext . Just <$> getOffset
|
|
h <- letterChar <|> symbolChar
|
|
t <- many (letterChar <|> digitChar <|> symbolChar)
|
|
case h:t of
|
|
"#t" -> pure $ Literal co (mkLit True)
|
|
"#f" -> pure $ Literal co (mkLit False)
|
|
other -> pure $ Symbol co (fromString other)
|
|
|
|
|
|
skipChar :: Char -> Parser ()
|
|
skipChar c = void (char c)
|
|
|
|
oParen :: Parser ()
|
|
oParen = skipChar '('
|
|
|
|
cParen :: Parser ()
|
|
cParen = skipChar ')'
|
|
|
|
oBrace :: Parser ()
|
|
oBrace = skipChar '{'
|
|
|
|
cBrace :: Parser ()
|
|
cBrace = skipChar '}'
|
|
|
|
oBracket :: Parser ()
|
|
oBracket= skipChar '['
|
|
|
|
cBracket :: Parser ()
|
|
cBracket = skipChar ']'
|
|
|
|
someEnclosedBy :: Parser ()
|
|
-> Parser a
|
|
-> Parser ()
|
|
-> Parser [a]
|
|
|
|
someEnclosedBy o i c = do
|
|
between o c (many (sc >> i)) -- <|> parseError (error (show ("WTF!", ctx))) -- (ParseError (ListParseError ctx))
|
|
|
|
list :: forall c . MegaConstraints c
|
|
=> Parser() -> Parser (Syntax c)
|
|
|
|
list sp = L.lexeme sp $ do
|
|
co <- MegaContext . Just <$> getOffset
|
|
List co <$> choice [ someEnclosedBy oParen (syntax sp) cParen
|
|
, someEnclosedBy oBrace (syntax sp) cBrace
|
|
, someEnclosedBy oBracket (syntax sp) cBracket
|
|
]
|
|
|
|
syntax :: forall c . MegaConstraints c
|
|
=> Parser () -> Parser (Syntax c)
|
|
syntax sp = choice [ symbol sp
|
|
, stringLit sp
|
|
, intLit sp
|
|
, list sp
|
|
]
|
|
|
|
merely :: Parser a -> Parser a
|
|
merely f = do
|
|
sc
|
|
r <- f
|
|
sc
|
|
eof
|
|
pure r
|
|
|
|
parseSyntax :: forall c . MegaConstraints c
|
|
=> String -> Either ParseFail (Syntax c)
|
|
|
|
parseSyntax = parse (merely (syntax sc)) "input"
|
|
|
|
top :: forall c . MegaConstraints c => Parser [Syntax c]
|
|
top = many $ do
|
|
t <- topStmt
|
|
sc
|
|
pure t
|
|
|
|
topTerm :: forall c . MegaConstraints c => Parser (Syntax c)
|
|
topTerm = do
|
|
sc
|
|
co <- MegaContext . Just <$> getOffset
|
|
s0 <- symbol scTop
|
|
ss <- many (syntax scTop)
|
|
|
|
void eol <|> eof
|
|
pure $ List co (s0:ss)
|
|
|
|
topStmt :: forall c . MegaConstraints c => Parser (Syntax c)
|
|
topStmt = topTerm <|> syntax scTop
|
|
|
|
parseTop :: forall c . MegaConstraints c
|
|
=> String -> Either ParseFail [Syntax c]
|
|
|
|
parseTop = parse top "input"
|
|
|