def test_repeated_layouts(self): class Block(Node): pass def space_layout(dispatcher, node, before, after, prev): yield SimpleChunk(' ') def newline_layout(dispatcher, node, before, after, prev): yield SimpleChunk('n') dispatcher = Dispatcher( definitions={ 'Node': (Space, ), 'Block': (JoinAttr(Iter(), value=(Newline, )), ) }, token_handler=None, # not actually produced layout_handlers={ Space: space_layout, Newline: newline_layout, # drop the subsequent space (Newline, Space): newline_layout, }, deferrable_handlers={}, ) n0 = Block([]) self.assertEqual('', ''.join(c.text for c in walk(dispatcher, n0))) n1 = Block([Node([])] * 1) self.assertEqual(' ', ''.join(c.text for c in walk(dispatcher, n1))) n2 = Block([Node([])] * 2) self.assertEqual(' n', ''.join(c.text for c in walk(dispatcher, n2))) n3 = Block([Node([])] * 3) self.assertEqual(' nn', ''.join(c.text for c in walk(dispatcher, n3)))
def test_deferrable_handler_literal_continuation(self): dispatcher = Dispatcher({}, None, {}, {}) node = Node() node.value = '"foo\\\r\nbar"' self.assertEqual( '"foobar"', deferrable_handler_literal_continuation(dispatcher, node))
def test_nested_layouts(self): def simple_layout_space(dispatcher, node, before, after, prev): yield SimpleChunk(' ') def nested_layout(dispatcher, node, before, after, prev): yield SimpleChunk('?') def noop(*a, **kw): return yield # pragma: no cover dispatcher = Dispatcher( definitions={'Node': ( Space, JoinAttr(Iter(), value=(Space, )), )}, token_handler=noop, layout_handlers={ Space: simple_layout_space, (Space, Space): noop, ((Space, Space), Space): nested_layout, }, deferrable_handlers={}, ) n0 = Node([]) self.assertEqual(' ', ''.join(c.text for c in walk(dispatcher, n0))) n1 = Node([n0]) self.assertEqual('', ''.join(c.text for c in walk(dispatcher, n1))) n2 = Node([n1, n0]) self.assertEqual('?', ''.join(c.text for c in walk(dispatcher, n2)))
def test_layout_handler_newline_optional_pretty(self): # yes using the lteral <CR><LF> is pretty hilarious, but just to # show that this is implemented to support whatever. dispatcher = Dispatcher({}, None, {}, {}, newline_str='<CR><LF>') newline = [('<CR><LF>', 0, 0, None, None)] def run(before, after, prev): return list( layout_handler_newline_optional_pretty( # node is not used. dispatcher, None, before, after, prev)) # brand new empty program self.assertEqual(run(None, None, None), empty) # first line of a program self.assertEqual(run(None, 'function', None), empty) # previous token produced a newline self.assertEqual(run('\n', 'function', None), empty) # next token produced a newline self.assertEqual(run('}', '\n', None), empty) # Previous layout produced a newline self.assertEqual(run('}', ' ', '\n'), empty) # Previous layout produced a newline, with the custom str self.assertEqual(run('}', ' ', '<CR><LF>'), empty) # The first layout rule self.assertEqual(run(';', 'function', None), newline)
def test_space_minimum(self): # initialise a barebone dispatcher. dispatcher = Dispatcher({}, None, {}, {}) def run(a, b): return list( layout_handler_space_minimum( # node and prev are not used. dispatcher, None, a, b, None)) self.assertEqual(run(None, None), empty) # for Assign self.assertEqual(run('a', ':'), empty) self.assertEqual(run('a', '='), empty) # for Unary self.assertEqual(run('!', 'a'), empty) self.assertEqual(run('f', '1'), space) self.assertEqual(run('1', 'f'), space) self.assertEqual(run('f', '+'), empty) self.assertEqual(run('f', '-'), empty) self.assertEqual(run('-', 'f'), empty) self.assertEqual(run('+', 'f'), empty) self.assertEqual(run('+', '-'), empty) self.assertEqual(run('-', '-'), space) self.assertEqual(run('+', '+'), space) self.assertEqual(run(',', 'y'), empty) self.assertEqual(run('1', ','), empty)
def walk(self, dispatcher, node): """ Walk through the node with a custom dispatcher for extraction of details that are required. """ deferrable_handlers = { Declare: self.declare, Resolve: self.register_reference, } layout_handlers = { PushScope: self.push_scope, PopScope: self.pop_scope, PushCatch: self.push_catch, # should really be different, but given that the # mechanism is within the same tree, the only difference # would be sanity check which should have been tested in # the first place in the primitives anyway. PopCatch: self.pop_scope, } if not self.shadow_funcname: layout_handlers[ResolveFuncName] = self.shadow_reference local_dispatcher = Dispatcher( definitions=dict(dispatcher), token_handler=None, layout_handlers=layout_handlers, deferrable_handlers=deferrable_handlers, ) return list(walk(local_dispatcher, node))
def test_indentation(self): # initialise a barebone dispatcher. dispatcher = Dispatcher({}, None, {}, {}, indent_str='<TAB>') layout = indent()()['layout_handlers'] newline = ('\n', 0, 0, None, None) indent1 = ('<TAB>', None, None, None, None) indent2 = ('<TAB><TAB>', None, None, None, None) def run(rule, before=None): return layout[rule](dispatcher, None, before, None, None) self.assertEqual(list(run(Newline)), [newline]) self.assertIsNone(run(Indent)) self.assertEqual(list(run(Newline)), [newline, indent1]) self.assertIsNone(run(Indent)) self.assertEqual(list(run(Newline)), [newline, indent2]) self.assertIsNone(run(Dedent)) self.assertEqual(list(run(Newline)), [newline, indent1]) self.assertIsNone(run(Dedent)) self.assertEqual(list(run(Newline)), [newline]) # negative shouldn't matter self.assertIsNone(run(Dedent)) self.assertEqual(list(run(Newline)), [newline]) # should move it back on the right track. self.assertIsNone(run(Indent)) self.assertEqual(list(run(Newline)), [newline]) layout = indent(indent_str=' ')()['layout_handlers'] self.assertIsNone(run(Indent)) self.assertEqual(list(run(Newline)), [newline, (' ', None, None, None, None)]) self.assertEqual(list(run(OptionalNewline, before='\n')), [])
def test_core_structures(self): # initialise a barebone dispatcher. node = Node() dispatcher = Dispatcher({}, None, {}, {}) self.assertEqual([( '{', 0, 0, None, None, )], list(layout_handler_openbrace(dispatcher, node, None, None, None))) self.assertEqual([( '}', 0, 0, None, None, )], list(layout_handler_closebrace(dispatcher, node, None, None, None))) self.assertEqual([( ';', 0, 0, None, None, )], list(layout_handler_semicolon(dispatcher, node, None, None, None))) # with token map node._token_map = { '{': [(0, 1, 1)], '}': [(1, 1, 2)], ';': [(2, 1, 3)], } self.assertEqual([( '{', 1, 1, None, None, )], list(layout_handler_openbrace(dispatcher, node, None, None, None))) self.assertEqual([( '}', 1, 2, None, None, )], list(layout_handler_closebrace(dispatcher, node, None, None, None))) self.assertEqual([( ';', 1, 3, None, None, )], list(layout_handler_semicolon(dispatcher, node, None, None, None)))
def test_space_optional_pretty(self): # initialise a barebone dispatcher. dispatcher = Dispatcher({}, None, {}, {}) def run(a, b): return list( layout_handler_space_optional_pretty( # node and prev are not used. dispatcher, None, a, b, None)) # also test out the cases where OptionalSpace was defined. self.assertEqual(run(None, None), empty) self.assertEqual(run('a', None), empty) self.assertEqual(run(None, 'a'), empty) # for Assign self.assertEqual(run('a', ':'), empty) self.assertEqual(run('a', '='), space) self.assertEqual(run('1', '='), space) self.assertEqual(run('a', '+='), space) self.assertEqual(run('a', '-='), space) self.assertEqual(run('a', '*='), space) self.assertEqual(run('a', '/='), space) self.assertEqual(run('a', '%='), space) self.assertEqual(run('a', '&='), space) self.assertEqual(run('a', '^='), space) self.assertEqual(run('a', '|='), space) self.assertEqual(run('a', '<<='), space) self.assertEqual(run('a', '>>='), space) self.assertEqual(run('a', '>>>='), space) # these rules are not defined, since they typically shouldn't # happen and that BinOp rules should use Space. self.assertEqual(run('+', 'a'), empty) self.assertEqual(run('-', 'a'), empty) self.assertEqual(run('*', 'a'), empty) self.assertEqual(run('/', 'a'), empty) # for Unary self.assertEqual(run('!', 'a'), empty) self.assertEqual(run('f', '1'), space) self.assertEqual(run('1', 'f'), space) self.assertEqual(run('f', '++'), empty) self.assertEqual(run('f', '--'), empty) self.assertEqual(run('--', 'f'), empty) self.assertEqual(run('++', 'f'), empty) self.assertEqual(run('+', '-'), empty) self.assertEqual(run('-', '+'), empty) self.assertEqual(run('-', '-'), space) self.assertEqual(run('+', '+'), space)
def test_bad_definition_rule(self): (token_handler, layout_handlers, deferrable_handlers, self.declared_vars) = setup_handlers(self) with self.assertRaises(TypeError) as e: Dispatcher( definitions={'Node': (object(), )}, token_handler=token_handler, layout_handlers=layout_handlers, deferrable_handlers=deferrable_handlers, ) self.assertIn( "definition for 'Node' contain unsupported rule (got:", e.exception.args[0], )
def setup_defaults(self): # provide just enough of the everything that is required. token_handler, layout_handlers, deferrable_handlers, declared_vars = ( setup_handlers(self)) self.dispatcher = Dispatcher( definitions={ 'ES5Program': ( children_newline, Newline, ), 'VarStatement': ( Text(value='var'), Space, children_comma, Text(value=';'), ), 'VarDecl': ( Attr(Declare('identifier')), Space, Operator(value='='), Space, Attr('initializer'), ), 'Identifier': (Attr(Resolve()), ), 'PropIdentifier': (Attr('value'), ), 'Number': (Attr('value'), ), 'DotAccessor': ( Attr('node'), Text(value='.'), Attr('identifier'), ), 'FunctionCall': ( Attr('identifier'), Attr('args'), ), 'Arguments': ( Text(value='('), JoinAttr('items', value=(Text(value=','), Space)), Text(value=')'), ), }, token_handler=token_handler, layout_handlers=layout_handlers, deferrable_handlers=deferrable_handlers, ) self.declared_vars = declared_vars
def test_join_attr_issue_36(self): # JoinAttr defined with parameter `value=None` resulted in # infinite recursion due to the walker will assume that no rule # is provided, triggering rule lookup which would repeat the # work that was done. class Block(Node): pass token_handler, layout_handlers, deferrable_handlers, declared_vars = ( setup_handlers(self)) dispatcher = Dispatcher( definitions={ 'Block': (JoinAttr(Iter(), value=None), ), 'Node': (Text(value='', ), ), }, token_handler=token_handler, layout_handlers={}, deferrable_handlers={}, ) nodes = Block([Node([])] * 3) self.assertEqual(3, len(list(walk(dispatcher, nodes))))
def test_clone_definitions(self): marker = tuple() dispatcher = Dispatcher({'Node': marker}, {}, {}, {}) self.assertEqual(dict(dispatcher), {'Node': marker})
def test_empty(self): dispatcher = Dispatcher({}, {}, {}, {}) self.assertEqual(dict(dispatcher), {})