Created
February 22, 2014 13:32
-
-
Save starwing/9154859 to your computer and use it in GitHub Desktop.
a C declaration parser written in Lua
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
local parse_declare | |
local function parse_typespec(decl, s, pos) | |
-- typespec: cv_decl* spec cv_decl* | |
-- cv_decl: 'const' | 'volatile' | |
while true do | |
local spec, newpos = s:match("^%s*([%w_]+)%s*()", pos) | |
if not spec then break end | |
if spec == "const" then | |
decl.const = true | |
elseif spec == "volatile" then | |
decl.volatile = true | |
elseif not decl.base then | |
decl.base = spec | |
else | |
break | |
end | |
pos = newpos | |
end | |
if decl.base then | |
return decl, nil, pos | |
else | |
return nil, "type spec expected", pos | |
end | |
end | |
local function parse_args(s, pos) | |
-- args: arglist? '...'? | |
-- arglist: declare (',' declare)* | |
local args = {} | |
while true do | |
local dot3, newpos = s:match("^%s*(%.%.%.)%s*()", pos) | |
if dot3 then return args, nil, newpos end | |
local decl, err, newpos = parse_declare({}, s, pos) | |
if not decl then return nil, err, newpos end | |
args[#args + 1] = decl | |
pos = newpos | |
if pos > #s then break end | |
local comma, newpos = s:match("^%s*(,)%s*()", pos) | |
if not comma then | |
return nil, "missing ',' after decl", pos | |
end | |
pos = newpos | |
end | |
return args, nil, pos | |
end | |
local function parse_postdecl(decl, s, pos) | |
-- postdecl: array_decl* | func_decl | |
while true do | |
local array_decl, newpos = s:match("^%s*(%b[])%s*()", pos) | |
if not array_decl then break end | |
if not decl.array then decl.array = {} end | |
decl.array[#decl.array+1] = array_decl:sub(2,-2) | |
pos = newpos | |
end | |
if not decl.array then | |
local func_decl, newpos = s:match("^%s*(%b())%s*()", pos) | |
if func_decl then | |
local args, err, nestpos = parse_args(func_decl:sub(2, -2)) | |
if not args then return nil, err, pos+nestpos+1 end | |
decl.args = args | |
pos = newpos | |
end | |
end | |
return decl, nil, pos | |
end | |
local function parse_declarator(decl, s, pos) | |
-- declarator: '*'* '(' declarator ')' postdecl | |
-- | '*'* name postdecl | (empty) | |
local pointer, newpos = s:match("^%s*([%s%*]+)%s*()", pos) | |
if pointer then | |
decl.pointer = #(pointer:gsub("%s+", "")) | |
pos = newpos | |
end | |
local begin, nest, newpos = s:match("^%s*()(%b())%s*()", pos) | |
if nest then | |
local nestdecl, err, nestpos = parse_declarator({}, nest:sub(2, -2)) | |
if not nestdecl then return nil, err, newpos end | |
if nestpos < #nest-1 then | |
return nil, "trailling tokens in sub declarator", begin+nestpos | |
end | |
if not next(nestdecl) then | |
nestdecl = decl | |
else | |
nestdecl.derived = decl | |
end | |
local decl, err, pos = parse_postdecl(decl, s, newpos) | |
if not decl then return nil, err, pos end | |
return nestdecl, nil, pos | |
end | |
local name, newpos = s:match("^%s*([%w_]+)%s*()", pos) | |
if name then | |
decl.name = name | |
return parse_postdecl(decl, s, newpos) | |
end | |
return decl, nil, pos or 1 | |
end | |
function parse_declare(decl, s, pos) | |
-- declare: typespec declarator | |
local decl, err, pos = parse_typespec(decl, s, pos) | |
if not decl then return nil, err, pos end | |
local decl, err, pos = parse_declarator(decl, s, pos) | |
if not decl then return nil, err, pos end | |
return decl, nil, pos | |
end | |
local function declare(s, pos) | |
local decl, err, pos = parse_declare({}, s, pos) | |
if not decl then | |
error(err..", near '"..s:sub(pos).."'") | |
end | |
pos = s:match("^%s*()", pos) | |
if pos <= #s then | |
error("trailling tokens, near '"..s:sub(pos).."'") | |
end | |
return decl | |
end | |
local serpent = require 'serpent' | |
local tests = { | |
[[ int ]], | |
[[ int a ]], | |
[[ int *a ]], | |
[[ int ***a ]], | |
[[ int ***a[] ]], | |
[[ int *(*a)[] ]], | |
[[ int *(*a)[1][2][] ]], | |
[[ int (*)(int,int,float b) ]], | |
[[ int *(*a)(int,int,float b) ]], | |
[[ int *(*a)(int,int()(int)) ]], | |
} | |
for i, v in ipairs(tests) do | |
print("test", v) | |
print(serpent.block(declare(v))) | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment