#!/usr/bin/env python
# -*- coding: UTF-8 -*-
#    This file is part of arena.
#   
#    arena 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 3 of the License or, at your
#    option, any later version.
#
#    arena is distributed in the hope that it will be useful, but
#    WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABLITY
#    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 arena.  If not, see <http://www.gnu.org/licenses/>. 

## @file
#  This file defines several classes that aid in determining the semantics of a 
#  Markdown-style computer language.

from Library.Parse.Lex.Languages import AComputerLanguage, CharacterType, \
  TokenType

## Markdown opcodes understood by the parser.
class MarkdownOpcode:
    none = 0
    dlTab = TokenType.special + 0x0900
    dlEndOfLine = TokenType.special + 0x1000
    dlEndOfStream = TokenType.special + 0x1900
    dlBeginImage = TokenType.special + 0x2100
    dlOpenBracket = TokenType.special + 0x5B00
    dlCloseBracket = TokenType.special + 0x5D00
    dlEndIdentifier = TokenType.special + 0x5D01
    dlPipe = TokenType.special + 0x7C00
    dlColon = TokenType.special + 0x3A00 
    opTab = TokenType.operator + 0x2000
    dlOpenParen = TokenType.special + 0x2800
    dlCloseParen = TokenType.special + 0x2900
    dlPostfixHeader = TokenType.special + 0x3D00
    dlAsterisk = TokenType.special + 0x2A00
    dlStrong1 = TokenType.special + 0x2A01
    dlUnderscore = TokenType.special + 0x5F00
    dlStrong2 = TokenType.special + 0x5F01
    opUnorderedList1 = TokenType.operator + 0x2B00
    opUnorderedList2 = TokenType.operator + 0x2D00
    opHorizRule1 = TokenType.operator + 0x2A01
    opHorizRule2 = TokenType.operator + 0x2A02
    opHorizRule3 = TokenType.operator + 0x2D00
    opHorizRule4 = TokenType.operator + 0x2D01
    opHorizRule5 = TokenType.operator + 0x5F00
    opHorizRule6 = TokenType.operator + 0x5F01
    dlPeriod = TokenType.special + 0x2E00
    dlHash = TokenType.special + 0x2300
    opHeader2 = TokenType.operator + 0x2300
    opHeader3 = TokenType.operator + 0x2301
    opHeader4 = TokenType.operator + 0x2302
    opHeader5 = TokenType.operator + 0x2303
    opHeader6 = TokenType.operator + 0x2304
    opAmpersand = TokenType.operator + 0x2600
    dlBeginXMLTag = TokenType.special + 0x3C00
    opBlockquote = TokenType.operator + 0x3E00
    dlAt = TokenType.special + 0x4000
    dlListDelimiter = TokenType.special + 0x2C00
    opEscape = TokenType.operator + 0x5C00
    dlBacktick = TokenType.special + 0x6000    

## Translates opcode literals into opcode values
MarkdownOpcodes = {
    "\t":     MarkdownOpcode.dlTab,
    "\n":     MarkdownOpcode.dlEndOfLine,
    "\x19":   MarkdownOpcode.dlEndOfStream,
    "![":     MarkdownOpcode.dlBeginImage,
    "[":      MarkdownOpcode.dlOpenBracket,
    "]":      MarkdownOpcode.dlCloseBracket,
    "]:":     MarkdownOpcode.dlEndIdentifier,
    "|":      MarkdownOpcode.dlPipe,
    ":":      MarkdownOpcode.dlColon,
    "(":      MarkdownOpcode.dlOpenParen,
    ")":      MarkdownOpcode.dlCloseParen,
    "=":      MarkdownOpcode.dlPostfixHeader,
    "*":      MarkdownOpcode.dlAsterisk,
    "**":     MarkdownOpcode.dlStrong1,
    "_":      MarkdownOpcode.dlUnderscore,
    "__":     MarkdownOpcode.dlStrong2,
    "+":      MarkdownOpcode.opUnorderedList1,
    "-":      MarkdownOpcode.opUnorderedList2,
    "***":    MarkdownOpcode.opHorizRule1,
    "* * *":  MarkdownOpcode.opHorizRule2,
    "---":    MarkdownOpcode.opHorizRule3,
    "- - -":  MarkdownOpcode.opHorizRule4,
    "___":    MarkdownOpcode.opHorizRule5,
    "_ _ _":  MarkdownOpcode.opHorizRule6,
    ".":      MarkdownOpcode.dlPeriod,
    "#":      MarkdownOpcode.dlHash,
    "##":     MarkdownOpcode.opHeader2,
    "###":    MarkdownOpcode.opHeader3,
    "####":   MarkdownOpcode.opHeader4,
    "#####":  MarkdownOpcode.opHeader5,
    "######": MarkdownOpcode.opHeader6,
    "<":      MarkdownOpcode.dlBeginXMLTag,
    "&":      MarkdownOpcode.opAmpersand,
    ">":      MarkdownOpcode.opBlockquote,
    "@":      MarkdownOpcode.dlAt,
    ",":      MarkdownOpcode.dlListDelimiter,
    "\\":     MarkdownOpcode.opEscape,
    "`":      MarkdownOpcode.dlBacktick
}

## Markdown syntax rulesets
MarkdownRules = {
    "list/unordered": (
        MarkdownOpcode.opUnorderedList1, MarkdownOpcode.opUnorderedList2,
        MarkdownOpcode.dlAsterisk
    ),
    "list/beginning/follow": (
        TokenType.space
    ),
    "link/ending": (
        MarkdownOpcode.dlCloseBracket, MarkdownOpcode.dlCloseParen
    ),
    "link/automatic": (
        MarkdownOpcode.dlColon, MarkdownOpcode.dlAt
    ),
    "block/strong": (
        MarkdownOpcode.dlStrong1, MarkdownOpcode.dlStrong2
    ),
    "line/header": (
        MarkdownOpcode.dlHash, MarkdownOpcode.opHeader2, 
        MarkdownOpcode.opHeader3, MarkdownOpcode.opHeader4,
        MarkdownOpcode.opHeader5, MarkdownOpcode.opHeader6
    ),
    "line/header/postfix": (
        MarkdownOpcode.dlPostfixHeader, MarkdownOpcode.opUnorderedList2
    ),
    "delimiter/hrule": (
        MarkdownOpcode.opHorizRule1, MarkdownOpcode.opHorizRule2,
        MarkdownOpcode.opHorizRule3, MarkdownOpcode.opHorizRule4,
        MarkdownOpcode.opHorizRule5, MarkdownOpcode.opHorizRule6
    ),
    "delimiter/more": (
        MarkdownOpcode.opHorizRule1, MarkdownOpcode.opHorizRule2
    )
}

## Markdown character types
MarkdownCharacterTypes = {
    CharacterType.symbol: "'+-*/=<>()^.,{}\\:[]`&_#!?@\t",
    CharacterType.letter: ("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                           "abcdefghijklmnopqrstuvwxyz"),
    CharacterType.alpha: "$%",
    CharacterType.digit: "0123456789",
    CharacterType.numeric: ".-ABCDEFXabcdefx",
    CharacterType.space: ("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x0B\x0C"
                          "\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19"
                          "\x1A\x1B\x1C\x1D\x1E\x1F\x20\x7F"),
    CharacterType.quote: "\"'",
    CharacterType.endOfLine: "\x0A\x0D",
    CharacterType.endOfStream: "\x19"
}
    
## A class that represents the syntax and rules of a Markdown-style language.
class AMarkdownLanguage(AComputerLanguage):
    ## Constructor
    def init(Self):
        result = AComputerLanguage.init(Self)
        
        if result:
            # Set private attributes
            # This language is not case-sensitive
            Self._caseAware = False
            # This language has a tab size of 4
            Self._tabSize = 4
            # Opcodes recognized by the language
            Self.registerOpcodes(**MarkdownOpcodes)
            # Rules of the language
            Self._Rules.update(**MarkdownRules)
            # Character types recognized by the language
            for key in MarkdownCharacterTypes.iterkeys():
                Self.characters[key] = MarkdownCharacterTypes[key]
            
        return result
        
    # Properties ---------------------------------------------------------------
    ## Retrieve the tab size set by the language
    def tabSize(Self):
        return Self._tabSize
