#!/usr/bin/env python
# -*- encoding: utf-8 -*-
#
# Copyright (c) 2016 ASMlover. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
#  * Redistributions of source code must retain the above copyright
#    notice, this list ofconditions and the following disclaimer.
#
#  * Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in
#    the documentation and/or other materialsprovided with the
#    distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

from collections import namedtuple
import tyr_ast as ast
from tyr_error import TyrSyntaxError

MatchPattern = namedtuple('Match', ['pattern', 'body'])
ConditionElif = namedtuple('ConditionElif', ['cond', 'body'])

class ParserError(TyrSyntaxError):
    def __init__(self, message, token):
        super(ParserError, self).__init__(token.lineno, token.column, message)

def enter_scope(parser, name):
    class State(object):
        def __enter__(self):
            parser.scope.append(name)

        def __exit__(self, exc_type, exc_val, exc_tb):
            parser.scope.pop()

    return State()

class Subparser(object):
    PRECEDENCE = {
        'call': 10,
        'subscript': 10,

        'unary': 9,

        '*': 7,
        '/': 7,
        '%': 7,

        '+': 6,
        '-': 6,

        '>': 5,
        '>= ': 5,
        '<': 5,
        '<=': 5,

        '==': 4,
        '!=': 4,

        '&&': 3,

        '||': 2,

        '..': 1,
        '...': 1,
    }

    def get_subparser(self, token, subparsers, default=None):
        cls = subparsers.get(token.name, default)
        if cls:
            return cls()

class PrefixSubparser(Subparser):
    def parse(self, parser, tokens):
        raise NotImplementedError()

class InfixSubparser(Subparser):
    def parse(self, parser, tokens, left):
        raise NotImplementedError()

    def get_precedence(self, token):
        raise NotImplementedError()

class NumberExpress(PrefixSubparser):
    """number_expr: NUMBER"""
    def parse(self, parser, tokens):
        token = tokens.consume_expected('NUMBER')
        return ast.Number(token.value)

class StringExpression(PrefixSubparser):
    """str_expr: STRING"""
    def parse(self, parser, tokens):
        token = tokens.consume_expected('STRING')
        return ast.String(token.value)

class NameExpression(PrefixSubparser):
    """name_expr: NAME"""
    def parse(self, parser, tokens):
        toke = tokens.consume_expected('NAME')
        return ast.Identifier(token.value)

class UnaryOperatorExpression(PrefixSubparser):
    """prefix_expr: OPERATOR expr"""
    SUPPORTED_OPERATORS = ['-', '!']
    def parse(self, parser, tokens):
        token = tokens.consume_expected('OPERATOR')
        if token.value not in self.SUPPORTED_OPERATORS:
            raise ParserError('Unary operator {} is not supported'.format(token.value), token)
        right = Expression().parse(parser, tokens, self.get_precedence(token))
        if not right:
            raise ParserError('Excepted expression'.format(token.value), tokens.consume())
        return ast.UnaryOperator(token.value, right)

    def get_precedence(self, token):
        return self.PRECEDENCE['unary']

class GroupExpression(PrefixSubparser):
    """group_expr: LPAREN expr RPAREN"""
    def parse(self, parser, tokens):
        tokens.consume_expected('LPAREN')
        right = Expression().parse(parser, tokens)
        tokens.consume_expected('RPAREN')
        return right

class ArrayExpression(PrefixSubparser):
    """array_epxr: LBRACK list_of_expr? RBRACK"""
    def parse(self, parser, tokens):
        tokens.consume_expected('LBRACK')
        items = ListOfExpressions().parse(parser, tokens)
        tokens.consume_expected('RBRACK')
        return ast.Array(items)

@@ -4657,8 +4657,973 @@
 (items)%0A
+%0Aclass DictionaryExpression(PrefixSubparser):%0A    %22%22%22dict_expr: LCBRACK (expr COLON expr COMMA)* RCBRACK%22%22%22%0A    def parse_keyvalues(self, parser, tokens):%0A        items = %5B%5D%0A        while not tokens.is_end():%0A            key = Expression().parse(parser, tokens)%0A            if key:%0A                tokens.consume_expected('COLON')%0A                value = Expression().parse(parser, tokens)%0A                if value is None:%0A                    raise ParserError('Dictionary value excepted', tokens.consume())%0A                items.append((key, value))%0A            else:%0A                break%0A            if tokens.current().name == 'COMMA':%0A                tokens.consume_expected('COMMA')%0A            else:%0A                break%0A        return items%0A%0A    def parse(self, parser, tokens):%0A        tokens.consume_expected('LCBRACK')%0A        items = self.parse_keyvalues(parser, tokens)%0A        tokens.consume_expected('RCBRACK')%0A        return ast.Dictionary(items)%0A

add dictionary expression