~rabbits/uxn

02ecc2ee7d412a92701e48a33b9b67ad26bf98a8 — Andrew Alderwick 4 months ago 4abb47e
(asma) Rebalance the rune tree.
3 files changed, 141 insertions(+), 322 deletions(-)

M etc/asma.lua
M etc/asma.moon
M projects/library/asma.tal
M etc/asma.lua => etc/asma.lua +82 -175
@@ 1,196 1,103 @@
local spairs
spairs = function(t)
  local keys
  do
local output = assert(io.open('.asma.tal', 'w'))
local process_subtree
process_subtree = function(items)
  local middle = math.floor(#items / 2 + 1.25)
  local node = items[middle]
  if not node then
    return 
  end
  node.left = process_subtree((function()
    local _accum_0 = { }
    local _len_0 = 1
    for k in pairs(t) do
      _accum_0[_len_0] = k
      _len_0 = _len_0 + 1
    end
    keys = _accum_0
  end
  table.sort(keys)
  local i = 0
  return function()
    i = i + 1
    return keys[i], t[keys[i]]
  end
end
local trees = {
  ['asma-opcodes'] = { }
}
local opcodes_in_order = { }
do
  local wanted = false
  for l in assert(io.lines('src/uxnasm.c')) do
    if l == 'static char ops[][4] = {' then
      wanted = true
    elseif wanted then
      if l == '};' then
        break
      end
      for w in l:gmatch('[^%s",][^%s",][^%s",]') do
        if w ~= '---' then
          trees['asma-opcodes'][w] = {
            ('"%s 00'):format(w),
            ''
          }
        end
        table.insert(opcodes_in_order, w)
    for i, item in ipairs(items) do
      if i < middle then
        _accum_0[_len_0] = item
        _len_0 = _len_0 + 1
      end
    end
  end
  assert(#opcodes_in_order == 32, 'didn\'t find 32 opcodes in assembler code!')
end
do
  local representation = setmetatable({
    ['&'] = '26 00 ( & )'
  }, {
    __index = function(self, c)
      return ("'%s 00"):format(c)
    end
  })
  local process
  process = function(label, t)
    trees[label] = { }
    for k, v in pairs(t) do
      trees[label][('%02x'):format(k:byte())] = {
        representation[k],
        (':%s'):format(v)
      }
    return _accum_0
  end)())
  node.right = process_subtree((function()
    local _accum_0 = { }
    local _len_0 = 1
    for i, item in ipairs(items) do
      if i > middle then
        _accum_0[_len_0] = item
        _len_0 = _len_0 + 1
      end
    end
  end
  process('asma-first-char-normal', {
    ['%'] = 'asma-macro-define',
    ['|'] = 'asma-pad-absolute',
    ['$'] = 'asma-pad-relative',
    ['@'] = 'asma-label-define',
    ['&'] = 'asma-sublabel-define',
    ['#'] = 'asma-literal-hex',
    ['.'] = 'asma-literal-zero-addr',
    [','] = 'asma-literal-rel-addr',
    [';'] = 'asma-literal-abs-addr',
    [':'] = 'asma-abs-addr',
    ["'"] = 'asma-raw-char',
    ['"'] = 'asma-raw-word',
    ['{'] = 'asma-ignore',
    ['}'] = 'asma-ignore',
    ['['] = 'asma-ignore',
    [']'] = 'asma-ignore',
    ['('] = 'asma-comment-start',
    [')'] = 'asma-comment-end',
    ['~'] = 'asma-include'
  })
  process('asma-first-char-macro', {
    ['('] = 'asma-comment-start',
    [')'] = 'asma-comment-end',
    ['{'] = 'asma-ignore',
    ['}'] = 'asma-macro-end'
  })
  process('asma-first-char-comment', {
    ['('] = 'asma-comment-more',
    [')'] = 'asma-comment-less'
  })
    return _accum_0
  end)())
  return node
end
local traverse_node
traverse_node = function(t, min, max, lefts, rights)
  local i = math.ceil((min + max) / 2)
  if min < i then
    lefts[t[i]] = (':&%s'):format(traverse_node(t, min, i - 1, lefts, rights))
  end
  if i < max then
    rights[t[i]] = (':&%s'):format(traverse_node(t, i + 1, max, lefts, rights))
  end
  return t[i]
end
local traverse_tree
traverse_tree = function(t)
  local lefts, rights = { }, { }
  local keys
local process_tree
process_tree = function(items)
  local sorted_items
  do
    local _accum_0 = { }
    local _len_0 = 1
    for k in pairs(t) do
      _accum_0[_len_0] = k
    for _index_0 = 1, #items do
      local item = items[_index_0]
      _accum_0[_len_0] = item
      _len_0 = _len_0 + 1
    end
    keys = _accum_0
    sorted_items = _accum_0
  end
  table.sort(keys)
  return lefts, rights, traverse_node(keys, 1, #keys, lefts, rights)
end
local ptr
ptr = function(s)
  if s then
    return (':&%s'):format(s)
  table.sort(sorted_items, function(a, b)
    return a.order < b.order
  end);
  (process_subtree(sorted_items)).label = '&_entry'
  for _index_0 = 1, #items do
    local item = items[_index_0]
    output:write(('\t%-11s %-10s %-12s %s%s\n'):format(item.label, item.left and item.left.ref or ' $2', (item.right and item.right.ref or ' $2') .. item.extra, item.key, item.rest))
  end
  return ' $2'
end
local ordered_opcodes
ordered_opcodes = function(t)
  local i = 0
  return function()
    i = i + 1
    local v = opcodes_in_order[i]
    if t[v] then
      return v, t[v]
    elseif v then
      return false, {
        '"--- 00',
        ''
      }
local parse_tree
parse_tree = function(it)
  local items = { }
  for l in it do
    if l == '' then
      process_tree(items)
      output:write('\n')
      return 
    end
  end
end
local printout = true
local fmt
fmt = function(...)
  return (('\t%-11s %-10s %-12s %-14s %s '):format(...):gsub(' +$', '\n'))
end
do
  local _with_0 = assert(io.open('projects/library/asma.tal.tmp', 'w'))
  for l in assert(io.lines('projects/library/asma.tal')) do
    if l:match('--- cut here ---') then
      break
    local item = {
      extra = ''
    }
    item.key, item.rest = l:match('^%s*%S+%s+%S+%s+%S+%s+(%S+)(.*)')
    if item.key:match('^%&') then
      item.extra = (' %s'):format(item.key)
      item.key, item.rest = item.rest:match('^%s+(%S+)(.*)')
    end
    _with_0:write(l)
    _with_0:write('\n')
  end
  _with_0:write('( --- 8< ------- 8< --- cut here --- 8< ------- 8< --- )\n')
  _with_0:write('(          automatically generated code below          )\n')
  _with_0:write('(          see etc/asma.moon for instructions          )\n')
  _with_0:write('\n(')
  _with_0:write(fmt('label', 'less', 'greater', 'key', 'binary'))
  _with_0:write(fmt('', 'than', 'than', 'string', 'data )'))
  _with_0:write('\n')
  for name, tree in spairs(trees) do
    _with_0:write(('@%s\n'):format(name))
    local lefts, rights, entry = traverse_tree(tree)
    local sort_fn
    if name == 'asma-opcodes' then
      if rights[opcodes_in_order[1]] then
        rights[opcodes_in_order[1]] = rights[opcodes_in_order[1]] .. ' &_disasm'
      else
        rights[opcodes_in_order[1]] = ' $2 &_disasm'
      end
      sort_fn = ordered_opcodes
    if item.key:match('^%"') then
      item.order = item.key:sub(2)
    elseif item.key:match('^%x%x') then
      item.order = string.char(tonumber(item.key, 16))
    else
      sort_fn = spairs
      error(('unknown key: %q'):format(item.key))
    end
    for k, v in sort_fn(tree) do
      local label
      if k == entry then
        label = '&_entry'
      elseif k then
        label = ('&%s'):format(k)
      else
        label = ''
      end
      _with_0:write(fmt(label, lefts[k] or ' $2', rights[k] or ' $2', unpack(v)))
    if item.order:match('^%a') then
      item.label = ('&%s'):format(item.order)
    elseif item.order:match('^.$') then
      item.label = ('&%x'):format(item.order:byte())
    else
      error(('unknown label: %q'):format(item.order))
    end
    _with_0:write('\n')
    item.ref = (':%s'):format(item.label)
    table.insert(items, item)
  end
end
local it = assert(io.lines('projects/library/asma.tal'))
local waiting_for_cut = true
for l in it do
  output:write(l)
  output:write('\n')
  if l:find('--- cut here ---', 1, true) then
    waiting_for_cut = false
  end
  if not waiting_for_cut and '@' == l:sub(1, 1) then
    parse_tree(it)
  end
  _with_0:close()
end
return os.execute('mv projects/library/asma.tal.tmp projects/library/asma.tal')
output:close()
return os.execute('mv .asma.tal projects/library/asma.tal')

M etc/asma.moon => etc/asma.moon +53 -141
@@ 1,10 1,7 @@
--
-- Asma tree helper script
--
-- This script updates the trees at the end of projects/library/asma.tal when
-- Uxn's opcode set changes or new runes (first character of tokens) are
-- created, so that new changes in the C assembler can be incorporated rapidly
-- into asma.
-- This script balances the trees at the end of projects/library/asma.tal.
--
-- To run, you need Lua or LuaJIT, and just run etc/asma.lua from the top
-- directory of Uxn's git repository:


@@ 18,145 15,60 @@
-- file changes.
--

spairs = (t) ->
	keys = [ k for k in pairs t ]
	table.sort keys
	i = 0
	->
		i = i + 1
		keys[i], t[keys[i]]
output = assert io.open '.asma.tal', 'w'

trees = {
	['asma-opcodes']: {}
}
process_subtree = (items) ->
    middle = math.floor #items / 2 + 1.25
    node = items[middle]
    if not node
        return
    node.left = process_subtree [ item for i, item in ipairs items when i < middle ]
    node.right = process_subtree [ item for i, item in ipairs items when i > middle ]
    node

opcodes_in_order = {}
process_tree = (items) ->
    sorted_items = [ item for item in *items ]
    table.sort sorted_items, (a, b) -> a.order < b.order
    (process_subtree sorted_items).label = '&_entry'
    for item in *items
        output\write '\t%-11s %-10s %-12s %s%s\n'\format item.label, item.left and item.left.ref or ' $2', (item.right and item.right.ref or ' $2') .. item.extra, item.key, item.rest

do -- opcodes
	wanted = false
	for l in assert io.lines 'src/uxnasm.c'
		if l == 'static char ops[][4] = {'
			wanted = true
		elseif wanted
			if l == '};'
				break
			for w in l\gmatch '[^%s",][^%s",][^%s",]'
				if w != '---'
					trees['asma-opcodes'][w] = {
						'"%s 00'\format w
						''
					}
				table.insert opcodes_in_order, w
	assert #opcodes_in_order == 32, 'didn\'t find 32 opcodes in assembler code!'
parse_tree = (it) ->
    items = {}
    for l in it
        if l == ''
            process_tree items
            output\write '\n'
            return
        item = { extra: '' }
        item.key, item.rest = l\match '^%s*%S+%s+%S+%s+%S+%s+(%S+)(.*)'
        if item.key\match '^%&'
            item.extra = ' %s'\format item.key
            item.key, item.rest = item.rest\match '^%s+(%S+)(.*)'
        if item.key\match '^%"'
            item.order = item.key\sub 2
        elseif item.key\match '^%x%x'
            item.order = string.char tonumber item.key, 16
        else
            error 'unknown key: %q'\format item.key
        if item.order\match '^%a'
            item.label = '&%s'\format item.order
        elseif item.order\match '^.$'
            item.label = '&%x'\format item.order\byte!
        else
            error 'unknown label: %q'\format item.order
        item.ref = ':%s'\format item.label
        table.insert items, item

do -- first characters
	representation = setmetatable {
		'&': '26 00 ( & )'
	},
		__index: (c) => "'%s 00"\format c
	process = (label, t) ->
		trees[label] = {}
		for k, v in pairs t
			trees[label]['%02x'\format k\byte!] = {
				representation[k]
				':%s'\format v
			}
	process 'asma-first-char-normal',
		'%': 'asma-macro-define'
		'|': 'asma-pad-absolute'
		'$': 'asma-pad-relative'
		'@': 'asma-label-define'
		'&': 'asma-sublabel-define'
		'#': 'asma-literal-hex'
		'.': 'asma-literal-zero-addr'
		',': 'asma-literal-rel-addr'
		';': 'asma-literal-abs-addr'
		':': 'asma-abs-addr'
		"'": 'asma-raw-char'
		'"': 'asma-raw-word'
		'{': 'asma-ignore'
		'}': 'asma-ignore'
		'[': 'asma-ignore'
		']': 'asma-ignore'
		'(': 'asma-comment-start'
		')': 'asma-comment-end'
		'~': 'asma-include'
	process 'asma-first-char-macro',
		'(': 'asma-comment-start'
		')': 'asma-comment-end'
		'{': 'asma-ignore'
		'}': 'asma-macro-end'
	process 'asma-first-char-comment',
		'(': 'asma-comment-more'
		')': 'asma-comment-less'

traverse_node = (t, min, max, lefts, rights) ->
	i = math.ceil (min + max) / 2
	if min < i
		lefts[t[i]]  = ':&%s'\format traverse_node t, min, i - 1, lefts, rights
	if i < max
		rights[t[i]] = ':&%s'\format traverse_node t, i + 1, max, lefts, rights
	return t[i]

traverse_tree = (t) ->
	lefts, rights = {}, {}
	keys = [ k for k in pairs t ]
	table.sort keys
	lefts, rights, traverse_node keys, 1, #keys, lefts, rights

ptr = (s) ->
	if s
		return ':&%s'\format s
	return ' $2'

ordered_opcodes = (t) ->
	i = 0
	->
		i = i + 1
		v = opcodes_in_order[i]
		if t[v]
			return v, t[v]
		elseif v
			return false, { '"--- 00', '' }

printout = true

fmt = (...) ->
	('\t%-11s %-10s %-12s %-14s %s '\format(...)\gsub ' +$', '\n')

with assert io.open 'projects/library/asma.tal.tmp', 'w'
	for l in assert io.lines 'projects/library/asma.tal'
		if l\match '--- cut here ---'
			break
		\write l
		\write '\n'
	\write '( --- 8< ------- 8< --- cut here --- 8< ------- 8< --- )\n'
	\write '(          automatically generated code below          )\n'
	\write '(          see etc/asma.moon for instructions          )\n'
	\write '\n('
	\write fmt 'label', 'less', 'greater', 'key', 'binary'
	\write fmt '', 'than', 'than', 'string', 'data )'
	\write '\n'
	for name, tree in spairs trees
		\write '@%s\n'\format name
		lefts, rights, entry = traverse_tree tree
		sort_fn = if name == 'asma-opcodes'
			if rights[opcodes_in_order[1]]
				rights[opcodes_in_order[1]] ..= ' &_disasm'
			else
				rights[opcodes_in_order[1]] = ' $2 &_disasm'
			ordered_opcodes
		else
			spairs
		for k, v in sort_fn tree
			label = if k == entry
				'&_entry'
			elseif k
				'&%s'\format k
			else
				''
			\write fmt label, lefts[k] or ' $2', rights[k] or ' $2', unpack v
		\write '\n'
	\close!
os.execute 'mv projects/library/asma.tal.tmp projects/library/asma.tal'
it = assert io.lines 'projects/library/asma.tal'
waiting_for_cut = true
for l in it
    output\write l
    output\write '\n'
    if l\find '--- cut here ---', 1, true
        waiting_for_cut = false
    if not waiting_for_cut and '@' == l\sub 1, 1
        parse_tree it
output\close!
os.execute 'mv .asma.tal projects/library/asma.tal'


M projects/library/asma.tal => projects/library/asma.tal +6 -6
@@ 908,17 908,17 @@
	&23         :&22        $2          "# 00          :asma-literal-hex
	&24         :&23       :&25         "$ 00          :asma-pad-relative
	&25          $2         $2          "% 00          :asma-macro-define
	&26         :&24       :&29         26 00 ( & )    :asma-sublabel-define
	&26         :&24       :&2c         26 00 ( & )    :asma-sublabel-define
	&28          $2         $2          "( 00          :asma-comment-start
	&29         :&28       :&2c         ") 00          :asma-comment-end
	&2c          $2        :&2d         ", 00          :asma-literal-rel-addr
	&29         :&28        $2          ") 00          :asma-comment-end
	&2c         :&29       :&2d         ", 00          :asma-literal-rel-addr
	&2d          $2         $2          "- 00          :asma-zero-addr
	&_entry     :&26       :&7b         ". 00          :asma-literal-zero-addr
	&3a          $2         $2          ": 00          :asma-abs-addr
	&3b         :&3a       :&3d         "; 00          :asma-literal-abs-addr
	&3d          $2        :&40         "= 00          :asma-abs-addr
	&3b         :&3a        $2          "; 00          :asma-literal-abs-addr
	&3d         :&3b       :&40         "= 00          :asma-abs-addr
	&40          $2         $2          "@ 00          :asma-label-define
	&7b         :&3b       :&7d         "{ 00          :asma-ignore
	&7b         :&3d       :&7d         "{ 00          :asma-ignore
	&7c          $2         $2          "| 00          :asma-pad-absolute
	&7d         :&7c       :&7e         "} 00          :asma-ignore
	&7e          $2         $2          "~ 00          :asma-include