Skip to content
Snippets Groups Projects
Commit 6bacfd87 authored by Mike Bayer's avatar Mike Bayer
Browse files

more logic for control lines, ternaries

parent 7dac1fc2
No related branches found
No related tags found
No related merge requests found
...@@ -11,6 +11,7 @@ class Lexer(object): ...@@ -11,6 +11,7 @@ class Lexer(object):
self.lineno = 1 self.lineno = 1
self.match_position = 0 self.match_position = 0
self.tag = [] self.tag = []
self.control_line = []
def match(self, regexp, flags=None): def match(self, regexp, flags=None):
"""match the given regular expression string and flags to the current text position. """match the given regular expression string and flags to the current text position.
...@@ -49,7 +50,15 @@ class Lexer(object): ...@@ -49,7 +50,15 @@ class Lexer(object):
self.nodes.append(node) self.nodes.append(node)
if isinstance(node, parsetree.Tag): if isinstance(node, parsetree.Tag):
self.tag.append(node) self.tag.append(node)
elif isinstance(node, parsetree.ControlLine):
if node.isend:
self.control_line.pop()
elif node.is_primary:
self.control_line.append(node)
elif len(self.control_line) and not self.control_line[-1].is_ternary(node.keyword):
raise exceptions.SyntaxException("Keyword '%s' not a legal ternary for keyword '%s'" % (node.keyword, self.control_line[-1].keyword), self.matched_lineno, self.matched_charpos)
def parse(self): def parse(self):
length = len(self.text) length = len(self.text)
while (True): while (True):
...@@ -134,7 +143,6 @@ class Lexer(object): ...@@ -134,7 +143,6 @@ class Lexer(object):
\Z # end of string \Z # end of string
)""", re.X | re.S) )""", re.X | re.S)
if match: if match:
text = match.group(1) text = match.group(1)
self.append_node(parsetree.Text, text) self.append_node(parsetree.Text, text)
...@@ -175,6 +183,12 @@ class Lexer(object): ...@@ -175,6 +183,12 @@ class Lexer(object):
raise exceptions.SyntaxException("Invalid control line: '%s'" % text, self.matched_lineno, self.matched_charpos) raise exceptions.SyntaxException("Invalid control line: '%s'" % text, self.matched_lineno, self.matched_charpos)
(isend, keyword) = m2.group(1, 2) (isend, keyword) = m2.group(1, 2)
isend = (isend is not None) isend = (isend is not None)
if isend:
if not len(self.control_line):
raise exceptions.SyntaxException("No starting keyword '%s' for '%s'" % (keyword, text), self.matched_lineno, self.matched_charpos)
elif self.control_line[-1].keyword != keyword:
raise exceptions.SyntaxException("Keyword '%s' doesn't match keyword '%s'" % (text, self.control_line[-1].keyword), self.matched_lineno, self.matched_charpos)
self.append_node(parsetree.ControlLine, keyword, isend, text) self.append_node(parsetree.ControlLine, keyword, isend, text)
else: else:
self.append_node(parsetree.Comment, text) self.append_node(parsetree.Comment, text)
......
"""object model defining a Mako template.""" """object model defining a Mako template."""
from mako import exceptions, ast from mako import exceptions, ast, util
class Node(object): class Node(object):
"""base class for a Node in the parse tree.""" """base class for a Node in the parse tree."""
...@@ -18,13 +18,22 @@ class ControlLine(Node): ...@@ -18,13 +18,22 @@ class ControlLine(Node):
(markup) (markup)
% endif % endif
""" """
def __init__(self, keyword, text, isend, **kwargs): def __init__(self, keyword, isend, text, **kwargs):
super(ControlLine, self).__init__(**kwargs) super(ControlLine, self).__init__(**kwargs)
self.text = text self.text = text
self.keyword = keyword self.keyword = keyword
self.isend = isend self.isend = isend
self.is_primary = keyword in ['for','if', 'while', 'try']
def is_ternary(self, keyword):
"""return true if the given keyword is a ternary keyword for this ControlLine"""
return keyword in {
'if':util.Set(['else', 'elif']),
'try':util.Set(['except', 'finally']),
'for':util.Set(['else'])
}.get(self.keyword, [])
def __repr__(self): def __repr__(self):
return "ControlLine(%s, %s, %s, %s)" % (repr(self.keyword), repr(self.isend), repr(self.text), repr((self.lineno, self.pos))) return "ControlLine(%s, %s, %s, %s)" % (repr(self.keyword), repr(self.text), repr(self.isend), repr((self.lineno, self.pos)))
class Text(Node): class Text(Node):
"""defines plain text in the template.""" """defines plain text in the template."""
......
...@@ -125,10 +125,52 @@ text text la la ...@@ -125,10 +125,52 @@ text text la la
""" """
nodes = Lexer(template).parse() nodes = Lexer(template).parse()
print nodes #print nodes
assert repr(nodes) == r"""[Text('\ntext text la la\n', (1, 1)), ControlLine('if', 'if foo():', False, (3, 1)), Text(' mroe text la la blah blah\n', (4, 1)), ControlLine('if', 'endif', True, (5, 1)), Text('\n and osme more stuff\n', (6, 1)), ControlLine('for', 'for l in range(1,5):', False, (8, 1)), Text(' tex tesl asdl l is ', (9, 1)), Expression('l', [], (9, 24)), Text(' kfmas d\n', (9, 28)), ControlLine('for', 'endfor', True, (10, 1)), Text(' tetx text\n \n', (11, 1))]""" assert repr(nodes) == r"""[Text('\ntext text la la\n', (1, 1)), ControlLine('if', 'if foo():', False, (3, 1)), Text(' mroe text la la blah blah\n', (4, 1)), ControlLine('if', 'endif', True, (5, 1)), Text('\n and osme more stuff\n', (6, 1)), ControlLine('for', 'for l in range(1,5):', False, (8, 1)), Text(' tex tesl asdl l is ', (9, 1)), Expression('l', [], (9, 24)), Text(' kfmas d\n', (9, 28)), ControlLine('for', 'endfor', True, (10, 1)), Text(' tetx text\n \n', (11, 1))]"""
def test_unmatched_control(self):
template = """
% if foo:
% for x in range(1,5):
% endif
"""
try:
nodes = Lexer(template).parse()
assert False
except exceptions.SyntaxException, e:
assert str(e) == "Keyword 'endif' doesn't match keyword 'for' at line: 5 char: 1"
def test_unmatched_control_2(self):
template = """
% if foo:
% for x in range(1,5):
% endlala
% endif
"""
try:
nodes = Lexer(template).parse()
assert False
except exceptions.SyntaxException, e:
assert str(e) == "Keyword 'endlala' doesn't match keyword 'for' at line: 5 char: 1"
def test_ternary_control(self):
template = """
% if x:
hi
% elif y+7==10:
there
% elif lala:
lala
% else:
hi
% endif
"""
nodes = Lexer(template).parse()
#print nodes
assert repr(nodes) == r"""[ControlLine('if', 'if x:', False, (1, 1)), Text(' hi\n', (3, 1)), ControlLine('elif', 'elif y+7==10:', False, (4, 1)), Text(' there\n', (5, 1)), ControlLine('elif', 'elif lala:', False, (6, 1)), Text(' lala\n', (7, 1)), ControlLine('else', 'else:', False, (8, 1)), Text(' hi\n', (9, 1)), ControlLine('if', 'endif', True, (10, 1))]"""
def test_integration(self): def test_integration(self):
template = """<%namespace name="foo" file="somefile.html"/> template = """<%namespace name="foo" file="somefile.html"/>
# inherit from foobar.html # inherit from foobar.html
...@@ -152,7 +194,7 @@ text text la la ...@@ -152,7 +194,7 @@ text text la la
</table> </table>
""" """
nodes = Lexer(template).parse() nodes = Lexer(template).parse()
print nodes #print nodes
assert repr(nodes) == r"""[NamespaceTag('namespace', {'name': '"foo"', 'file': '"somefile.html"'}, (1, 1), []), Text('\n', (1, 46)), Comment('inherit from foobar.html', (2, 1)), InheritTag('inherit', {'file': '"foobar.html"'}, (3, 1), []), Text('\n\n', (3, 31)), ComponentTag('component', {'name': '"header"'}, (5, 1), ["Text('\\n <div>header</div>\\n', (5, 27))"]), Text('\n', (7, 14)), ComponentTag('component', {'name': '"footer"'}, (8, 1), ["Text('\\n <div> footer</div>\\n', (8, 27))"]), Text('\n\n<table>\n', (10, 14)), ControlLine('for', 'for j in data():', False, (13, 1)), Text(' <tr>\n', (14, 1)), ControlLine('for', 'for x in j:', False, (15, 1)), Text(' <td>Hello ', (16, 1)), Expression('x', ['h'], (16, 23)), Text('</td>\n', (16, 30)), ControlLine('for', 'endfor', True, (17, 1)), Text(' </tr>\n', (18, 1)), ControlLine('for', 'endfor', True, (19, 1)), Text('</table>\n', (20, 1))]""" assert repr(nodes) == r"""[NamespaceTag('namespace', {'name': '"foo"', 'file': '"somefile.html"'}, (1, 1), []), Text('\n', (1, 46)), Comment('inherit from foobar.html', (2, 1)), InheritTag('inherit', {'file': '"foobar.html"'}, (3, 1), []), Text('\n\n', (3, 31)), ComponentTag('component', {'name': '"header"'}, (5, 1), ["Text('\\n <div>header</div>\\n', (5, 27))"]), Text('\n', (7, 14)), ComponentTag('component', {'name': '"footer"'}, (8, 1), ["Text('\\n <div> footer</div>\\n', (8, 27))"]), Text('\n\n<table>\n', (10, 14)), ControlLine('for', 'for j in data():', False, (13, 1)), Text(' <tr>\n', (14, 1)), ControlLine('for', 'for x in j:', False, (15, 1)), Text(' <td>Hello ', (16, 1)), Expression('x', ['h'], (16, 23)), Text('</td>\n', (16, 30)), ControlLine('for', 'endfor', True, (17, 1)), Text(' </tr>\n', (18, 1)), ControlLine('for', 'endfor', True, (19, 1)), Text('</table>\n', (20, 1))]"""
if __name__ == '__main__': if __name__ == '__main__':
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment