-
-
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 9 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 |
---|---|---|
|
@@ -171,6 +171,22 @@ | |
|
||
] | ||
}, | ||
{ | ||
"uuid": "a9c2fbda-a1e4-42dd-842f-4de5bb361b91", | ||
"slug": "dot-dsl", | ||
"core": false, | ||
"unlocked_by": null, | ||
"difficulty": 5, | ||
"topics": [ | ||
"equality", | ||
"classes", | ||
"lists", | ||
"maps", | ||
"object_oriented_programming", | ||
"test_driven_development", | ||
"transforming" | ||
] | ||
}, | ||
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. The current version of exercism goes through the exercises in the order specified in the config. I don't think that this should be a particularly early exercise, so can you move this section down to around line 750 or so (the exact location isn't too important). 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. Moved it lower! |
||
{ | ||
"uuid": "43eaf8bd-0b4d-4ea9-850a-773f013325ef", | ||
"slug": "anagram", | ||
|
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 | ||
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. This section should also go into a 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. Done! |
||
|
||
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 and len(item) != 4: | ||
raise TypeError("Graph item malformed") | ||
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. Is this check really necessary? Surely the conditionals below will achieve the same but with a more informative output? 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. hmm but without these checks,
might raise an Or should I just say
? 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. Well spotted, I think that would be a sensible change. 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. Done! |
||
|
||
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.
What's the reasoning for including
maps
,test_driven_development
andtransforming
?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.
When I left
maps
there, I was thinking of dictionaries. Now that I think of it again, I thinkmaps
probably means something else, in this case.For
object_oriented_programming
, I thought that since we were using classes, it was fair to leave that as a topic.For
test_driven_development
, I think there isn't any other way to complete this other than by reading through the test cases and building up the DSL, so I thought TDD is accurate here.As for
transforming
, I thought it was fair to say that DSLs are a transformation of one representation of data to another.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.
Thanks for your explanation. I don't think that
maps
is a suitable topic here, butdomain_specific_languages
andgraphs
would probably be a sensible additions.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!