Skip to content

Include ffi lib? #4

Description

@gynt

Options:
https://github.com/zhaojh329/lua-ffi
https://github.com/q66/cffi-lua

Or roll our own:

c11 = require("c11")
lpegrex = require("lpegrex")

node = c11([[

    struct A {
        int a;
        short b;
        char c;
        void * d;
        char field_0x100;
        char e[100];
        char * f;
    };
    
]], "struct_test")

type_size_lookup = {
    ["char"] = 1,
    ["byte"] = 1,
    ["short"] = 2,
    ["int"] = 4,
    ["void *"] = 4,
    ["char *"] = 4,
}

type_getter_lookup = {
    ["char"] = "readByte",
    ["byte"] = "readByte",
    ["short"] = "readSmallInteger",
    ["int"] = "readInteger",
    ["void *"] = "readInteger",
    ["char *"] = "readInteger",
    ["char[*]"] = "readString",
}

type_setter_lookup = {
    ["char"] = "writeByte",
    ["byte"] = "writeByte",
    ["short"] = "writeSmallInteger",
    ["int"] = "writeInteger",
    ["void *"] = "writeInteger",
    ["char *"] = "writeInteger",
    ["char[*]"] = "writeString",
}

-- todo implement this before c11 parsing
type_preprocessor = {
    ["undefined"] = "char",
    ["byte"] = "char",
    ["undefined2"] = "short",
    ["undefined4"] = "int",
}

function generate_struct(node, ignore_prefix)
    local ignoring = false
    if ignore_prefix ~= nil then
        ignoring = true
    end
    if node.tag ~= "struct-or-union-specifier" then error() end
    if node[1] ~= "struct" then error() end
    name = node[3]
    print(string.format("generate_struct: struct name: %s", name))
    
    sdl = node[4]
    
    offset = 0
    
    getters = {}
    setters = {}
    
    for k, sd in ipairs(sdl) do        
        type_size = nil
        
        sql = sd[1]
        tp = sql[1]
        type_string = tp[1]
        
        dl = sd[2]
        sdr = dl[1]
        d = sdr[1]
        
        pointer_string = nil
        
        if d[1].tag == "identifier" then
            id = d[1]        
            identifier_string = id[1]
            
            full_type_string = type_string
        elseif d[1].tag == "pointer" then
            p = d[1]
            id = p[3]
            identifier_string = id[1]
            pointer_string = (pointer_string or "") .. "*"
            
            full_type_string = type_string .. " " .. pointer_string
        elseif d[1].tag == "declarator-subscript" then
            node_to_solve = d[1]
            subscript = d[1]
            id = subscript[1]
            identifier_string = id[1]
            integer_constant = subscript[3]
            type_size = integer_constant[1] * type_size_lookup[type_string]
            -- error("node_to_solve")
            
            full_type_string = type_string .. "[*]"
        else
            error(d[1].tag)
        end    
        
        if ignoring and identifier_string:sub(1, ignore_prefix:len()) == ignore_prefix then
            -- continue
        else
        
            -- if identifier_string == "new" or identifier_string == "at" then
                -- error(string.format("reserved name: %s", identifier_string))
            -- end
            
            if type_size == nil then
                type_size = type_size_lookup[full_type_string]
            end
            
            if type_size == nil then error(full_type_string) end

            print(string.format("name: %s, type: %s, size: %s, offset: %s", identifier_string, full_type_string, type_size, offset))
            
            g = type_getter_lookup[full_type_string]
            if g == nil then error(full_type_string) end
            
            if full_type_string == "char[*]" then
                getter = string.format([[
                    if k == "%s" then
                        return %s(address + %s, %s)
                    end
                ]], identifier_string, g, offset, type_size)
                
                s = type_setter_lookup[full_type_string]
                if s == nil then error(full_type_string) end
                
                setter = string.format([[
                    if k == "%s" then
                        if v:len() >= %s then
                            error("string too large")
                        end
                        return %s(address + %s, v)
                    end
                ]], identifier_string, type_size, s, offset)
            else
                getter = string.format([[
                    if k == "%s" then
                        return %s(address + %s)
                    end
                ]], identifier_string, g, offset)
                
                s = type_setter_lookup[full_type_string]
                if s == nil then error(full_type_string) end
                
                setter = string.format([[
                    if k == "%s" then
                        return %s(address + %s, v)
                    end
                ]], identifier_string, s, offset)
            end

            
            table.insert(getters, getter)
            table.insert(setters, setter)
            
        end
        
        offset = offset + type_size
        
             
    end
    
    size = offset

    return string.format([[
return function(address)
    local allocated = false
    local deallocated = nil
    local size = %s
    if address == nil then
        print("allocating")
        allocated = true
        deallocated = false
        address = allocate(size)
    end
    if type(address) ~= "number" then
        error("not a valid address:" .. tostring(address))
    end
    
    local mt = {
        __index = function(t, k)
%s
        end,
        
        __newindex = function(t, k, v)
%s
        end,
        
        __gc = function(t)
            if allocated and deallocated == false then
                deallocate(address)
                deallocated = true
            end
        end,
        
        __len = function(t)
            return size
        end,
    }
    
    return setmetatable({}, mt)
end
    ]], size, table.concat(getters, "\n\n"), table.concat(setters, "\n\n"))
end

function translation_unit_to_struct_specifier(node)
    return node[1][1][1][1][1] -- todo: ignores possibility of multiple struct definitions
end

lua = generate_struct(translation_unit_to_struct_specifier(node))

compile = load(lua, "generator", 't', {
    error = error,
    tostring = tostring,
    print = print,
    type = type,
    table = table,
    string = string,
    setmetatable = setmetatable,
    allocate = function(size) fakeResult = 0x400000 + size; print(string.format("allocate %s => %X", size, fakeResult)); return fakeResult end,
    deallocate = function(address) print(string.format("deallocate %X", address)) end,
    readByte = function(address) print(string.format("readByte %X", address)) end,
    readSmallInteger = function(address) print(string.format("readSmallInteger %X", address)) end,
    readInteger = function(address) print(string.format("readInteger %X", address)) end,
    readString = function(address, size) print(string.format("readString %X %s", address, size)) end,
    writeByte = function(address) print(string.format("writeByte %X", address)) end,
    writeSmallInteger = function(address) print(string.format("writeSmallInteger %X", address)) end,
    writeInteger = function(address) print(string.format("writeInteger %X", address)) end,
    writeString = function(address, data) print(string.format("writeString %X %s", address, data)) end,
})

A = compile()
a = A(0x400000)
b = A() -- allocated

print(#b)

b = nil
collectgarbage()

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions