Skip to content

Commit

Permalink
Refactor the wrappers processing step (#145)
Browse files Browse the repository at this point in the history
* Refactor the wrappers processing step

* remove flag
  • Loading branch information
planetis-m authored Oct 27, 2024
1 parent 2352c52 commit aa52417
Show file tree
Hide file tree
Showing 2 changed files with 187 additions and 137 deletions.
9 changes: 1 addition & 8 deletions tools/wrapper/naylib_wrapper.nim
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,6 @@ proc parseCommandLine(outputpath, configpath: var string): bool =
discard
result = outputpath.len > 0 and configpath.len > 0

proc processApi(ctx: var ApiContext, config: ConfigData) =
filterIgnoredSymbols(ctx, config)
processStructs(ctx, config)
processEnums(ctx, config)
processAliases(ctx, config)
processFunctions(ctx, config)

proc generateWrapper(ctx: ApiContext; outputpath: string; config: ConfigData) =
var b = openBuilder(outputpath, header = config.cHeader)
genBindings(b, ctx, config.moduleHeader, config.afterEnums, config.afterObjects,
Expand All @@ -37,7 +30,7 @@ proc main =

let config = parseConfig(configpath)
var ctx = ApiContext(api: parseApi(config.apiDefinition))
processApi(ctx, config)
processApiTypes(ctx, config)
generateWrapper(ctx, outputpath, config)

main()
315 changes: 186 additions & 129 deletions tools/wrapper/processor.nim
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ proc getReplacement(x, y: string, config: ConfigData): string =
proc getReplacement(x: string, config: ConfigData): string =
result = getOrDefault(config.typeReplacements, (x, ""))

proc filterIgnoredSymbols*(ctx: var ApiContext; config: ConfigData) =
proc filterIgnoredSymbols(ctx: var ApiContext; config: ConfigData) =
keepIf(ctx.api.defines, x => isNotIgnored(x.name, config))
keepIf(ctx.api.structs, x => isNotIgnored(x.name, config))
keepIf(ctx.api.callbacks, x => isNotIgnored(x.name, config))
Expand Down Expand Up @@ -67,121 +67,176 @@ proc updateType(typeVar: var string; module, name: string;
proc updateType(typeVar: var string; name: string; pointerType: PointerType; config: ConfigData) =
updateType(typeVar, name, "", pointerType, config)

proc processEnums*(ctx: var ApiContext; config: ConfigData) =
proc checkCstringType(fnc: FunctionInfo, kind: string, config: ConfigData): bool =
kind == "cstring" and fnc.name notin config.wrappedFuncs and hasVarargs notin fnc.flags

proc isOpenArrayParameter(x, y: string, config: ConfigData): bool =
(x, y) in config.openArrayParameters

proc isVarargsParam(param: ParamInfo): bool =
param.name == "args" and param.`type` == "..."

proc sortEnumValues(enm: var EnumInfo, config: ConfigData) =
sort(enm.values, proc (x, y: ValueInfo): int = cmp(x.value, y.value))

proc processEnumName(enm: var EnumInfo, config: ConfigData) =
if shouldRemoveNamespacePrefix(enm.name, config):
removePrefix(enm.name, config.namespacePrefix)

proc processEnumValues(enm: var EnumInfo, config: ConfigData) =
proc removePrefixes(name: string, config: ConfigData): string =
result = name
for prefix in config.enumValuePrefixes:
if result.startsWith(prefix) and
not isDigit(result[prefix.len]):
if result.startsWith(prefix) and not isDigit(result[prefix.len]):
result.removePrefix(prefix)
break

for val in mitems(enm.values):
val.name = removePrefixes(val.name, config).camelCaseAscii()

proc processEnums(ctx: var ApiContext, config: ConfigData) =
# Execute the processing stages in order
for enm in mitems(ctx.api.enums):
sort(enm.values, proc (x, y: ValueInfo): int = cmp(x.value, y.value))
if shouldRemoveNamespacePrefix(enm.name, config):
removePrefix(enm.name, config.namespacePrefix)
for val in mitems(enm.values):
val.name = removePrefixes(val.name, config).camelCaseAscii()
sortEnumValues(enm, config)
processEnumName(enm, config)
processEnumValues(enm, config)

proc processAliases*(ctx: var ApiContext; config: ConfigData) =
for alias in mitems(ctx.api.aliases):
if shouldMarkAsDistinct(alias.name, config):
alias.flags.incl isDistinct
proc processAliasFlags(alias: var AliasInfo, config: ConfigData) =
if shouldMarkAsDistinct(alias.name, config):
alias.flags.incl isDistinct

proc preprocessStructs(ctx: var ApiContext, config: ConfigData) =
for obj in mitems(ctx.api.structs):
if shouldMarkAsMangled(obj.name, config):
obj.flags.incl isMangled
if shouldMarkAsComplete(obj.name, config):
obj.flags.incl isCompleteStruct
if shouldMarkAsPrivate(obj.name, config):
obj.flags.incl isPrivate

for fld in mitems(obj.fields):
if shouldMarkAsPrivate(obj.name, fld.name, config) or isPrivate in obj.flags:
fld.flags.incl isPrivate
if isArray(obj.name, fld.name, config):
fld.flags.incl isPtArray

proc processStructs*(ctx: var ApiContext; config: ConfigData) =
preprocessStructs(ctx, config)
proc processAliases(ctx: var ApiContext, config: ConfigData) =
# Execute the processing stages in order
for alias in mitems(ctx.api.aliases):
processAliasFlags(alias, config)

proc processStructFlags(obj: var StructInfo, config: ConfigData) =
if shouldMarkAsMangled(obj.name, config):
obj.flags.incl isMangled
if shouldMarkAsComplete(obj.name, config):
obj.flags.incl isCompleteStruct
if shouldMarkAsPrivate(obj.name, config):
obj.flags.incl isPrivate

proc processStructFields(obj: var StructInfo, config: ConfigData) =
for fld in mitems(obj.fields):
if shouldMarkAsPrivate(obj.name, fld.name, config) or isPrivate in obj.flags:
fld.flags.incl isPrivate
if isArray(obj.name, fld.name, config):
fld.flags.incl isPtArray

proc processStructNames(obj: var StructInfo, config: ConfigData) =
if shouldRemoveNamespacePrefix(obj.name, config):
obj.importName = obj.name
removePrefix(obj.name, config.namespacePrefix)

template effectiveName(obj: StructInfo): untyped =
if obj.importName != "": obj.importName
else: obj.name

proc updateFieldTypes(obj: var StructInfo, config: ConfigData) =
for fld in mitems(obj.fields):
let pointerType = if isPtArray in fld.flags: ptArray else: ptPtr
updateType(fld.`type`, effectiveName(obj), fld.name, pointerType, config)

proc finalizeProcessing(obj: var StructInfo, ctx: var ApiContext, config: ConfigData) =
for fld in mitems(obj.fields):
# Handle read-only fields
if isReadOnlyField(effectiveName(obj), fld.name, config):
ctx.readOnlyFieldAccessors.add ParamInfo(
name: fld.name, `type`: obj.name, dirty: fld.`type`
)
fld.flags.incl isPrivate
# Handle array accessors
if {isPtArray, isPrivate} * fld.flags == {isPtArray}:
let tmp = capitalizeAscii(fld.name)
ctx.boundCheckedArrayAccessors.add AliasInfo(
`type`: obj.name, name: obj.name & tmp
)
# Mark array fields as private
if isPtArray in fld.flags:
fld.flags.incl isPrivate

proc processStructs(ctx: var ApiContext, config: ConfigData) =
# Execute the processing stages in order
for obj in mitems(ctx.api.structs):
if shouldRemoveNamespacePrefix(obj.name, config):
obj.importName = obj.name
removePrefix(obj.name, config.namespacePrefix)

template objName: untyped =
if obj.importName != "": obj.importName else: obj.name

for fld in mitems(obj.fields):
updateType(fld.`type`, objName, fld.name,
if isPtArray in fld.flags: ptArray else: ptPtr, config)
if isReadOnlyField(objName, fld.name, config):
ctx.readOnlyFieldAccessors.add ParamInfo(
name: fld.name, `type`: obj.name, dirty: fld.`type`)
fld.flags.incl isPrivate
if {isPtArray, isPrivate} * fld.flags == {isPtArray}:
let tmp = capitalizeAscii(fld.name)
ctx.boundCheckedArrayAccessors.add AliasInfo(
`type`: obj.name, name: obj.name & tmp)
if isPtArray in fld.flags:
fld.flags.incl isPrivate

proc checkCstringType(fnc: FunctionInfo, kind: string, config: ConfigData): bool =
kind == "cstring" and fnc.name notin config.wrappedFuncs and hasVarargs notin fnc.flags

proc isOpenArrayParameter(x, y: string, config: ConfigData): bool =
(x, y) in config.openArrayParameters

proc isVarargsParam(param: ParamInfo): bool =
param.name == "args" and param.`type` == "..."

proc preprocessFunctions(ctx: var ApiContext, config: ConfigData) =
for fnc in mitems(ctx.api.functions):
if shouldMarkAsMangled(fnc.name, config):
fnc.flags.incl isMangled
if fnc.name in config.wrappedFuncs:
fnc.flags.incl isWrappedFunc
fnc.flags.incl isPrivate
if shouldMarkAsPrivate(fnc.name, config):
fnc.flags.incl isPrivate
if fnc.name in config.noSideEffectsFuncs:
fnc.flags.incl isFunc

if fnc.params.len > 0 and isVarargsParam(fnc.params[^1]):
fnc.flags.incl hasVarargs
fnc.params.setLen(fnc.params.high)

for i, param in enumerate(fnc.params.mitems):
if isArray(fnc.name, param.name, config):
param.flags.incl isPtArray
let pointerType =
if isPtArray in param.flags: ptArray
elif isOutParameter(fnc.name, param.name, config): ptOut
elif isPrivate notin fnc.flags: ptVar
else: ptPtr
let paramType = convertType(param.`type`, pointerType)
if checkCstringType(fnc, paramType, config):
param.flags.incl isString
fnc.flags.incl isAutoWrappedFunc
if isOpenArrayParameter(fnc.name, param.name, config):
param.flags.incl isOpenArray
fnc.params[i+1].flags.incl isArrayLen
fnc.flags.incl isAutoWrappedFunc
if paramType.startsWith("var "):
param.flags.incl isVarParam
param.dirty = paramType
if fnc.returnType != "void":
if isArray(fnc.name, config):
fnc.flags.incl isPtArray
let returnType = convertType(fnc.returnType)
if checkCstringType(fnc, returnType, config):
fnc.flags.incl isString
fnc.flags.incl isAutoWrappedFunc
if isAutoWrappedFunc in fnc.flags:
fnc.flags.incl isPrivate

proc processFunctions*(ctx: var ApiContext; config: ConfigData) =
processStructFlags(obj, config)
processStructFields(obj, config)
processStructNames(obj, config)
updateFieldTypes(obj, config)
finalizeProcessing(obj, ctx, config)

# Individual processing stages
proc processFunctionFlags(fnc: var FunctionInfo, config: ConfigData) =
if shouldMarkAsMangled(fnc.name, config):
fnc.flags.incl isMangled
if fnc.name in config.wrappedFuncs:
fnc.flags.incl isWrappedFunc
fnc.flags.incl isPrivate
if shouldMarkAsPrivate(fnc.name, config):
fnc.flags.incl isPrivate
if fnc.name in config.noSideEffectsFuncs:
fnc.flags.incl isFunc

proc processVarargs(fnc: var FunctionInfo, config: ConfigData) =
if fnc.params.len > 0 and isVarargsParam(fnc.params[^1]):
fnc.flags.incl hasVarargs
fnc.params.setLen(fnc.params.high)

proc processParameters(fnc: var FunctionInfo, config: ConfigData) =
for i, param in enumerate(fnc.params.mitems):
if isArray(fnc.name, param.name, config):
param.flags.incl isPtArray
let pointerType =
if isPtArray in param.flags: ptArray
elif isOutParameter(fnc.name, param.name, config): ptOut
elif isPrivate notin fnc.flags: ptVar
else: ptPtr
let paramType = convertType(param.`type`, pointerType)
if checkCstringType(fnc, paramType, config):
param.flags.incl isString
fnc.flags.incl isAutoWrappedFunc
if isOpenArrayParameter(fnc.name, param.name, config):
param.flags.incl isOpenArray
fnc.params[i+1].flags.incl isArrayLen
fnc.flags.incl isAutoWrappedFunc
if paramType.startsWith("var "):
param.flags.incl isVarParam
param.dirty = paramType

proc processReturnType(fnc: var FunctionInfo, config: ConfigData) =
if fnc.returnType != "void":
if isArray(fnc.name, config):
fnc.flags.incl isPtArray
let returnType = convertType(fnc.returnType)
if checkCstringType(fnc, returnType, config):
fnc.flags.incl isString
fnc.flags.incl isAutoWrappedFunc
if isAutoWrappedFunc in fnc.flags:
fnc.flags.incl isPrivate

proc updateParameterTypes(fnc: var FunctionInfo, config: ConfigData) =
for i, param in enumerate(fnc.params.mitems):
if isOpenArray in param.flags:
param.dirty = convertType(param.`type`, ptOpenArray)
param.flags.incl isOpenArray
fnc.params[i+1].dirty = param.name # stores array name
let pointerType =
if isPtArray in param.flags: ptArray
elif isOutParameter(fnc.name, param.name, config): ptOut
elif isPrivate notin fnc.flags: ptVar
else: ptPtr
updateType(param.`type`, fnc.name, param.name, pointerType, config)

proc updateReturnType(fnc: var FunctionInfo, config: ConfigData) =
if fnc.returnType != "void":
let pointerType =
if isPtArray in fnc.flags: ptArray
elif isPrivate notin fnc.flags: ptVar
else: ptPtr
updateType(fnc.returnType, fnc.name, pointerType, config)

proc generateNames(fnc: var FunctionInfo, config: ConfigData) =
proc shouldRemoveSuffix(name: string, config: ConfigData): bool =
name in config.functionOverloads

Expand All @@ -196,27 +251,29 @@ proc processFunctions*(ctx: var ApiContext; config: ConfigData) =
break
result = uncapitalizeAscii(result)

preprocessFunctions(ctx, config)
fnc.importName = (if isMangled in fnc.flags: "rl" else: "") & fnc.name
fnc.name = generateProcName(fnc.name, config)

proc finalizeProcessing(fnc: var FunctionInfo, ctx: var ApiContext, config: ConfigData) =
if isAutoWrappedFunc in fnc.flags:
ctx.funcsToWrap.add fnc

proc processFunctions(ctx: var ApiContext, config: ConfigData) =
# Execute the processing stages in order
for fnc in mitems(ctx.api.functions):
for i, param in enumerate(fnc.params.mitems):
if isOpenArray in param.flags:
param.dirty = convertType(param.`type`, ptOpenArray)
param.flags.incl isOpenArray
fnc.params[i+1].dirty = param.name # stores array name
let pointerType =
if isPtArray in param.flags: ptArray
elif isOutParameter(fnc.name, param.name, config): ptOut
elif isPrivate notin fnc.flags: ptVar
else: ptPtr
updateType(param.`type`, fnc.name, param.name, pointerType, config)
if fnc.returnType != "void":
let pointerType =
if isPtArray in fnc.flags: ptArray
elif isPrivate notin fnc.flags: ptVar
else: ptPtr
updateType(fnc.returnType, fnc.name, pointerType, config)

fnc.importName = (if isMangled in fnc.flags: "rl" else: "") & fnc.name
fnc.name = generateProcName(fnc.name, config)
if isAutoWrappedFunc in fnc.flags:
ctx.funcsToWrap.add fnc
processFunctionFlags(fnc, config)
processVarargs(fnc, config)
processParameters(fnc, config)
processReturnType(fnc, config)
updateParameterTypes(fnc, config)
updateReturnType(fnc, config)
generateNames(fnc, config)
finalizeProcessing(fnc, ctx, config)

proc processApiTypes*(ctx: var ApiContext, config: ConfigData) =
# Process all type categories
filterIgnoredSymbols(ctx, config)
processEnums(ctx, config)
processAliases(ctx, config)
processStructs(ctx, config)
processFunctions(ctx, config)

0 comments on commit aa52417

Please sign in to comment.