From a91ac948b037f58217d89553fa031a18960b9623 Mon Sep 17 00:00:00 2001 From: Lars Wirzenius Date: Sun, 9 Jun 2013 11:56:29 +0100 Subject: Implement BlockParser --- yarnlib/block_parser.py | 112 +++++++++++++++++++++++++++++++++++++++++- yarnlib/block_parser_tests.py | 14 +++--- 2 files changed, 118 insertions(+), 8 deletions(-) (limited to 'yarnlib') diff --git a/yarnlib/block_parser.py b/yarnlib/block_parser.py index 8fd87d2..09a7a22 100644 --- a/yarnlib/block_parser.py +++ b/yarnlib/block_parser.py @@ -16,6 +16,116 @@ # =*= License: GPL-3+ =*= +import yarnlib + + +# Parse a sequence of textual blocks into Story and Implementation +# objects, and their constituent objects. + class BlockParser(object): - pass + def __init__(self): + self.stories = [] + self.implementations = [] + self.line_parsers = { + 'STORY': self.parse_story, + 'GIVEN': self.parse_given, + 'WHEN': self.parse_when, + 'THEN': self.parse_then, + 'FINALLY': self.parse_finally, + 'AND': self.parse_and, + 'IMPLEMENTS': self.parse_implementing, + } + + def parse_blocks(self, blocks): + while blocks: + blocks = self.parse_one(blocks) + + def parse_one(self, blocks): + assert blocks + block = blocks[0] + assert block + t = block.split('\n', 1) + assert len(t) in [1,2] + if len(t) == 1: + line1 = block + block = '' + else: + line1, block = t + if block: + blocks[0] = block + else: + del blocks[0] + + words = line1.split() + if not words: + return blocks + rest = ' '.join(words[1:]) + + for keyword in self.line_parsers: + if words[0] == keyword: + return self.line_parsers[keyword](rest, blocks) + + raise StoryTestSyntaxError("Syntax error: unknown step: %s" % line1) + + def parse_story(self, line, blocks): + self.stories.append(yarnlib.Story(line)) + return blocks + + def parse_simple(self, what, line, blocks): + if not self.stories: + raise StoryTestSyntaxError('Syntax errror: %s before STORY' % what) + step = yarnlib.StoryStep(what, line) + self.stories[-1].steps.append(step) + return blocks + + def parse_given(self, line, blocks): + return self.parse_simple('GIVEN', line, blocks) + + def parse_when(self, line, blocks): + return self.parse_simple('WHEN', line, blocks) + + def parse_then(self, line, blocks): + return self.parse_simple('THEN', line, blocks) + + def parse_finally(self, line, blocks): + return self.parse_simple('FINALLY', line, blocks) + + def parse_and(self, line, blocks): + if not self.stories: + raise StoryTestSyntaxError('Syntax errror: AND before STORY') + story = self.stories[-1] + if not story.steps: + raise StoryTestSyntaxError( + 'Syntax errror: AND before what it would continue') + step = story.steps[-1] + assert step.what in self.line_parsers + return self.line_parsers[step.what](line, blocks) + + def parse_implementing(self, line, blocks): + words = line.split() + if len(words) < 2: + raise StoryTestSyntaxError( + 'Syntax error: IMPLEMENTING must have what and regexp') + what = words[0] + regexp = ' '.join(words[1:]) + if blocks: + block = blocks[0] + shell = [] + rest = [] + for block_line in block.splitlines(): + if rest or block_line.startswith('IMPLEMENTS'): + rest.append(block_line) + else: + shell.append(block_line) + shell = '\n'.join(shell) + if rest: + blocks[0] = '\n'.join(rest) + else: + del blocks[0] + else: + shell = '' + implementation = yarnlib.Implementation(what, regexp, shell) + self.implementations.append(implementation) + return blocks + diff --git a/yarnlib/block_parser_tests.py b/yarnlib/block_parser_tests.py index 1c14a80..ff1feab 100644 --- a/yarnlib/block_parser_tests.py +++ b/yarnlib/block_parser_tests.py @@ -42,17 +42,17 @@ class BlockParserTests(unittest.TestCase): self.assertEqual(len(story.steps), 3) self.assertEqual(story.steps[0].what, 'GIVEN') self.assertEqual(story.steps[0].text, 'bar') - self.assertEqual(story.steps[0].what, 'WHEN') - self.assertEqual(story.steps[0].text, 'foobar') - self.assertEqual(story.steps[0].what, 'THEN') - self.assertEqual(story.steps[0].text, 'yoyo') + self.assertEqual(story.steps[1].what, 'WHEN') + self.assertEqual(story.steps[1].text, 'foobar') + self.assertEqual(story.steps[2].what, 'THEN') + self.assertEqual(story.steps[2].text, 'yoyo') def test_normalises_whitespace(self): - self.parser.parse_code_blocks(['STORY foo bar ']) + self.parser.parse_blocks(['STORY foo bar ']) self.assertEqual(self.parser.stories[0].name, 'foo bar') def test_parses_implements_in_a_block_by_itself(self): - self.parser.parse_code_blocks(['IMPLEMENTS GIVEN foo\ntrue']) + self.parser.parse_blocks(['IMPLEMENTS GIVEN foo\ntrue']) impls = self.parser.implementations self.assertEqual(len(impls), 1) self.assertEqual(impls[0].what, 'GIVEN') @@ -60,7 +60,7 @@ class BlockParserTests(unittest.TestCase): self.assertEqual(impls[0].shell, 'true') def test_parses_two_implements_in_a_code_block(self): - self.parser.parse_code_blocks( + self.parser.parse_blocks( ['IMPLEMENTS GIVEN foo\ntrue\nIMPLEMENTS WHEN bar\ncat /dev/null']) impls = self.parser.implementations self.assertEqual(len(impls), 2) -- cgit v1.2.1