descript-lang-0.2.0.0: src/Descript/Free/Parse.hs
{-# LANGUAGE OverloadedStrings #-}
module Descript.Free.Parse
( parse
, parseValue
) where
import Descript.Free.Data
import Descript.Lex hiding (parse)
import Descript.Misc
import Text.Megaparsec hiding (parse)
import Core.Text.Megaparsec
import qualified Data.List.NonEmpty as NonEmpty
import Prelude hiding (any)
type Parser a = Parsec RangedError (RangeStream Lex) a
type RParser a = Parser (a Range)
-- | Parses a source from the given file and contents.
parse :: ParseAction (RangeStream Lex) (RangeStream TopLevel)
parse = runLaterParser topLevels
-- | Parses an individual value from the given file and contents.
parseValue :: ParseAction (RangeStream Lex) (Value Range)
parseValue = runLaterParser value
topLevels :: Parser (RangeStream TopLevel)
topLevels = topLevel `manySepBy` sep
topLevel :: RParser TopLevel
topLevel
= label "top-level declaration"
$ TopLevelModuleDecl <$> moduleDecl
<|> TopLevelImportDecl <$> importDecl
<|> ranged (TopLevelPhaseSep <$ phaseSep)
<|> ranged (mkTopLevel <$@> value <*@> valueRefinement)
moduleDecl :: RParser ModuleDecl
moduleDecl
= ranged $ label "module declaration"
$ ModuleDecl <$@> (punc DeclModule *> modulePath)
importDecl :: RParser ImportDecl
importDecl
= ranged $ label "import"
$ mkImportDecl
<$@> (punc DeclImport *> modulePath)
<*@> optional srcImports
<*@> optional dstImports
srcImports :: Parser [ImportRecord Range]
srcImports
= label "\"[\" (followed by import records)"
$ punc OpenBracket *> importRecord `manySepBy` sep <* punc CloseBracket
dstImports :: Parser [ImportRecord Range]
dstImports
= label "\"{\" (followed by import records)"
$ punc OpenBrace *> importRecord `manySepBy` sep <* punc CloseBrace
importRecord :: RParser ImportRecord
importRecord
= ranged $ label "import record"
$ mkImportRecord <$@> symbol <*@> optional (punc ArrowEqFwd *> symbol)
modulePath :: RParser ModulePath
modulePath
= ranged $ label "module path"
$ ModulePath <$@> symbol `someSepBy` punc PathFwd
phaseSep :: Parser ()
phaseSep = label "phase separator" $ punc PhaseSep
valueRefinement :: RParser ValueRefinement
valueRefinement
= ToRecordDecl <$ punc Period
<|> ToReducer <$> (punc Colon *> value)
<|> ToQuery <$ punc Question
value :: RParser Value
value
= ranged $ label "value"
$ Value <$@> NonEmpty.toList <$> part `someSepBy` punc Union
part :: RParser Part
part
= label "part"
$ PartPrim <$> prim
<|> PartRecord <$> record
<|> PartPropPath <$> propPath
record :: RParser Record
record
= ranged $ label "record"
$ Record
<$@> try (symbol <* punc OpenBracket)
<*@> property `manySepBy` sep <* punc CloseBracket
property :: RParser Property
property
= label "property"
$ ranged (PropertyDef <$@> try (symbol <* punc Colon) <*@> value)
<|> PropertySingle <$> value
propPath :: RParser PropPath
propPath
= ranged $ label "property path"
$ PropPath <$@> pathElem `someSepBy` punc PathFwd
pathElem :: RParser PathElem
pathElem
= ranged $ label "path element"
$ mkPathElem <$@> symbol <*@> optional (punc PathBwd *> symbol)
sep :: Parser ()
sep = punc Sep
punc :: (() -> Punc ()) -> Parser ()
punc = exactlyR . LexPunc . ($ ())
symbol :: RParser Symbol
symbol = mapSatisfy lexToSymbol
prim :: RParser Prim
prim = mapSatisfy lexToPrim