module ASTUtil where {----------------------------- This is a collection of utility functions I've made. Most of them are nothing special, but they do make things easier and cleaner in the rest of the code. Many of them imitate more general functions specialized for AST nodes. See ASTTranslate.hs for more. Originally created by Sage LaTorra for Summer of Code 2006. ------------------------------} import ASTDefinition {-Removes a given character from a string.-} removeChar :: Char -> String -> String removeChar _ [] = [] removeChar todrop instr = case ((head instr)==todrop) of True -> (tail instr) False -> (head instr):(removeChar todrop (tail instr)) --Makes a list of strings from a list of lists of strings. makeList :: [[String]] -> [String] makeList [] = [] makeList alist = (head alist)++(makeList (tail alist)) --Take a string and return a list of strings with each element being a word in that string. --The second argument is what's already been collected (allowing you to start the list of words with your own list, if need be) --To just get the contents of the string broken into words, just call makeWords instring [""] makeWords :: String -> [String] -> [String] makeWords [] curout = curout makeWords inst curout = if ((head inst)==' ') then (makeWords (drop 2 inst) ([(head (tail inst))]:curout)) else (makeWords (tail inst) (((head curout)++[(head inst)]):(tail curout))) --drop the leading character if it matches the first argument of the call dropLeadingChar :: Char -> String -> String dropLeadingChar _ [] = [] dropLeadingChar todrop astring = if ((head astring)==todrop) then (tail astring) else astring --Returns the first instance of a given type of node from a list of nodes. extractNodetype :: P5AST -> [P5AST] -> P5AST extractNodetype _ [] = (AbstractNode "UnknownAbs" []) extractNodetype node nlist = if (matchOnType node (head nlist)) then (head nlist) else (extractNodetype node (tail nlist)) extractNodetypeSkip :: Int -> P5AST -> [P5AST] -> P5AST extractNodetypeSkip 0 node nlist = extractNodetype node nlist extractNodetypeSkip _ _ [] = (AbstractNode "UnknownAbs" []) extractNodetypeSkip skips node nlist = if (matchOnType node (head nlist)) then (extractNodetypeSkip (skips-1) node (tail nlist)) else (extractNodetypeSkip skips node (tail nlist)) --Get just the kids of a given node ([] in case of literal node). extractKids :: P5AST -> [P5AST] extractKids (AbstractNode atype kids) = kids extractKids (Heredoc doc start end kids) = kids extractKids (LiteralNode _ _ _) = [] --When called on the kids of a node that make up a function call, this function extracts the second arguement (the first thing after a comma) getSecondArg :: [P5AST] -> P5AST getSecondArg [] = (AbstractNode "UnknownAbs" []) getSecondArg list = if (matchWithoutEnc (head list) (AbstractNode "listelem" [])) then (dropComma (head list)) else (getSecondArg (tail list)) --Drops the comma from an argument to a function (since the literal comma gets stuck in with with argument) dropComma :: P5AST -> P5AST dropComma (AbstractNode "listelem" kids) = (head (tail kids)) dropComma (AbstractNode atype kids) = (AbstractNode atype kids) dropComma (Heredoc doc start end kids) = (Heredoc doc start end kids) dropComma (LiteralNode atype enc uni) = (LiteralNode atype enc uni) --Extracts the first text node (based only on type, not other fields) extractText :: [P5AST] -> P5AST extractText [] = (LiteralNode "text" "1" "") extractText kids = if ((getLType (head kids))=="text") then (head kids) else (extractText (tail kids)) --Match based only on subtype matchOnType :: P5AST -> P5AST -> Bool matchOnType (AbstractNode atype1 kids1) (AbstractNode atype2 kids2) = if (atype1==atype2) then True else False matchOnType (LiteralNode atype1 _ _) (LiteralNode atype2 _ _) = if (atype1==atype2) then True else False matchOnType _ _ = False --Get the type of a literal node getLType :: P5AST -> String getLType (AbstractNode sometype kids) = "UnknownLit" getLType (Heredoc _ start end kids) = "UnknownLit" getLType (LiteralNode sometype enc uni) = sometype --Join a list of lists of nodes into a list of nodes join :: [[P5AST]] -> [P5AST] join [] = [] join lists = (head lists)++(join (tail lists)) --Turn a list of strings into a single string joinString :: [String] -> String joinString [] = [] joinString strs = (head strs)++(joinString (tail strs)) --Extract the uni field from a node extractUni :: P5AST -> String extractUni (LiteralNode _ _ uni) = uni extractUni (AbstractNode _ _) = "" extractUni (Heredoc _ _ _ _) = "" {-A simple search convenience function, returns true when the given node is in the list matches based on type (Abstract or Literal) subtype (Junk, Op_leave, Sigil, etc.) and, in the case of literal nodes, the uni part of the node.-} isIn :: P5AST -> [P5AST] -> Bool isIn _ [] = False isIn node list = if (matchWithoutEnc (head list) node) then True else (isIn node (tail list)) {-Searches for the given list of nodes in another list allows any number of nodes between the nodes being searched for matches like isIn Useage: isInOrder [NodesBeingSearchedFor] [SearchTarget]-} isInOrder :: [P5AST] -> [P5AST] -> Bool isInOrder [] [] = True isInOrder _ [] = False isInOrder [] _ = True isInOrder nodes list = if (matchWithoutEnc (head list) (head nodes)) then (isInOrder (tail nodes) (tail list)) else (isInOrder nodes (tail list)) --Like isInOrder, except the nodes have to follow directly after one another. isInSequence :: [P5AST] -> [P5AST] -> Bool isInSequence _ [] = True isInSequence [] _ = False isInSequence nodes list = if (allMatch nodes list) then True else (isInSequence (tail nodes) list) {-This function is just like isInSequence, but it only matches on type -} isInSequenceType :: [P5AST] -> [P5AST] -> Bool isInSequenceType _ [] = True isInSequenceType [] _ = False isInSequenceType nodes list = if (allMatchType nodes list) then True else (isInSequence (tail nodes) list) {-Just like allMatch, but only matching on type-} allMatchType :: [P5AST] -> [P5AST] -> Bool allMatchType [] [] = True allMatchType _ [] = False allMatchType [] _ = False allMatchType list1 list2 = case [(matchOnType (head list1) (LiteralNode "junk" "" "")), (matchOnType (head list2) (LiteralNode "junk" "" ""))] of [True, True] -> (allMatch (tail list1) (tail list2)) [True, False] -> (allMatch (tail list1) list2) [False, True] -> (allMatch list1 (tail list2)) [False, False] -> if (matchOnType (head list1) (head list2)) then (allMatch (tail list1) (tail list2)) else False --Make sure two lists match allMatch :: [P5AST] -> [P5AST] -> Bool allMatch [] [] = True allMatch _ [] = False allMatch [] _ = False allMatch list1 list2 = case [(matchOnType (head list1) (LiteralNode "junk" "" "")), (matchOnType (head list2) (LiteralNode "junk" "" ""))] of [True, True] -> (allMatch (tail list1) (tail list2)) [True, False] -> (allMatch (tail list1) list2) [False, True] -> (allMatch list1 (tail list2)) [False, False] -> if (matchWithoutEnc (head list1) (head list2)) then (allMatch (tail list1) (tail list2)) else False {-Matches nodes based on type (Heredoc, Abstract or Literal) subtype (Junk, Op_leave, PNothing, etc) and (in the case of literal nodes) on the uni field. Used in the above search functions.-} matchWithoutEnc :: P5AST -> P5AST -> Bool matchWithoutEnc (LiteralNode type1 _ uni1) (LiteralNode type2 _ uni2) = if (and [(uni1==uni2), (type1==type2)]) then True else False matchWithoutEnc (AbstractNode type1 kids1) (AbstractNode type2 kids2) = if (type1 == type2) then True else False matchWithoutEnc (Heredoc doc1 start1 end1 kids1) (Heredoc doc2 start2 end2 kids2) = if (and [(matchWithoutEnc start1 start2),(matchWithoutEnc end1 end2), doc1==doc2]) then True else False matchWithoutEnc _ _ = False