Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementing a simple graph api #14

Merged
merged 35 commits into from
Aug 10, 2017
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
e0ada79
updated readme
Jun 28, 2017
aa2a7c2
Spacing changes
Jun 28, 2017
fff5d44
Updated to simple interface, list based
Jul 13, 2017
ede942c
Removed old implementation and altered the current parser to use the …
Jul 17, 2017
2520073
VHDL working again, most likely the list of nodes it used was reorder…
Jul 18, 2017
4a2f7bc
split insertEdges into addEdge and updateEdge
Jul 18, 2017
aad0b01
Fixed git issues, last few commits contain details of this commit
Jul 18, 2017
645eb02
Another iteration on the api, taking the idea that the maps and list …
Jul 19, 2017
4f24710
Applied hlint
Jul 19, 2017
c004654
bump stack to lts-8.22
Jul 19, 2017
8518bd0
Update to readme and example
Jul 19, 2017
e00d071
Added a second function which returns a pangraph or throws errors rat…
Jul 19, 2017
73ed92f
Changed vertices and edges function in Graph.hs to use Map.Strict ele…
Jul 20, 2017
83ee458
Removed dead code, improved error handling with a sum type in Error.h…
Jul 24, 2017
288e7a1
Changed Edge show implementation to make it compilable
Jul 24, 2017
95c8348
Merged Error.hs and GraphTypes.hs into Pangraph.hs
Jul 24, 2017
6799b76
Removed Error.hs in favour of Maybe to reflect errors in graphs
Jul 25, 2017
8c1f677
Improved implementation of *ContainsKey functions
Jul 25, 2017
3df235d
Applied hlint and uses of case statements to specialised functions
Jul 27, 2017
4b18f17
Moved the reading example into an area that is compiled
Jul 27, 2017
8a6b9e6
Apply Hlint
Jul 27, 2017
c23f4f8
Updated readme
Jul 27, 2017
666a081
Refactor of Test.hs following your style recommendations
Jul 27, 2017
58ad815
Formatting changes in monadic code, bug fix for VHDL testing
Jul 28, 2017
b6011fb
Used HUnit in testing, no file IO is required for testing the run now
Aug 4, 2017
30143ab
Added Haddock, updates to readme
Aug 4, 2017
20ead9b
Spacing Change
Aug 4, 2017
ab76a56
Fixed mistake in example
thisiswhereitype Aug 4, 2017
ae4b278
Bump to lts-9.00
thisiswhereitype Aug 6, 2017
2f0be80
Merge branch 'master' of github.com:thisiswhereitype/pangraph
thisiswhereitype Aug 6, 2017
9afc3d2
Haddock typo
thisiswhereitype Aug 7, 2017
d55bb02
Fixed all --wall warnings
thisiswhereitype Aug 7, 2017
dd42b08
Fixed --wall messages in test/
thisiswhereitype Aug 7, 2017
46333ec
VHDL now exported through Pangraph.VHDL with ByteString Interface, re…
thisiswhereitype Aug 9, 2017
dab67f7
Moved the VHDL writer to have the same nomenclature as the reset of t…
thisiswhereitype Aug 10, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 23 additions & 54 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,42 +5,23 @@
This is a Haskell Library for parsing graph formats such as [GraphML](http://graphml.graphdrawing.org/) and the XML files produced by [Workcraft](https://www.workcraft.org/).
The library currently implements parsing only the nodes and edges of undirected graphs.
See the User Guide for how to use this, with examples below.
Also information on current graph support below that.
Information on current graph support can be found below that.

## Getting Started
The library `Pangraph` exports the following types:
## How to use Pangraph

Pangraph offers an api in the module `Pangraph` for accessing of graphs including constructors and getters.
You can construct entire graph or edit the results of parser.
These are imported independently form their own modules:
```
data Att = Att (String, String) deriving (Show, Eq)
data Node = Node [Att] deriving (Show, Eq)
data Edge = Edge [Att] deriving (Show, Eq)
data ShortGraph = ShortGraph [Node] [Edge] deriving (Show, Eq)
data ShortFile = ShortFile [ShortGraph] deriving (Show, Eq)
import Pangraph.GraphML.Parser
```

The first five types above define a graph. When parsed one ShortFile will be produced containing one ShortGraph containing zero or more edges and arcs. The library does not check the graphs for correctness however.

To parse graphs you must import them directly.
All parsers currently export the following:
```
import Pangraph.GraphML.Writer
import Pangraph.Workcraft.Parser
formatToPangraph :: format -> Pangraph
```

All parsers export the following:
```
module Pangraph.Example.Parser
( parseFile
, parseString
)where
```
To use these functions,
And writers export:
```
module Pangraph.GraphML.Writer
( writeGraph
) where
...
```
An example of reading this code can be found in `Examples` below

## Binary generation

Expand All @@ -56,44 +37,32 @@ cabal install

## Examples

### Parsing a Graph file
```
module Reading where
### Parsing a Graph file
Repeated here the code from `src/Pangraph/Examples`:
```haskell
module Pangraph.Examples.Reading where

import Prelude hiding (readFile)

import Data.ByteString (readFile)

import System.IO
import Pangraph
import qualified Pangraph.GraphML.Parser as G
import qualified Pangraph.GraphML.Parser as GraphML_P

main::IO()
main=do
main :: IO ()
main = do
fileName <- getLine
file <- readFile fileName
let graph = G.parseAsString file
putStr $ show graph
```

### Parsing a graph and then writing to a file
print (GraphML_P.parse file)
```
module Writing where

import System.IO
import Pangraph
import qualified Pangraph.GraphML.Writer as G

main::IO()
main=do
filePath <- getLine
G.writeGraph filePath graph
where
graph = ShortFile [ShortGraph [Node [Att ("id","n0")],Node [Att ("id","n1")],Node [Att ("id","n2")]] [Edge [Att ("source","n0"),Att ("target","n2")]]]
```
## Graph support
### [GraphML](http://graphml.graphdrawing.org/)
GraphML files are currently:
- Parsing: Ok
- Writing: Ok
- Writing: **Unimplemented**

### [Workcraft](https://www.workcraft.org/)
Workcraft files are currently:
- Parsing: need to be unzipped manually and given a path to the `model.xml` file that was unzipped.
- Parsing: **Unimplemented**
- Writing: **Unimplemented**
114 changes: 56 additions & 58 deletions Test.hs
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,15 @@

module Main where

import Data.Maybe

import Data.ByteString.Char8 (pack)
import qualified Data.ByteString as BS
import qualified Pangraph as P
import qualified Pangraph.GraphML.Parser as GraphML_P
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GraphML is sufficiently clear as a qualification name.

import qualified Pangraph.VHDL.EnvironmentWriter as VHDL_E
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There seem to be no need to give different qualified names to VHDL modules, so use VHDL for both.

import qualified Pangraph.VHDL.GraphWriter as VHDL_G


type Parser = GraphML_P.Template -> BS.ByteString -> Either BS.ByteString P.Pangraph

main :: IO ()
main = do
putStrLn "\n------Testing begins------"
Expand All @@ -22,72 +21,71 @@ main = do
putStrLn "VHDL:"
testVHDL
putStrLn "\n------All Tests Passed------"
where

testShowInstance :: IO ()
testShowInstance = case a == (show b) of
True -> putStrLn "-Pangraph show instance passed."
False -> error $ "!Show instance failed test: " ++ show b ++ "!=" ++ a
where
a = fst showInstanceTestCase
b = snd showInstanceTestCase
showInstanceTestCase :: (String, P.Pangraph)
showInstanceTestCase = ("Pangraph [Node [Attribute (\"id\",\"0\")],Node [Attribute (\"id\",\"1\")]] [Edge [Attribute (\"source\",\"0\"),Attribute (\"target\",\"1\")]]",
P.makePangraph [P.makeNode [P.makeAttribute ("id","0")],P.makeNode [P.makeAttribute ("id","1")]] [P.makeEdge [P.makeAttribute ("source","0"),P.makeAttribute ("target","1")]])
testShowInstance = do
literal <-fmap (head . lines) $ readFile "test/show-string.txt"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should avoid reading files during tests. There is no point to test readFile, we should focus on testing our own code instead.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a test that the show instance has not been changed between commits unexpectedly, I just moved string literal into the text file to remove clutter from the where clause.

Copy link
Collaborator Author

@thisiswhereitype thisiswhereitype Jul 28, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the avoidance of IO to make it clear exactly what is being tested? I think maybe the test directory should mirror the src directory in terms of testing this will allow the files to be clearer and do less IO.

test/Pangraph.hs
test/Pangraph/GraphML.hs
             /VHDL.hs

let graph = fromMaybe
(error "Sample graph failed to build") $
P.makePangraph
sampleVertices [
P.makeEdge
[("source","0"), ("target","1")]
(head sampleVertices, sampleVertices !! 1)
]
sampleVertices = [P.makeVertex "0" [("id","0")]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sampleVertices should come first, as it's evaluated first.

,P.makeVertex "1" [ ("id","1")]]
if literal == show (graph :: P.Pangraph)
then putStrLn "-Instance passed"
else error
$ "!Show instance failed test: \""
++ literal
++ "\"\nSample(above) != Graph(below)\n\""
++ show graph
++ "\""

-- Without escapes:
-- Pangraph [Node [Attribute ("id","0")],Node [Attribute ("id","1")]] [Edge [Attribute ("source","0"),Attribute ("target","1")]]
-- makePangraph [makeVertex "0" [("id","0")],makeVertex "1" [("id","1")]] [makeEdge [("source","0"),("target","1")] (makeVertex "0" [("id","0")],makeVertex "1" [("id","1")])]

testGraphML :: IO ()
testGraphML = do
file <- fmap pack $ readFile path
case (parser (head GraphML_P.template) file) of
Left x -> error (show x)
Right y -> if y == graphs
then putStrLn "-Parse Passed"
else error $ "!Test failed\n" ++ show y
where
path :: FilePath
path = "examples/graphs/small.graphml"
parser :: Parser
parser = GraphML_P.parseTemplateToPangraph
graphs :: P.Pangraph
graphs =
P.makePangraph
[P.makeNode [P.makeAttribute ("id","n0")],
P.makeNode [P.makeAttribute ("id","n1")],
P.makeNode [P.makeAttribute ("id","n2")]]
[P.makeEdge
[P.makeAttribute ("source","n0"),
P.makeAttribute ("target","n2")]]
file <- BS.readFile "examples/graphs/small.graphml"
let graph = fromMaybe (error "Sample graph failed to build") graph'
graph' = P.makePangraph sampleVertices
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now it's much easier to read, but graph' should come first, as otherwise the reader again has to jump back and forth.

[P.makeEdge
[("source","n0"),("target","n2")]
(head sampleVertices, sampleVertices !! 2)]
sampleVertices = [
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same for sampleVertices -- this works thanks to laziness, but let's help the reader and write things in the evaluation order, whenever possible.

P.makeVertex "n0" [ ("id","n0")],
P.makeVertex "n1" [ ("id","n1")],
P.makeVertex "n2" [ ("id","n2")]]

if graph == GraphML_P.unsafeParse file
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is unsafe version used in the test? This relies on the fact that the current implementation actually reports an error, but it doesn't have to. I suggest use a safe parse function here and appropriately handle the erroneous case.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unsafe throws errors on Hexml returning an error or the graph being malformed which is checked still, the only difference is the code here does not need to handle a Maybe type.
Was it your intention that unsafe will not perform checking on the graph?

then putStrLn "-Parse Passed"
else error $ "!Test failed\n"
++ show graph
++ "\n!=\n"
++ show (GraphML_P.unsafeParse file)

testVHDL :: IO ()
testVHDL = do
files <- mapM BS.readFile graphPaths
vhdlEnvironment <- mapM readFile enviromentPaths
vhdlGraph <- mapM readFile networkPaths
files <- mapM BS.readFile ["examples/graphs/n1/n1.graphml",
"examples/graphs/n2/n2.graphml"]
vhdlEnvironment <- mapM readFile ["examples/graphs/n1/n1-sim-environment.vhdl",
"examples/graphs/n2/n2-sim-environment.vhdl"]
vhdlGraph <- mapM readFile ["examples/graphs/n1/n1-graph.vhdl",
"examples/graphs/n2/n2-graph.vhdl"]
let test1 = zip files vhdlEnvironment
let test2 = zip files vhdlGraph
putStrLn $ concatMap (\a -> applyTest a VHDL_E.writeEnvironmentVhdl) test1
putStrLn $ concatMap (\a -> applyTest a VHDL_G.writeGraphVhdl) test2

putStrLn $ concatMap (`applyTest` VHDL_E.writeEnvironmentVhdl) test1
putStrLn $ concatMap (`applyTest` VHDL_G.writeGraphVhdl) test2
where
-- args: A graphML file, the string which should result, the function which maps the file to result
applyTest :: (BS.ByteString, String) -> (P.Pangraph -> String) -> String
applyTest (g, example) f = case (parser (head GraphML_P.template) g ) of
Left x -> error (show x)
Right p -> case example == f p of
True -> "-VHDL passed test\n"
False -> error $ "!VHDL failed test : " ++ show example ++ "\n!=\n" ++ (show $ f p)


graphPaths :: [FilePath]
graphPaths = ["examples/graphs/n1/n1.graphml",
"examples/graphs/n2/n2.graphml"]
enviromentPaths :: [FilePath]
enviromentPaths = ["examples/graphs/n1/n1-sim-environment.vhdl",
"examples/graphs/n2/n2-sim-environment.vhdl"]
networkPaths :: [FilePath]
networkPaths = ["examples/graphs/n1/n1-graph.vhdl",
"examples/graphs/n2/n2-graph.vhdl"]
parser :: Parser
parser = GraphML_P.parseTemplateToPangraph
applyTest (raw, sample) f =
if show (GraphML_P.unsafeParse raw) == sample
then error $ "!VHDL failed test : "
++ show sample
++ "\n!=\n"
++ show (GraphML_P.unsafeParse raw)
else "-VHDL passed test\n"
13 changes: 0 additions & 13 deletions examples/code/Reading.hs

This file was deleted.

16 changes: 7 additions & 9 deletions fantasi/Tuura/Fantasi/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,11 @@ main = do
simulationEnvVhdlPath = optSimName options

-- parse graph
case P.parseTemplateToPangraph (head P.template) (pack graphMLPath) of
Left l -> error $ show l
Right graphParsed -> do
let graphVHDL = W2.writeGraphVhdl graphParsed
let simEnvVHDL = W1.writeEnvironmentVhdl graphParsed
let pangraph = P.unsafeParse (pack graphMLPath)
let graphVHDL = W2.writeGraphVhdl pangraph
let simEnvVHDL = W1.writeEnvironmentVhdl pangraph

-- output vhdl graph
writeFile graphVHDLPath graphVHDL
-- output vhdl simulation environment
writeFile simulationEnvVhdlPath simEnvVHDL
-- output vhdl graph
writeFile graphVHDLPath graphVHDL
-- output vhdl simulation environment
writeFile simulationEnvVhdlPath simEnvVHDL
4 changes: 3 additions & 1 deletion pangraph.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@ library
, Pangraph.GraphML.Parser
, Pangraph.VHDL.GraphWriter
, Pangraph.VHDL.EnvironmentWriter
other-modules: Pangraph.XMLTemplate
, Pangraph.XMLTemplate
other-modules: Pangraph.Examples.Reading
build-depends: base >= 4.8 && < 5
, bytestring
, hexml
, algebraic-graphs
, containers
default-language: Haskell2010
GHC-options: -Wall -fwarn-tabs -O2

Expand Down
Loading