GLM 0.3.0.0 → 0.5.0.0
raw patch · 15 files changed
+582/−468 lines, 15 filesdep +aesonnew-component:exe:glm2json
Dependencies added: aeson
Files
- GLM.cabal +11/−4
- README.md +93/−0
- src/Dot.hs +0/−100
- src/DotMain.hs +6/−0
- src/GLM/Dot.hs +100/−0
- src/GLM/Nesting.hs +76/−0
- src/GLM/Parser.hs +129/−0
- src/GLM/Tokenizer.hs +129/−0
- src/JSONMain.hs +6/−0
- src/Main.hs +0/−29
- src/Nesting.hs +0/−75
- src/Parser2.hs +0/−128
- src/PropMain.hs +29/−0
- src/Tokenizer.hs +0/−129
- test/TestMain.hs +3/−3
GLM.cabal view
@@ -1,5 +1,5 @@ name: GLM-version: 0.3.0.0+version: 0.5.0.0 synopsis: Simple Gridlab-D GLM parser and utilities. description: Simple Gridlab-D GLM parser and utilities. category: Language@@ -10,6 +10,7 @@ maintainer: maydwell@gmail.com build-type: Simple cabal-version: >=1.10+extra-source-files: README.md source-repository head type: git@@ -18,20 +19,26 @@ Library default-language: Haskell2010 Hs-source-dirs: src- Exposed-modules: Dot, Nesting, Parser2, Tokenizer+ Exposed-modules: GLM.Dot, GLM.Nesting, GLM.Parser, GLM.Tokenizer build-depends: base <= 5, parsec, interpolate, bytestring, pureMD5, transformers, lens, mtl, test-framework, test-framework-quickcheck2, QuickCheck, test-framework-th executable glm2props default-language: Haskell2010- main-is: Main.hs+ main-is: PropMain.hs build-depends: base <= 5, parsec, lens, test-framework, test-framework-th, test-framework-quickcheck2, pureMD5, interpolate, bytestring, mtl, GLM hs-source-dirs: src executable glm2dot default-language: Haskell2010- main-is: Dot.hs+ main-is: DotMain.hs build-depends: base <= 5, parsec, lens, test-framework, test-framework-th, test-framework-quickcheck2, pureMD5, interpolate, bytestring, mtl, GLM+ hs-source-dirs: src++executable glm2json+ default-language: Haskell2010+ main-is: JSONMain.hs+ build-depends: base <= 5, parsec, lens, test-framework, test-framework-th, test-framework-quickcheck2, pureMD5, interpolate, bytestring, mtl, GLM, aeson hs-source-dirs: src Test-Suite test-glm
+ README.md view
@@ -0,0 +1,93 @@+# GLM++A small [GridLab-D](http://gridlab-d.sourceforge.net) markup parser.++Currently incomplete, but does work for simple files.++Includes the executables:++* glm2props - List properties found in file+* glm2dot - Output a Dot file for use with GraphViz+* glm2json - Convert GLM format to JSON format++Library functions are also exposesd under:++* GLM.Dot+* GLM.Nesting+* GLM.Parser2+* GLM.Tokenizer+* GLM.JSON++## Library++The main library interface can be found in GLM.Parser.++## glm2dot++Usage:++ Usage: glm2dot [-h|--help] [-e|--edges] [-f|--flatten] [FILE]*++ --flatten creates new linked nodes for nested nodes+ --edge only renders nodes that are connected to others+++Also accepts GLM format on STDIN.+++## glm2json++## glmprops+++Downloads:++* <http://sordina.binaries.s3.amazonaws.com/GLM-0.5.0.0-MacOSX-10.9.5-13F1077.zip>+* <http://sordina.binaries.s3.amazonaws.com/GLM-0.4.0.0-MacOSX-10.9.5-13F1077.zip>+* <http://sordina.binaries.s3.amazonaws.com/glm2props-0.3.0.0-MacOSX-10.9.5-13F1077.zip>+* <http://sordina.binaries.s3.amazonaws.com/glm2dot-0.3.0.0-MacOSX-10.9.5-13F1077.zip>+* <http://sordina.binaries.s3.amazonaws.com/glm2props-0.1.0.0-MacOSX-10.9.5-13F1077.zip>+* <http://sordina.binaries.s3.amazonaws.com/glm2dot-0.1.0.0-MacOSX-10.9.5-13F1077.zip>+++Example:++ bash-3.2$ head IEEE_4_node.glm+ // Exercise 4.1.1++ clock {+ timestamp '2000-01-01 0:00:00';+ timezone EST+5EDT;+ }++ module powerflow {+ solver_method NR;+ }++ bash-3.2$ glm2dot IEEE_4_node.glm++ digraph {+ // Missed entry ["clock"] - noname+ // Missed entry ["module","powerflow"] - powerflow+ "460bc4159" [label="overhead_line_conductor100"];+ "f98a57e6e" [label="overhead_line_conductor101"];+ "3ae695a1a" [label="line_spacing200"];+ "125c9f13f" [label="line_configuration300"];+ "6666bc20c" [label="transformer_configuration400"];+ "164546f60" [label="node1"];+ "89b8519c3" [label="overhead_line"];+ "164546f60" -> "78882aaeb"; // node1 -> node2+ "78882aaeb" [label="node2"];+ "72f1da2f1" [label="transformer23"];+ "78882aaeb" -> "1315e07dc"; // node2 -> node3+ "1315e07dc" [label="node3"];+ "ff93d9775" [label="overhead_line:34"];+ "1315e07dc" -> "6bf7ab42e"; // node3 -> load4+ "6bf7ab42e" [label="load4"];+ }++Graphical output:++ glm2dot -e IEEE_37node.glm | dot -Tpng -o blah.png && open blah.png++
− src/Dot.hs
@@ -1,100 +0,0 @@--{-# LANGUAGE QuasiQuotes #-}-{-# LANGUAGE ViewPatterns #-}--module Dot where--import Parser2-import qualified Nesting as N--import Data.Maybe-import System.Environment-import System.Exit-import System.IO-import Data.Digest.Pure.MD5-import Data.List-import Data.String.Interpolate-import qualified Data.ByteString.Lazy.Char8 as BS--data Options = Options { edges :: Bool- , flatten :: Bool } deriving (Eq, Show)--def :: Options-def = Options { edges = False- , flatten = False }--str5 :: String -> String-str5 = take 9 . show . md5 . BS.pack--main :: IO ()-main = getArgs >>= start def--start :: Options -> [String] -> IO ()-start _ ["-h" ] = help-start _ ["--help" ] = help-start o ("-e" : args) = start (o {edges = True}) args-start o ("--edges" : args) = start (o {edges = True}) args-start o ("-f" : args) = start (o {flatten = True}) args-start o ("--flatten": args) = start (o {flatten = True}) args-start o args = go args >>= mapM_ (outputResult o)--outputResult :: Options -> ParseResult -> IO ()-outputResult _ (Left issue) = putStrLn "Got an error:" >> spew issue >> exitFailure-outputResult opts (Right results) = putStrLn [i|digraph {#{unl $ concatMap graph (filter crit ung)}}|]- where- unl s = "\n" ++ unlines (map ("\t" ++) s)- ung = if (flatten opts) then N.flatten results else results- rt = concatMap refs ung- crit = criteria (edges opts) rt--spew :: Show a => a -> IO ()-spew = hPutStrLn stderr . show--criteria :: Bool -> [String] -> Entry -> Bool-criteria False _ _ = True-criteria True l e = isJust $ find (== name e) l--refs :: Entry -> [String]-refs e@(Entry _ p) = fromMaybe [] $ do- f <- lookup "from" c- t <- lookup "to" c- return [name e, f, t]- where- c = catProps p--help :: IO ()-help = putStrLn "Usage: glm2dot [-h|--help] [-e|--edges] [-f|--flatten] [FILE]*"--go :: [String] -> IO [ParseResult]-go xs@(_:_) = mapM processFile xs-go [] = (return . glmParser "<STDIN>") `fmap` getContents--processFile :: String -> IO ParseResult-processFile f = glmParser f `fmap` readFile f--chash :: Entry -> String-chash = (++ "ef") . take 4 . str5 . (!! 1) . unSelector--graph :: Entry -> [String]-graph e@(Entry ("object":_:_) _) = fromMaybe [ [i|"#{nhash e}" [label="#{name e}", fillcolor="##{chash e}", style=filled];|] ] (edge e)-graph e@(Entry s _) = [ [i|// Missed entry #{s} - #{name e}|] ]--edge :: Entry -> Maybe [String]-edge e@(Entry _ p) = do- f <- lookup "from" c- t <- lookup "to" c- return [[i|"#{str5 f}" -> "#{str5 t}" [label="#{name e}"]; // #{f} -> #{t}|]]- where- c = catProps p--nhash :: Entry -> String-nhash = str5 . name--name :: Entry -> String-name (Entry (_:s:_) p) = maybe s noquote (lookup "name" c) where c = catProps p-name _ = "noname"---- TODO: Shouldn't need this now...----noquote :: String -> String-noquote = filter (/= '\'')
+ src/DotMain.hs view
@@ -0,0 +1,6 @@+module Main where++import qualified GLM.Dot as D++main :: IO ()+main = D.main
+ src/GLM/Dot.hs view
@@ -0,0 +1,100 @@++{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE ViewPatterns #-}++module GLM.Dot where++import GLM.Parser+import qualified GLM.Nesting as N++import Data.Maybe+import System.Environment+import System.Exit+import System.IO+import Data.Digest.Pure.MD5+import Data.List+import Data.String.Interpolate+import qualified Data.ByteString.Lazy.Char8 as BS++data Options = Options { edges :: Bool+ , flatten :: Bool } deriving (Eq, Show)++def :: Options+def = Options { edges = False+ , flatten = False }++str5 :: String -> String+str5 = take 9 . show . md5 . BS.pack++main :: IO ()+main = getArgs >>= start def++start :: Options -> [String] -> IO ()+start _ ["-h" ] = help+start _ ["--help" ] = help+start o ("-e" : args) = start (o {edges = True}) args+start o ("--edges" : args) = start (o {edges = True}) args+start o ("-f" : args) = start (o {flatten = True}) args+start o ("--flatten": args) = start (o {flatten = True}) args+start o args = go args >>= mapM_ (outputResult o)++outputResult :: Options -> ParseResult -> IO ()+outputResult _ (Left issue) = putStrLn "Got an error:" >> spew issue >> exitFailure+outputResult opts (Right results) = putStrLn [i|digraph {#{unl $ concatMap graph (filter crit ung)}}|]+ where+ unl s = "\n" ++ unlines (map ("\t" ++) s)+ ung = if (flatten opts) then N.flatten results else results+ rt = concatMap refs ung+ crit = criteria (edges opts) rt++spew :: Show a => a -> IO ()+spew = hPutStrLn stderr . show++criteria :: Bool -> [String] -> Entry -> Bool+criteria False _ _ = True+criteria True l e = isJust $ find (== name e) l++refs :: Entry -> [String]+refs e@(Entry _ p) = fromMaybe [] $ do+ f <- lookup "from" c+ t <- lookup "to" c+ return [name e, f, t]+ where+ c = catProps p++help :: IO ()+help = putStrLn "Usage: glm2dot [-h|--help] [-e|--edges] [-f|--flatten] [FILE]*"++go :: [String] -> IO [ParseResult]+go xs@(_:_) = mapM processFile xs+go [] = (return . glmParser "<STDIN>") `fmap` getContents++processFile :: String -> IO ParseResult+processFile f = glmParser f `fmap` readFile f++chash :: Entry -> String+chash = (++ "ef") . take 4 . str5 . (!! 1) . unSelector++graph :: Entry -> [String]+graph e@(Entry ("object":_:_) _) = fromMaybe [ [i|"#{nhash e}" [label="#{name e}", fillcolor="##{chash e}", style=filled];|] ] (edge e)+graph e@(Entry s _) = [ [i|// Missed entry #{s} - #{name e}|] ]++edge :: Entry -> Maybe [String]+edge e@(Entry _ p) = do+ f <- lookup "from" c+ t <- lookup "to" c+ return [[i|"#{str5 f}" -> "#{str5 t}" [label="#{name e}"]; // #{f} -> #{t}|]]+ where+ c = catProps p++nhash :: Entry -> String+nhash = str5 . name++name :: Entry -> String+name (Entry (_:s:_) p) = maybe s noquote (lookup "name" c) where c = catProps p+name _ = "noname"++-- TODO: Shouldn't need this now...+--+noquote :: String -> String+noquote = filter (/= '\'')
+ src/GLM/Nesting.hs view
@@ -0,0 +1,76 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TypeFamilies #-}++module GLM.Nesting where++import GLM.Parser++import Data.Maybe+import Control.Lens+import Control.Monad.State++import Test.Framework+import Test.Framework.TH+import Test.Framework.Providers.QuickCheck2++-- Tests++tests :: Test+tests = $(testGroupGenerator)++-- Properties++prop_flatten :: Bool+prop_flatten = (== 3) $ length $ flatten [Entry ["l1"] [Prop ("name", "n1"), Nested (Entry ["l2"] [])]]++-- Main Nesting Function++flatten :: [Entry] -> [Entry]+flatten entries = evalState (flatPack entries) 0++-- Nesting Helper Functions++catNested :: Entry -> [Entry]+catNested = toListOf (contents . each . _Nested)++addParent :: String -> Entry -> Entry+addParent p = over contents (++ [Prop ("parent",p)])++addName :: String -> Entry -> Entry+addName n = over contents (++ [Prop ("name",n)])++phantomLink :: String -> String -> Entry+phantomLink f t = Entry ["object","link"] [Prop ("name", "nl_"), Prop ("from", f), Prop ("to", t)]++flatPack :: [Entry] -> State Int [Entry]+flatPack es = do+ r <- mapM unNest es+ return $ (map stripNested es) ++ concat r++-- TODO: This could be better...+stripNested :: Entry -> Entry+stripNested e = set contents (map Prop (e ^.. contents . each . _Prop)) e++unNest :: Entry -> State Int [Entry]+unNest e = do+ r <- mapM (fabulate e) (catNested e)+ flatPack $ concat r++fabulate :: Entry -> Entry -> State Int [Entry]+fabulate p c = do+ modify succ+ s <- get+ let cname = getType c ++ show s ++ "_"+ return [c & addName cname & addParent pname, phantomLink pname cname]+ where+ pname = getName p++getName :: Entry -> String+getName = fromMaybe "unnamed" . lookup "name" . toListOf (contents . each . _Prop)++getType :: Entry -> String+getType (Entry (_:t:_) _) = t+getType _ = "unknown"
+ src/GLM/Parser.hs view
@@ -0,0 +1,129 @@++{-# OPTIONS_GHC -fno-warn-unused-do-bind #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE NoMonomorphismRestriction #-}++module GLM.Parser where++import qualified GLM.Tokenizer as T++import qualified Text.Parsec as P+import Text.Parsec ((<|>))+import Control.Lens+import Data.Either++import Test.Framework+import Test.Framework.TH+import Test.Framework.Providers.QuickCheck2++import Control.Applicative hiding (many, (<|>))++-- Data Types and Instances++data EntryItem = Prop (String, String)+ | Nested Entry deriving (Show, Eq)++data Entry = Entry { _selector :: [String ]+ , _contents :: [EntryItem] } deriving (Show, Eq)++makePrisms ''EntryItem+makeLenses ''Entry++-- TODO: Temporary++type ParseResult = Either P.ParseError [Entry]++unSelector :: Entry -> [String]+unSelector = _selector+unContents :: Entry -> [EntryItem]+unContents = _contents++catProps :: Each s s EntryItem EntryItem => s -> [(String, String)]+catProps = toListOf (each . _Prop)++-- Tests++tests :: Test+tests = $(testGroupGenerator)++-- Positive Testing Properties++prop_topLevel_1, prop_topLevel_2, prop_topLevel_3, prop_topLevel_4, prop_topLevel_5, prop_topLevel_6, prop_topLevel_7 :: Bool+prop_topLevel_1 = isLen 1 $ glmParser "TEST" "x { y zzz; }"+prop_topLevel_2 = isLen 2 $ glmParser "TEST" "q { r s; }; t u {v w x;}"+prop_topLevel_3 = isLen 2 $ glmParser "TEST" "q { a {b c}; r s; }; t u {v w x;}"+prop_topLevel_4 = isLen 2 $ glmParser "TEST" "q { a {b c}; r s \"a super string!\"; }; t u {v w x;}"+prop_topLevel_5 = isLen 2 $ glmParser "TEST" "q { a {b c}; r s \"a super string!\"; }; t u {v w x;}\n"+prop_topLevel_6 = isLen 2 $ glmParser "TEST" "a { b c; }\n// comment\nc d { e f g; }"+prop_topLevel_7 = isLen 3 $ glmParser "TEST" "a { b c; }\nmodule tape;\nc d { e f g; }"++-- Imported from old parser++prop_glmParser_1, prop_glmParser_2, prop_glmParser_3, prop_glmParser_4 :: Bool+prop_glmParser_1 = isRight $ glmParser "TEST" "object foo {\n\thello world;\n}"+prop_glmParser_2 = isRight $ glmParser "TEST" "object foo {\n\thello world;\n}\n"+prop_glmParser_3 = isLen 2 $ glmParser "TEST" "object foo {\n\ta b;\n};\nobject bar {\n\tc d;\n};\n"+prop_glmParser_4 = isRight $ glmParser "TEST" "object foo { a b; object x { y z; }; }; object bar { c d; };"++-- Negative Testing Properties++prop_topLevel_neg_1 :: Bool+prop_topLevel_neg_1 = isLeft $ glmParser "TEST" "a { b c;sdf''' \n\n/ }} }\n// comment\nc d { e f g; }"++-- Testing Helpers++isLen :: Int -> Either t [a] -> Bool+isLen n (Right l) = length l == n+isLen _ _ = False++-- String Parser combining Tokenization and GLM Parsing++glmParser :: FilePath -> String -> Either P.ParseError [Entry]+glmParser f s = do+ x <- P.parse T.parseTokens (f ++ " (TOKENS)") s+ y <- P.parse topLevel (f ++ " (GLM)" ) (stripComments x)+ return y++stripComments :: [(T.Token, b)] -> [(T.Token, b)]+stripComments = filter (not . (T.?> _1 . T._TComment))++-- Assume that comments have been stripped out of the stream++topLevel :: T.T [ Entry ]+topLevel = P.sepEndBy entry (P.optional T.pTSemi)++entry :: T.T Entry+entry = do+ sel <- P.many1 T.pTString+ braced sel <|> modl sel++modl :: [T.Token] -> T.T Entry+modl p = return $ Entry (selWords p) []++selWords :: Each s s T.Token T.Token => s -> [String]+selWords p = (p ^.. each . T._TString)++entryItems :: T.T [EntryItem]+entryItems = P.sepEndBy item T.pTSemi++item :: T.T EntryItem+item = do+ p <- prop+ P.try (nested p) <|> return (Prop (p ^. _head . T._TString, unwords $ p ^.. _tail . each . T._TString))++prop :: T.T [ T.Token ]+prop = P.many1 T.pTString++nested :: [T.Token] -> T.T EntryItem+nested p = Nested <$> braced p++braced :: [T.Token] -> T.T Entry+braced p = do+ T.pTLBrace+ c <- entryItems+ T.pTRBrace+ return $ Entry (selWords p) c
+ src/GLM/Tokenizer.hs view
@@ -0,0 +1,129 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TemplateHaskell #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}++module GLM.Tokenizer where++import qualified Text.Parsec as P+import Text.Parsec ((<|>))+import Data.String+import Data.Either+import Data.Maybe+import Data.Monoid+import Control.Lens+import Control.Applicative hiding (many, (<|>))++import Test.Framework+import Test.Framework.TH+import Test.Framework.Providers.QuickCheck2++-- Data Types and Instances++type P a = P.Parsec String () a+type T a = P.Parsec [TokenPos] () a++type TokenPos = (Token, P.SourcePos)++data Token = TString String+ | TComment String+ | LBrace+ | RBrace+ | Semi+ deriving (Eq, Show)++makePrisms ''Token++instance s ~ String => IsString (P s) where fromString = P.string+instance t ~ Token => IsString (T t) where fromString = pTParse -- Should just "Do the right thing"++(?>) :: s -> Getting (First a) s a -> Bool+d ?> p = isJust $ d ^? p++parseTokens :: P [TokenPos]+parseTokens = P.sepEndBy parseTokenPos (P.many P.space)++parseTokenPos :: P TokenPos+parseTokenPos = do+ pos <- P.getPosition+ tok <- pToken+ return (tok, pos)++pToken :: P Token+pToken = pComment <|> pLBrace <|> pRBrace <|> pSemi <|> pString <|> pWord++-- Tests++tests :: Test+tests = $(testGroupGenerator)++-- Props++prop_tokens_1, prop_tokens_2, prop_tokens_3, prop_tokens_4, prop_tokens_5 :: Bool+prop_tokens_1 = isRight $ P.parse parseTokens "TEST" "\"he\\\"as\\ndf\\\"llo\";;;"+prop_tokens_2 = isRight $ P.parse parseTokens "TEST" "\"he\\\"as\\ndf\\\"llo\"; ;;\n//wtf alksdjhfs \n;;"+prop_tokens_3 = Right LBrace == P.parse pToken "TEST" "{"+prop_tokens_4 = Right RBrace == P.parse pToken "TEST" "}"+prop_tokens_5 = Right RBrace == P.parse pToken "TEST" "}"++-- TokenPos Combinators++pTStringE, pTCommentE, pTParse :: String -> T Token+pTParse s = tSatisfy ((== P.parse pToken "inline" s) . Right . fst)+pTStringE s = tSatisfy ((== TString s) . fst)+pTCommentE s = tSatisfy ((== TComment s) . fst)++pTLBrace, pTRBrace, pTSemi, pTComment, pTString, pTAny :: T Token+pTAny = tSatisfy (const True)+pTString = tSatisfy (?> _1 . _TString )+pTComment = tSatisfy (?> _1 . _TComment)+pTLBrace = tSatisfy (?> _1 . _LBrace )+pTRBrace = tSatisfy (?> _1 . _RBrace )+pTSemi = tSatisfy (?> _1 . _Semi )++-- Token Parsers++pString, pWord, pLBrace, pRBrace, pSemi, pComment :: P Token+pString = fmap TString parseString+pWord = fmap TString parseWord+pLBrace = "{" *> return LBrace+pRBrace = "}" *> return RBrace+pSemi = ";" *> return Semi+pComment = do+ start <- "#" <|> "//"+ body <- P.many $ P.noneOf "\r\n"+ P.optional P.endOfLine+ return $ TComment $ start ++ body++-- Word Parser++parseWord :: P String+parseWord = P.many1 (P.noneOf " ;\n\t\r{}\"\\#")++-- String Parser++escape :: P String+escape = do+ d <- P.char '\\'+ c <- P.oneOf "\\\"0nrvtbf" -- all the characters which can be escaped+ return [d, c]++nonEscape :: P Char+nonEscape = P.noneOf "\\\"\0\n\r\v\t\b\f"++character :: P String+character = fmap return nonEscape <|> escape++parseString :: P String+parseString = fmap concat ( P.char '"' *> P.many character <* P.char '"' )++-- Primitives for token stream++advance :: P.SourcePos -> t -> [TokenPos] -> P.SourcePos+advance _ _ ((_, pos) : _) = pos+advance pos _ [] = pos++tSatisfy :: (TokenPos -> Bool) -> T Token+tSatisfy f = P.tokenPrim show advance (\c -> if f c then Just (fst c) else Nothing)
+ src/JSONMain.hs view
@@ -0,0 +1,6 @@+module Main where++import qualified GLM.JSON as J++main :: IO ()+main = J.main
− src/Main.hs
@@ -1,29 +0,0 @@-module Main where--import System.Environment-import Parser2--main :: IO ()-main = getArgs >>= start--start :: [String] -> IO ()-start ["-h"] = help-start ["--help"] = help-start args = go args >>= mapM_ outputResult--outputResult :: ParseResult -> IO ()-outputResult (Left issue) = print "Got an error:" >> print issue-outputResult (Right results) = mapM_ print results--help :: IO ()-help = putStrLn "Usage: glm [FILE]*"--go :: [String] -> IO [ParseResult]-go xs@(_:_) = mapM processFile xs-go [] = fmap return $ getContents >>= processContents "<STDIN>"--processFile :: String -> IO ParseResult-processFile f = readFile f >>= processContents f--processContents :: String -> String -> IO ParseResult-processContents f c = return $ glmParser f c
− src/Nesting.hs
@@ -1,75 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE TemplateHaskell #-}-{-# LANGUAGE TypeFamilies #-}--module Nesting where--import Parser2-import Data.Maybe-import Control.Lens-import Control.Monad.State--import Test.Framework-import Test.Framework.TH-import Test.Framework.Providers.QuickCheck2---- Tests--tests :: Test-tests = $(testGroupGenerator)---- Properties--prop_flatten :: Bool-prop_flatten = (== 3) $ length $ flatten [Entry ["l1"] [Prop ("name", "n1"), Nested (Entry ["l2"] [])]]---- Main Nesting Function--flatten :: [Entry] -> [Entry]-flatten entries = evalState (flatPack entries) 0---- Nesting Helper Functions--catNested :: Entry -> [Entry]-catNested = toListOf (contents . each . _Nested)--addParent :: String -> Entry -> Entry-addParent p = over contents (++ [Prop ("parent",p)])--addName :: String -> Entry -> Entry-addName n = over contents (++ [Prop ("name",n)])--phantomLink :: String -> String -> Entry-phantomLink f t = Entry ["object","link"] [Prop ("name", "nl_"), Prop ("from", f), Prop ("to", t)]--flatPack :: [Entry] -> State Int [Entry]-flatPack es = do- r <- mapM unNest es- return $ (map stripNested es) ++ concat r---- TODO: This could be better...-stripNested :: Entry -> Entry-stripNested e = set contents (map Prop (e ^.. contents . each . _Prop)) e--unNest :: Entry -> State Int [Entry]-unNest e = do- r <- mapM (fabulate e) (catNested e)- flatPack $ concat r--fabulate :: Entry -> Entry -> State Int [Entry]-fabulate p c = do- modify succ- s <- get- let cname = getType c ++ show s ++ "_"- return [c & addName cname & addParent pname, phantomLink pname cname]- where- pname = getName p--getName :: Entry -> String-getName = fromMaybe "unnamed" . lookup "name" . toListOf (contents . each . _Prop)--getType :: Entry -> String-getType (Entry (_:t:_) _) = t-getType _ = "unknown"
− src/Parser2.hs
@@ -1,128 +0,0 @@--{-# OPTIONS_GHC -fno-warn-unused-do-bind #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE TemplateHaskell #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE NoMonomorphismRestriction #-}--module Parser2 where--import qualified Tokenizer as T-import qualified Text.Parsec as P-import Text.Parsec ((<|>))-import Control.Lens-import Data.Either--import Test.Framework-import Test.Framework.TH-import Test.Framework.Providers.QuickCheck2--import Control.Applicative hiding (many, (<|>))---- Data Types and Instances--data EntryItem = Prop (String, String)- | Nested Entry deriving (Show, Eq)--data Entry = Entry { _selector :: [String ]- , _contents :: [EntryItem] } deriving (Show, Eq)--makePrisms ''EntryItem-makeLenses ''Entry---- TODO: Temporary--type ParseResult = Either P.ParseError [Entry]--unSelector :: Entry -> [String]-unSelector = _selector-unContents :: Entry -> [EntryItem]-unContents = _contents--catProps :: Each s s EntryItem EntryItem => s -> [(String, String)]-catProps = toListOf (each . _Prop)---- Tests--tests :: Test-tests = $(testGroupGenerator)---- Positive Testing Properties--prop_topLevel_1, prop_topLevel_2, prop_topLevel_3, prop_topLevel_4, prop_topLevel_5, prop_topLevel_6, prop_topLevel_7 :: Bool-prop_topLevel_1 = isLen 1 $ glmParser "TEST" "x { y zzz; }"-prop_topLevel_2 = isLen 2 $ glmParser "TEST" "q { r s; }; t u {v w x;}"-prop_topLevel_3 = isLen 2 $ glmParser "TEST" "q { a {b c}; r s; }; t u {v w x;}"-prop_topLevel_4 = isLen 2 $ glmParser "TEST" "q { a {b c}; r s \"a super string!\"; }; t u {v w x;}"-prop_topLevel_5 = isLen 2 $ glmParser "TEST" "q { a {b c}; r s \"a super string!\"; }; t u {v w x;}\n"-prop_topLevel_6 = isLen 2 $ glmParser "TEST" "a { b c; }\n// comment\nc d { e f g; }"-prop_topLevel_7 = isLen 3 $ glmParser "TEST" "a { b c; }\nmodule tape;\nc d { e f g; }"---- Imported from old parser--prop_glmParser_1, prop_glmParser_2, prop_glmParser_3, prop_glmParser_4 :: Bool-prop_glmParser_1 = isRight $ glmParser "TEST" "object foo {\n\thello world;\n}"-prop_glmParser_2 = isRight $ glmParser "TEST" "object foo {\n\thello world;\n}\n"-prop_glmParser_3 = isLen 2 $ glmParser "TEST" "object foo {\n\ta b;\n};\nobject bar {\n\tc d;\n};\n"-prop_glmParser_4 = isRight $ glmParser "TEST" "object foo { a b; object x { y z; }; }; object bar { c d; };"---- Negative Testing Properties--prop_topLevel_neg_1 :: Bool-prop_topLevel_neg_1 = isLeft $ glmParser "TEST" "a { b c;sdf''' \n\n/ }} }\n// comment\nc d { e f g; }"---- Testing Helpers--isLen :: Int -> Either t [a] -> Bool-isLen n (Right l) = length l == n-isLen _ _ = False---- String Parser combining Tokenization and GLM Parsing--glmParser :: FilePath -> String -> Either P.ParseError [Entry]-glmParser f s = do- x <- P.parse T.parseTokens (f ++ " (TOKENS)") s- y <- P.parse topLevel (f ++ " (GLM)" ) (stripComments x)- return y--stripComments :: [(T.Token, b)] -> [(T.Token, b)]-stripComments = filter (not . (T.?> _1 . T._TComment))---- Assume that comments have been stripped out of the stream--topLevel :: T.T [ Entry ]-topLevel = P.sepEndBy entry (P.optional T.pTSemi)--entry :: T.T Entry-entry = do- sel <- P.many1 T.pTString- braced sel <|> modl sel--modl :: [T.Token] -> T.T Entry-modl p = return $ Entry (selWords p) []--selWords :: Each s s T.Token T.Token => s -> [String]-selWords p = (p ^.. each . T._TString)--entryItems :: T.T [EntryItem]-entryItems = P.sepEndBy item T.pTSemi--item :: T.T EntryItem-item = do- p <- prop- P.try (nested p) <|> return (Prop (p ^. _head . T._TString, unwords $ p ^.. _tail . each . T._TString))--prop :: T.T [ T.Token ]-prop = P.many1 T.pTString--nested :: [T.Token] -> T.T EntryItem-nested p = Nested <$> braced p--braced :: [T.Token] -> T.T Entry-braced p = do- T.pTLBrace- c <- entryItems- T.pTRBrace- return $ Entry (selWords p) c
+ src/PropMain.hs view
@@ -0,0 +1,29 @@+module Main where++import System.Environment+import GLM.Parser++main :: IO ()+main = getArgs >>= start++start :: [String] -> IO ()+start ["-h"] = help+start ["--help"] = help+start args = go args >>= mapM_ outputResult++outputResult :: ParseResult -> IO ()+outputResult (Left issue) = print "Got an error:" >> print issue+outputResult (Right results) = mapM_ print results++help :: IO ()+help = putStrLn "Usage: glm [FILE]*"++go :: [String] -> IO [ParseResult]+go xs@(_:_) = mapM processFile xs+go [] = fmap return $ getContents >>= processContents "<STDIN>"++processFile :: String -> IO ParseResult+processFile f = readFile f >>= processContents f++processContents :: String -> String -> IO ParseResult+processContents f c = return $ glmParser f c
− src/Tokenizer.hs
@@ -1,129 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE TemplateHaskell #-}-{-# OPTIONS_GHC -fno-warn-orphans #-}--module Tokenizer where--import qualified Text.Parsec as P-import Text.Parsec ((<|>))-import Data.String-import Data.Either-import Data.Maybe-import Data.Monoid-import Control.Lens-import Control.Applicative hiding (many, (<|>))--import Test.Framework-import Test.Framework.TH-import Test.Framework.Providers.QuickCheck2---- Data Types and Instances--type P a = P.Parsec String () a-type T a = P.Parsec [TokenPos] () a--type TokenPos = (Token, P.SourcePos)--data Token = TString String- | TComment String- | LBrace- | RBrace- | Semi- deriving (Eq, Show)--makePrisms ''Token--instance s ~ String => IsString (P s) where fromString = P.string-instance t ~ Token => IsString (T t) where fromString = pTParse -- Should just "Do the right thing"--(?>) :: s -> Getting (First a) s a -> Bool-d ?> p = isJust $ d ^? p--parseTokens :: P [TokenPos]-parseTokens = P.sepEndBy parseTokenPos (P.many P.space)--parseTokenPos :: P TokenPos-parseTokenPos = do- pos <- P.getPosition- tok <- pToken- return (tok, pos)--pToken :: P Token-pToken = pComment <|> pLBrace <|> pRBrace <|> pSemi <|> pString <|> pWord---- Tests--tests :: Test-tests = $(testGroupGenerator)---- Props--prop_tokens_1, prop_tokens_2, prop_tokens_3, prop_tokens_4, prop_tokens_5 :: Bool-prop_tokens_1 = isRight $ P.parse parseTokens "TEST" "\"he\\\"as\\ndf\\\"llo\";;;"-prop_tokens_2 = isRight $ P.parse parseTokens "TEST" "\"he\\\"as\\ndf\\\"llo\"; ;;\n//wtf alksdjhfs \n;;"-prop_tokens_3 = Right LBrace == P.parse pToken "TEST" "{"-prop_tokens_4 = Right RBrace == P.parse pToken "TEST" "}"-prop_tokens_5 = Right RBrace == P.parse pToken "TEST" "}"---- TokenPos Combinators--pTStringE, pTCommentE, pTParse :: String -> T Token-pTParse s = tSatisfy ((== P.parse pToken "inline" s) . Right . fst)-pTStringE s = tSatisfy ((== TString s) . fst)-pTCommentE s = tSatisfy ((== TComment s) . fst)--pTLBrace, pTRBrace, pTSemi, pTComment, pTString, pTAny :: T Token-pTAny = tSatisfy (const True)-pTString = tSatisfy (?> _1 . _TString )-pTComment = tSatisfy (?> _1 . _TComment)-pTLBrace = tSatisfy (?> _1 . _LBrace )-pTRBrace = tSatisfy (?> _1 . _RBrace )-pTSemi = tSatisfy (?> _1 . _Semi )---- Token Parsers--pString, pWord, pLBrace, pRBrace, pSemi, pComment :: P Token-pString = fmap TString parseString-pWord = fmap TString parseWord-pLBrace = "{" *> return LBrace-pRBrace = "}" *> return RBrace-pSemi = ";" *> return Semi-pComment = do- start <- "#" <|> "//"- body <- P.many $ P.noneOf "\r\n"- P.optional P.endOfLine- return $ TComment $ start ++ body---- Word Parser--parseWord :: P String-parseWord = P.many1 (P.noneOf " ;\n\t\r{}\"\\#")---- String Parser--escape :: P String-escape = do- d <- P.char '\\'- c <- P.oneOf "\\\"0nrvtbf" -- all the characters which can be escaped- return [d, c]--nonEscape :: P Char-nonEscape = P.noneOf "\\\"\0\n\r\v\t\b\f"--character :: P String-character = fmap return nonEscape <|> escape--parseString :: P String-parseString = fmap concat ( P.char '"' *> P.many character <* P.char '"' )---- Primitives for token stream--advance :: P.SourcePos -> t -> [TokenPos] -> P.SourcePos-advance _ _ ((_, pos) : _) = pos-advance pos _ [] = pos--tSatisfy :: (TokenPos -> Bool) -> T Token-tSatisfy f = P.tokenPrim show advance (\c -> if f c then Just (fst c) else Nothing)
test/TestMain.hs view
@@ -4,9 +4,9 @@ import Test.Framework -import qualified Parser2 as P-import qualified Tokenizer as T-import qualified Nesting as N+import qualified GLM.Parser as P+import qualified GLM.Tokenizer as T+import qualified GLM.Nesting as N main :: IO () main = defaultMain