-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
Implement exercise dot-dsl #978
Changes from 14 commits
76044f9
894953e
1baf9c9
6726f1f
c2986aa
27f7c8b
dd62afe
095b711
812140a
e6fec30
45da0f9
3ee16a1
3a98b60
e778b6d
c45c50d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
## Description of DSL | ||
|
||
A graph, in this DSL, is an object of type `Graph`, taking a list of one | ||
or more | ||
|
||
+ attributes | ||
+ nodes | ||
+ edges | ||
|
||
described as tuples. | ||
|
||
The implementations of `Node` and `Edge` provided in `dot_dsl.py`. | ||
|
||
Observe the test cases in `dot_dsl_test.py` to understand the DSL's design. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
# Dot Dsl | ||
|
||
Write a Domain Specific Language similar to the Graphviz dot language. | ||
|
||
A [Domain Specific Language | ||
(DSL)](https://en.wikipedia.org/wiki/Domain-specific_language) is a | ||
small language optimized for a specific domain. | ||
|
||
For example the dot language of [Graphviz](http://graphviz.org) allows | ||
you to write a textual description of a graph which is then transformed | ||
into a picture by one of the graphviz tools (such as `dot`). A simple | ||
graph looks like this: | ||
|
||
graph { | ||
graph [bgcolor="yellow"] | ||
a [color="red"] | ||
b [color="blue"] | ||
a -- b [color="green"] | ||
} | ||
|
||
Putting this in a file `example.dot` and running `dot example.dot -T png | ||
-o example.png` creates an image `example.png` with red and blue circle | ||
connected by a green line on a yellow background. | ||
|
||
Create a DSL similar to the dot language. | ||
|
||
## Description of DSL | ||
|
||
A graph, in this DSL, is an object of type `Graph`, taking a list of one | ||
or more | ||
|
||
+ attributes | ||
+ nodes | ||
+ edges | ||
|
||
described as tuples. | ||
|
||
The implementations of `Node` and `Edge` provided in `dot_dsl.py`. | ||
|
||
Observe the test cases in `dot_dsl_test.py` to understand the DSL's design. | ||
|
||
## Submitting Exercises | ||
|
||
Note that, when trying to submit an exercise, make sure the solution is in the `exercism/python/<exerciseName>` directory. | ||
|
||
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`. | ||
|
||
For more detailed information about running tests, code style and linting, please see the [help page](http://exercism.io/languages/python). | ||
|
||
## Submitting Incomplete Solutions | ||
|
||
It's possible to submit an incomplete solution so you can see how others have completed the exercise. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
NODE, EDGE, ATTR = range(3) | ||
|
||
|
||
class Node(object): | ||
def __init__(self, name, attrs={}): | ||
self.name = name | ||
self.attrs = attrs | ||
|
||
def __eq__(self, other): | ||
return self.name == other.name and self.attrs == other.attrs | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm wondering whether it might be better not to implement this but to instead hint that it should be implemented? I feel like rather a lot of code is being supplied pre-written, without the user having to think about how to check for equality. I think that we should either set a lower There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was concerned that without defining
requires Perhaps we could say that as part of TDD, the person working on this topic should seek to first complete There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looking at it again, I think it should be ok to leave it as it is - I'm not a great fan of supplying too much code, but the definition is also fairly easy and could border on being tedious. |
||
|
||
|
||
class Edge(object): | ||
def __init__(self, src, dst, attrs={}): | ||
self.src = src | ||
self.dst = dst | ||
self.attrs = attrs | ||
|
||
def __eq__(self, other): | ||
return (self.src == other.src and | ||
self.dst == other.dst and | ||
self.attrs == other.attrs) | ||
|
||
|
||
class Graph(object): | ||
def __init__(self, data=[]): | ||
pass |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
import unittest | ||
|
||
from dot_dsl import Graph, Node, Edge, NODE, EDGE, ATTR | ||
|
||
|
||
class DotDslTest(unittest.TestCase): | ||
def test_empty_graph(self): | ||
g = Graph() | ||
|
||
self.assertEqual(g.nodes, []) | ||
self.assertEqual(g.edges, []) | ||
self.assertEqual(g.attrs, {}) | ||
|
||
def test_graph_with_one_node(self): | ||
g = Graph([ | ||
(NODE, "a", {}) | ||
]) | ||
|
||
self.assertEqual(g.nodes, [Node("a")]) | ||
self.assertEqual(g.edges, []) | ||
self.assertEqual(g.attrs, {}) | ||
|
||
def test_graph_with_one_node_with_keywords(self): | ||
g = Graph([ | ||
(NODE, "a", {"color": "green"}) | ||
]) | ||
|
||
self.assertEqual(g.nodes, [Node("a", {"color": "green"})]) | ||
self.assertEqual(g.edges, []) | ||
self.assertEqual(g.attrs, {}) | ||
|
||
def test_graph_with_one_edge(self): | ||
g = Graph([ | ||
(EDGE, "a", "b", {}) | ||
]) | ||
|
||
self.assertEqual(g.nodes, []) | ||
self.assertEqual(g.edges, [Edge("a", "b", {})]) | ||
self.assertEqual(g.attrs, {}) | ||
|
||
def test_graph_with_one_attribute(self): | ||
g = Graph([ | ||
(ATTR, "foo", "1") | ||
]) | ||
|
||
self.assertEqual(g.nodes, []) | ||
self.assertEqual(g.edges, []) | ||
self.assertEqual(g.attrs, {"foo": "1"}) | ||
|
||
def test_graph_with_attributes(self): | ||
g = Graph([ | ||
(ATTR, "foo", "1"), | ||
(ATTR, "title", "Testing Attrs"), | ||
(NODE, "a", {"color": "green"}), | ||
(NODE, "c", {}), | ||
(NODE, "b", {"label", "Beta!"}), | ||
(EDGE, "b", "c", {}), | ||
(EDGE, "a", "b", {"color": "blue"}), | ||
(ATTR, "bar", "true") | ||
]) | ||
|
||
self.assertEqual(g.nodes, [Node("a", {"color": "green"}), | ||
Node("c", {}), | ||
Node("b", {"label", "Beta!"})]) | ||
self.assertEqual(g.edges, [Edge("b", "c", {}), | ||
Edge("a", "b", {"color": "blue"})]) | ||
self.assertEqual(g.attrs, { | ||
"foo": "1", | ||
"title": "Testing Attrs", | ||
"bar": "true" | ||
}) | ||
|
||
def test_malformed_graph(self): | ||
with self.assertRaises(TypeError): | ||
Graph(1) | ||
|
||
with self.assertRaises(TypeError): | ||
Graph("problematic") | ||
|
||
def test_malformed_graph_item(self): | ||
with self.assertRaises(TypeError): | ||
Graph([ | ||
() | ||
]) | ||
|
||
with self.assertRaises(TypeError): | ||
Graph([ | ||
(ATTR, ) | ||
]) | ||
|
||
def test_malformed_attr(self): | ||
with self.assertRaises(ValueError): | ||
Graph([ | ||
(ATTR, 1, 2, 3) | ||
]) | ||
|
||
def test_malformed_node(self): | ||
with self.assertRaises(ValueError): | ||
Graph([ | ||
(NODE, 1, 2, 3) | ||
]) | ||
|
||
def test_malformed_EDGE(self): | ||
with self.assertRaises(ValueError): | ||
Graph([ | ||
(EDGE, 1, 2) | ||
]) | ||
|
||
def test_unknown_item(self): | ||
with self.assertRaises(ValueError): | ||
Graph([ | ||
(99, 1, 2) | ||
]) | ||
|
||
|
||
if __name__ == '__main__': | ||
unittest.main() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
NODE, EDGE, ATTR = range(3) | ||
|
||
|
||
class Node(object): | ||
def __init__(self, name, attrs={}): | ||
self.name = name | ||
self.attrs = attrs | ||
|
||
def __eq__(self, other): | ||
return self.name == other.name and self.attrs == other.attrs | ||
|
||
|
||
class Edge(object): | ||
def __init__(self, src, dst, attrs={}): | ||
self.src = src | ||
self.dst = dst | ||
self.attrs = attrs | ||
|
||
def __eq__(self, other): | ||
return (self.src == other.src and | ||
self.dst == other.dst and | ||
self.attrs == other.attrs) | ||
|
||
|
||
class Graph(object): | ||
def __init__(self, data=[]): | ||
self.nodes = [] | ||
self.edges = [] | ||
self.attrs = {} | ||
|
||
if not isinstance(data, list): | ||
raise TypeError("Graph data malformed") | ||
|
||
for item in data: | ||
if len(item) < 3: | ||
raise TypeError("Graph item incomplete") | ||
|
||
type_ = item[0] | ||
if type_ == ATTR: | ||
if len(item) != 3: | ||
raise ValueError("ATTR malformed") | ||
self.attrs[item[1]] = item[2] | ||
elif type_ == NODE: | ||
if len(item) != 3: | ||
raise ValueError("NODE malformed") | ||
self.nodes.append(Node(item[1], item[2])) | ||
elif type_ == EDGE: | ||
if len(item) != 4: | ||
raise ValueError("EDGE malformed") | ||
self.edges.append(Edge(item[1], item[2], item[3])) | ||
else: | ||
raise ValueError("Unknown item {}".format(item[0])) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This section should also go into a
.meta/hints.md
file so that it doesn't get lost if we regenerate the READMEs in the future.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!