Skip to content

Commit 5a81d6a

Browse files
ackerleytngNathan Parsons
authored andcommitted
Implement exercise dot-dsl (exercism#978)
1 parent 6ffba30 commit 5a81d6a

6 files changed

Lines changed: 279 additions & 0 deletions

File tree

config.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -763,6 +763,23 @@
763763
"object_oriented_programming"
764764
]
765765
},
766+
{
767+
"uuid": "a9c2fbda-a1e4-42dd-842f-4de5bb361b91",
768+
"slug": "dot-dsl",
769+
"core": false,
770+
"unlocked_by": null,
771+
"difficulty": 5,
772+
"topics": [
773+
"equality",
774+
"classes",
775+
"lists",
776+
"domain_specific_languages",
777+
"graphs",
778+
"object_oriented_programming",
779+
"test_driven_development",
780+
"transforming"
781+
]
782+
},
766783
{
767784
"uuid": "dc6e61a2-e9b9-4406-ba5c-188252afbba1",
768785
"slug": "transpose",

exercises/dot-dsl/.meta/hints.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
## Description of DSL
2+
3+
A graph, in this DSL, is an object of type `Graph`, taking a list of one
4+
or more
5+
6+
+ attributes
7+
+ nodes
8+
+ edges
9+
10+
described as tuples.
11+
12+
The implementations of `Node` and `Edge` provided in `dot_dsl.py`.
13+
14+
Observe the test cases in `dot_dsl_test.py` to understand the DSL's design.

exercises/dot-dsl/README.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Dot Dsl
2+
3+
Write a Domain Specific Language similar to the Graphviz dot language.
4+
5+
A [Domain Specific Language
6+
(DSL)](https://en.wikipedia.org/wiki/Domain-specific_language) is a
7+
small language optimized for a specific domain.
8+
9+
For example the dot language of [Graphviz](http://graphviz.org) allows
10+
you to write a textual description of a graph which is then transformed
11+
into a picture by one of the graphviz tools (such as `dot`). A simple
12+
graph looks like this:
13+
14+
graph {
15+
graph [bgcolor="yellow"]
16+
a [color="red"]
17+
b [color="blue"]
18+
a -- b [color="green"]
19+
}
20+
21+
Putting this in a file `example.dot` and running `dot example.dot -T png
22+
-o example.png` creates an image `example.png` with red and blue circle
23+
connected by a green line on a yellow background.
24+
25+
Create a DSL similar to the dot language.
26+
27+
## Description of DSL
28+
29+
A graph, in this DSL, is an object of type `Graph`, taking a list of one
30+
or more
31+
32+
+ attributes
33+
+ nodes
34+
+ edges
35+
36+
described as tuples.
37+
38+
The implementations of `Node` and `Edge` provided in `dot_dsl.py`.
39+
40+
Observe the test cases in `dot_dsl_test.py` to understand the DSL's design.
41+
42+
## Submitting Exercises
43+
44+
Note that, when trying to submit an exercise, make sure the solution is in the `exercism/python/<exerciseName>` directory.
45+
46+
For example, if you're submitting `bob.py` for the Bob exercise, the submit command would be something like `exercism submit <path_to_exercism_dir>/python/bob/bob.py`.
47+
48+
For more detailed information about running tests, code style and linting, please see the [help page](http://exercism.io/languages/python).
49+
50+
## Submitting Incomplete Solutions
51+
52+
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

exercises/dot-dsl/dot_dsl.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
NODE, EDGE, ATTR = range(3)
2+
3+
4+
class Node(object):
5+
def __init__(self, name, attrs={}):
6+
self.name = name
7+
self.attrs = attrs
8+
9+
def __eq__(self, other):
10+
return self.name == other.name and self.attrs == other.attrs
11+
12+
13+
class Edge(object):
14+
def __init__(self, src, dst, attrs={}):
15+
self.src = src
16+
self.dst = dst
17+
self.attrs = attrs
18+
19+
def __eq__(self, other):
20+
return (self.src == other.src and
21+
self.dst == other.dst and
22+
self.attrs == other.attrs)
23+
24+
25+
class Graph(object):
26+
def __init__(self, data=[]):
27+
pass

exercises/dot-dsl/dot_dsl_test.py

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import unittest
2+
3+
from dot_dsl import Graph, Node, Edge, NODE, EDGE, ATTR
4+
5+
6+
class DotDslTest(unittest.TestCase):
7+
def test_empty_graph(self):
8+
g = Graph()
9+
10+
self.assertEqual(g.nodes, [])
11+
self.assertEqual(g.edges, [])
12+
self.assertEqual(g.attrs, {})
13+
14+
def test_graph_with_one_node(self):
15+
g = Graph([
16+
(NODE, "a", {})
17+
])
18+
19+
self.assertEqual(g.nodes, [Node("a")])
20+
self.assertEqual(g.edges, [])
21+
self.assertEqual(g.attrs, {})
22+
23+
def test_graph_with_one_node_with_keywords(self):
24+
g = Graph([
25+
(NODE, "a", {"color": "green"})
26+
])
27+
28+
self.assertEqual(g.nodes, [Node("a", {"color": "green"})])
29+
self.assertEqual(g.edges, [])
30+
self.assertEqual(g.attrs, {})
31+
32+
def test_graph_with_one_edge(self):
33+
g = Graph([
34+
(EDGE, "a", "b", {})
35+
])
36+
37+
self.assertEqual(g.nodes, [])
38+
self.assertEqual(g.edges, [Edge("a", "b", {})])
39+
self.assertEqual(g.attrs, {})
40+
41+
def test_graph_with_one_attribute(self):
42+
g = Graph([
43+
(ATTR, "foo", "1")
44+
])
45+
46+
self.assertEqual(g.nodes, [])
47+
self.assertEqual(g.edges, [])
48+
self.assertEqual(g.attrs, {"foo": "1"})
49+
50+
def test_graph_with_attributes(self):
51+
g = Graph([
52+
(ATTR, "foo", "1"),
53+
(ATTR, "title", "Testing Attrs"),
54+
(NODE, "a", {"color": "green"}),
55+
(NODE, "c", {}),
56+
(NODE, "b", {"label", "Beta!"}),
57+
(EDGE, "b", "c", {}),
58+
(EDGE, "a", "b", {"color": "blue"}),
59+
(ATTR, "bar", "true")
60+
])
61+
62+
self.assertEqual(g.nodes, [Node("a", {"color": "green"}),
63+
Node("c", {}),
64+
Node("b", {"label", "Beta!"})])
65+
self.assertEqual(g.edges, [Edge("b", "c", {}),
66+
Edge("a", "b", {"color": "blue"})])
67+
self.assertEqual(g.attrs, {
68+
"foo": "1",
69+
"title": "Testing Attrs",
70+
"bar": "true"
71+
})
72+
73+
def test_malformed_graph(self):
74+
with self.assertRaises(TypeError):
75+
Graph(1)
76+
77+
with self.assertRaises(TypeError):
78+
Graph("problematic")
79+
80+
def test_malformed_graph_item(self):
81+
with self.assertRaises(TypeError):
82+
Graph([
83+
()
84+
])
85+
86+
with self.assertRaises(TypeError):
87+
Graph([
88+
(ATTR, )
89+
])
90+
91+
def test_malformed_attr(self):
92+
with self.assertRaises(ValueError):
93+
Graph([
94+
(ATTR, 1, 2, 3)
95+
])
96+
97+
def test_malformed_node(self):
98+
with self.assertRaises(ValueError):
99+
Graph([
100+
(NODE, 1, 2, 3)
101+
])
102+
103+
def test_malformed_EDGE(self):
104+
with self.assertRaises(ValueError):
105+
Graph([
106+
(EDGE, 1, 2)
107+
])
108+
109+
def test_unknown_item(self):
110+
with self.assertRaises(ValueError):
111+
Graph([
112+
(99, 1, 2)
113+
])
114+
115+
116+
if __name__ == '__main__':
117+
unittest.main()

exercises/dot-dsl/example.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
NODE, EDGE, ATTR = range(3)
2+
3+
4+
class Node(object):
5+
def __init__(self, name, attrs={}):
6+
self.name = name
7+
self.attrs = attrs
8+
9+
def __eq__(self, other):
10+
return self.name == other.name and self.attrs == other.attrs
11+
12+
13+
class Edge(object):
14+
def __init__(self, src, dst, attrs={}):
15+
self.src = src
16+
self.dst = dst
17+
self.attrs = attrs
18+
19+
def __eq__(self, other):
20+
return (self.src == other.src and
21+
self.dst == other.dst and
22+
self.attrs == other.attrs)
23+
24+
25+
class Graph(object):
26+
def __init__(self, data=[]):
27+
self.nodes = []
28+
self.edges = []
29+
self.attrs = {}
30+
31+
if not isinstance(data, list):
32+
raise TypeError("Graph data malformed")
33+
34+
for item in data:
35+
if len(item) < 3:
36+
raise TypeError("Graph item incomplete")
37+
38+
type_ = item[0]
39+
if type_ == ATTR:
40+
if len(item) != 3:
41+
raise ValueError("ATTR malformed")
42+
self.attrs[item[1]] = item[2]
43+
elif type_ == NODE:
44+
if len(item) != 3:
45+
raise ValueError("NODE malformed")
46+
self.nodes.append(Node(item[1], item[2]))
47+
elif type_ == EDGE:
48+
if len(item) != 4:
49+
raise ValueError("EDGE malformed")
50+
self.edges.append(Edge(item[1], item[2], item[3]))
51+
else:
52+
raise ValueError("Unknown item {}".format(item[0]))

0 commit comments

Comments
 (0)