packages feed

clua-0.1: parse-bin.lua

-- Goes together with the struct wrapper generator.

-- Tools
local function table_copy(t)
  local u = { }
  for k, v in pairs(t) do u[k] = v end
  return setmetatable(u, getmetatable(t))
end


parse = {}

-- Wrap table as abstract stream.
function parse.array2stream(arr)
   if (arr) then
      local i = 0
      return
      function()
         i = i + 1
         return arr[i]
      end
   else
      -- Allow arr to be nil in case we assume it's a stream of zeros.
      return
      function() 
         return 0
      end
   end
end

-- Parsers for "int", "float", "struct", "array"
function parse.int(stream, structs, signed, bits)
   local bytes = bits/8
   local accu = 0
   local shift = 1
   local byte = 0
   while (bytes > 0) do
      byte = assert(stream())
      accu = accu + shift * byte;
      shift = shift * 0x100
      bytes = bytes - 1
   end
   if ((signed == 'S') and (byte > 0x7F)) then
      accu = accu - shift
   end
   return accu
end

function parse.float()
   assert(nil) -- FIXME
end

function parse.struct(stream, structs, struct_name)
   return parse.table(stream, structs, structs[struct_name])
end

function parse.array(stream, structs, size, type_spec)
   if (size == 0) then
      return {}
   else
      local arr = {}
      for i=1, size do
         arr[i] = parse.type(stream, structs, type_spec)
      end
      return arr
   end
end



-- Element dispatcher.
function parse.type(stream, structs, type_spec)
   local spec = table_copy(type_spec)
   local type = assert(table.remove(spec,1))
   -- print (type)
   local fun = assert(parse[type])
   return fun(stream, structs, table.unpack(spec))
end

-- Table dispatcher
function parse.table(stream, structs, tab_spec) 
   local tab = {}
   for i,rec_spec in pairs(tab_spec) do
      local type_spec, name = table.unpack(rec_spec)
      tab[name] = parse.type(stream, structs, type_spec)
   end
   return tab
end

-- Functional interface
function parse.array2table (array, structs, tab_spec)
   return parse.table (parse.array2stream(array), structs, tab_spec)
end


-- Wrap table as abstract stream.
unparse = {}
function unparse.array2stream(t)
   return
   function(byte)
      table.insert(t, byte)
   end
end

function unparse.int(stream, val, structs, signed, bits)
   local bytes = bits/8
   local accu = val
   while (bytes > 0) do
      local byte = accu % 0x100
      stream(byte)
      accu = math.floor(accu / 0x100)
      bytes = bytes - 1
   end
end

function unparse.struct(stream, val, structs, struct_name)
   unparse.table(stream, val, structs, structs[struct_name])
end

function unparse.array(stream, arr, structs, size, type_spec)
   if (size > 0) then
      for i=1,size do
         unparse.type(stream, arr[i], structs, type_spec)
      end
   end
end

function unparse.type(stream, val, structs, type_spec)
   local spec = table_copy(type_spec)
   local type = assert(table.remove(spec,1))
   -- print (type)
   unparse[type](stream, val, structs, table.unpack(spec))
end

function unparse.table(stream, tab, structs, tab_spec) 
   for i,rec_spec in pairs(tab_spec) do
      local type_spec, name = table.unpack(rec_spec)
      unparse.type(stream, tab[name], structs, type_spec)
   end
end

-- Functional interface
function unparse.table2array(tab, structs, tab_spec)
   local arr = {}
   unparse.table(unparse.array2stream(arr), tab, structs, tab_spec)
   return arr
end