cparsing-0.1.0.0: MiniC/Parser/Base.hs
{-# LANGUAGE FlexibleContexts, FlexibleInstances, MultiParamTypeClasses
, OverlappingInstances, TupleSections, ScopedTypeVariables #-}
-- | Parser combinators to handle additional information assigned to AST nodes.
-- Has functions to handle source information and semantic information.
module MiniC.Parser.Base where
import MiniC.Representation
import MiniC.SourceNotation
import MiniC.SymbolTable
import MiniC.Semantics
import MiniC.Helpers
import MiniC.Instances
import MiniC.AST
import SourceCode.ASTNode
import SourceCode.ASTElems
import SourceCode.SourceTree
import SourceCode.SourceInfo
import Text.Preprocess.Rewrites
import Data.Maybe
import qualified Data.Map as M
import Data.Function
import Data.List
import Text.Parsec
import Text.Parsec.PosOps
import Text.Parsec.ExtraCombinators
import Control.Monad.Reader
import Control.Monad.State
import Control.Applicative hiding ((<|>), many)
import Control.Lens
import Debug.Trace
-- * C parser types
type CParser a = ParsecT CStream (CUserState BI) CMonad a
type CStream = String
type CMonad = Reader RewriteSet
type SymbolEntryBI = SymbolEntry BI
-- Overrides the normal MonadState instance for a parser that relies on an underlying
-- state monad. But we need to use user state, because it resets when parsing one
-- alternative fails.
instance MonadState (CUserState a) (ParsecT CStream (CUserState a) CMonad) where
get = getState
put = setState
-- * Handling source info
-- | Adds node information to a parsed AST node.
-- Composes the source template by cutting out children nodes.
withInfo :: CParser (BI -> b) -> CParser b
withInfo p = do inp <- getInput
(res, sr) <- captureSourceRange p
return $ res (NodeInfo (BasicInfo sr inp) emptySemaInfo)
-- | Orders the parsed AST node to compose it's node information from it's children.
inheritInfo :: CParser (BI -> b BI) -> CParser (b BI)
inheritInfo p = p <*> pure noNodeInfo
-- | Replaces the node information in a node with the current node information.
-- Discards node information composed by 'unifyInfo'
withNewInfo :: (ASTNode a BI) => CParser (a BI) -> CParser (a BI)
withNewInfo p = withInfo (p >>= \res -> return $ \inf -> setInfo inf res)
-- * Handling semantic info
-- | Nodes that represent some kind of declaration that can be put into the symbol table
class CanBeRegistered node where
-- | Gets the namespace of the declaration
nameSpace :: node ni -> NameSpace
nameSpace _ = NormalNS
-- | Creates a symbol table entry from qualified name and declaration
createEntry :: SourceInfo ni => QualifiedName -> node ni -> SymbolEntry ni
-- | Adds a declaration to the symbol table. Solves redeclaration.
registerDeclaration :: forall node . (Show (node BI), Id node BI, ASTNode node BI, CanBeRegistered node) => node BI -> CParser (node BI)
registerDeclaration decl
| Just name <- view identStr <$> getId decl
= do qualName <- inScope (nameSpace decl) name
let decl' = decl & info.semanticInfo.declQualName .~ Just qualName
symbolTable.symbolMap %= M.insert qualName (createEntry qualName decl')
return decl'
-- when the declaration has no name (for example: anonymous parameter) do not register
registerDeclaration decl
= return decl
instance CanBeRegistered VariableDeclaration where
createEntry _ (VariableDeclaration ss qt asm init _ info)
= VariableEntry (astGetList ss) qt (asm ^. astMaybe) (init ^. astMaybe) info
instance CanBeRegistered (ASTTriple VariableDefinition (ASTList StorageSpecifier) QualifiedType) where
createEntry _ (ASTTriple (VariableDefinition qual asm init info) ss qt _)
= VariableEntry (astGetList ss) (applyQualTo qual qt noNodeInfo) (asm ^. astMaybe) (init ^. astMaybe) info
instance CanBeRegistered ParameterDeclaration where
createEntry _ = ParameterEntry
instance CanBeRegistered FunctionDeclaration where
createEntry _ = FunctionEntry
instance CanBeRegistered TypeDefinition where
nameSpace td = case td ^. typeDefType of TypeDef {} -> NormalNS
_ -> ComplexNS
createEntry _ = TypeEntry
instance CanBeRegistered Variant where
createEntry _ = VariantEntry
-- | Parses the given declaration in an inner scope
insideScope :: String -> CParser a -> CParser a
insideScope s p = do currentScope %= \(Scope sx) -> Scope (s:sx)
currentIds %= (0:)
res <- p
currentScope %= \(Scope sx) -> Scope (tail sx)
currentIds %= tail
return res
-- | Parses the given declaration inside an anonym scope
-- (for example: anonym structs, compound stmts)
insideAnonymScope :: CParser a -> CParser a
insideAnonymScope p
= do let currentId = currentIds . ix 0
id <- gets (head . view currentIds)
currentId += 1
insideScope (show id) p
insideScope' :: Maybe (Ident ni) -> CParser a -> CParser a
insideScope' (Just id) = insideScope (view identStr id)
insideScope' Nothing = insideAnonymScope
-- | Gets the qualified name of an id in current scope
inScope :: NameSpace -> String -> CParser QualifiedName
inScope ns s = QualifiedName ns <$> gets (view currentScope) <*> pure (genScalarQual s)
where genScalarQual s = Scalar (ASTJust (Ident s (noNodeInfo & sourceInfo.niTemplate .~ Just [TextElem s])))
(noNodeInfo & sourceInfo.niTemplate .~ Just [NodeElem (NthChildOf 0 (NthChildOf 0 Current))])
-- | Looks up a name in the symbol table
lookupName' :: NameSpace -> String -> CParser (Maybe (QualifiedName, SymbolEntryBI))
lookupName' ns s = do qname <- inScope ns s
gets (lookupSymbol qname . view symbolTable)
lookupName = lookupName' NormalNS
lookupNameBoth :: String -> CParser (Maybe (QualifiedName, SymbolEntryBI))
lookupNameBoth s = mplus <$> lookupName' NormalNS s <*> lookupName' ComplexNS s
-- | Checks that a given name represents a type
checkIsTypeSymbol :: IdentBI -> CParser IdentBI
checkIsTypeSymbol res
= do let simpleName = res ^. identStr
entry <- lookupNameBoth simpleName
case entry of
Just (name, TypeEntry {}) -> return (res & identInfo.semanticInfo.referenceQualName .~ Just name)
_ -> fail $ "Unknown type name: " ++ simpleName
-- | Checks that the given name can appear in an expression
checkIsExpression :: IdentBI -> CParser IdentBI
checkIsExpression res
= do let simpleName = res ^. identStr
entry <- lookupName simpleName
case entry of
Nothing -> fail $ "Unknown name: " ++ simpleName
Just (name, TypeEntry {}) -> fail $ "Value expected, type found: " ++ simpleName
Just (name, _) -> return (res & identInfo.semanticInfo.referenceQualName .~ Just name)
debugSymbolTable :: CParser ()
debugSymbolTable
= gets (show . (each._2 %~ take 40 . show) . M.assocs . view (symbolTable.symbolMap))
>>= flip trace (return ())