# -*- coding: utf-8 -*- 

# lp4all: literate programming embedded in source code as wiki comments
# Copyright (C) 2006 Jean-Marie Favreau, Frédéric Lehobey, David Mentré
#                    and Thomas Petazzoni
# 
# 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.

import sys
try:
    import lex
    import yacc
except ImportError:
    try:
        from ply import lex
        from ply import yacc
    except ImportError:
        print "python-ply is required, please install."
        sys.exit (1)

from iface import *
from tree_struct import *
from parse_exception import *

class SimpleParser:
    def __init__(self, debug = 0):
        # build lex/yacc objects
        self.lexer = lex.lex(module=self, debug = debug, lextab = "slextab")
        self.yacc = yacc.yacc(module=self, debug = debug, tabmodule = "sparsetab")

    def trace(self, text):
        #print text
        pass


    tokens = (
        'TEXT',
        'SPECIALCHAR',

        'CODE',
        'STRONG',
        'CANCEL',
        'EM',

        'REF','LINK',
        'TEXT_IN_LINK',
        'ENDLINK',

        'LABEL'
    )


    def t_STRONG(self, t):
        r'\*\*(\*?[^\*])*\*\*'
        return t

    def t_CANCEL(self, t):
        r'~~(~?[^~])*~~'
        t.lineno += t.value.count("\n")
        return t

    def t_EM(self, t):
        r'\/\/(\/?[^\/])*\/\/'
        t.lineno += t.value.count("\n")
        return t

    def t_ENDLINK(self, t):
        r'\]\]'
        return t



    def t_CODE(self, t):
        r'\^\^(\^?[^\^])*\^\^'
        t.lineno += t.value.count("\n")
        return t


    def t_REF(self, t):
        r'\[\[\#[^]\#@\|]+'
        t.lineno += t.value.count("\n")
        return t

    def t_LINK(self, t):
        r'\[\[[^]\ \|]+'
        t.lineno += t.value.count("\n")
        return t

    def t_LABEL(self, t):
        r'\[@[^]\#@\|]+@\]'
        t.lineno += t.value.count("\n")
        return t




    def t_TEXT(self, t):
        r'[^]%\/\*\[@=\#\^~\|]+'
        t.lineno += t.value.count("\n")
        return t

    def t_TEXT_IN_LINK(self, t):
        r'\|(\]?[^]]+)+'
        t.lineno += t.value.count("\n")
        return t

    def t_SPECIALCHAR(self, t):
        r'[%\/\*\[=\]#@~^\|]'
        return t


    def t_error(self, t):
        print "Illegal character '%s' (line %s" % (t.value[0], t.lineno)
        t.skip(1)



    # expression line
    def p_expressionline_sp_start(self, p):
        '''statement : SPECIALCHAR expression'''
        p[0] = NodeContentText(children = [NodeContentText(p[1]), p[2]])

    def p_expressionline_sp_end(self, p):
        '''statement : expression SPECIALCHAR'''
        p[0] = NodeContentText(children = [p[1], NodeContentText(p[2])])

    def p_expressionline(self, p):
        '''statement : expression'''
        p[0] = p[1]


    # only one special char
    def p_expression_addspecialchar(self, p):
        '''expression : expression SPECIALCHAR expression'''
        self.trace("special char: " + p[2])
        p[0] = NodeContentText(children = [p[1], NodeContentText(p[2]), p[3]])


    # define expression properties
    def p_expression_expression(self, p):
        'expression : expression expression'
        p[0] = NodeContentText(children = [p[1], p[2]])


    def p_expression_strong(self, p):
        'expression : STRONG'
        p[0] = NodeContentStrong(children = [self.childrenParser.parseSimpleComment(content = p[1][2:][:-2], lineno = p.lineno(1))])

    def p_expression_cancel(self, p):
        'expression : CANCEL'
        p[0] = NodeContentCancel(children = [self.childrenParser.parseSimpleComment(content = p[1][2:][:-2], lineno = p.lineno(1))])

    def p_expression_em(self, p):
        'expression : EM'
        p[0] = NodeContentEmphasize(children = [self.childrenParser.parseSimpleComment(content = p[1][2:][:-2], lineno = p.lineno(1))])


    # code
    def p_block_code(self, p):
        'expression : CODE'
        self.trace("code: " + p[1][2:][:-2] + "\n\n")
        p[0] = NodeContentCode(text = p[1][2:][:-2])


    def p_expression_all(self, p):
        '''expression : TEXT'''
        p[0] = NodeContentText(text = p[1])


    # link
    def p_block_link_expr(self, p):
        '''expression : LINK TEXT_IN_LINK ENDLINK'''
        self.trace("link (+): " + p[1][2:])
        p[0] = NodeContentLink(uri = p[1][2:], children = [self.childrenParser.parseSimpleComment(content = p[2][1:], lineno = p.lineno(2))])

    def p_block_link(self, p):
        '''expression : LINK ENDLINK'''
        self.trace("link: " + p[1][2:])
        p[0] = NodeContentLink(uri = p[1][2:])

    # ref
    def p_block_ref_expr(self, p):
        '''expression : REF TEXT_IN_LINK ENDLINK'''
        self.trace("ref (+): " + p[1][3:])
        p[0] = NodeContentRef(ref = p[1][3:], children = [self.childrenParser.parseSimpleComment(content = p[2][1:], lineno = p.lineno(2))])

    def p_block_ref(self, p):
        '''expression : REF ENDLINK'''
        self.trace("ref: " + p[1][3:])
        p[0] = NodeContentRef(ref = p[1][3:])

    # label
    def p_block_label(self, p):
        '''expression : LABEL'''
        self.trace("label " + p[1][2:][:-2])
        p[0] = NodeContentLabel(label = p[1][2:][:-2])


    def p_error(self, p):
        try:
            raise ParseException("Syntax error at '%s'" % p.value, p.lineno)
        except AttributeError:
            raise ParseException("Syntax error", self.lineno, self.lineno + self.nbline)



    # parse a simple comment and build associated nodes
    def parseSimpleComment(self, content, debug = 0, lineno = 1, endlineno = 1):

        # if parser for children (recursive pass) has not been built, create it
        if not 'childrenParser' in self.__dict__:
            self.childrenParser = SimpleParser(debug = debug)

        # init lineno
        self.lexer.lineno = lineno
        self.lineno = lineno
        self.nbline = content.count("\n")
        # run parser
        return self.yacc.parse(content, debug = debug, lexer = self.lexer)