build_dag = (t, dag = {}, i = 1, j = #t, level = 0) -> if i > j return mid = math.floor (i + j) / 2 dag[t[mid]] = { (build_dag t, dag, i, mid - 1, level + 1) (build_dag t, dag, mid + 1, j, level + 1) } t[mid], dag append_dag = (node, dag, k) -> i = k > node and 2 or 1 next_node = dag[node][i] if next_node return append_dag next_node, dag, k dag[node][i] = k dag[k] = {} build_dag_from_chars = (s, ...) -> t = [ s\sub i, i for i = 1, #s ] table.sort t root, dag = build_dag t for i = 1, select '#', ... append_dag root, dag, (select i, ...) return root, dag check_terminals = (dag, s) -> for i = 1, #s k = s\sub i, i assert not dag[k][1], '%s has left child node'\format k assert not dag[k][2], '%s has right child node'\format k dump = (f, root, dag, level = 0) -> if dag[root][1] dump f, dag[root][1], dag, level + 1 f\write ' '\rep level f\write root f\write '\n' if dag[root][2] dump f, dag[root][2], dag, level + 1 -- deal with opcodes write_opcode_tree = do byte_to_opcode = {} byte = false for l in assert io.lines 'src/assembler.c' if l\match '^%s*char%s+ops%[%]%[4%]' byte = 0 elseif l\match '%}' byte = false elseif byte for opcode in l\gmatch '"([A-Z-][A-Z-][A-Z-])"' byte_to_opcode[byte] = opcode byte += 1 order_to_opcode = [ byte_to_opcode[i] for i = 0, #byte_to_opcode when byte_to_opcode[i] != '---' ] table.sort order_to_opcode root, opcode_to_links = build_dag order_to_opcode (f) -> for i = 0, #byte_to_opcode opcode = byte_to_opcode[i] f\write '\t' if opcode == root f\write '$root ' elseif opcode != '---' f\write '$op-%s '\format opcode\lower! else f\write ' ' for j = 1, 2 if opcode != '---' and opcode_to_links[opcode][j] f\write '.$op-%s '\format opcode_to_links[opcode][j]\lower! else f\write '[ 0000 ] ' if i == 0 f\write '$disasm ' else f\write ' ' if opcode != '---' f\write '[ %s ]'\format opcode else f\write '[ ??? ]' if i == 0 f\write ' $asm' f\write '\n' type_byte = (size, has_subtree) -> n1 = has_subtree and '8' or '0' n2 = switch size when '1' '1' when '2' '3' else '0' n1 .. n2 globals = {} add_globals = (root, dag, key_to_label, key_to_contents, pad_before = '', pad_after = '') -> for k in pairs dag l = '' if k == root l ..= '@%s\n'\format key_to_label('root')\gsub '%s', '' l ..= '@%s '\format key_to_label k for j = 1, 2 if dag[k][j] l ..= '.%s '\format key_to_label dag[k][j] else l ..= '%s[ 0000 ]%s '\format pad_before, pad_after l ..= key_to_contents k l ..= '\n' globals[key_to_label(k)\gsub '%s', ''] = l globals[key_to_label('root')\gsub '%s', ''] = '' do root, dag = build_dag_from_chars '{}[]%@$;|=~,.^#"\0', '(', ')' check_terminals dag, ')' -- dump io.stdout, root, dag convert = { ['.']: 'dot' ['\0']: 'nul' } label_name = (s) -> 'first-char-%-3s'\format convert[s] or s label_value = (k) -> '[ %02x ]'\format k\byte! add_globals root, dag, label_name, label_value, ' ', ' ' devices = {} add_device = (name, fields) -> field_sizes = { k, size for k, size in fields\gmatch '(%S+) (%d+)' } field_sizes.pad = nil field_names = [ k for k in pairs field_sizes ] table.sort field_names root, dag = build_dag field_names label_name = (k) -> 'l-%-14s'\format name .. '-' .. k label_value = (k) -> '%-17s [ %s ] .%s.%s'\format '[ %s 00 ]'\format(k), type_byte(field_sizes[k], false), name, k add_globals root, dag, label_name, label_value, ' ', ' ' table.insert devices, name add_devices = -> table.sort devices root, dag = build_dag devices label_name = (k) -> 'l-%-14s'\format k label_value = (k) -> '%-17s [ %s ] .%s .l-%s-root'\format '[ %s 00 ]'\format(k), type_byte(0, true), k, k add_globals root, dag, label_name, label_value, ' ', ' ' filename = 'projects/software/assembler.usm' f = assert io.open '%s.tmp'\format(filename), 'w' -- f = io.stdout state = 'normal' machine = normal: (l) -> if l\match '%$disasm .*%$asm' write_opcode_tree f state = 'opcode' elseif l\match '^%@' if l == '@RESET' add_devices! for k in l\gmatch '%@(%S+)' if globals[k] f\write globals[k] globals[k] = nil return f\write l f\write '\n' else if l\match '^%|%x%x%x%x %;' add_device l\match '%;(%S+) %{ (.*) %}' f\write l f\write '\n' opcode: (l) -> if not l\match '%[' f\write l f\write '\n' state = 'normal' for l in assert io.lines filename machine[state] l for _, l in pairs globals f\write l f\close! assert 0 == os.execute 'mv %s %s.bak'\format filename, filename assert 0 == os.execute 'mv %s.tmp %s'\format filename, filename