import collections

equal_class = collections.OrderedDict()

equal_class['END_EQCLS'] = {'bits': 0, 'body':[] }
equal_class['ENT_EQCLS'] = {'bits': 3, 'body':['', '{}[]:,', ' \t\r\n', '"', '-0123456789', 't', 'f', 'n']}
equal_class['STR_EQCLS'] = {'bits': 2, 'body':['', '\0', '"', '\\']}
equal_class['ESC_EQCLS'] = {'bits': 2, 'body':['', '\\rftnb"/', 'u']}
equal_class['HEX_EQCLS'] = {'bits': 1, 'body':['', '0123456789abcdefABCDEF']}
equal_class['NUM_EQCLS'] = {'bits': 2, 'body':['', '-', '0', '123456789']}
equal_class['FRAC_EQCLS'] ={'bits': 2, 'body':['', '.', 'eE', '0123456789']}
equal_class['EXP_EQCLS'] = {'bits': 2, 'body':['', '+-', '0123456789']}
equal_class['TRUE_T_EQCLS'] = {'bits': 1, 'body':['', 'r']}
equal_class['TRUE_R_EQCLS'] = {'bits': 1, 'body':['', 'u']}
equal_class['TRUE_U_EQCLS'] = {'bits': 1, 'body':['', 'e']}
equal_class['FALSE_F_EQCLS'] = {'bits': 1, 'body':['', 'a']}
equal_class['FALSE_A_EQCLS'] = {'bits': 1, 'body':['', 'l']}
equal_class['FALSE_L_EQCLS'] = {'bits': 1, 'body':['', 's']}
equal_class['FALSE_S_EQCLS'] = {'bits': 1, 'body':['', 'e']}
equal_class['NULL_N_EQCLS'] = {'bits': 1, 'body':['', 'u']}
equal_class['NULL_U_EQCLS'] = {'bits': 1, 'body':['', 'l']}

# flag :  txt_inc,  src_not_inc,  tok_inc
states = [
    {
        'state': 'END',
        'body': ['END'],
        'flag': ['010'],
        'eqcls': 'END_EQCLS'
    },
    {
        'state': 'ENT',
        'body': ['END', 'ENT', 'ENT', 'STR', 'NUM', 'TRUE_T', 'FALSE_F', 'NULL_N'],
        'flag': ['010', '101', '100', '110', '010', '110',    '110',     '110'   ],
        'eqcls': 'ENT_EQCLS'
    },
    {
        'state': 'STR',
        'body': ['STR', 'END', 'ENT', 'ESC'],
        'flag': ['110', '010', '101', '110'],
        'eqcls': 'STR_EQCLS'
    },
    {
        'state': 'ESC',
        'body': ['END', 'STR', 'HEX'],
        'flag': ['010', '110', '110'],
        'eqcls': 'ESC_EQCLS'
    },
    {
        'state': 'HEX',
        'body': ['END', 'HEX1'],
        'flag': ['010', '110'],
        'eqcls': 'HEX_EQCLS'
    },
    {
        'state': 'HEX1',
        'body': ['END', 'HEX2'],
        'flag': ['010', '110'],
        'eqcls': 'HEX_EQCLS'
    },
    {
        'state': 'HEX2',
        'body': ['END', 'HEX3'],
        'flag': ['010', '110'],
        'eqcls': 'HEX_EQCLS'
    },
    {
        'state': 'HEX3',
        'body': ['END', 'STR'],
        'flag': ['010', '110'],
        'eqcls': 'HEX_EQCLS'
    },
    {
        'state': 'NUM',
        'body': ['END', 'MINUS', 'ZERO', 'ONENINE'],
        'flag': ['010', '110',   '110',  '110'    ],
        'eqcls': 'NUM_EQCLS'
    },
    {
        'state': 'MINUS',
        'body': ['END', 'END', 'ZERO', 'ONENINE'],
        'flag': ['010', '010', '110',  '110'    ],
        'eqcls': 'NUM_EQCLS'
    },
    {
        'state': 'ZERO',
        'body': ['FRAC_S', 'END', 'END', 'END'],
        'flag': ['010',    '010', '010', '010'],
        'eqcls': 'NUM_EQCLS'
    },
    {
        'state': 'ONENINE',
        'body': ['FRAC_S', 'END', 'ONENINE', 'ONENINE'],
        'flag': ['010',    '010', '110',     '110'],
        'eqcls': 'NUM_EQCLS',
    },
    {
        'state': 'FRAC_S',
        'body': ['ENT', 'FRAC_O', 'EXP_S', 'END'],
        'flag': ['001', '110', '110', '010'],
        'eqcls': 'FRAC_EQCLS',
    },
    {
        'state': 'FRAC_O',
        'body': ['ENT', 'END', 'EXP_S', 'FRAC_O'],
        'flag': ['001', '010', '110', '110'],
        'eqcls': 'FRAC_EQCLS',
    },
    {
        'state': 'EXP_S',
        'body': ['END', 'EXP_O', 'EXP_O'],
        'flag': ['010', '110', '110'],
        'eqcls': 'EXP_EQCLS',
    },
    {
        'state': 'EXP_O',
        'body': ['ENT', 'END', 'EXP_O'],
        'flag': ['001', '010', '110'],
        'eqcls': 'EXP_EQCLS',
    },
    {
        'state': 'TRUE_T',
        'body': ['END', 'TRUE_R'],
        'flag': ['010', '110'],
        'eqcls': 'TRUE_T_EQCLS',
    },
    {
        'state': 'TRUE_R',
        'body': ['END', 'TRUE_U'],
        'flag': ['010', '110'],
        'eqcls': 'TRUE_R_EQCLS',
    },
    {
        'state': 'TRUE_U',
        'body': ['END', 'ENT'],
        'flag': ['010', '101'],
        'eqcls': 'TRUE_U_EQCLS',
    },
    {
        'state': 'FALSE_F',
        'body': ['END', 'FALSE_A'],
        'flag': ['010', '110'],
        'eqcls': 'FALSE_F_EQCLS',
    },
    {
        'state': 'FALSE_A',
        'body': ['END', 'FALSE_L'],
        'flag': ['010', '110'],
        'eqcls': 'FALSE_A_EQCLS',
    },
    {
        'state': 'FALSE_L',
        'body': ['END', 'FALSE_S'],
        'flag': ['010', '110'],
        'eqcls': 'FALSE_L_EQCLS',
    },
    {
        'state': 'FALSE_S',
        'body': ['END', 'ENT'],
        'flag': ['010', '101'],
        'eqcls': 'FALSE_S_EQCLS',
    },
    {
        'state': 'NULL_N',
        'body': ['END', 'NULL_U'],
        'flag': ['010', '110'],
        'eqcls': 'NULL_N_EQCLS',
    },
    {
        'state': 'NULL_U',
        'body': ['END', 'NULL_L'],
        'flag': ['010', '110'],
        'eqcls': 'NULL_U_EQCLS',
    },
    {
        'state': 'NULL_L',
        'body': ['END', 'ENT'],
        'flag': ['010', '101'],
        'eqcls': 'NULL_U_EQCLS',
    }
]


# equal class 에 shift 추가 (32비트에 순서대로 자신의 비트수만큼 점유)
def init_shift_equal_class():
    cnt = 0
    for k, v in equal_class.items():
        v['shift'] = cnt
        cnt += v['bits']

# 상태의 인덱스 MAP을 구함
def gene_state_idx_map():
    ret = {}
    cnt = 0
    for itm in states:
        ret[itm['state']] = cnt
        cnt += len(itm['body'])
    return ret



init_shift_equal_class()
idx_map = gene_state_idx_map()

print(idx_map)


# equal class 를 구함 256개의 값 (ASCII)
# 에 따라서 정해진 위치의 비트수만큼 equal class 를 구함
def gene_eqcls():
    ret = []
    for c in range(256):
        ret.append('')
        for k, v in equal_class.items():
            if k != 'END_EQCLS':
                adder = '0' * v['bits']
                for i, itm in enumerate(v['body']):
                    if chr(c) in itm:
                        adder = bin(i)[2:].zfill(v['bits'])
                        break
                ret[c] = adder + ret[c]

    for i, itm in enumerate(ret):
        print (i, itm)

    return ret


#
generated_eqcls = gene_eqcls()

# 상태 테이블 생성 (1차원 배열)
#  equal class 의 비트매스크를 정의하는 mask byte
#  equal class 의 Shift 숫자 를 저장한 shift byte
#  상태에 따라 포인트증가, 토큰증가, 시작포인터증가 등을 제어하는 Flag byte
#  상태의 숫자를 기록하는  state byte
# 총 4바이트
def gene_tbl():
    sep = ''
    tbl = []
    eq_map = {}

    for state in states:
        eq_map[state['state']] = state['eqcls']

    for row, state in enumerate(states):
        for i, itm in enumerate(state['body']):

            # eqcls byte
            ## shift
            tar = equal_class[eq_map[itm]]
            #tar = equal_class[state['eqcls']]
            shift_byte = bin(tar['shift'])[2:].zfill(8)
            ## mask
            mask_byte = ('1' * tar['bits']).zfill(8)

            # fail bits +  flag bit = flag tyes
            flag_byte = bin(row)[2:].zfill(5) + state['flag'][i]

            # state byte
            state_byte = bin(idx_map[itm])[2:].zfill(8)
            tbl.append(
                mask_byte + sep +
                shift_byte + sep +
                flag_byte + sep +
                state_byte
            )

    print(tbl)

    return tbl


generated_tbl = gene_tbl()

##### simulation  table logic

def flag_inc(state, shift):
    st = int(state,2)
    r = st >> shift
    r = r & 0x1
    return r


def str_inc(state, l):
    flag = flag_inc(state, 9)
    mask = int('1' * 32, 2)
    mask = bin(mask + flag)[2:]
    if len(mask) > 32:
        mask = mask[len(mask)-32:]
    mask = int(mask, 2)
    return l & mask

def get_cls(cls, state):
    c = int(cls,2)
    st = int(state,2)
    shift = (st&0xFF0000)>>16
    mask  = st>>24
    return (c>>shift)&mask

def get_state(state):
    st = int(state,2)
    return st & 0xff

def get_char(p, txt):
    if p >= len(txt):
        return 0
    else:
        return ord(txt[p])



def get_fail_flag(state):
    st = int(state,2)
    return (st & 0xf800) >> 11


def get_state_nm(idx):
    for k, v in idx_map.items():
        if v == idx:
            return k
    return ''

# C언어와 동일한 로직을 구현하여 동작여부 확인
def simul_jnjson(eqcls, tbl, txt):
    print('start lexing:', txt)
    p = 0
    s = 0
    t_idx = 0
    t_s = s
    t_len = 0
    #        start  ,shift  ,flags  ,state  ,
    state = '00000111000000000000001000000001'

    while True: # do{ } while();
        p += flag_inc(state, 10)
        t_s = s
        t_len = p - s
        t_idx += flag_inc(state, 8)
        s += str_inc(state, p-s)

        cls = eqcls[get_char(p, txt)]
        cls = get_cls(cls, state)
        idx = get_state(state) + cls
        state = tbl[idx]
        print('p[%s] : t[%s] .s=%s, .len=%d , %s, cls:%s nxt:%s, idx:%s %s' % (
                chr(get_char(p,txt)),
                t_idx,
                txt[t_s],
                t_len,
                eqcls[get_char(p, txt)],
                cls,
                state,
                idx,
                get_state_nm(get_state(state)),
            )
        )

        if get_state(state) > 0:
            pass
        else:
            break

    fail_row = get_fail_flag(state)

    if fail_row <= 1 and get_char(p, txt) == 0:
        print('DONE')
    else:
        print('TOKEN ERROR:', fail_row, state)

def test_cases():
    cs = [
        '{}[ ]:\t, ', # entry default
        '[]:;', # entry error
        '"hello world\\r\\n\\""', # string
        '{}:,"hell\\r\\n\\""[,,,]',  # string
        '"hell', # string err
        '"\\uabcd"', # hex
        '0', # num
        '-1', # num
        '-12.234', # num
        '-0.320E+12', # num
        'false',
        'true',
        'null',
        'fa:,[]', # err
        '[]nu"ll', # err
    ]
    for c in cs:
        simul_jnjson(generated_eqcls, generated_tbl, c)


test_cases()



### generated source

def gen_c_src(eqcls, tbl):
    eqcls_arr = 'int eqcls[256] = {\n'
    for i, cls in enumerate(eqcls):
        eqcls_arr += '%10s, '%(hex(int(cls,2)))
        if (i + 1) % 8 == 0:
            eqcls_arr += '\n'
    eqcls_arr += '};\n'

    print(eqcls_arr)

    tbl_arr = 'int tbl[] = {\n'
    for i, t in enumerate(tbl):
        tbl_arr += '%10s, '%(hex(int(t,2)))
        if (i + 1) % 8 == 0:
            tbl_arr += '\n'
    tbl_arr += '};\n'

    print(tbl_arr)

    print('//start =', hex(int('00000111000000000000001000000001',2)))

    main_src = '''
char* p = in_buf;
char* s = in_buf;
jn_str_t* t = out_toks;
int state = 0x7000201;
unsigned int cls = 0;
do
{
    p += (state>>10) & 0x1;
    t->s = s;
    t->len = p - s;
    t += (state>>8) & 0x1;
    s += (p - s) & (UINT_MAX + ((state>>9) & 0x1));
    cls = eqcls[(unsigned char)(*p)];
    cls = (cls>>((state & 0x00FF0000)>>16))&(state>>24);
	state = tbl[ (state & 0x000000FF) + cls ];
}
while( (state & 0x000000FF) > 0);
    '''

    print(main_src)

gen_c_src(generated_eqcls, generated_tbl)