mirror of
https://github.com/Nexoniarz/Finch.git
synced 2026-11-03 08:54:25 +00:00
C import: unions (SDL_Event) and their fields; #defines that are expressions, casts or text; typedef'd structs no function mentions; FLAG_A | FLAG_B fits a u64
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
6142e47e71
commit
d0666cf788
7 changed files with 314 additions and 13 deletions
165
src/cimport.fch
165
src/cimport.fch
|
|
@ -21,11 +21,13 @@ struct CFunc {
|
|||
str unsupported // non-empty: why Finch can't call it (yet)
|
||||
}
|
||||
|
||||
struct CConst { // enum values and simple #define numbers
|
||||
struct CConst { // enum values and #define constants
|
||||
bool isFloat
|
||||
int i
|
||||
float f
|
||||
str text // a float as written, so no digits are lost: 3.14159265358979323846
|
||||
bool isStr // #define NAME "text"
|
||||
str s
|
||||
}
|
||||
|
||||
struct CField {
|
||||
|
|
@ -44,6 +46,7 @@ struct CStruct {
|
|||
int align
|
||||
str unsupported // why it can't be used by value (pointers to it still work)
|
||||
bool hasHidden
|
||||
bool isUnion // every field starts at byte 0
|
||||
}
|
||||
|
||||
struct CImports {
|
||||
|
|
@ -75,6 +78,7 @@ struct Importer {
|
|||
map[str]str recordNames // record USR -> Finch name (a typedef name wins)
|
||||
map[str]bool defining // USR -> true while its fields are being mapped
|
||||
int anon
|
||||
[]str exprMacros // #defines that are expressions: clang works out their values
|
||||
}
|
||||
|
||||
fn text(CXString s) -> str {
|
||||
|
|
@ -169,6 +173,7 @@ fn defineRecord(ptr[Importer] imp, CXType type) -> str {
|
|||
s := CStruct(name: name, size: size, align: int(clang_Type_getAlignOf(t)))
|
||||
if clang_getCursorKind(decl) == CXCursor_UnionDecl {
|
||||
s.unsupported = "is a C union"
|
||||
s.isUnion = true
|
||||
}
|
||||
// the fields are collected by a callback that gets the importer and the struct through one pointer
|
||||
ctx := FieldCtx(imp: imp, s: addr(s))
|
||||
|
|
@ -194,9 +199,10 @@ fn visitField(CXCursor field, ptr data) -> i32 {
|
|||
if clang_Cursor_isBitField(field) != 0 {
|
||||
c.s.unsupported = "has bit fields"
|
||||
f.hidden = true
|
||||
} else if ft.kind == CXType_Record && clang_getCursorKind(clang_getTypeDeclaration(ft)) == CXCursor_UnionDecl {
|
||||
f.hidden = true // unions are kept as opaque bytes
|
||||
} else {
|
||||
if ft.kind == CXType_Record && clang_getCursorKind(clang_getTypeDeclaration(ft)) == CXCursor_UnionDecl {
|
||||
c.s.unsupported = "has a union inside" // its fields can be used, but not passed by value
|
||||
}
|
||||
m := mapType(c.imp, ft, false, false)
|
||||
if m.ok {
|
||||
f.ty = m.ty
|
||||
|
|
@ -293,9 +299,6 @@ fn mapType(ptr[Importer] imp, CXType type, bool signature, bool param) -> Mapped
|
|||
return ok(ast.ptrTo(inner.ty))
|
||||
}
|
||||
if k == CXType_Record {
|
||||
if clang_getCursorKind(clang_getTypeDeclaration(t)) == CXCursor_UnionDecl {
|
||||
return no("uses a C union by value")
|
||||
}
|
||||
name := defineRecord(imp, t)
|
||||
if name.len == 0 {
|
||||
return no("uses an incomplete C struct by value")
|
||||
|
|
@ -366,19 +369,93 @@ fn parseInt(str lit) -> Number {
|
|||
return Number(c: CConst(i: n), ok: true)
|
||||
}
|
||||
|
||||
// Simple #defines: a single number, maybe negative, maybe in parentheses.
|
||||
fn macroValue(ptr[Importer] imp, CXCursor c) -> Number {
|
||||
// The tokens after a #define's name; `safe` is false if any of them could not be part of a
|
||||
// constant expression (braces, semicolons, statements), so it is never handed to clang.
|
||||
struct MacroTokens {
|
||||
[]str parts
|
||||
bool safe
|
||||
}
|
||||
|
||||
fn macroTokens(ptr[Importer] imp, CXCursor c) -> MacroTokens {
|
||||
ptr[CXToken] tokens = null
|
||||
u32 n = 0
|
||||
clang_tokenize(imp.tu, clang_getCursorExtent(c), addr(tokens), addr(n))
|
||||
[]str parts
|
||||
out := MacroTokens(safe: true)
|
||||
allowed := ["(", ")", "+", "-", "*", "/", "%", "<<", ">>", "|", "&", "^", "~", "!", "<", ">", "<=", ">=", "==", "!=", "&&", "||", "?", ":", ","]
|
||||
words := ["sizeof", "unsigned", "signed", "int", "long", "short", "char", "float", "double", "const", "_Bool"]
|
||||
depth := 0
|
||||
for k in 1..int(n) { // token 0 is the macro's name
|
||||
if clang_getTokenKind(tokens[k]) == CXToken_Comment {
|
||||
kind := clang_getTokenKind(tokens[k])
|
||||
if kind == CXToken_Comment {
|
||||
continue
|
||||
}
|
||||
parts.push(text(clang_getTokenSpelling(imp.tu, tokens[k])))
|
||||
t := text(clang_getTokenSpelling(imp.tu, tokens[k]))
|
||||
out.parts.push(t)
|
||||
if kind == CXToken_Punctuation {
|
||||
if !allowed.contains(t) {
|
||||
out.safe = false
|
||||
}
|
||||
if t == "(" {
|
||||
depth += 1
|
||||
} else if t == ")" {
|
||||
depth -= 1
|
||||
}
|
||||
} else if kind == CXToken_Keyword && !words.contains(t) {
|
||||
out.safe = false
|
||||
}
|
||||
}
|
||||
clang_disposeTokens(imp.tu, tokens, n)
|
||||
if depth != 0 {
|
||||
out.safe = false
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// "text" as C writes it -> the text: \n \t \" \\ \0 and \xNN; "a" "b" are joined
|
||||
fn cString([]str parts) -> Number {
|
||||
out := ""
|
||||
for p in parts {
|
||||
if p.len < 2 || p[0] != '"' || p[p.len - 1] != '"' {
|
||||
return Number()
|
||||
}
|
||||
i := 1
|
||||
while i < p.len - 1 {
|
||||
c := p[i]
|
||||
if c == '\\' && i + 1 < p.len - 1 {
|
||||
i += 1
|
||||
e := p[i]
|
||||
if e == 'n' {
|
||||
c = '\n'
|
||||
} else if e == 't' {
|
||||
c = '\t'
|
||||
} else if e == 'r' {
|
||||
c = '\r'
|
||||
} else if e == '0' {
|
||||
c = char(0)
|
||||
} else if e == 'x' {
|
||||
v := 0
|
||||
while i + 1 < p.len - 1 && "0123456789abcdefABCDEF".contains(str(p[i + 1])) {
|
||||
i += 1
|
||||
v = v * 16 + parseInt("0x" + str(p[i])).c.i
|
||||
}
|
||||
c = char(v & 255)
|
||||
} else {
|
||||
c = e
|
||||
}
|
||||
}
|
||||
out = out + str(c)
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
return Number(c: CConst(isStr: true, s: out), ok: true)
|
||||
}
|
||||
|
||||
// Simple #defines: a single number, maybe negative, maybe in parentheses; or a "text".
|
||||
fn macroValue(ptr[Importer] imp, CXCursor c) -> Number {
|
||||
parts := macroTokens(imp, c).parts
|
||||
if parts.len > 0 && parts[0].starts_with("\"") {
|
||||
return cString(parts)
|
||||
}
|
||||
|
||||
while parts.len >= 2 && parts[0] == "(" && parts[parts.len - 1] == ")" {
|
||||
parts.remove(0)
|
||||
|
|
@ -438,6 +515,7 @@ fn visit(ptr[Importer] imp, CXCursor c) {
|
|||
if !used && !imp.out.structs.has(name) {
|
||||
imp.recordNames[usr] = name
|
||||
}
|
||||
defineRecord(imp, u) // usable even if no function mentions it: Motion m
|
||||
}
|
||||
} else if kind == CXCursor_FunctionDecl {
|
||||
if imp.out.fns.has(name) {
|
||||
|
|
@ -483,7 +561,7 @@ fn visit(ptr[Importer] imp, CXCursor c) {
|
|||
if g.ok {
|
||||
imp.out.globals[name] = g.ty
|
||||
}
|
||||
} else if kind == CXCursor_StructDecl {
|
||||
} else if kind == CXCursor_StructDecl || kind == CXCursor_UnionDecl {
|
||||
if clang_isCursorDefinition(c) != 0 && name.len > 0 && !name.contains(" ") {
|
||||
usr := text(clang_getCursorUSR(c))
|
||||
if !imp.recordNames.has(usr) {
|
||||
|
|
@ -500,9 +578,17 @@ fn visit(ptr[Importer] imp, CXCursor c) {
|
|||
if clang_Cursor_isMacroBuiltin(c) != 0 || clang_Cursor_isMacroFunctionLike(c) != 0 {
|
||||
return
|
||||
}
|
||||
if imp.out.consts.has(name) {
|
||||
return
|
||||
}
|
||||
v := macroValue(imp, c)
|
||||
if v.ok && !imp.out.consts.has(name) {
|
||||
if v.ok {
|
||||
imp.out.consts[name] = v.c
|
||||
return
|
||||
}
|
||||
mt := macroTokens(imp, c)
|
||||
if mt.safe && mt.parts.len > 0 {
|
||||
imp.exprMacros.push(name) // (1 << 3), (FLAG_A | FLAG_B), ((Uint64)1): asked of clang later
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -563,7 +649,60 @@ fn importHeaders([]Header headers, []str dirs, str target, bool windows, str ccF
|
|||
imp.header = h.path
|
||||
clang_visitChildren(clang_getTranslationUnitCursor(tu), addr(visitTop), addr(imp))
|
||||
clang_disposeTranslationUnit(tu)
|
||||
evalMacros(addr(imp), index, h, argv)
|
||||
}
|
||||
clang_disposeIndex(index)
|
||||
return imp.out
|
||||
}
|
||||
|
||||
// #defines that are expressions: each becomes `static const __auto_type __finch_m_NAME = (NAME);`
|
||||
// in a second look at the header, and clang works out the value. One that isn't a constant
|
||||
// (a type, a function call) just gives clang an error there, and is left out.
|
||||
fn evalMacros(ptr[Importer] imp, ptr index, Header h, []ptr[char] argv) {
|
||||
if imp.exprMacros.len == 0 {
|
||||
return
|
||||
}
|
||||
src := "#include \"" + h.path + "\"\n"
|
||||
for m in imp.exprMacros {
|
||||
src = src + "static const __auto_type __finch_m_" + m + " = (" + m + ");\n"
|
||||
}
|
||||
[]str none
|
||||
imp.exprMacros = none
|
||||
file := host.unsavedFile("finch_macros.c", src)
|
||||
ptr tu = null
|
||||
err := clang_parseTranslationUnit2(index, "finch_macros.c", argv.ptr, i32(argv.len), addr(file), 1, u32(CXTranslationUnit_SkipFunctionBodies), addr(tu))
|
||||
if err != CXError_Success || tu == null {
|
||||
return
|
||||
}
|
||||
clang_visitChildren(clang_getTranslationUnitCursor(tu), addr(visitMacroVar), imp)
|
||||
clang_disposeTranslationUnit(tu)
|
||||
}
|
||||
|
||||
fn visitMacroVar(CXCursor c, CXCursor parent, ptr data) -> i32 {
|
||||
ptr[Importer] imp = data
|
||||
if clang_getCursorKind(c) != CXCursor_VarDecl {
|
||||
return CXChildVisit_Continue
|
||||
}
|
||||
name := text(clang_getCursorSpelling(c))
|
||||
if !name.starts_with("__finch_m_") {
|
||||
return CXChildVisit_Continue
|
||||
}
|
||||
name = name.sub(10, name.len)
|
||||
r := clang_Cursor_Evaluate(c)
|
||||
if r == null || imp.out.consts.has(name) {
|
||||
return CXChildVisit_Continue
|
||||
}
|
||||
k := clang_EvalResult_getKind(r)
|
||||
if k == CXEval_Int {
|
||||
imp.out.consts[name] = CConst(i: int(clang_EvalResult_getAsLongLong(r)))
|
||||
} else if k == CXEval_Float {
|
||||
v := clang_EvalResult_getAsDouble(r)
|
||||
if v == v && v - v == 0.0 { // not nan or inf
|
||||
imp.out.consts[name] = CConst(isFloat: true, f: v, text: str(v))
|
||||
}
|
||||
} else if k == CXEval_StrLiteral {
|
||||
imp.out.consts[name] = CConst(isStr: true, s: clang_EvalResult_getAsStr(r))
|
||||
}
|
||||
clang_EvalResult_dispose(r)
|
||||
return CXChildVisit_Continue
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue