#    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 == 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

    @staticmethod
    def escape_substitution(substitution):
        return re.sub('(%s|%s)' % (SubstitutionParser.REG_GROUP, SubstitutionParser.REG_ESCAPE), '\\\\\\1', substitution)

# ex:ts=4:et:
