From b62724c476e9a013094f0861f4e8f7a95909ae60 Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Tue, 8 Oct 2019 13:42:52 -0700 Subject: [PATCH] Tag interpretation implementation --- ptulsconv/transformations.py | 69 +++++++++++++++++++++++++++++++++++ tests/test_tag_interpreter.py | 41 +++++++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 tests/test_tag_interpreter.py diff --git a/ptulsconv/transformations.py b/ptulsconv/transformations.py index 6391d9c..8bba66a 100644 --- a/ptulsconv/transformations.py +++ b/ptulsconv/transformations.py @@ -1,4 +1,5 @@ from . import broadcast_timecode +from parsimonious import Grammar, NodeVisitor import math class Transformation: @@ -55,3 +56,71 @@ class TimecodeInterpreter(Transformation): return dict(frame_count=frame_count, logical_fps=lfps, drop_frame=drop_frame) +class TagInterpreter: + tag_grammar = Grammar( + r""" + document = modifier? line? word_sep? tag_list? + line = word (word_sep word)* + tag_list = tag* + tag = key_tag / short_tag / full_text_tag / tag_junk + key_tag = "[" key "]" word_sep? + short_tag = "$" key "=" word + full_text_tag = "{" key "=" value "}" word_sep? + key = ~"[A-Za-z][A-Za-z0-9_]*" + value = ~"[^}]+" + tag_junk = (word word_sep)* + word = ~"[^ \[\{\$][^ ]*" + word_sep = ~" +" + modifier = ("@" / "&") word_sep? + """ + ) + + class TagListVisitor(NodeVisitor): + def visit_document(self, _, visited_children): + modifier_opt, line_opt, _, tag_list_opt = visited_children + + return dict(line= next(iter(line_opt), None), + tags= next(iter(tag_list_opt), None), + mode= next(iter(modifier_opt), 'Normal') + ) + + def visit_line(self, node, _): + return str.strip(node.text, " ") + + def visit_modifier(self, node, _): + if node.text.startswith('@'): + return 'Timespan' + elif node.text.startswith('&'): + return 'Append' + else: + return 'Normal' + + def visit_tag_list(self, _, visited_children): + retdict = dict() + for child in visited_children: + k, v = child[0] + retdict[k] = v + return retdict + + def visit_key_tag(self, _, children): + return children[1].text, children[1].text + + def visit_short_tag(self, _, children): + return children[1].text, children[3].text + + def visit_full_text_tag(self, _, children): + return children[1].text, children[3].text + + def generic_visit(self, node, visited_children): + return visited_children or node + + def __init__(self): + pass + + def parse_tags(self, source): + parse_tree = self.tag_grammar.parse(source) + v = TagInterpreter.TagListVisitor() + return v.visit(parse_tree) + + + diff --git a/tests/test_tag_interpreter.py b/tests/test_tag_interpreter.py new file mode 100644 index 0000000..21a6445 --- /dev/null +++ b/tests/test_tag_interpreter.py @@ -0,0 +1,41 @@ +import unittest + +from ptulsconv.transformations import TagInterpreter + +class MyTestCase(unittest.TestCase): + def test_line(self): + ti = TagInterpreter() + s1 = ti.parse_tags("this is a test") + self.assertEqual(s1['line'], "this is a test") + self.assertEqual(s1['mode'], 'Normal') + self.assertEqual(len(s1['tags']), 0) + + s2 = ti.parse_tags("this! IS! Me! ** Typing! 123 <> |||") + self.assertEqual(s2['line'], "this! IS! Me! ** Typing! 123 <> |||") + self.assertEqual(s2['mode'], 'Normal') + self.assertEqual(len(s2['tags']), 0) + + def test_tags(self): + ti = TagInterpreter() + s1 = ti.parse_tags("{a=100}") + self.assertIn('tags', s1) + self.assertEqual(s1['tags']['a'], "100") + + s2 = ti.parse_tags("{b=This is a test} [option] $X=9") + self.assertEqual(s2['tags']['b'], 'This is a test') + self.assertEqual(s2['tags']['option'], 'option') + self.assertEqual(s2['tags']['X'], "9") + + def test_modes(self): + ti = TagInterpreter() + s1 = ti.parse_tags("@ Monday Tuesday {a=1}") + self.assertEqual(s1['mode'], 'Timespan') + + s2 = ti.parse_tags("Monday Tuesday {a=1}") + self.assertEqual(s2['mode'], 'Normal') + + s3 = ti.parse_tags("&Monday Tuesday {a=1}") + self.assertEqual(s3['mode'], 'Append') + +if __name__ == '__main__': + unittest.main()