xed/plugins/snippets/snippets/SubstitutionParser.py

203 lines
7.3 KiB
Python
Executable File

# Pluma 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 = '\\\\|\\(\\?|,|\\)'
def __init__(self, pattern, groups = {}, modifiers = {}):
self.pattern = pattern
self.groups = groups
self.REG_GROUP = '(?:(%s)|<(%s|%s)(?:,(%s))?>)' % (self.REG_ID, self.REG_ID, self.REG_NAME, self.REG_MOD)
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 == None or self._peek(tokens) != ',':
raise ParseError
truepart, tokens = self._parse(self._remains(tokens), ',')
if truepart == None:
raise ParseError
falsepart, tokens = self._parse(tokens, ')')
if falsepart == None:
raise ParseError
if condition:
return truepart, tokens
else:
return falsepart, tokens
def escape_substitution(substitution):
return re.sub('(%s|%s)' % (self.REG_GROUP, self.REG_ESCAPE), '\\\\\\1', substitution)
escapesubstitution = staticmethod(escape_substitution)
# ex:ts=8:et: