Skip to content

Commit 4760381

Browse files
author
Issac Trotts
committed
Make str() pretty-print the tree. Remove compose().
1 parent 445474b commit 4760381

File tree

5 files changed

+60
-29
lines changed

5 files changed

+60
-29
lines changed

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,14 @@ or
1515
Usage
1616
-----
1717

18+
The API can be used as follows:
19+
1820
>>> import cmakelists_parsing.parsing as cmp
1921
>>> cmakelists_contents = 'FIND_PACKAGE(ITK REQUIRED) # Hello, CMake!'
2022
>>> cmp.parse(cmakelists_contents)
2123
File([Command([u'ITK', u'REQUIRED', u'# Hello, CMake!'])])
24+
25+
There is also a command line utility called cmake_pprint that pretty-prints
26+
CMake files:
27+
28+
$ cmake_pprint CMakeLists.txt

cmakelists_parsing/parsing.py

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,20 @@
99
_Arg = namedtuple('Arg', 'contents comments')
1010
_Command = namedtuple('Command', 'name body comment')
1111
BlankLine = namedtuple('BlankLine', '')
12-
File = namedtuple('File', 'contents')
13-
# TODO: if, else, endif, function, endfunction, macro, endmacro
12+
13+
class File(list):
14+
"""Top node of the syntax tree for a CMakeLists file."""
15+
16+
def __str__(self):
17+
'''
18+
Returns the pretty-print string for tree
19+
with indentation given by the string tab.
20+
'''
21+
return '\n'.join(compose_lines(self)) + '\n'
22+
23+
def __repr__(self):
24+
return 'File(' + repr(list(self)) + ')'
25+
1426
class Comment(str):
1527
def __repr__(self):
1628
return 'Comment(' + str(self) + ')'
@@ -24,6 +36,9 @@ def Command(name, body, comment=None):
2436
class CMakeParseError(Exception):
2537
pass
2638

39+
def prettify(s):
40+
return str(parse(s))
41+
2742
def parse(s, path='<string>'):
2843
'''
2944
Parses a string s in CMakeLists format whose
@@ -37,19 +52,13 @@ def parse(s, path='<string>'):
3752
return File(items)
3853

3954
def strip_blanks(tree):
40-
return File([x for x in tree.contents if not isinstance(x, BlankLine)])
55+
return File([x for x in tree if not isinstance(x, BlankLine)])
4156

42-
def compose(tree, tab=' '):
43-
'''
44-
Returns the pretty-print string for tree
45-
with indentation given by the string tab.
46-
'''
47-
return '\n'.join(compose_lines(tree.contents, tab)) + '\n'
48-
49-
def compose_lines(tree_contents, tab):
57+
def compose_lines(tree_contents):
5058
"""
5159
Yields pretty-printed lines of a CMakeLists file.
5260
"""
61+
tab = ' '
5362
level = 0
5463
for item in tree_contents:
5564
if isinstance(item, (Comment, str)):
@@ -164,6 +173,7 @@ def tokenize(s):
164173
line_num += tok_contents.count('\n')
165174

166175
def main():
176+
# Parse arguments
167177
import argparse
168178
import sys
169179
parser = argparse.ArgumentParser(description='Pretty-print CMakeLists files.')
@@ -172,18 +182,24 @@ def main():
172182
parser.add_argument('-t', '--tree', action='store_true',
173183
help='print out the syntax trees')
174184
args = parser.parse_args()
185+
186+
# Gather files
175187
filenames = args.files
176-
files = (open(f) for f in filenames) if filenames else [sys.stdin]
177-
for f in files:
178-
with f:
179-
input = f.read()
180-
tree = parse(input)
188+
files = [('<stdin>', sys.stdin)]
189+
if filenames:
190+
files = [(name, open(name)) for name in filenames]
191+
192+
# Process files
193+
for (name, file) in files:
194+
with file:
195+
input = file.read()
196+
tree = parse(input, path=name)
181197
if args.tree:
182198
# Print out AST
183-
print(str(tree))
199+
print(repr(tree))
184200
else:
185201
# Pretty print
186-
print(compose(tree), end='')
202+
print(str(tree), end='')
187203

188204
if __name__ == '__main__':
189205
main()

example_inputs/macro.txt

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,3 @@ macro(hello MESSAGE)
44
endmacro(hello)
55
# call the macro with the string "hello world"
66
hello("hello world")
7-
# define a function hello
8-
function(hello MESSAGE)
9-
message(${MESSAGE})
10-
endfunction(hello)

tests/example_inputs_test.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ def yield_examples():
1414

1515
class ExamplesTestCase(unittest.TestCase):
1616
def test_idempotency_of_parse_unparse(self):
17-
round_trip = lambda s, path='<string>': cmp.compose(cmp.parse(s, path))
17+
round_trip = lambda s, path='<string>': str(cmp.parse(s, path))
1818
for path, contents in yield_examples():
1919
self.assertEqual(round_trip(contents, path),
2020
round_trip(round_trip(contents, path)),
@@ -23,6 +23,6 @@ def test_idempotency_of_parse_unparse(self):
2323
def test_tree_is_unchanged(self):
2424
for path, contents in yield_examples():
2525
expected = cmp.parse(contents, path)
26-
actual = cmp.parse(cmp.compose(cmp.parse(contents, path)))
26+
actual = cmp.parse(str(cmp.parse(contents, path)))
2727
msg = 'Failed on %s.\nExpected\n%s\n\nGot\n%s' % (path, expected, actual)
2828
self.assertEqual(expected, actual, msg)

tests/parsing_test.py

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import unittest
44

55
from cmakelists_parsing.parsing import (
6-
File, Command, Comment, BlankLine, Arg, parse, compose)
6+
File, Command, Comment, BlankLine, Arg, parse, prettify)
77

88
class ParsingTestCase(unittest.TestCase):
99
def setUp(self):
@@ -55,7 +55,7 @@ def test_idempotency_of_parsing_and_unparsing(self):
5555
FIND_PACKAGE(ITK REQUIRED)
5656
INCLUDE(${ITK_USE_FILE})
5757
'''
58-
round_trip = lambda s: compose(parse(s))
58+
round_trip = lambda s: str(parse(s))
5959
self.assertEqual(round_trip(input), round_trip(round_trip(input)))
6060

6161
def test_invalid_format_raises_an_exception(self):
@@ -97,7 +97,7 @@ def test_arg_comments_preserved(self):
9797
x # inline comment about x
9898
)
9999
'''
100-
output = compose(parse(input))
100+
output = str(parse(input))
101101

102102
def test_comments_preserved(self):
103103
input = '''\
@@ -111,7 +111,7 @@ def test_comments_preserved(self):
111111
"y" # inline comment about a quoted string "y"
112112
) # inline comment for Command2
113113
'''
114-
output = compose(parse(input))
114+
output = str(parse(input))
115115

116116
self.assertMultiLineEqual(input, output)
117117

@@ -141,7 +141,19 @@ def test_ifs_indented(self):
141141
endif(c)
142142
endif(a)
143143
'''
144-
self.assertMultiLineEqual(input, compose(parse(input)))
144+
self.assertMultiLineEqual(input, str(parse(input)))
145+
146+
def test_functions_indented(self):
147+
input = '''
148+
macro(hello MESSAGE)
149+
message(${MESSAGE})
150+
endmacro(hello) # call the macro with the string "hello world"
151+
hello("hello world")
152+
'''
153+
self.assertUnchangedByPrettyPrinting(input)
154+
155+
def assertUnchangedByPrettyPrinting(self, input):
156+
self.assertMultiLineEqual(input, prettify(input))
145157

146158
if __name__ == '__main__':
147159
unittest.main()

0 commit comments

Comments
 (0)