packages feed

rtk-0.12: Syntax.hs

-- | The normalized-grammar data types: 'NormalGrammar' and friends, the
-- representation the code generators ('GenX', 'GenY', 'GenAST', 'GenQ')
-- consume, plus the pure helpers over them.
--
-- The parsed-grammar half of the pipeline has no types of its own anymore:
-- since task 8c the front half computes directly over the GENERATED AST
-- ('GrammarParser''s @Grammar@\/@Rule@\/@Clause@, compiled from the
-- @test/golden/grammar/@ snapshot; helpers in "Frontend"). The historic
-- @InitialGrammar@\/@IRule@\/@IClause@ types are retired. The one place the
-- generated AST threads into THIS module is 'LClause': lexical rules keep
-- their clause unnormalized so 'GenX' can translate it to an Alex
-- specification.
module Syntax where

import Data.Char (isLower)
import Data.Data (Data)
import qualified Data.Map as M
import qualified Data.Set as S

import qualified GrammarParser as GP

type ConstructorName = String

type ID = String

-- | The first line of every generated artifact (lexer spec, parser spec,
-- quasi-quoter module). The banner names the grammar, not the grammar file:
-- a file path would make the generated output - and with it every golden
-- snapshot - depend on where the grammar happens to live. It also carries
-- no rtk version: a version in the banner would couple every release bump
-- to a full golden churn even when the generated code is byte-identical.
provenanceBanner :: String -> String
provenanceBanner name =
    "-- Generated by RTK from grammar '" ++ name ++ "'. Do not edit by hand."

data GrammarInfo =
  GrammarInfo
  {
     getStartRuleName :: Maybe String,
     getRuleToStartInfo :: M.Map String String,
     getNameCounter :: Int,
     getProxyRules :: S.Set String
  }
  deriving (Eq, Show, Data)

data AntiRule = AntiRule { arTypeName :: ID,
                           arQQName :: ID,
                           arConstr :: ID ,
                           arIsList :: Bool
                         }
                     deriving (Eq, Show, Data)

data NormalGrammar = NormalGrammar { getNGrammarName :: String,
                                     getSyntaxRuleGroups :: [SyntaxRuleGroup],
                                     getLexicalRules :: [LexicalRule],
                                     getAntiRules :: [AntiRule],
                                     getShortcuts :: [(String, String)],
                                     getNImports :: String,
                                     getGrammarInfo :: GrammarInfo }
                     deriving (Eq, Show, Data)

data SyntaxRuleGroup = SyntaxRuleGroup { getSDataTypeName :: ID,
                                         getSRules :: [SyntaxRule]}
                       deriving (Eq, Show, Data)

data SyntaxRule = SyntaxRule { getSRuleName :: ID,
                               getSClause :: SyntaxTopClause}
                       deriving (Eq, Show, Data)

data STManyOp = STStar
              | STPlus
                deriving (Eq, Show, Data)

data STSeq = STSeq ConstructorName [SyntaxSimpleClause]
             deriving (Eq, Show, Data)

data SyntaxTopClause = STMany STManyOp SyntaxSimpleClause (Maybe SyntaxSimpleClause)
                     | STOpt SyntaxSimpleClause
                     | STAltOfSeq { getAltOfSeq :: [STSeq] } -- alternative of sequences
                       deriving (Eq, Show, Data)

data SyntaxSimpleClause = SSId ID
                        | SSLifted ID
                        | SSIgnore ID
                          deriving (Eq, Show, Data)

data LexicalRule = LexicalRule { getLRuleDataType :: String,
                                 getLRuleFunc :: String,
                                 getLRuleName :: String,
                                 getLClause :: LClause}
                   | MacroRule { getLRuleName :: String, getLClause :: LClause}
                   deriving (Eq, Show, Data)

-- | A lexical rule's clause stays unnormalized all the way to 'GenX': it is
-- the generated front end's clause type (token text already cleaned by
-- @Frontend.cleanGrammarTokens@). Synthesized rules (keyword tokens, QQ
-- splice tokens, start-wrapper dummies) build their clauses with
-- 'GP.rtkNoPos' positions.
type LClause = GP.Clause

isLexicalRule :: String -> Bool
isLexicalRule [] = False
isLexicalRule (c:_) = isLower c

filterProxyRules :: S.Set ID -> [SyntaxRuleGroup] -> [SyntaxRuleGroup]
filterProxyRules proxyRules rules = filter (((flip S.notMember) proxyRules) . getSDataTypeName) rules