summaryrefslogtreecommitdiffstats
path: root/plugins/snippets/snippets/substitutionparser.py
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/snippets/snippets/substitutionparser.py')
-rw-r--r--plugins/snippets/snippets/substitutionparser.py203
1 files changed, 203 insertions, 0 deletions
diff --git a/plugins/snippets/snippets/substitutionparser.py b/plugins/snippets/snippets/substitutionparser.py
new file mode 100644
index 0000000..8469dd3
--- /dev/null
+++ b/plugins/snippets/snippets/substitutionparser.py
@@ -0,0 +1,203 @@
+# Gedit snippets plugin
+# Copyright (C) 2006-2007 Jesse van den Kieboom <jesse@icecrew.nl>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import re
+
+class ParseError(Exception):
+ def __str__(self):
+ return 'Parse error, resume next'
+
+class Modifiers:
+ def _first_char(s):
+ first = (s != '' and s[0]) or ''
+ rest = (len(s) > 1 and s[1:]) or ''
+
+ return first, rest
+
+ def upper_first(s):
+ first, rest = Modifiers._first_char(s)
+
+ return '%s%s' % (first.upper(), rest)
+
+ def upper(s):
+ return s.upper()
+
+ def lower_first(s):
+ first, rest = Modifiers._first_char(s)
+
+ return '%s%s' % (first.lower(), rest)
+
+ def lower(s):
+ return s.lower()
+
+ def title(s):
+ return s.title()
+
+ upper_first = staticmethod(upper_first)
+ upper = staticmethod(upper)
+ lower_first = staticmethod(lower_first)
+ lower = staticmethod(lower)
+ title = staticmethod(title)
+ _first_char = staticmethod(_first_char)
+
+class SubstitutionParser:
+ REG_ID = '[0-9]+'
+ REG_NAME = '[a-zA-Z_]+'
+ REG_MOD = '[a-zA-Z]+'
+ REG_ESCAPE = '\\\\|\\(\\?|,|\\)'
+
+ REG_GROUP = '(?:(%s)|<(%s|%s)(?:,(%s))?>)' % (REG_ID, REG_ID, REG_NAME, REG_MOD)
+
+ def __init__(self, pattern, groups = {}, modifiers = {}):
+ self.pattern = pattern
+ self.groups = groups
+
+ self.modifiers = {'u': Modifiers.upper_first,
+ 'U': Modifiers.upper,
+ 'l': Modifiers.lower_first,
+ 'L': Modifiers.lower,
+ 't': Modifiers.title}
+
+ for k, v in modifiers.items():
+ self.modifiers[k] = v
+
+ def parse(self):
+ result, tokens = self._parse(self.pattern, None)
+
+ return result
+
+ def _parse(self, tokens, terminator):
+ result = ''
+
+ while tokens != '':
+ if self._peek(tokens) == '' or self._peek(tokens) == terminator:
+ tokens = self._remains(tokens)
+ break
+
+ try:
+ res, tokens = self._expr(tokens, terminator)
+ except ParseError:
+ res, tokens = self._text(tokens)
+
+ result += res
+
+ return result, tokens
+
+ def _peek(self, tokens, num = 0):
+ return (num < len(tokens) and tokens[num])
+
+ def _token(self, tokens):
+ if tokens == '':
+ return '', '';
+
+ return tokens[0], (len(tokens) > 1 and tokens[1:]) or ''
+
+ def _remains(self, tokens, num = 1):
+ return (num < len(tokens) and tokens[num:]) or ''
+
+ def _expr(self, tokens, terminator):
+ if tokens == '':
+ return ''
+
+ try:
+ return {'\\': self._escape,
+ '(': self._condition}[self._peek(tokens)](tokens, terminator)
+ except KeyError:
+ raise ParseError
+
+ def _text(self, tokens):
+ return self._token(tokens)
+
+ def _substitute(self, group, modifiers = ''):
+ result = (self.groups.has_key(group) and self.groups[group]) or ''
+
+ for modifier in modifiers:
+ if self.modifiers.has_key(modifier):
+ result = self.modifiers[modifier](result)
+
+ return result
+
+ def _match_group(self, tokens):
+ match = re.match('\\\\%s' % self.REG_GROUP, tokens)
+
+ if not match:
+ return None, tokens
+
+ return self._substitute(match.group(1) or match.group(2), match.group(3) or ''), tokens[match.end():]
+
+ def _escape(self, tokens, terminator):
+ # Try to match a group
+ result, tokens = self._match_group(tokens)
+
+ if result != None:
+ return result, tokens
+
+ s = self.REG_GROUP
+
+ if terminator:
+ s += '|%s' % re.escape(terminator)
+
+ match = re.match('\\\\(\\\\%s|%s)' % (s, self.REG_ESCAPE), tokens)
+
+ if not match:
+ raise ParseError
+
+ return match.group(1), tokens[match.end():]
+
+ def _condition_value(self, tokens):
+ match = re.match('\\\\?%s\s*' % self.REG_GROUP, tokens)
+
+ if not match:
+ return None, tokens
+
+ groups = match.groups()
+ name = groups[0] or groups[1]
+
+ return self.groups.has_key(name) and self.groups[name] != None, tokens[match.end():]
+
+ def _condition(self, tokens, terminator):
+ # Match ? after (
+ if self._peek(tokens, 1) != '?':
+ raise ParseError
+
+ # Remove initial (? token
+ tokens = self._remains(tokens, 2)
+ condition, tokens = self._condition_value(tokens)
+
+ if condition is None or self._peek(tokens) != ',':
+ raise ParseError
+
+ truepart, tokens = self._parse(self._remains(tokens), ',')
+
+ if truepart is None:
+ raise ParseError
+
+ falsepart, tokens = self._parse(tokens, ')')
+
+ if falsepart is None:
+ raise ParseError
+
+ if condition:
+ return truepart, tokens
+ else:
+ return falsepart, tokens
+
+ @staticmethod
+ def escape_substitution(substitution):
+ return re.sub('(%s|%s)' % (SubstitutionParser.REG_GROUP, SubstitutionParser.REG_ESCAPE), '\\\\\\1', substitution)
+
+# ex:ts=4:et: