module Text.EBNF.SyntaxTree where {- Syntax Tree module for data type and related functions -} import Text.EBNF.Helper import Text.Parsec.Pos import Data.List as List import Data.Tuple import Data.Ord import Data.Maybe import Data.Aeson.Types import Data.Text (pack) import qualified Data.Foldable as Fold type Identifier = String type Content = String data SyntaxTree = SyntaxTree { identifier :: !Identifier, content :: !Content, position :: !SourcePos, children :: ![SyntaxTree] } deriving (Show, Eq) flattenSyntaxTree :: SyntaxTree -> [SyntaxTree] flattenSyntaxTree st = st:flattened where flattened = concat . map flattenSyntaxTree . children $ st findST :: (SyntaxTree -> Bool) -> SyntaxTree -> Maybe SyntaxTree findST p st | p st = Just st | isJust ch = fromJust ch | otherwise = Nothing where ch = find (\a -> isJust a) . map (findST p) $ children st instance Ord SyntaxTree where compare (SyntaxTree _ _ pos _) (SyntaxTree _ _ pos' _) = compare pos pos' instance ToJSON SyntaxTree where toJSON (SyntaxTree i c p ch) = (object [(pack "identifier") .= i, (pack "content") .= c, (pack "position") .= (toJSON p), (pack "children") .= (map (toJSON) ch)]) instance ToJSON SourcePos where toJSON pos = (object [(pack "name") .= (sourceName pos), (pack "line") .= (sourceLine pos), (pack "col") .= (sourceColumn pos)]) {-| returns a syntax tree similar to the one passed but with the given identifier. -} replaceIdentifier :: Identifier -> SyntaxTree -> SyntaxTree replaceIdentifier i st = (SyntaxTree (i) (content st) (position st) (children st)) {-| returns a syntax tree similar to the one passed but with the given content. -} replaceContent :: Content -> SyntaxTree -> SyntaxTree replaceContent c st = (SyntaxTree (identifier st) (c) (position st) (children st)) {-| returns a syntax tree similar to the one passed but with the given position. -} replacePosition :: SourcePos -> SyntaxTree -> SyntaxTree replacePosition p st = (SyntaxTree (identifier st) (content st) (p) (children st)) {-| returns a syntax tree similar to the one passed but with the given children. -} replaceChildren :: [SyntaxTree] -> SyntaxTree -> SyntaxTree replaceChildren ch st = (SyntaxTree (identifier st) (content st) (position st) (ch)) {-| inserts a syntax tree as a child, list is sorted by source code position -} insert :: SyntaxTree -> SyntaxTree -> SyntaxTree insert st st' = SyntaxTree (identifier st) (content st) (position st) (insertWhere (\a -> a > st') st' (children st)) {-| removes any children of `st` that equal `st'` -} remove :: SyntaxTree -> SyntaxTree -> SyntaxTree remove st st' = SyntaxTree (identifier st) (content st) (position st) (filter (\a -> a /= st') (children st)) {-| the content of the syntax tree is merged with it's parent if the predicate is met. -} collapse :: (SyntaxTree -> Bool) -> SyntaxTree -> SyntaxTree collapse predicate (SyntaxTree i c p ch) = (SyntaxTree i c' p ch') where ch' = map (collapse predicate) ch c' = concat (map content ch') {-| prune will remove any children of `tree` that satisfy `predicate`, recursively -} prune :: (SyntaxTree -> Bool) -> SyntaxTree -> SyntaxTree prune predicate (SyntaxTree i c p ch) = SyntaxTree i c p (map (prune predicate) (filter (not . predicate) ch)) {-| prune any children of `st` whose identifier begins with an underscore. might be useful for preventing syntax trees from being polluted by base cases of single characters by annotating the EBNF definition of single-char base cases such as single letters as `_letters_ = ...` -} pruneUnderscored :: SyntaxTree -> SyntaxTree pruneUnderscored st = prune (\a -> ((head (identifier a)) == '_')) st {- pruneIdentifier :: SyntaxTree -> Identifier -> SyntaxTree pruneIdentifier st identifier = prune (\s -> ) st -} isTerminal :: SyntaxTree -> Bool isTerminal (SyntaxTree _ _ _ []) = True isTerminal (SyntaxTree _ _ _ _) = False nulltree = SyntaxTree "" "" (newPos "" 0 0) []