Skip to content

Include ffi lib? #4

@gynt

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()

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