ptera-th-0.7.0.0: src/Language/Parser/Ptera/TH/Pipeline/SRB2ParserDec.hs
{-# LANGUAGE TemplateHaskell #-}
module Language.Parser.Ptera.TH.Pipeline.SRB2ParserDec where
import Language.Parser.Ptera.Prelude
import qualified Data.Bits as Bits
import qualified Data.EnumMap.Strict as EnumMap
import qualified Data.HashMap.Strict as HashMap
import qualified Language.Haskell.TH as TH
import qualified Language.Haskell.TH.Syntax as TH
import qualified Language.Parser.Ptera.Data.Alignable.Array as AlignableArray
import qualified Language.Parser.Ptera.Data.Symbolic.IntMap as SymbolicIntMap
import qualified Language.Parser.Ptera.Machine.LAPEG as LAPEG
import qualified Language.Parser.Ptera.Machine.PEG as PEG
import qualified Language.Parser.Ptera.Machine.SRB as SRB
import qualified Language.Parser.Ptera.Syntax.Grammar as Grammar
import qualified Language.Parser.Ptera.TH.Data.Bits.MaxBit as Bits
import Language.Parser.Ptera.TH.ParserLib
import qualified Language.Parser.Ptera.TH.Syntax as Syntax
type SemanticAction ctx = Grammar.Action (Syntax.SemActM ctx)
data PipelineParam = PipelineParam
{
startsTy :: TH.Q TH.Type,
rulesTy :: TH.Q TH.Type,
tokensTy :: TH.Q TH.Type,
tokenTy :: TH.Q TH.Type,
customCtxTy :: TH.Q TH.Type,
tokenBounds :: (Int, Int)
}
srb2QParser
:: PipelineParam
-> SRB.T Int StringLit (Maybe altDoc) (SemanticAction ctx)
-> TH.Q [TH.Dec]
srb2QParser param srb = do
let runnerFnName = TH.mkName "pteraTHRunner"
let parserInitialFnName = TH.mkName "pteraTHParserInitial"
let parserGetTokenNumFnName = TH.mkName "pteraTHParserGetTokenNum"
let parserTransFnName = TH.mkName "pteraTHParserTrans"
let parserAltKindFnName = TH.mkName "pteraTHParserAltKind"
let parserStateHelpFnName = TH.mkName "pteraTHParserStateHelp"
let parserAltHelpFnName = TH.mkName "pteraTHParserAltHelp"
let parserActionFnName = TH.mkName "pteraTHParserAction"
sequence
[ TH.SigD parserInitialFnName <$>
[t|Int -> Maybe Int|]
, outputParserInitialFn parserInitialFnName do SRB.initials srb
, TH.SigD parserGetTokenNumFnName <$>
[t|$(tokenTy param) -> Int|]
, TH.ValD do TH.VarP parserGetTokenNumFnName
<$> fmap TH.NormalB [e|\t ->
pteraTHTokenToTerminal (Proxy :: Proxy $(tokensTy param)) t
|]
<*> pure []
, TH.SigD parserTransFnName <$>
[t|Int -> Int -> Trans|]
, outputParserTransFn parserTransFnName
do tokenBounds param
do SRB.states srb
, TH.SigD parserAltKindFnName <$>
[t|Int -> AltKind|]
, outputParserAltKindFn parserAltKindFnName do SRB.alts srb
, TH.SigD parserStateHelpFnName <$>
[t|Int -> [(Int, Int)]|]
, outputParserStateHelpFn parserStateHelpFnName
do SRB.states srb
, TH.SigD parserAltHelpFnName <$>
[t|Int -> (StringLit, Maybe ())|]
, outputParserAltHelpFn parserAltHelpFnName
do SRB.alts srb
do SRB.vars srb
, TH.SigD parserActionFnName <$>
[t|Int -> ActionM $(customCtxTy param)|]
, outputParserActionFn parserActionFnName do SRB.alts srb
, TH.SigD runnerFnName <$>
[t|Parser
$(customCtxTy param)
$(rulesTy param)
$(tokenTy param)
$(startsTy param)
|]
, outputRunnerFn runnerFnName
parserInitialFnName
parserGetTokenNumFnName
parserTransFnName
parserAltKindFnName
parserStateHelpFnName
parserAltHelpFnName
parserActionFnName
]
outputParserInitialFn :: TH.Name -> EnumMap.EnumMap Int SRB.StateNum -> TH.Q TH.Dec
outputParserInitialFn parserInitialFnName initials =
TH.ValD do TH.VarP parserInitialFnName
<$> fmap TH.NormalB [e|\s ->
if s <= $(TH.lift ub)
then pteraTHArrayIndex table s
else Nothing
|]
<*> [d|
table :: PteraTHArray Int (Maybe Int)
table = pteraTHArrayFromList $(TH.lift ub) $(TH.ListE <$> qes)
|]
where
ub = if EnumMap.null initials
then -1
else let (i, _) = EnumMap.findMax initials in i
qes = traverse
do \i -> case EnumMap.lookup i initials of
Nothing ->
[e|Nothing|]
Just (SRB.StateNum s) ->
[e|Just $(TH.lift s) :: Maybe Int|]
[0..ub]
outputParserTransFn :: TH.Name
-> (Int, Int)
-> AlignableArray.T SRB.StateNum SRB.MState
-> TH.Q TH.Dec
outputParserTransFn parserTransFnName (minTokBound, maxTokBound) states = if
| tokBitSize + stateBitSize > 29 ->
error "exceed over bit size limited"
| otherwise ->
TH.ValD do TH.VarP parserTransFnName
<$> fmap TH.NormalB [e|\s0 c0 ->
let c1 = if c0 >= 0
then c0 - $(TH.lift minTokBound)
else $(TH.lift maxTokBound) + 1
s1 = $(stateTableLookupFn)
$(TH.lift tokBitSize)
stateTable
s0 c1
opsNum = $(opsNumTableLookupFn)
$(TH.lift tokBitSize)
opsNumTable
s0 c1
ops = pteraTHArrayIndex opsArr opsNum
in Trans s1 ops
|]
<*> [d|
stateTable = $(tableAddrExp stateByteSize
do reverse do outTransReprStates outTrans)
opsNumTable = $(tableAddrExp opsNumByteSize
do reverse do outTransReprOpsNums outTrans)
opsArr = pteraTHArrayFromList $(TH.lift opsNumMax)
$(TH.ListE <$>
do sequence do reverse do outTransReprTransOps outTrans)
|]
where
tokBitSize = Bits.maxBitSize
-- input tokens + special tokens (-1)
do maxTokBound - minTokBound + 1
tokMax = (1 `Bits.shiftL` tokBitSize) - 1
stateBitSize = Bits.maxBitSize do length states - 1
stateByteSize = if
| stateBitSize <= 8 -> 1
| stateBitSize <= 16 -> 2
| otherwise -> 4
stateTableLookupFn = if
| stateBitSize <= 8 -> [e|pteraTHLookupTable8|]
| stateBitSize <= 16 -> [e|pteraTHLookupTable16|]
| otherwise -> [e|pteraTHLookupTable32|]
outTrans = genOutTransRepr tokMax states
opsNumMax = outTransReprNextOpsNum outTrans - 1
opsNumBitSize = Bits.maxBitSize opsNumMax
opsNumByteSize = if
| opsNumBitSize <= 8 -> 1
| opsNumBitSize <= 16 -> 2
| otherwise -> 4
opsNumTableLookupFn = if
| opsNumBitSize <= 8 -> [e|pteraTHLookupTable8|]
| opsNumBitSize <= 16 -> [e|pteraTHLookupTable16|]
| otherwise -> [e|pteraTHLookupTable32|]
genOutTransRepr :: Int -> AlignableArray.T SRB.StateNum SRB.MState -> OutTransRepr
genOutTransRepr tokMax states = foldl'
do \acc0 srbState -> do
let srbTrans = SRB.stateTrans srbState
foldl'
do \acc1 i -> do
let (toSn, opsRepr) = case SymbolicIntMap.lookup i srbTrans of
Just (SRB.TransWithOps ops (SRB.StateNum x)) -> do
(x, OutTransWithOpsRepr ops)
Just (SRB.TransReduce alt) ->
(-1, OutTransReduce alt)
Nothing ->
(-1, OutTransWithOpsRepr [])
let opsMap0 = outTransReprOpsMap acc1
let nextOpsNum0 = outTransReprNextOpsNum acc1
let transOps0 = outTransReprTransOps acc1
let (opsNum, opsMap1, nextOpsNum1, transOps1) =
case HashMap.lookup opsRepr opsMap0 of
Just x ->
( x
, opsMap0
, nextOpsNum0
, transOps0
)
Nothing ->
( nextOpsNum0
, HashMap.insert opsRepr nextOpsNum0 opsMap0
, nextOpsNum0 + 1
, toTransOpsExp opsRepr:transOps0
)
OutTransRepr
{ outTransReprStates = toSn:outTransReprStates acc1
, outTransReprOpsNums = opsNum:outTransReprOpsNums acc1
, outTransReprTransOps = transOps1
, outTransReprNextOpsNum = nextOpsNum1
, outTransReprOpsMap = opsMap1
}
do acc0
do [0..tokMax]
do OutTransRepr
{
outTransReprStates = [],
outTransReprOpsNums = [],
outTransReprTransOps = [],
outTransReprNextOpsNum = 0,
outTransReprOpsMap = HashMap.empty
}
do toList states
tableAddrExp :: Int -> [Int] -> TH.Q TH.Exp
tableAddrExp unitSize ns = pure
do TH.LitE
do TH.StringPrimL
do concatMap
do \sn -> addrCodeUnitsLE unitSize
do fromEnum sn
do ns
-- | Should correspond @pteraTHLookupTable*@
addrCodeUnitsLE :: Int -> Int -> [Word8]
addrCodeUnitsLE unitSize n
| n >= 0 = take unitSize
do map
do \m -> fromInteger do toInteger do mod8bit m
do iterate (`Bits.shiftR` 8) n
| n == -1 = replicate unitSize 0xFF
| otherwise = error "unsupported"
where
mod8bit x = case Bits.bitSizeMaybe n of
Nothing -> x Bits..&. 0xFF
Just bs
| bs <= 8 -> x
| otherwise -> x Bits..&. 0xFF
toTransOpsExp :: OutTransOpsRepr -> TH.Q TH.Exp
toTransOpsExp = \case
OutTransWithOpsRepr ops ->
TH.ListE <$> traverse toTransOpExp ops
OutTransReduce (LAPEG.AltNum alt) ->
[e|[TransOpReduce $(TH.lift alt)]|]
toTransOpExp :: SRB.TransOp -> TH.Q TH.Exp
toTransOpExp = \case
SRB.TransOpEnter (LAPEG.VarNum v) needBack msn -> do
let sn = case msn of
Nothing ->
-1
Just (SRB.StateNum x) ->
x
[e|TransOpEnter $(TH.lift v) $(TH.lift needBack) $(TH.lift sn)|]
SRB.TransOpPushBackpoint (SRB.StateNum s) ->
[e|TransOpPushBackpoint $(TH.lift s)|]
SRB.TransOpHandleNot (LAPEG.AltNum alt) ->
[e|TransOpHandleNot $(TH.lift alt)|]
SRB.TransOpShift ->
[e|TransOpShift|]
data OutTransRepr = OutTransRepr
{
outTransReprStates :: [Int],
outTransReprOpsNums :: [Int],
outTransReprTransOps :: [TH.Q TH.Exp],
outTransReprNextOpsNum :: Int,
outTransReprOpsMap :: HashMap.HashMap OutTransOpsRepr Int
}
data OutTransOpsRepr
= OutTransWithOpsRepr [SRB.TransOp]
| OutTransReduce LAPEG.AltNum
deriving (Eq, Show, Generic)
instance Hashable OutTransOpsRepr
outputParserAltKindFn
:: TH.Name -> AlignableArray.T LAPEG.AltNum (LAPEG.Alt altDoc a)
-> TH.Q TH.Dec
outputParserAltKindFn parserAltKindFnName alts = TH.ValD
do TH.VarP parserAltKindFnName
<$> fmap TH.NormalB [e|\i -> pteraTHArrayIndex table i|]
<*> [d|
table :: PteraTHArray Int AltKind
table = pteraTHArrayFromList $(TH.lift do length alts - 1)
$(TH.ListE <$> traverse altKindExp do toList alts)
|]
where
altKindExp alt = case LAPEG.altKind alt of
AltSeq -> [e|AltSeq|]
AltAnd -> [e|AltAnd|]
AltNot -> [e|AltNot|]
outputParserStateHelpFn
:: TH.Name
-> AlignableArray.T SRB.StateNum SRB.MState
-> TH.Q TH.Dec
outputParserStateHelpFn fnName states = TH.ValD
do TH.VarP fnName
<$> fmap TH.NormalB [e|\i -> pteraTHArrayIndex table i|]
<*> [d|
table :: PteraTHArray Int [(Int, Int)]
table = pteraTHArrayFromList $(TH.lift do length states - 1)
$(TH.ListE <$> traverse stateHelpExp do toList states)
|]
where
stateHelpExp st =
let altItems = SRB.stateAltItems st
in TH.ListE <$> forM altItems \altItem -> do
let altNum :: Int = coerce do SRB.altItemAltNum altItem
pos :: Int = coerce do SRB.altItemCurPos altItem
[e|($(TH.lift altNum) :: Int, $(TH.lift pos) :: Int)|]
outputParserAltHelpFn
:: TH.Name
-> AlignableArray.T LAPEG.AltNum (LAPEG.Alt altDoc a)
-> AlignableArray.T LAPEG.VarNum (PEG.Var StringLit)
-> TH.Q TH.Dec
outputParserAltHelpFn parserAltHelpFnName alts vars = TH.ValD
do TH.VarP parserAltHelpFnName
<$> fmap TH.NormalB [e|\i -> pteraTHArrayIndex table i|]
<*> [d|
table :: PteraTHArray Int (StringLit, Maybe ())
table = pteraTHArrayFromList $(TH.lift do length alts - 1)
$(TH.ListE <$> traverse altHelpExp do toList alts)
|]
where
altHelpExp alt =
let v = AlignableArray.forceIndex vars do LAPEG.altVar alt
in [e|($(TH.lift do PEG.varHelp v), Nothing)|]
outputParserActionFn
:: TH.Name
-> AlignableArray.T LAPEG.AltNum (LAPEG.Alt altHelp (SemanticAction ctx))
-> TH.Q TH.Dec
outputParserActionFn parserActionFnName alts = TH.ValD
do TH.VarP parserActionFnName
<$> fmap TH.NormalB [e|
let arr = pteraTHArrayFromList $(TH.lift do length alts - 1)
$(pure do
TH.ListE
[ TH.VarE do altActionForAltFnName n
| (n, _) <- AlignableArray.assocs alts
]
)
in \i -> pteraTHArrayIndex arr i
|]
<*> traverse
do \(n, alt) -> TH.ValD
do TH.VarP do altActionForAltFnName n
<$> do TH.NormalB <$> altActionExp alt
<*> pure []
do AlignableArray.assocs alts
where
altActionForAltFnName (LAPEG.AltNum n) = TH.mkName
do "pteraTHParserActionForAlt" ++ show n
altActionExp alt = case LAPEG.altAction alt of
Grammar.Action act -> [e|
pteraTHAction $(Syntax.unsafeSemanticAction act)
|]
outputRunnerFn
:: TH.Name -> TH.Name -> TH.Name -> TH.Name -> TH.Name -> TH.Name -> TH.Name -> TH.Name
-> TH.Q TH.Dec
outputRunnerFn
runnerFnName
parserInitialFnName
parserGetTokenNumFnName
parserTransFnName
parserAltKindFnName
parserStateHelpFnName
parserAltHelpFnName
parserActionFnName
= TH.ValD do TH.VarP runnerFnName
<$> fmap TH.NormalB [e|pteraTHUnsafeRunner parser|]
<*> [d|
parser = RunnerParser
$(pure do TH.VarE parserInitialFnName)
$(pure do TH.VarE parserGetTokenNumFnName)
$(pure do TH.VarE parserTransFnName)
$(pure do TH.VarE parserAltKindFnName)
$(pure do TH.VarE parserStateHelpFnName)
$(pure do TH.VarE parserAltHelpFnName)
$(pure do TH.VarE parserActionFnName)
|]