-- Used for porting Uxntal code for use with the old assembler
-- in commit 82f7103a55c21b13f898b20e5d1e174e501bc825 with the
-- assembler that replaced it straight afterwards.

import P, R, S, C, Ct, Cp, V from require 'lpeg'

local labels, filename

opcode_translate =
    LDZ2: 'LDA'
    STZ2: 'STA'
    LDR: 'LDZ2'
    STR: 'STZ2'
    LDR2: 'LDA2'
    STR2: 'STA2'

grammar = P {
    'file'
    file: Ct(V'ows' * (V'atom' * V'ows') ^ 0) * Cp!
    ws: C S' \n\t' ^ 1
    ows: C S' \n\t' ^ 0
    atom: V'opcode' + V'comment' + V'variable' + V'addr' + V'literal' + V'setter' + V'getter' + V'short' + V'labeldef' + V'relative' + V'sublabel' + V'data' + V'macro' + V'macroref' + V'rawshort'
    comment: C P'(' * (1-V'ws'*P')') ^ 0 * V'ws' * P')'
    variable: (P';' / -> '@') * C(V'name') * V'ws' * (P'{' / ->'[') * V'ws' * ((P'' / -> '&') * C(V'name') * V'ws' * (P'' / -> '$') * C(V'name') * V'ws') ^ 0 * (P'}' / -> ']') / (...) ->
        var = select 2, ...
        r, w = if var\sub(1, 1) == var\sub(1, 1)\upper!
            ' DEI', ' DEO'
        else
            ' LDZ', ' STZ'
        for i = 7, select('#', ...), 6
            k = select i, ...
            rr, ww = if '2' == select i + 3, ...
                r .. '2', w .. '2'
            else
                r, w
            labels['~' .. var .. '.' .. k] = '.' .. var .. '/' .. k .. rr
            labels['=' .. var .. '.' .. k] = '.' .. var .. '/' .. k .. ww
            if i == 7
                labels['~' .. var] = '.' .. var .. rr
                labels['=' .. var] = '.' .. var .. ww
        ...
    name: R('az', 'AZ', '09', '__', '--', '++', '**', '//', '??') ^ 1
    addr: C(P'|') * (C(V'hex') / (i) ->
        if i == '0200'
            return '0100'
        if i\match '^01..$'
            return i\sub 3
        return i
    )
    literal: C P'#' * V'hex'
    hex: R('09', 'af', 'AF') ^ 1
    setter: C(P'=' * V'label') / (s) ->
        if not labels[s]
            error 'label not found: %q in %s'\format s, filename
        return labels[s]
    getter: C(P'~' * V'label') / (s) ->
        if not labels[s]
            error 'label not found: %q in %s'\format s, filename
        return labels[s]
    label: R('az', 'AZ', '09', '__', '--', '..', '$$', ']]', '))', '@@', '""', ',,', '##', '||', '{{', '}}', '%%', ';;', '^^', '~~', '==', '//') ^ 1
    short: (P',' / -> ';') * (C(V'label') / (s) -> (s\gsub '%$', '&'))
    rawshort: (P'.' / -> ':') * (C(V'label') / (s) -> (s\gsub '%$', '&'))
    opcode: (C(R'AZ' * R'AZ' * R'AZ' * P'2' ^ -1) / (s) -> opcode_translate[s] or s) * C P'r' ^ -1 * #V'ws'
    labeldef: C P'@' * V'label'
    relative: (P'^' / -> ',') * (C(V'label') / (s) -> (s\gsub '%$', '&'))
    sublabel: (P'$' / -> '&') * (C(V'label') / (s) -> (s\gsub '%$', '&'))
    data: C(P'[') * V'ws' * (V'data_item' * V'ws') ^ 0 * C(P']')
    macro: C(P'%' * V'name' * V'ws' * P'{') * V'ws' * (V'atom' * V'ows') ^ 0 * C P'}'
    macroref: C V'name'
    data_item: C(V'hex' * #V'ws') + V'data_string'
    data_string: C((1 - S' \n\t') ^ 1 - P']') / (s) -> '"', s
}

translate = (_filename) ->
    filename = _filename
    labels = {}
    f = assert io.open filename
    contents = f\read '*a'
    f\close!
    t, len = grammar\match contents
    if len <= #contents
        print '\027[32m%s\027[0;1m%s\027[0m'\format contents\sub(len - 100, len - 1), contents\sub(len, len + 100)
        error 'no match'
    filename = filename\gsub 'attic', 'auto'
    f = assert io.open filename, 'w'
    f\write table.concat(t)
    f\close!
    f = assert io.popen 'bin/assembler %s bin/boot.rom'\format filename
    for l in f\lines!
        print l
        if l == 'Error: Assembly[Failed]'
            os.exit 1
    f\close!
    os.exit 0

translate 'attic/software/assembler.tal'
os.exit 0

translate 'attic/tests/opcodes.tal'
translate 'attic/tests/basics.tal'

-- for k, v in pairs t
--     print k