ptera-core-0.2.0.0: src/Language/Parser/Ptera/Pipeline/Grammar2PEG.hs
module Language.Parser.Ptera.Pipeline.Grammar2PEG where
import Language.Parser.Ptera.Prelude
import qualified Data.EnumMap.Strict as EnumMap
import qualified Language.Parser.Ptera.Data.HFList as HFList
import qualified Language.Parser.Ptera.Machine.PEG as PEG
import qualified Language.Parser.Ptera.Machine.PEG.Builder as PEGBuilder
import qualified Language.Parser.Ptera.Syntax.Grammar as Grammar
grammar2Peg :: Enum start => Enum nonTerminal => Enum terminal
=> Grammar.FixedGrammar start nonTerminal terminal elem varDoc altDoc action
-> PEG.T start varDoc altDoc (Grammar.Action action)
grammar2Peg g = runIdentity do PEGBuilder.build builder where
builder = do
initialBuilderCtx <- get
let initialCtx = Context
{ ctxBuilder = initialBuilderCtx
, ctxVarMap = EnumMap.empty
, ctxDisplayNonTerminals = Grammar.grammarDisplayNonTerminals g
}
let finalCtx = execState pipeline initialCtx
put do ctxBuilder finalCtx
pipeline = do
forM_ do EnumMap.assocs do Grammar.grammarStarts g
do \(s, v) -> grammarStartPipeline s v
forM_ do EnumMap.assocs do Grammar.grammarRules g
do \(v, e) -> grammarRulePipeline v e
type Pipeline start nonTerminal varDoc altDoc action =
State (Context start nonTerminal varDoc altDoc action)
data Context start nonTerminal varDoc altDoc action = Context
{ ctxBuilder :: PEGBuilder.Context start varDoc altDoc (Grammar.Action action)
, ctxVarMap :: EnumMap.EnumMap nonTerminal PEG.VarNum
, ctxDisplayNonTerminals :: EnumMap.EnumMap nonTerminal varDoc
}
grammarStartPipeline :: Enum start => Enum nonTerminal
=> start -> nonTerminal -> Pipeline start nonTerminal varDoc altDoc action ()
grammarStartPipeline s v = do
newV <- getNewVar v
liftBuilder do PEGBuilder.addInitial s newV
grammarRulePipeline :: Enum nonTerminal => Enum terminal
=> nonTerminal -> Grammar.RuleExpr nonTerminal terminal elem altDoc action
-> Pipeline start nonTerminal varDoc altDoc action ()
grammarRulePipeline v (Grammar.RuleExpr alts) = do
newV <- getNewVar v
newAlts <- forM alts \alt -> grammarAltPipeline alt
let newRule = PEG.Rule newAlts
liftBuilder do PEGBuilder.addRule newV newRule
grammarAltPipeline :: Enum nonTerminal => Enum terminal
=> Grammar.Alt nonTerminal terminal elem altDoc action r
-> Pipeline start nonTerminal varDoc altDoc action PEG.AltNum
grammarAltPipeline (Grammar.Alt e d act) = do
newUs <- grammarExprPipeline e
let newAct = Grammar.Action act
let newAlt = PEG.Alt
{ altKind = PEG.AltSeq
, altUnitSeq = newUs
, altAction = newAct
, altHelp = d
}
liftBuilder do PEGBuilder.genNewAlt newAlt
grammarExprPipeline :: forall start nonTerminal terminal elem varDoc altDoc action us
. Enum nonTerminal => Enum terminal
=> Grammar.Expr nonTerminal terminal elem us
-> Pipeline start nonTerminal varDoc altDoc action [PEG.Unit]
grammarExprPipeline e = do
revUs <- HFList.hfoldMWithIndex []
do \acc _ u -> do
newU <- grammarUnitPipeline u
pure do newU:acc
do e
pure do reverse revUs
grammarUnitPipeline :: Enum nonTerminal => Enum terminal
=> Grammar.Unit nonTerminal terminal elem u
-> Pipeline start nonTerminal varDoc altDoc action PEG.Unit
grammarUnitPipeline = \case
Grammar.UnitToken t ->
pure do PEG.UnitTerminal do fromEnum t
Grammar.UnitVar v -> do
newV <- getNewVar v
pure do PEG.UnitNonTerminal newV
getNewVar :: Enum nonTerminal
=> nonTerminal -> Pipeline start nonTerminal varDoc altDoc action PEG.VarNum
getNewVar v = do
vmap <- ctxVarMap <$> get
case EnumMap.lookup v vmap of
Just newV ->
pure newV
Nothing -> do
displayNonTerminals <- ctxDisplayNonTerminals <$> get
let d = case EnumMap.lookup v displayNonTerminals of
Just x -> x
Nothing -> error "Not found any rules for a non-terminal."
newV <- liftBuilder
do PEGBuilder.genNewVar
do PEG.Var
{ varHelp = d
}
modify' \ctx -> ctx
{ ctxVarMap = EnumMap.insert v newV
do ctxVarMap ctx
}
pure newV
liftBuilder
:: PEGBuilder.T start varDoc altDoc (Grammar.Action action) Identity r
-> Pipeline start nonTerminal varDoc altDoc action r
liftBuilder builder = do
ctx <- get
let (x, builderCtx) = runState builder do ctxBuilder ctx
put do ctx { ctxBuilder = builderCtx }
pure x