imparse 0.0.0.2 → 0.0.0.3
raw patch · 8 files changed
+232/−175 lines, 8 filesdep +indentsdep +parsecPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependencies added: indents, parsec
API changes (from Hackage documentation)
- Text.Imparse.AbstractSyntax: instance Annotated Choice
- Text.Imparse.AbstractSyntax: instance Annotated Choices
- Text.Imparse.AbstractSyntax: instance Annotated Element
- Text.Imparse.AbstractSyntax: instance Annotated Parser
- Text.Imparse.AbstractSyntax: instance Annotated Production
- Text.Imparse.Analysis: ChoiceIndentedSuffix :: Tag
+ Text.Imparse.AbstractSyntax: instance Annotate Choice
+ Text.Imparse.AbstractSyntax: instance Annotate Choices
+ Text.Imparse.AbstractSyntax: instance Annotate Element
+ Text.Imparse.AbstractSyntax: instance Annotate Parser
+ Text.Imparse.AbstractSyntax: instance Annotate Production
+ Text.Imparse.AbstractSyntax: isOp :: String -> Bool
+ Text.Imparse.Analysis: allOps :: Parser Analysis -> [String]
+ Text.Imparse.Compile.Haskell: State :: StateExtensionFresh -> StateExtensionString -> State
+ Text.Imparse.Compile.Haskell: data State
+ Text.Imparse.Compile.Haskell: instance HasFresh State
+ Text.Imparse.Compile.Haskell: instance HasString State
+ Text.Imparse.Compile.Haskell: instance StateExtension State
- Text.Imparse.Analysis: characterization :: Annotated a => a Analysis -> Characterization
+ Text.Imparse.Analysis: characterization :: Annotate a => a Analysis -> Characterization
- Text.Imparse.Analysis: initialNonTerminals :: Annotated a => a Analysis -> InitialNonTerminals
+ Text.Imparse.Analysis: initialNonTerminals :: Annotate a => a Analysis -> InitialNonTerminals
- Text.Imparse.Analysis: initialTerminals :: Annotated a => a Analysis -> InitialTerminals
+ Text.Imparse.Analysis: initialTerminals :: Annotate a => a Analysis -> InitialTerminals
- Text.Imparse.Analysis: reachable :: Annotated a => a Analysis -> ReachableNonTerminals
+ Text.Imparse.Analysis: reachable :: Annotate a => a Analysis -> ReachableNonTerminals
- Text.Imparse.Analysis: tags :: Annotated a => a Analysis -> [Tag]
+ Text.Imparse.Analysis: tags :: Annotate a => a Analysis -> [Tag]
- Text.Imparse.Compile.Haskell: toAbstractSyntax :: String -> Parser a -> Compile String ()
+ Text.Imparse.Compile.Haskell: toAbstractSyntax :: String -> Parser a -> Compile State ()
- Text.Imparse.Compile.Haskell: toDatatype :: Parser a -> Compile String ()
+ Text.Imparse.Compile.Haskell: toDatatype :: Parser a -> Compile State ()
- Text.Imparse.Compile.Haskell: toParsec :: String -> Parser Analysis -> Compile String ()
+ Text.Imparse.Compile.Haskell: toParsec :: String -> Parser Analysis -> Compile State ()
- Text.Imparse.Compile.Haskell: toParsecDefs :: Parser Analysis -> Compile String ()
+ Text.Imparse.Compile.Haskell: toParsecDefs :: Parser Analysis -> Compile State ()
- Text.Imparse.Compile.Haskell: toReportFuns :: Parser a -> Compile String ()
+ Text.Imparse.Compile.Haskell: toReportFuns :: Parser a -> Compile State ()
- Text.Imparse.Compile.Haskell: toRichReport :: String -> Parser a -> Compile String ()
+ Text.Imparse.Compile.Haskell: toRichReport :: String -> Parser a -> Compile State ()
Files
- Main.hs +8/−11
- Text/Imparse.hs +29/−17
- Text/Imparse/AbstractSyntax.hs +30/−22
- Text/Imparse/Analysis.hs +29/−30
- Text/Imparse/Compile/Haskell.hs +94/−77
- Text/Imparse/Parse.hs +27/−9
- Text/Imparse/Report.hs +8/−6
- imparse.cabal +7/−3
Main.hs view
@@ -1,20 +1,17 @@ ---------------------------------------------------------------- -- --- Imparse --- Cross-platform/-language parser generator. --- --- Main.hs --- Haskell executable wrapper for the Imparse parser parser. --- +-- | Imparse +-- Cross-platform and -language parser generator. -- --- * Usage: +-- @Main.hs@ -- --- imparse "path/file.p" +-- Main module for the Haskell implementation of the Imparse +-- parser. Compiled into a Haskell executable wrapper for the +-- Imparse parser definition parser. -- ---------------------------------------------------------------- --- Main module for the Haskell implementation of the --- Imparse parser. +-- module Main where @@ -25,7 +22,7 @@ import Text.Imparse (cmd) ---------------------------------------------------------------- --- The main function. +-- | The main function. main :: IO () main =
Text/Imparse.hs view
@@ -1,28 +1,36 @@ ---------------------------------------------------------------- -- --- Imparse --- Cross-platform/-language parser generator. +-- | Imparse +-- Cross-platform and -language parser generator. -- --- Text/Imparse.hs +-- @Text\/Imparse.hs@ +-- -- Haskell implementation of the Imparse parser parser. +-- Useful functions for the imparser parser generator. -- ---------------------------------------------------------------- --- Useful functions for the imparser parser generator. +-- module Text.Imparse where import Data.Char (toUpper) import Data.List (splitAt, elemIndex) -import System.Directory (createDirectory, removeDirectoryRecursive, doesDirectoryExist, doesFileExist, removeFile) +import System.Directory ( + createDirectory, + removeDirectoryRecursive, + doesDirectoryExist, + doesFileExist, + removeFile + ) import System.Environment (getArgs) import System.IO () import Prelude hiding (catch) import System.IO.Error hiding (catch) import Control.Exception (throwIO, catch) -import qualified Control.Compilation.Compile as C +import Control.Compilation.String (compiled) import qualified Text.UxADT as U (uxadt, javaScriptModule) import Text.RichReports (report) import Text.Ascetic.HTML (html) @@ -34,8 +42,8 @@ import Text.Imparse.Compile.Haskell ---------------------------------------------------------------- --- The target of the output, as specified by the command-line --- arguments. +-- | The target of the output, as specified by the command-line +-- arguments. type HaskellModulePrefix = String @@ -53,8 +61,8 @@ ot:ots -> emitHaskell ots ---------------------------------------------------------------- --- Take a file path in the form of a string, and try to parse --- the contents of the file into abstract syntax. +-- | Take a file path in the form of a string, and try to parse +-- the contents of the file into abstract syntax. parseShow :: String -> IO () parseShow fname = @@ -76,8 +84,8 @@ } ---------------------------------------------------------------- --- Take a file path in the form of a string, read it, and --- process it as specified by the command line. +-- | Take a file path in the form of a string, read it, and +-- process it as specified by the command line. nothing :: IO () nothing = return () @@ -140,10 +148,11 @@ Nothing -> do nothing Just pre -> do moduleName <- return $ (\(c:cs) -> toUpper c : cs) fname - writeAndPutStr (fdir ++ "AbstractSyntax") "hs" (C.extract (toAbstractSyntax pre parser) "") - writeAndPutStr (fdir ++ "Report") "hs" (C.extract (toRichReport pre parser) "") - writeAndPutStr (fdir ++ "Parse") "hs" (C.extract (toParsec pre parser) "") - + putStr $ " Emitting Haskell implementation of \"" ++ moduleName ++ "\"...\n" + writeAndPutStr (fdir ++ "AbstractSyntax") "hs" (compiled (toAbstractSyntax pre parser)) + writeAndPutStr (fdir ++ "Report") "hs" (compiled (toRichReport pre parser)) + writeAndPutStr (fdir ++ "Parse") "hs" (compiled (toParsec pre parser)) + putStr "\n" } } @@ -152,7 +161,10 @@ "\nUsage:\n\n" ++ " imparse [optional flags] path/file.p\n\n" ++ "Flags:\n\n" - ++ "" + ++ " -html\n" + ++ " Emit HTML report containing parser static analysis results.\n\n" + ++ " -hs \"Name.Prefix.For.Modules\"\n" + ++ " Emit Haskell implementations of abstract syntax, parser, and\n report generator with specified module name prefix.\n\n" cmd :: [OutputTarget] -> [String] -> IO () cmd [] [] = usage
Text/Imparse/AbstractSyntax.hs view
@@ -1,8 +1,10 @@ ---------------------------------------------------------------- -- --- Imparse +-- | Imparse +-- Cross-platform and -language parser generator. -- --- Text/Imparse/AbstractSyntax.hs +-- @Text\/Imparse\/AbstractSyntax.hs@ +-- -- Data structure for Imparse parser definitions. -- @@ -12,15 +14,18 @@ module Text.Imparse.AbstractSyntax where -import Data.String.Utils (join) +import Data.Char (isAlpha) import Data.List (nub) +import Data.String.Utils (join) import qualified Text.RichReports as R import qualified Text.UxADT as U -import qualified StaticAnalysis.All as A +import qualified StaticAnalysis.Annotate as A +import qualified StaticAnalysis.Analyze as A +import qualified StaticAnalysis.Analysis as A ---------------------------------------------------------------- --- Parser data structure. +-- | Parser data structure. type Import = String type NonTerminal = String @@ -76,30 +81,30 @@ deriving Eq ---------------------------------------------------------------- --- Static analysis annotation setting and retrieval. +-- | Static analysis annotation setting and retrieval. -instance A.Annotated Parser where - annotate (Parser _ ms ps) a = Parser a ms ps +instance A.Annotate Parser where + annotate a (Parser _ ms ps) = Parser a ms ps annotation (Parser a ms ps) = a -instance A.Annotated Production where - annotate (Production _ e css) a = Production a e css +instance A.Annotate Production where + annotate a (Production _ e css) = Production a e css annotation (Production a _ _) = a -instance A.Annotated Choices where - annotate (Choices _ cs) a = Choices a cs +instance A.Annotate Choices where + annotate a (Choices _ cs) = Choices a cs annotation (Choices a _) = a -instance A.Annotated Choice where - annotate (Choice _ mc asc es) a = Choice a mc asc es +instance A.Annotate Choice where + annotate a (Choice _ mc asc es) = Choice a mc asc es annotation (Choice a _ _ _) = a -instance A.Annotated Element where - annotate e a = case e of +instance A.Annotate Element where + annotate a e = case e of NonTerminal _ e -> NonTerminal a e - Many e ms -> Many (A.annotate e a) ms - May e -> May (A.annotate e a) - Indented w e -> Indented w $ A.annotate e a + Many e ms -> Many (A.annotate a e) ms + May e -> May (A.annotate a e) + Indented w e -> Indented w $ A.annotate a e _ -> e annotation e = case e of NonTerminal a _ -> a @@ -109,8 +114,11 @@ _ -> A.unanalyzed ---------------------------------------------------------------- --- Functions for inspecting parser instances. +-- | Functions for inspecting parser instances. +isOp :: String -> Bool +isOp s = not (s `elem` ["(",")","[","]","{","}"]) && not (and (map isAlpha s)) + isData :: Element a -> Bool isData e = case e of NonTerminal _ _ -> True @@ -135,7 +143,7 @@ productionNonTerminal (Production _ nt _) = nt ---------------------------------------------------------------- --- Functions for converting a parser into a UXADT instance string. +-- | Functions for converting a parser into a UXADT instance string. instance U.ToUxADT (Parser a) where uxadt (p@(Parser _ _ ps)) = @@ -174,7 +182,7 @@ RegExp r -> U.C "RegExp" [U.S r] ---------------------------------------------------------------- --- Functions for converting a parser into an ASCII string. +-- | Functions for converting a parser into an ASCII string. instance Show (Parser a) where show (Parser _ _ ps) = join "\n\n" (map show ps) ++ "\n"
Text/Imparse/Analysis.hs view
@@ -1,13 +1,15 @@ ---------------------------------------------------------------- -- --- Imparse +-- | Imparse +-- Cross-platform and -language parser generator. -- --- Text/Imparse/Analysis.hs +-- @Text\/Imparse\/Analysis.hs@ +-- -- Analyzer/validator for Imparse parsers. -- ---------------------------------------------------------------- --- +-- module Text.Imparse.Analysis where @@ -23,8 +25,8 @@ import Text.Imparse.Report ---------------------------------------------------------------- --- Analysis data structure, instance declarations, accessors, --- and mutators. +-- | Analysis data structure, instance declarations, accessors, +-- and mutators. type InitialNonTerminals = [A.NonTerminal] type InitialTerminals = [A.Terminal] @@ -59,7 +61,6 @@ | ChoiceRecursive | ChoiceRecursivePrefix | ChoiceRecursiveInfix - | ChoiceIndentedSuffix | ChoiceConstructorDuplicate | NonTerminalUnbound deriving (Eq, Show) @@ -71,19 +72,19 @@ tag a ts' = case a of Analyzed ts c -> Analyzed (nub $ ts' ++ ts) c -tags :: S.Annotated a => a Analysis -> [Tag] +tags :: S.Annotate a => a Analysis -> [Tag] tags d = let Analyzed ts _ = S.annotation d in ts -initialTerminals :: S.Annotated a => a Analysis -> InitialTerminals +initialTerminals :: S.Annotate a => a Analysis -> InitialTerminals initialTerminals d = let Analyzed _ (ts, _, _) = S.annotation d in ts -initialNonTerminals :: S.Annotated a => a Analysis -> InitialNonTerminals +initialNonTerminals :: S.Annotate a => a Analysis -> InitialNonTerminals initialNonTerminals d = let Analyzed _ (_, ns, _) = S.annotation d in ns -reachable :: S.Annotated a => a Analysis -> ReachableNonTerminals +reachable :: S.Annotate a => a Analysis -> ReachableNonTerminals reachable d = let Analyzed _ (_, _, rns) = S.annotation d in rns -characterization :: S.Annotated a => a Analysis -> Characterization +characterization :: S.Annotate a => a Analysis -> Characterization characterization d = let Analyzed _ c = S.annotation d in c combine :: [Characterization] -> Characterization @@ -93,7 +94,7 @@ mapCmb f xs = let (xs', cs) = unzip $ map f xs in (xs', combine cs) ---------------------------------------------------------------- --- Reporting of analysis results. +-- | Reporting of analysis results. instance R.ToMessages Analysis where messages a = case a of @@ -131,7 +132,6 @@ ChoiceRecursive -> [R.Text "Recursive"] ChoiceRecursivePrefix -> [R.Text "RecursivePrefix"] ChoiceRecursiveInfix -> [R.Text "RecursiveInfix"] - ChoiceIndentedSuffix -> [R.Text "IndentedSuffix"] ChoiceConstructorDuplicate -> [R.Text "ConstructorDuplicate"] NonTerminalUnbound -> [R.Text "Unbound"] _ -> [] @@ -145,9 +145,9 @@ _ -> [] ---------------------------------------------------------------- --- Baseline analysis (initial non-/terminals and reachable --- non-terminals) and its closure (fully recursive --- characterization of initial and reachable non-/terminals). +-- | Baseline analysis (initial non-/terminals and reachable +-- non-terminals) and its closure (fully recursive +-- characterization of initial and reachable non-/terminals). baseline :: A.Parser Analysis -> A.Parser Analysis baseline (A.Parser a ims ps) = A.Parser (Analyzed [] r) ims ps' where @@ -221,7 +221,7 @@ lookP e' (A.Production (Analyzed _ c) e _) = if e == e' then [c] else [] ---------------------------------------------------------------- --- Property derivation and tagging algorithms. +-- | Property derivation and tagging algorithms. tagging :: A.Parser Analysis -> A.Parser Analysis tagging (A.Parser a ims ps) = A.Parser a ims (map production ps) where @@ -264,18 +264,6 @@ if nt1 == e && nt2 == e && isJust mc then [ChoiceRecursiveInfix] else [] _ -> [] ) - ++ (let chkTerm e = case e of A.Terminal _ -> True ; _ -> False - chkNotIndented e = case e of A.Indented _ _ -> False ; _ -> True - chkLast e = case e of A.Indented True (A.Many _ _) -> True ; _ -> False - in if ( length es > 1 - && and (map chkNotIndented (init es)) - && or (map chkTerm (init es)) - && chkLast (last es) - ) then - [ChoiceIndentedSuffix] - else - [] - ) analyze :: A.Parser Analysis -> A.Parser Analysis analyze parser = @@ -328,7 +316,7 @@ in A.Parser a ims ps''' ---------------------------------------------------------------- --- Other useful functions. +-- | Other useful functions. infixPrefixOps :: A.Parser Analysis -> [String] infixPrefixOps (A.Parser _ _ ps) = @@ -345,5 +333,16 @@ c@(A.Choice _ _ _ [A.NonTerminal _ nt1, A.Terminal (A.Explicit op), A.NonTerminal _ nt2]) <- cs, ChoiceRecursiveInfix `elem` tags c ] + +allOps :: A.Parser Analysis -> [String] +allOps (p@(A.Parser _ _ ps)) = + nub $ infixPrefixOps p + ++ [s | + A.Production _ _ css <- ps, + A.Choices _ cs <- css, + A.Choice _ _ _ es <- cs, + A.Terminal (A.Explicit s) <- es, + A.isOp s + ] --eof
Text/Imparse/Compile/Haskell.hs view
@@ -1,8 +1,10 @@ ---------------------------------------------------------------- -- --- Imparse +-- | Imparse +-- Cross-platform and -language parser generator. -- --- Text/Imparse/Compile/Haskell.hs +-- @Text\/Imparse\/Compile\/Haskell.hs@ +-- -- Compilation from an Imparse parser definition to a Haskell -- implementation of a abstract syntax data type and Parsec -- parser. @@ -14,29 +16,48 @@ module Text.Imparse.Compile.Haskell where -import Data.Char (toLower) +import Data.Char (isAlphaNum, toLower) import Data.List (nub, (\\)) import Data.String.Utils (join, replace) import Data.Maybe (catMaybes) -import Control.Compilation.Compile +import Control.Compilation (Compile, StateExtension(..), nothing) +import Control.Compilation.String +import Control.Compilation.Fresh import Text.Imparse.AbstractSyntax import qualified Text.Imparse.Analysis as S ---------------------------------------------------------------- --- Helper functions. +-- | State data structure +data State = + State StateExtensionFresh StateExtensionString + +instance StateExtension State where + initial = State initial initial + +instance HasFresh State where + project (State i s) = i + inject i (State _ s) = State i s + +instance HasString State where + project (State i s) = s + inject s (State i _) = State i s + +---------------------------------------------------------------- +-- | Helper functions. + toLowerFirst :: String -> String toLowerFirst [] = [] toLowerFirst (c:cs) = toLower c : cs ---------------------------------------------------------------- --- Compilation to abstract syntax data type definition. +-- | Compilation to abstract syntax data type definition. -toAbstractSyntax :: String -> Parser a -> Compile String () +toAbstractSyntax :: String -> Parser a -> Compile State () toAbstractSyntax prefix p = do prefix <- return $ if prefix == "" then "" else prefix ++ "." - raw $ "-- This module generated automatically by imparse.\n\n" + raw $ "-- This module was generated automatically by imparse.\n\n" raw $ "module " ++ prefix ++ "AbstractSyntax\n" raw " where" newlines 2 @@ -44,9 +65,9 @@ newline raw "--eof" -toDatatype :: Parser a -> Compile String () +toDatatype :: Parser a -> Compile State () toDatatype (Parser _ _ ps) = - let production :: Production a -> Compile String () + let production :: Production a -> Compile State () production (Production _ e css) = do raw "data " raw e @@ -59,7 +80,7 @@ unindent newlines 2 - choices :: [Choice a] -> Compile String () + choices :: [Choice a] -> Compile State () choices cs = case cs of [c] -> do choice c @@ -70,7 +91,7 @@ raw "| " choices cs - choice :: Choice a -> Compile String () + choice :: Choice a -> Compile State () choice c = case c of Choice _ con _ es -> do con <- @@ -81,7 +102,7 @@ mapM element es nothing - element :: Element a -> Compile String () + element :: Element a -> Compile State () element e = case e of NonTerminal _ entity -> do { raw " "; raw entity } Many e _ -> do { raw " ["; elementNoSp e; raw "]" } @@ -91,7 +112,7 @@ Terminal t -> do { raw " "; terminal t } _ -> do nothing - elementNoSp :: Element a -> Compile String () + elementNoSp :: Element a -> Compile State () elementNoSp e = case e of NonTerminal _ entity -> do { raw entity } Many e _ -> do { raw "["; element e; raw "]" } @@ -99,7 +120,7 @@ May e -> do { raw "(Maybe "; elementNoSp e; raw ")" } _ -> element e - terminal :: Terminal -> Compile String () + terminal :: Terminal -> Compile State () terminal t = case t of StringLiteral -> raw "String" NaturalLiteral -> raw "Integer" @@ -114,11 +135,11 @@ nothing ---------------------------------------------------------------- --- Compilation to rich reporting instance declarations. +-- | Compilation to rich reporting instance declarations. -toRichReport :: String -> Parser a -> Compile String () +toRichReport :: String -> Parser a -> Compile State () toRichReport prefix p = - do raw $ "-- This module generated automatically by imparse.\n\n" + do raw $ "-- This module was generated automatically by imparse.\n\n" prefix <- return $ if prefix == "" then "" else prefix ++ "." raw $ "module " ++ prefix ++ "Report" newline @@ -132,9 +153,9 @@ newline raw "--eof" -toReportFuns :: Parser a -> Compile String () +toReportFuns :: Parser a -> Compile State () toReportFuns (Parser _ _ ps) = - let production :: Production a -> Compile String () + let production :: Production a -> Compile State () production (Production _ e css) = do raw $ "instance R.ToReport " ++ e ++ " where" indent @@ -147,12 +168,12 @@ unindent newline - choices :: Choices a -> Compile String () + choices :: Choices a -> Compile State () choices (Choices a cs) = case cs of [] -> do nothing c:cs -> do { choice c; newline; choices (Choices a cs) } - choice :: Choice a -> Compile String () + choice :: Choice a -> Compile State () choice c = case c of Choice _ con _ es -> do con <- @@ -193,24 +214,24 @@ nothing ---------------------------------------------------------------- --- Compilation to Parsec parser. +-- | Compilation to Parsec parser. -toParsec :: String -> Parser S.Analysis -> Compile String () +toParsec :: String -> Parser S.Analysis -> Compile State () toParsec prefix (p@(Parser _ _ ((Production _ eRoot _):_))) = - do raw $ "-- This module generated automatically by imparse.\n\n" + do raw $ "-- This module was generated automatically by imparse.\n\n" prefix <- return $ if prefix == "" then "" else prefix ++ "." raw $ "module " ++ prefix ++ "Parse\n where\n" newline raw $ "import " ++ prefix ++ "AbstractSyntax" newlines 2 - reservedOpNames <- return $ nub $ S.infixPrefixOps p - opLetters <- return $ nub $ concat reservedOpNames + reservedOpNames <- return $ nub $ S.allOps p + opLetters <- return $ nub $ [c | c <- concat reservedOpNames, not $ isAlphaNum c] reservedNames <- return $ (nub [r | Explicit r <- terminals p]) \\ reservedOpNames raw "----------------------------------------------------------------\n-- Parser to convert concrete syntax to abstract syntax.\n\n" raw "import Text.Parsec\n" - raw "import qualified Text.Parsec.Indent as PI (withBlock, runIndent)\n" + raw "import qualified Text.Parsec.Indent as PI (runIndent, checkIndent, withPos, indented, block)\n" raw "import qualified Text.Parsec.Token as PT\n" raw "import qualified Text.Parsec.Expr as PE\n" raw "import qualified Text.ParserCombinators.Parsec.Language as PL\n" @@ -226,30 +247,37 @@ raw "----------------------------------------------------------------\n-- Parsec-specific configuration definitions and synonyms.\n\n" raw "langDef :: PL.GenLanguageDef String () ParseState\n" raw "langDef = PL.javaStyle\n" - raw " { PL.identStart = oneOf \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijkmlnopqrstuvwxyz_\" -- Only lowercase.\n" - raw " , PL.identLetter = alphaNum <|> oneOf \"_'\"\n" - raw " , PL.opStart = PL.opLetter langDef\n" + raw $ " { PL.identStart = oneOf \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijkmlnopqrstuvwxyz_\" -- Only lowercase.\n" + raw $ " , PL.identLetter = alphaNum <|> oneOf \"_'\"\n" + raw $ " , PL.opStart = PL.opLetter langDef\n" raw $ " , PL.opLetter = oneOf \"" ++ opLetters ++ "\"\n" raw $ " , PL.reservedOpNames = [" ++ join "," ["\"" ++ rO ++ "\"" | rO <- reservedOpNames] ++ "]\n" raw $ " , PL.reservedNames = [" ++ join "," ["\"" ++ rO ++ "\"" | rO <- reservedNames] ++ "]\n" - raw " , PL.commentLine = \"#\"\n" - raw " }\n\n" + raw $ " , PL.commentLine = \"#\"\n" + raw " }" + newlines 2 raw "lang :: PT.GenTokenParser [Char] () ParseState\n" - raw "lang = PT.makeTokenParser langDef\n\n" + raw "lang = PT.makeTokenParser langDef" + newlines 2 raw "whiteSpace = PT.whiteSpace lang\n" raw "symbol = PT.symbol lang\n" raw "rO = PT.reservedOp lang\n" raw "res = PT.reserved lang\n" raw "identifier = PT.identifier lang\n" - raw "natural = PT.natural lang\n\n" + raw "natural = PT.natural lang" + newlines 2 raw "binary name f assoc = PE.Infix (do{PT.reservedOp lang name; return f}) assoc\n" - raw "prefix name f = PE.Prefix (do{PT.reservedOp lang name; return f})\n\n" - raw "withIndent p1 p2 f = PI.withBlock f p1 p2\n\n" + raw "prefix name f = PE.Prefix (do{PT.reservedOp lang name; return f})" + newlines 2 raw "con :: ParseFor String\n" - raw "con = do { c <- oneOf \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\" ; cs <- option \"\" identifier ; return $ c:cs }\n\n" + raw "con = do { c <- oneOf \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\" ; cs <- option \"\" identifier ; return $ c:cs }" + newlines 2 raw "flag :: ParseFor String\n" raw "flag = do { cs <- many1 (oneOf \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\") ; return cs }\n" - raw "-- caps = do { cs <- many1 (oneOf \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\") ; return cs }\n\n" + raw "-- caps = do { cs <- many1 (oneOf \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\") ; return cs }" + newlines 2 + raw "block0 p = PI.withPos $ do { r <- many (PI.checkIndent >> p); return r }\n" + raw "may p = option Nothing (do {x <- p; return $ Just x})\n" raw "(<?|>) p1 p2 = (try p1) <|> p2\n\n" raw "----------------------------------------------------------------\n-- Parser definition.\n\n" raw $ "root = do { whiteSpace ; r <- p" ++ eRoot ++ " ; eof ; return r }" @@ -257,9 +285,12 @@ toParsecDefs p raw "--eof" -toParsecDefs :: Parser S.Analysis -> Compile String () +toParsecDefs :: Parser S.Analysis -> Compile State () toParsecDefs (Parser _ _ ps) = - let production :: Production S.Analysis -> Compile String () + let explicitCmb :: String -> String + explicitCmb s = if isOp s then "rO" else "res" + + production :: Production S.Analysis -> Compile State () production (p@(Production _ e css)) = do raw $ "p" ++ e ++ " =" ( if S.ProductionInfixPrefixThenDeterministic `elem` S.tags p then @@ -289,7 +320,7 @@ else do indent ( if ((length css > 1) || (or [length cs > 1 | Choices _ cs <- css])) then - do { newline ; raw " " } + do { newline ; raw " " } else raw " " ) @@ -298,7 +329,7 @@ newline ) - choices :: [Choice S.Analysis] -> Compile String () + choices :: [Choice S.Analysis] -> Compile State () choices cs = case cs of [c] -> do choice c @@ -307,27 +338,11 @@ c:cs -> do choice c newline - raw "<|> " + raw "<?|> " choices cs - choice :: Choice S.Analysis -> Compile String () + choice :: Choice S.Analysis -> Compile State () choice (c@(Choice _ con _ es)) = - if S.ChoiceIndentedSuffix `elem` S.tags c then - do ves <- return $ init [("v" ++ show k, es!!k) | k <- [0..length es-1]] - con <- - case con of - Nothing -> do { c <- fresh; return $ "C" ++ c } - Just con -> return con - nt <- return $ (\(Indented w (Many (NonTerminal _ nt) _)) -> nt) $ last es - raw $ "withIndent (" - raw "do {" - raw $ join "; " (map element ves) - raw "; " - raw $ "return $ (" ++ join ", " (catMaybes (map arg ves)) ++ ")" - raw "}) " - raw $ "p" ++ nt - raw $ " (\\(" ++ (join ", " $ catMaybes (map arg ves)) ++ ") vs -> " ++ con ++ " " ++ join " " (catMaybes (map arg ves)) ++ " vs)" - else do ves <- return $ [("v" ++ show k, es!!k) | k <- [0..length es-1]] con <- case con of @@ -340,21 +355,23 @@ raw "}" element :: (String, Element S.Analysis) -> String - element (v, e) = case e of - NonTerminal _ nt -> v ++ " <- p" ++ nt - May (Many (NonTerminal _ nt) sep) -> - let comb = maybe "many" (\_ -> "sepBy") sep - suffix = maybe "" (\sep -> " (res \"" ++ sep ++ "\")") sep - in v ++ " <- " ++ comb ++ " p" ++ nt ++ suffix - Many (NonTerminal _ nt) sep -> - let comb = maybe "many" (\_ -> "sepBy") sep - suffix = maybe "" (\sep -> " (res \"" ++ sep ++ "\")") sep - in v ++ " <- " ++ comb ++ "1" ++ " p" ++ nt ++ suffix - May (NonTerminal _ nt) -> v ++ " <- option p" ++ nt - Indented False e' -> element (v, e') - Indented True e' -> "" - Terminal t -> terminal v t - _ -> "" + element (v, e) = + let mkP e = case e of + NonTerminal _ nt -> "p" ++ nt + Many e' Nothing -> "(many1 (" ++ mkP e' ++ "))" + Many e' (Just sep) -> "(sepBy1 " ++ mkP e' ++ " (" ++ explicitCmb sep ++ " \"" ++ sep ++ "\"))" + May (Many e' Nothing) -> "(many (" ++ mkP e' ++ "))" + May (Many e' (Just sep)) -> "(sepBy " ++ mkP e' ++ " (" ++ explicitCmb sep ++ " \"" ++ sep ++ "\"))" + May e' -> "(may (" ++ mkP e' ++ "))" + Indented False e' -> mkP e' + Indented True e' -> + case e' of + Many e' Nothing -> "(PI.indented >> PI.block (" ++ mkP e' ++ "))" + May (Many e' Nothing) -> "(PI.indented >> block0 (" ++ mkP e' ++ "))" + _ -> "" + in case e of + Terminal t -> terminal v t + _ -> case mkP e of "" -> "" ; p -> v ++ " <- " ++ p arg :: (String, Element S.Analysis) -> Maybe String arg (v, e) = case e of @@ -378,7 +395,6 @@ terminal :: String -> Terminal -> String terminal v t = case t of - Explicit s -> "res \"" ++ s ++ "\"" StringLiteral -> v ++ " <- literal" NaturalLiteral -> v ++ " <- natural" DecimalLiteral -> v ++ " <- decimal" @@ -386,6 +402,7 @@ Constructor -> v ++ " <- con" Flag -> v ++ " <- flag" RegExp r -> "regexp" + Explicit s -> explicitCmb s ++ " \"" ++ s ++ "\"" in do mapM production ps nothing
Text/Imparse/Parse.hs view
@@ -1,20 +1,22 @@ ---------------------------------------------------------------- -- --- Imparse +-- | Imparse +-- Cross-platform and -language parser generator. -- --- Text/Imparse/Parse.hs +-- @Text\/Imparse\/Parse.hs@ +-- -- Parser for Imparse parser specification concrete syntax. -- ---------------------------------------------------------------- --- +-- module Text.Imparse.Parse (parseParser) where import Data.Char (isAlpha, isAlphaNum) import Data.Maybe (catMaybes) -import Data.List (isPrefixOf, findIndex) +import Data.List (nub, findIndex, isPrefixOf) import Data.List.Split (splitOn, splitWhen) import Data.Text (unpack, strip, pack) @@ -23,7 +25,7 @@ import Text.Imparse.AbstractSyntax ---------------------------------------------------------------- --- Exported functions. +-- | Exported functions. parseParser :: A.Analysis a => String -> Either String (Parser a) parseParser s = @@ -31,12 +33,12 @@ in Right $ Parser A.unanalyzed [] $ catMaybes [pProductionOrDelimiters (trim b) | b <- blocks] ---------------------------------------------------------------- --- Parsing functions. +-- | Parsing functions. pProductionOrDelimiters :: A.Analysis a => String -> Maybe (Production a) -pProductionOrDelimiters s = case splitOn "\n" s of +pProductionOrDelimiters s = case splitOn "\n" (noEmptyLines s) of line:lines -> - case splitOn " " line of + case nonemp $ splitOn " " line of [entity, "::="] -> Just $ Production A.unanalyzed entity $ @@ -48,7 +50,7 @@ _ -> Nothing pChoice :: A.Analysis a => String -> Maybe (Choice a) -pChoice s = case filter ((/=) "") $ splitOn " " (trim s) of +pChoice s = case nonemp $ splitOn " " (trim s) of "|":es -> Just $ Choice A.unanalyzed Nothing AssocNone [pElement e | e <- es, e /= ""] "<":es -> Just $ Choice A.unanalyzed Nothing AssocLeft [pElement e | e <- es, e /= ""] ">":es -> Just $ Choice A.unanalyzed Nothing AssocRight [pElement e | e <- es, e /= ""] @@ -101,10 +103,26 @@ ---------------------------------------------------------------- -- Helpful auxiliary functions. +nonemp :: [String] -> [String] +nonemp = filter ((/=) "") + trim :: String -> String trim = unpack.strip.pack ends :: String -> String -> String -> Bool ends p s t = isPrefixOf p t && isPrefixOf (reverse s) (reverse t) + +noEmptyLines :: String -> String +noEmptyLines s = case s of + '\n':s' -> + case findIndex (=='\n') s' of + Nothing -> s + Just i -> + if nub (take i s') == [' '] then + "\n\n" ++ noEmptyLines (drop (i+1) s') + else + "\n" ++ (noEmptyLines s') + c :s' -> c : noEmptyLines s' + "" -> "" --eof
Text/Imparse/Report.hs view
@@ -1,13 +1,15 @@ ---------------------------------------------------------------- -- --- Imparse +-- | Imparse +-- Cross-platform and -language parser generator. -- --- Text/Imparse/Report.hs +-- @Text\/Imparse\/Report.hs@ +-- -- Generation of rich reports from parser definitions. -- ---------------------------------------------------------------- --- +-- module Text.Imparse.Report where @@ -20,8 +22,8 @@ import Text.Imparse.AbstractSyntax ---------------------------------------------------------------- --- Functions for converting a parser abstract syntax instance --- into a rich report. +-- | Functions for converting a parser abstract syntax instance +-- into a rich report. instance (R.ToHighlights a, R.ToMessages a) => R.ToReport (Parser a) where report (Parser _ _ ps) = R.Finalize $ R.Conc [R.report p | p <- ps] @@ -81,7 +83,7 @@ StringLiteral -> R.key "`$" NaturalLiteral -> R.key "`#" DecimalLiteral -> R.key "`#.#" - Identifier -> R.key "`id" + Identifier -> R.key "`var" Constructor -> R.key "`con" Flag -> R.key "`flag" RegExp r -> R.Span [] [] [R.key "`{", R.Text r, R.key "}"]
imparse.cabal view
@@ -1,5 +1,5 @@ Name: imparse -Version: 0.0.0.2 +Version: 0.0.0.3 Cabal-Version: >= 1.6 License: GPL-3 License-File: LICENSE @@ -27,7 +27,9 @@ compilation, ascetic, uxadt, - richreports + richreports, + parsec, + indents Executable imparse Main-Is: Main.hs @@ -41,7 +43,9 @@ compilation, ascetic, uxadt, - richreports + richreports, + parsec, + indents Source-Repository head Type: git