Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Include ffi lib? #4

Open
gynt opened this issue Jan 15, 2025 · 0 comments
Open

Include ffi lib? #4

gynt opened this issue Jan 15, 2025 · 0 comments

Comments

@gynt
Copy link
Owner

gynt commented Jan 15, 2025

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()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant