From 1efbc0327aa00b5bdda724c9e76094826ae15a3f Mon Sep 17 00:00:00 2001
From: Mike Bayer <mike_mp@zzzcomputing.com>
Date: Thu, 2 Feb 2012 11:41:34 -0500
Subject: [PATCH] Fix for [ticket:20] and [ticket:86] much thanks to Eevee

---
 CHANGES            |  6 ++++++
 mako/__init__.py   |  2 +-
 mako/lexer.py      | 41 +++++++++++++++++++++--------------------
 test/test_lexer.py | 17 +++++++++++++++++
 4 files changed, 45 insertions(+), 21 deletions(-)

diff --git a/CHANGES b/CHANGES
index 9a34681..cab28ad 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,9 @@
+0.6.2
+- [bug] The ${{"foo":"bar"}} parsing issue is fixed!!
+  The legendary Eevee has slain the dragon!
+  [ticket:20].  Also fixes quoting issue
+  at [ticket:86].
+
 0.6.1
 - [bug] Added special compatibility for the 0.5.0
   Cache() constructor, which was preventing file
diff --git a/mako/__init__.py b/mako/__init__.py
index 0740ac1..40033ee 100644
--- a/mako/__init__.py
+++ b/mako/__init__.py
@@ -5,5 +5,5 @@
 # the MIT License: http://www.opensource.org/licenses/mit-license.php
 
 
-__version__ = '0.6.1'
+__version__ = '0.6.2'
 
diff --git a/mako/lexer.py b/mako/lexer.py
index 217674f..9e0bc5e 100644
--- a/mako/lexer.py
+++ b/mako/lexer.py
@@ -91,31 +91,32 @@ class Lexer(object):
  
     def parse_until_text(self, *text):
         startpos = self.match_position
+        text_re = r'|'.join(text)
+        brace_level = 0
         while True:
             match = self.match(r'#.*\n')
             if match:
                 continue
-            match = self.match(r'(\"\"\"|\'\'\'|\"|\')')
+            match = self.match(r'(\"\"\"|\'\'\'|\"|\')((?<!\\)\\\1|.)*?\1', re.S)
             if match:
-                m = self.match(r'.*?%s' % match.group(1), re.S)
-                if not m:
-                    raise exceptions.SyntaxException(
-                                "Unmatched '%s'" % 
-                                match.group(1), 
-                                **self.exception_kwargs)
-            else:
-                match = self.match(r'(%s)' % r'|'.join(text))
-                if match:
-                    return \
-                        self.text[startpos:self.match_position-len(match.group(1))],\
-                        match.group(1)
-                else:
-                    match = self.match(r".*?(?=\"|\'|#|%s)" % r'|'.join(text), re.S)
-                    if not match:
-                        raise exceptions.SyntaxException(
-                                    "Expected: %s" % 
-                                    ','.join(text), 
-                                    **self.exception_kwargs)
+                continue
+            match = self.match(r'(%s)' % text_re)
+            if match:
+                if match.group(1) == '}' and brace_level > 0:
+                    brace_level -= 1
+                    continue
+                return \
+                    self.text[startpos:self.match_position-len(match.group(1))],\
+                    match.group(1)
+            match = self.match(r"(.*?)(?=\"|\'|#|%s)" % text_re, re.S)
+            if match:
+                brace_level += match.group(1).count('{')
+                brace_level -= match.group(1).count('}')
+                continue
+            raise exceptions.SyntaxException(
+                        "Expected: %s" % 
+                        ','.join(text), 
+                        **self.exception_kwargs)
  
     def append_node(self, nodecls, *args, **kwargs):
         kwargs.setdefault('source', self.text)
diff --git a/test/test_lexer.py b/test/test_lexer.py
index 538da97..61204ff 100644
--- a/test/test_lexer.py
+++ b/test/test_lexer.py
@@ -557,6 +557,23 @@ print('''
                       False, (1, 1)),
                       Text(u" '''and now some text '''", (10,11))]))
 
+    def test_tricky_code_4(self):
+        template = \
+            """<% foo = "\\"\\\\" %>"""
+        nodes = Lexer(template).parse()
+        self._compare(nodes, TemplateNode({},
+                      [Code(u"""foo = "\\"\\\\" \n""",
+                      False, (1, 1))]))
+
+    def test_tricky_code_5(self):
+        template = \
+            """before ${ {'key': 'value'} } after"""
+        nodes = Lexer(template).parse()
+        self._compare(nodes, TemplateNode({},
+                      [Text(u'before ', (1, 1)),
+                      Expression(u" {'key': 'value'} ", [], (1, 8)),
+                      Text(u' after', (1, 29))]))
+
     def test_control_lines(self):
         template = \
             """
-- 
GitLab