Skip to content
This repository was archived by the owner on Aug 4, 2026. It is now read-only.

Commit 38f4e7e

Browse files
committed
fix: parse more edge cases
- struct def without typedef - forward typedef - typedef union w/ anonymous struct
1 parent 876643c commit 38f4e7e

3 files changed

Lines changed: 127 additions & 29 deletions

File tree

src/init.lua

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,17 @@ function Context:rewriteNode(node)
9797
return { kind = k, name = renamed, asm_name = node.asm_name or node.name, ret = self:rewriteType(node.ret), params = self:rewriteParams(node.params), attrs = node.attrs }
9898
elseif k == "extern_var" then
9999
return { kind = k, name = renamed, asm_name = node.name, type = self:rewriteType(node.type) }
100+
elseif k == "struct_def" then
101+
local fields
102+
if node.fields then
103+
fields = {}
104+
for _, f in ipairs(node.fields) do
105+
fields[#fields + 1] = { type = self:rewriteType(f.type), name = f.name, array_size = f.array_size, attrs = f.attrs }
106+
end
107+
end
108+
109+
local renamed_tag = node.tag and (self.names[node.tag] or node.tag)
110+
return { kind = k, kw = node.kw, tag = renamed_tag, fields = fields, variants = node.variants }
100111
end
101112

102113
error("unknown node kind: " .. tostring(node.kind))
@@ -108,11 +119,14 @@ function Context:cdef(code)
108119
local ok, nodes, err = Parser.new():parse(tokens)
109120
if not ok then error("ffix: " .. tostring(err)) end
110121

111-
-- first pass: register all declared names
122+
-- first pass: register all declared names and tags
112123
for _, node in ipairs(nodes) do
113124
if node.name then
114125
self.names[node.name] = self.pfx .. "_" .. node.name
115126
end
127+
if node.tag and not self.names[node.tag] then
128+
self.names[node.tag] = self.pfx .. "_" .. node.tag
129+
end
116130
end
117131

118132
-- second pass: rewrite and emit

src/parser.lua

Lines changed: 94 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,24 @@ function Parser:parseType()
230230
return { qualifiers = quals, name = name, pointer = pointer, reference = reference or nil }
231231
end
232232

233+
---@return string?
234+
function Parser:parseArraySize()
235+
if not self:consume("[") then return nil end
236+
local parts = {}
237+
while not self:consume("]") do
238+
local t = self:advance()
239+
if t.variant == "ident" then
240+
parts[#parts + 1] = t.ident
241+
elseif t.variant == "number" then
242+
local n = t.number
243+
parts[#parts + 1] = n == math.floor(n) and tostring(math.floor(n)) or tostring(n)
244+
else
245+
parts[#parts + 1] = t.variant
246+
end
247+
end
248+
return table.concat(parts)
249+
end
250+
233251
---@return ffix.c.Parser.Field[]
234252
function Parser:parseFields()
235253
local fields = {}
@@ -241,25 +259,27 @@ function Parser:parseFields()
241259
else
242260
name_tok = self:expect("ident")
243261
end
244-
local array_size
245-
if self:consume("[") then
246-
local parts = {}
247-
while not self:consume("]") do
248-
local t = self:advance()
249-
if t.variant == "ident" then
250-
parts[#parts + 1] = t.ident
251-
elseif t.variant == "number" then
252-
local n = t.number
253-
parts[#parts + 1] = n == math.floor(n) and tostring(math.floor(n)) or tostring(n)
254-
else
255-
parts[#parts + 1] = t.variant
256-
end
262+
local array_size = self:parseArraySize()
263+
local attrs = self:parseAttrs()
264+
fields[#fields + 1] = { type = ftype, name = name_tok and name_tok.ident, array_size = array_size, attrs = attrs }
265+
-- comma-separated names sharing the same base type: unsigned int lo, hi;
266+
while self:consume(",") do
267+
local extra_ptr = 0
268+
while self:consume("*") do extra_ptr = extra_ptr + 1 end
269+
local extra_name = self:expect("ident")
270+
local extra_type
271+
if ftype.inline_kind then
272+
extra_type = { qualifiers = ftype.qualifiers, inline_kind = ftype.inline_kind,
273+
inline_tag = ftype.inline_tag, inline_fields = ftype.inline_fields,
274+
inline_variants = ftype.inline_variants, inline_attrs = ftype.inline_attrs,
275+
pointer = ftype.pointer + extra_ptr, reference = ftype.reference }
276+
else
277+
extra_type = { qualifiers = ftype.qualifiers, name = ftype.name,
278+
pointer = ftype.pointer + extra_ptr, reference = ftype.reference }
257279
end
258-
array_size = table.concat(parts)
280+
fields[#fields + 1] = { type = extra_type, name = extra_name.ident, array_size = self:parseArraySize() }
259281
end
260-
local attrs = self:parseAttrs()
261282
self:expect(";")
262-
fields[#fields + 1] = { type = ftype, name = name_tok and name_tok.ident, array_size = array_size, attrs = attrs }
263283
end
264284
return fields
265285
end
@@ -362,27 +382,52 @@ function Parser:parseAttrs()
362382
return attrs
363383
end
364384

365-
---@return ffix.c.Parser.Node
385+
---@return ffix.c.Parser.Node[]
366386
function Parser:parseDecl()
367387
if self:consume("typedef") then
368388
local kw = self:peek()
369389

370390
if kw and (kw.variant == "struct" or kw.variant == "union") then
391+
local kw_str = kw.variant
371392
self:advance()
372393
local pre_attrs = self:parseAttrs()
373394
local tag_tok = self:consume("ident")
395+
396+
-- forward typedef: typedef struct Foo Foo; (no body follows)
397+
if not (self:peek() and self:peek().variant == "{") then
398+
if not tag_tok then error("expected tag or '{' after " .. kw_str) end
399+
local name = self:expect("ident")
400+
self:expect(";")
401+
return {{ kind = "typedef_alias",
402+
type = { qualifiers = {}, name = kw_str .. " " .. tag_tok.ident, pointer = 0 },
403+
name = name.ident }}
404+
end
405+
374406
self:expect("{")
375407
local fields = self:parseFields()
376408
local post_attrs = self:parseAttrs()
377-
local name = self:expect("ident")
378-
self:expect(";")
379409
local attrs
380410
if pre_attrs or post_attrs then
381411
attrs = {}
382412
if pre_attrs then for _, a in ipairs(pre_attrs) do attrs[#attrs + 1] = a end end
383413
if post_attrs then for _, a in ipairs(post_attrs) do attrs[#attrs + 1] = a end end
384414
end
385-
return { kind = "typedef_struct", tag = tag_tok and tag_tok.ident, fields = fields, name = name.ident, attrs = attrs }
415+
416+
local first_name = self:expect("ident")
417+
local result = {{ kind = "typedef_struct", kw = kw_str,
418+
tag = tag_tok and tag_tok.ident, fields = fields,
419+
name = first_name.ident, attrs = attrs }}
420+
-- additional declarators: typedef struct { } Foo, *FooPtr;
421+
while self:consume(",") do
422+
local ptr = 0
423+
while self:consume("*") do ptr = ptr + 1 end
424+
local alias_name = self:expect("ident")
425+
result[#result + 1] = { kind = "typedef_alias",
426+
type = { qualifiers = {}, name = first_name.ident, pointer = ptr },
427+
name = alias_name.ident }
428+
end
429+
self:expect(";")
430+
return result
386431
end
387432

388433
if kw and kw.variant == "enum" then
@@ -392,7 +437,7 @@ function Parser:parseDecl()
392437
local variants = self:parseVariants()
393438
local name = self:expect("ident")
394439
self:expect(";")
395-
return { kind = "typedef_enum", tag = tag_tok and tag_tok.ident, variants = variants, name = name.ident }
440+
return {{ kind = "typedef_enum", tag = tag_tok and tag_tok.ident, variants = variants, name = name.ident }}
396441
end
397442

398443
local ret = self:parseType()
@@ -404,20 +449,41 @@ function Parser:parseDecl()
404449
self:expect(")")
405450
local params = self:parseParams()
406451
self:expect(";")
407-
return { kind = "typedef_fnptr", ret = ret, name = name.ident, params = params }
452+
return {{ kind = "typedef_fnptr", ret = ret, name = name.ident, params = params }}
408453
end
409454

410455
local name = self:expect("ident")
411456
self:expect(";")
412-
return { kind = "typedef_alias", type = ret, name = name.ident }
457+
return {{ kind = "typedef_alias", type = ret, name = name.ident }}
413458
end
414459

415460
if self:consume("extern") then
416461
local type = self:parseType()
417462
local name = self:expect("ident")
418463
local asm_name = self:parseAsmName()
419464
self:expect(";")
420-
return { kind = "extern_var", type = type, name = name.ident, asm_name = asm_name }
465+
return {{ kind = "extern_var", type = type, name = name.ident, asm_name = asm_name }}
466+
end
467+
468+
-- bare struct/union/enum definition: struct Foo { ... };
469+
local kw_tok = self:peek()
470+
if kw_tok and (kw_tok.variant == "struct" or kw_tok.variant == "union" or kw_tok.variant == "enum") then
471+
local saved = self.ptr
472+
self:advance()
473+
local tag_tok = self:consume("ident")
474+
if self:peek() and self:peek().variant == "{" then
475+
self:advance()
476+
local fields, variants
477+
if kw_tok.variant == "enum" then
478+
variants = self:parseVariants()
479+
else
480+
fields = self:parseFields()
481+
end
482+
self:expect(";")
483+
return {{ kind = "struct_def", kw = kw_tok.variant,
484+
tag = tag_tok and tag_tok.ident, fields = fields, variants = variants }}
485+
end
486+
self.ptr = saved
421487
end
422488

423489
local ret = self:parseType()
@@ -426,8 +492,7 @@ function Parser:parseDecl()
426492
local asm_name = self:parseAsmName()
427493
local attrs = self:parseAttrs()
428494
self:expect(";")
429-
430-
return { kind = "fn_decl", ret = ret, name = name.ident, params = params, asm_name = asm_name, attrs = attrs }
495+
return {{ kind = "fn_decl", ret = ret, name = name.ident, params = params, asm_name = asm_name, attrs = attrs }}
431496
end
432497

433498
---@param tokens ffix.c.Tokenizer.Token[]
@@ -439,7 +504,9 @@ function Parser:parse(tokens)
439504
local nodes = {}
440505
local ok, err = pcall(function()
441506
while self.ptr <= #self.tokens do
442-
nodes[#nodes + 1] = self:parseDecl()
507+
for _, node in ipairs(self:parseDecl()) do
508+
nodes[#nodes + 1] = node
509+
end
443510
end
444511
end)
445512

src/printer.lua

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,9 @@ function Printer:node(node)
7777
return "typedef " .. self:typedName(node.type, node.name) .. ";"
7878

7979
elseif k == "typedef_struct" then
80+
local kw_str = node.kw or "struct"
8081
local attr_str = (node.attrs and #node.attrs > 0) and (" " .. self:attrsStr(node.attrs)) or ""
81-
local lines = { "typedef struct" .. (node.tag and (" " .. node.tag) or "") .. attr_str .. " {" }
82+
local lines = { "typedef " .. kw_str .. (node.tag and (" " .. node.tag) or "") .. attr_str .. " {" }
8283
for _, f in ipairs(node.fields) do
8384
local arr = f.array_size and ("[" .. f.array_size .. "]") or ""
8485
local fattr = (f.attrs and #f.attrs > 0) and (" " .. self:attrsStr(f.attrs)) or ""
@@ -87,6 +88,22 @@ function Printer:node(node)
8788
lines[#lines + 1] = "} " .. node.name .. ";"
8889
return table.concat(lines, "\n")
8990

91+
elseif k == "struct_def" then
92+
local lines = { node.kw .. (node.tag and (" " .. node.tag) or "") .. " {" }
93+
if node.fields then
94+
for _, f in ipairs(node.fields) do
95+
local arr = f.array_size and ("[" .. f.array_size .. "]") or ""
96+
local fattr = (f.attrs and #f.attrs > 0) and (" " .. self:attrsStr(f.attrs)) or ""
97+
lines[#lines + 1] = "\t" .. self:typedName(f.type, f.name) .. arr .. fattr .. ";"
98+
end
99+
else
100+
for _, v in ipairs(node.variants) do
101+
lines[#lines + 1] = "\t" .. v.name .. ","
102+
end
103+
end
104+
lines[#lines + 1] = "};"
105+
return table.concat(lines, "\n")
106+
90107
elseif k == "typedef_enum" then
91108
local lines = { "typedef enum" .. (node.tag and (" " .. node.tag) or "") .. " {" }
92109
for _, v in ipairs(node.variants) do

0 commit comments

Comments
 (0)