import time
import tab
from mode import Fundamental
from lex import Grammar, PatternRule, RegionRule, NocasePatternRule
from lex import NocaseRegionRule, NocasePatternMatchRule
from mode.python import StringGrammar2
from mode.pipe import Pipe
from method.shell import Interact

class CodeGrammar(Grammar):
    rules = [
        PatternRule('word', r'[^ ]+'),
        PatternRule('spaces', r' +'),
        PatternRule('eol', r' +'),
    ]

class DataGrammar(Grammar): rules = [PatternRule('data', r'[^)]+')]
class LineGrammar(Grammar): rules = [PatternRule('data', r'.+')]
class StringGrammar3(Grammar): rules = [PatternRule('data', r'[^)]+')]

class ForthGrammar(Grammar):
    rules = [
        PatternRule('comment', r"\\(?: .*)?\n$"),
        RegionRule('comment', r'\((?= |\n|$)', DataGrammar, r'\)'),
        NocaseRegionRule('comment', r'0 \[if\]', DataGrammar, r'\[(?:endif|then)\]'),
        RegionRule('string', r'[.cs]" ', StringGrammar2, r'"'),
        RegionRule('string', r'[.s]\\" ', StringGrammar2, r'"'),
        RegionRule('string', r'\.\( ', StringGrammar3, r'\)'),

        # builtin
        NocasePatternRule('builtin', r'(?:true|false|on|off)(?= |\n|$)'),

        # mathn
        NocasePatternRule('keyword', r'(?:\+|-|\*/mod|\*/|\*|/mod|/|mod|negate|abs|min|max|and|or|xor|not|lshift|rshift|invert|2\*|2/|2\+|2-|1\+|1-|8\*|under\+|m\+|m\*/|m\*|um/mod|um\*|fm/mod|sm/rem|d\+|d-|dnegate|dabs|dmin|dmax|d2\*|d2/|f\+|f-|f\*\*|f\*|f/|fnegate|fabs|fmax|fmin|floor|fround|fsqrt|fexpm1|fexp|flnp1|fln|flog|falog|fsincos|fsinh|fsin|fcosh|fcos|ftanh|ftan|fasinh|fasin|facosh|facos|fatan2|fatanh|fatan|f2\*|f2/|1/f|f~rel|f~abs|f~|0<>|0<=|0<|0=|0>=|0>|<>|<=|<|>=|>|=|u<=|u<|u>=|u>|d0<=|d0<>|d0<|d0=|d0>=|d0>|d<=|d<>|d<|d=|d>=|d>|du<=|du<|du>=|du>|within|\?negate|\?dnegate)(?= |\n|$)'),
        # stack
        NocasePatternRule('keyword', r'(?:drop|nip|dup|over|tuck|swap|rot|-rot|\?dup|pick|roll|2drop|2nip|2dup|2over|2tuck|2swap|2rot|2-rot|3dup|4dup|5dup|3drop|4drop|5drop|8drop|4swap|4rot|4-rot|4tuck|8swap|8dup|>r|r>|r@|rdrop|2>r|2r>|2r@|2rdrop|4>r|4r>|4r@|4rdrop|fdrop|fnip|fdup|fover|ftuck|fswap|frot)(?= |\n|$)'),
        # pointer
        NocasePatternRule('keyword', r'(?:forthsp|sp@|sp!|fp@|fp!|rp@|rp!|lp@|lp!)(?= |\n|$)'),
        # address
        NocasePatternRule('keyword', r'(?:@|!|\+!|c@|c!|2@|2!|f@|f!|sf@|sf!|df@|df!|chars|char\+|cells|cell\+|cell|align|aligned|floats|float\+|float|faligned|falign|sfloats|sfloat\+|sfaligned|sfalign|dfloats|dfloat\+|dfaligned|dfalign|maxaligned|maxalign|cfaligned|cfalign|address-unit-bits|allot|allocate|here|move|erase|cmove>|cmove|fill|blank)(?= |\n|$)'),
        # conditional (*)
        NocasePatternRule('builtin', r'(?:if|else|endif|then|case|of|endof|endcase|\?dup-if|\?dup-0=-if|ahead|cs-pick|cs-roll|catch|throw|within)(?= |\n|$)'),
        # iter (*)
        NocasePatternRule('builtin', r'(?:begin|while|repeat|until|again|\?do|loop|i|j|k|\+do|u\+do|u-do|-do|do|\+loop|-loop|unloop|leave|\?leave|exit|done|for|next)(?= |\n|$)'),

        # define
        NocasePatternRule('builtin', r'(?:constant|2constant|fconstant|variable|2variable|fvariable|create|user|to|defer|is|does>|immediate|compile-only|compile|restrict|interpret|postpone|execute|literal|create-interpret/compile|interpretation>|<interpretation|compilation>|<compilation|\]|lastxt|comp\'|postpone|find-name|name>int|name\?int|name>comp|name>string|state|c;|cvariable|,|2,|f,|c,|\[(?:ifdef|ifundef|then|endif|then|else|\?do|do|loop|\+loop|next|begin|until|again|while|repeat|comp\'|\'|compile)\])(?= |\n|$)'),

        # assembly (*)
        NocaseRegionRule('code', 'code', CodeGrammar, 'end-code'),
        NocasePatternRule('builtin', r'(?:assembler|code|end-code|;code|flush-icache|c,)(?= |\n|$)'),

        # xyz
        NocasePatternMatchRule('x', r'(:)( +)([^ ]+)', r'delimiter', r'spaces', r'function'),
        PatternRule('delimiter', r"[:;\[\]]"),
        NocasePatternRule('number', r"'[a-z](?= |$)"),
        NocasePatternRule('number', r'%?-?[0-1]+\.?(?= |$)'),
        NocasePatternRule('number', r'[&#]?-?[0-9]+\.?(?= |$)'),
        NocasePatternRule('number', r"(?:0x|\$)[0-9a-f]+\.?(?= |$)"),
        NocasePatternRule('number', r'[+-]?[0-9]+\.?e?[+-][0-9]+(?= |$)'),
        PatternRule('forth.word', r'[^ ]+'),
        PatternRule('spaces', r' +'),
        PatternRule('eol', r'\n'),
    ]

class GforthStart(Interact):
    args = []
    modename = 'forthpipe'
    reuse = True
    def _execute(self, w, **vargs):
        Interact._execute(self, w, bname='*GForth*', cmd='gforth')
class GforthLoadFile(Interact):
    args = []
    modename = 'forthpipe'
    reuse = True
    def _execute(self, w, **vargs):
        Interact._execute(self, w, bname='*GForth*', cmd='gforth')
        b = w.application.get_buffer_by_name('*GForth*')
        time.sleep(2.0)
        path = w.buffer.path
        b.pipe_write('s" ' + path + '" included\n')

br = RegionRule('banner', r'^Gforth \d+\.\d+\.\d+', LineGrammar, r"^Type `bye' to exit\n$")
class ForthPipeGrammar(Grammar):
    rules = [br] + ForthGrammar.rules

class ForthPipe(Pipe):
    name    = 'forthpipe'
    grammar = ForthPipeGrammar

dopen = {}
for s in ['if', 'case', 'of', '?dup-if', '?dup-0=-if', 'begin', 'while',
          'until', '?do', 'for', 'code']:
    dopen[s] = None
dclose = {}
for s in ['then', 'endif', 'again']:
    dclose[s] = None
class ForthTabber(tab.StackTabber2):
    open_tokens     = {'builtin': dopen, 'delimiter': {':': ';'}}
    close_tokens    = {'builtin': dclose, 'delimiter': set([';'])}
    continue_tokens = {'nomatch': set()}
    fixed_indent    = True
    def is_base(self, y):
        return y == 0
    def _is_indent(self, t):
        return t.name == 'spaces'
    def _is_ignored(self, t):
        return t.fqname() in ('spaces', 'eol', 'comment', 'comment.start',
                              'comment.data', 'comment.null', 'comment.end')
    def _handle_other_token(self, y, tokens, start, end, i, t):
        tab.StackTabber2._handle_other_token(self, y, tokens, start, end, i, t)
        if t.match('builtin', 'else'):
            if len(self.stack) > 1:
                self.curr_level = self.stack[-2].level
            else:
                self.curr_level = 0

class Forth(Fundamental):
    name       = 'FORTH'
    extensions = ['.fs', '.fi', '.fb']
    grammar    = ForthGrammar
    commentc   = '\\ '
    actions    = [GforthStart, GforthLoadFile]
    tabbercls  = ForthTabber
    colors     = {
        'forth.word': ('yellow', 'default', 'bold'),
        'forth.code': ('green', 'default', 'bold'),
    }

def install(*args):
    Forth.install(*args)
    ForthPipe.install(*args)