203 lines
7.3 KiB
Python
Executable File
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:
|