hydra-0.1.0: src/main/haskell/Hydra/Util/GrammarToModule.hs
module Hydra.Util.GrammarToModule where
import Hydra.All
import Hydra.Impl.Haskell.Dsl.Types as Types
import Hydra.Impl.Haskell.Dsl.Terms as Terms
import Hydra.Impl.Haskell.Dsl.Standard
import Hydra.CoreEncoding
import qualified Data.List as L
import qualified Data.Map as M
import qualified Data.Maybe as Y
grammarToModule :: Namespace -> Grammar -> Maybe String -> Module Meta
grammarToModule ns (Grammar prods) desc = Module ns elements [] desc
where
elements = pairToElement <$> L.concat (L.zipWith (makeElements False) (capitalize . fst <$> prodPairs) (snd <$> prodPairs))
where
prodPairs = (\(Production (Symbol s) pat) -> (s, pat)) <$> prods
pairToElement (lname, typ) = Element (toName lname) (Terms.element _Type) (encodeType typ)
toName lname = fromQname ns lname
findNames pats = L.reverse $ fst (L.foldl nextName ([], M.empty) pats)
where
nextName (names, nameMap) pat = (nn:names, M.insert rn ni nameMap)
where
rn = rawName pat
(nn, ni) = case M.lookup rn nameMap of
Nothing -> (rn, 1)
Just i -> (rn ++ show (i+1), i+1)
rawName pat = case pat of
PatternNil -> "none"
PatternIgnored _ -> "ignored"
PatternLabeled (LabeledPattern (Label l) _) -> l
PatternConstant (Constant c) -> decapitalize $ withCharacterAliases c
PatternRegex _ -> "regex"
PatternNonterminal (Symbol s) -> decapitalize s
PatternSequence _ -> "sequence"
PatternAlternatives _ -> "alts"
PatternOption p -> decapitalize (rawName p)
PatternStar p -> "listOf" ++ capitalize (rawName p)
PatternPlus p -> "listOf" ++ capitalize (rawName p)
isComplex pat = case pat of
PatternLabeled (LabeledPattern _ p) -> isComplex p
PatternSequence _ -> True
PatternAlternatives _ -> True
_ -> False
makeElements omitTrivial lname pat = forPat pat
where
forPat pat = case pat of
PatternNil -> trivial
PatternIgnored _ -> []
PatternLabeled (LabeledPattern (Label _) p) -> forPat p
PatternConstant _ -> trivial
PatternRegex _ -> [(lname, Types.string)]
PatternNonterminal (Symbol other) -> [(lname, Types.nominal $ toName other)]
PatternSequence pats -> forRecordOrUnion True Types.record pats
PatternAlternatives pats -> forRecordOrUnion False Types.union pats
PatternOption p -> mod "Option" Types.optional p
PatternStar p -> mod "Elmt" Types.list p
PatternPlus p -> mod "Elmt" nonemptyList p
trivial = if omitTrivial then [] else [(lname, Types.unit)]
forRecordOrUnion isRecord c pats = (lname, c fields):els
where
fieldPairs = Y.catMaybes $ L.zipWith (toField isRecord) (findNames pats) pats
fields = fst <$> fieldPairs
els = L.concat (snd <$> fieldPairs)
toField isRecord n p = if ignore
then Nothing
else Just $ descend n f2 p
where
f2 ((lname, typ):rest) = (FieldType (FieldName n) typ, rest)
ignore = if isRecord
then case p of
PatternConstant _ -> True
_ -> False
else False
mod n f p = descend n f2 p
where
f2 ((lname, typ):rest) = (lname, f typ):rest
descend n f p = f $ if isComplex p
then (lname, Types.nominal (toName $ fst $ L.head cpairs)):cpairs
else if L.null cpairs
then [(lname, Types.unit)]
else (lname, snd (L.head cpairs)):L.tail cpairs
where
cpairs = makeElements False (childName lname n) p
childName lname n = lname ++ "." ++ capitalize n