c2nImport ret, del specials from header, cache if exec not die, ast2 type pragma improvements
This commit is contained in:
parent
8b9c39f42e
commit
e06917f40f
6 changed files with 306 additions and 105 deletions
|
|
@ -5,8 +5,6 @@ author = "genotrance"
|
|||
description = "C/C++ interop for Nim"
|
||||
license = "MIT"
|
||||
|
||||
# this gives Warning: Binary 'nimterop/toast' was already installed from source directory
|
||||
# when running `nimble install --verbose -y`
|
||||
bin = @["nimterop/toast"]
|
||||
installDirs = @["nimterop"]
|
||||
installFiles = @["config.nims"]
|
||||
|
|
@ -37,7 +35,7 @@ task test, "Test":
|
|||
buildToastTask()
|
||||
|
||||
execTest "tests/tast2.nim"
|
||||
#execCmd "nim c -f -d:HEADER -r tests/tast2.nim"
|
||||
execCmd "nim c -f -d:HEADER -r tests/tast2.nim"
|
||||
|
||||
execTest "tests/tnimterop_c.nim"
|
||||
execCmd "nim cpp -f -r tests/tnimterop_cpp.nim"
|
||||
|
|
@ -53,5 +51,6 @@ task test, "Test":
|
|||
# getHeader tests
|
||||
withDir("tests"):
|
||||
execCmd("nim e getheader.nims")
|
||||
execCmd("nim e wrappers.nims")
|
||||
|
||||
docsTask()
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ proc getOverrideOrSkip(nimState: NimState, node: TSNode, origname: string, kind:
|
|||
let
|
||||
# Get cleaned name for symbol, set parent so that cOverride is ignored
|
||||
name = nimState.getIdentifier(origname, kind, parent = "override")
|
||||
|
||||
|
||||
override = nimState.getOverride(origname, kind)
|
||||
|
||||
var
|
||||
|
|
@ -115,7 +115,7 @@ proc newConstDef(nimState: NimState, node: TSNode, fname = "", fval = ""): PNode
|
|||
else:
|
||||
# node[0] = identifier = const name
|
||||
nimState.getNodeVal(node.getAtom())
|
||||
|
||||
|
||||
name = nimState.getIdentifier(origname, nskConst)
|
||||
info = nimState.getLineInfo(node)
|
||||
ident = nimState.getIdent(name, info)
|
||||
|
|
@ -182,7 +182,7 @@ proc addPragma(nimState: NimState, node: TSNode, pragma: PNode, name: string, va
|
|||
let
|
||||
pinfo = nimState.getLineInfo(node.getAtom())
|
||||
pident = nimState.getIdent(name, pinfo, exported = false)
|
||||
|
||||
|
||||
if value.isNil:
|
||||
pragma.add pident
|
||||
else:
|
||||
|
|
@ -248,19 +248,28 @@ proc newPragmaExpr(nimState: NimState, node: TSNode, ident: PNode, pragmas: seq[
|
|||
result.add ident
|
||||
result.add nimState.newPragma(node, pragmas)
|
||||
|
||||
proc newTypeIdent(nimState: NimState, node: TSNode, kind = nskType, fname = "", pragmas: seq[string] = @[]): PNode =
|
||||
proc newTypeIdent(nimState: NimState, node: TSNode, kind = nskType, fname = "", pragmas: seq[string] = @[], istype = false): PNode =
|
||||
# Create nkTypeDef PNode with first ident if `nskType` else just create an nkPostfix node for `nskProc`
|
||||
#
|
||||
# If `fname`, use it instead of node.getAtom() for name
|
||||
# If `pragmas`, add as nkPragmaExpr but only if `nskType` since procs add pragmas elsewhere
|
||||
# If `istype` is set, this is a typedef, else struct/union so add {.importc: "struct/union X".} when includeHeader
|
||||
let
|
||||
(tname, origname, info) = nimState.getNameInfo(node.getAtom(), kind)
|
||||
(tname, torigname, info) = nimState.getNameInfo(node.getAtom(), kind)
|
||||
|
||||
name =
|
||||
origname =
|
||||
if fname.nBl:
|
||||
fname
|
||||
else:
|
||||
torigname
|
||||
|
||||
# Process name if forced, getNameInfo() already runs getIdentifier()
|
||||
name =
|
||||
if fname.nBl:
|
||||
nimState.getIdentifier(fname, kind)
|
||||
else:
|
||||
tname
|
||||
|
||||
ident = nimState.getIdent(name, info)
|
||||
|
||||
if name.Bl:
|
||||
|
|
@ -277,20 +286,39 @@ proc newTypeIdent(nimState: NimState, node: TSNode, kind = nskType, fname = "",
|
|||
# ),
|
||||
# nkEmpty()
|
||||
# )
|
||||
let
|
||||
var
|
||||
pragmas =
|
||||
if nimState.includeHeader and tname == origname:
|
||||
# Need to add impShort
|
||||
pragmas & nimState.impShort
|
||||
if nimState.includeHeader:
|
||||
# Need to add header and importc
|
||||
if istype and name == origname:
|
||||
# Need to add impShort since neither struct/union nor name change
|
||||
pragmas & nimState.impShort
|
||||
else:
|
||||
# Add header shortcut, additional pragmas added later
|
||||
pragmas & (nimState.impShort & "H")
|
||||
else:
|
||||
pragmas
|
||||
|
||||
prident =
|
||||
if pragmas.nBl and not ident.isNil:
|
||||
if pragmas.nBl:
|
||||
nimState.newPragmaExpr(node, ident, pragmas)
|
||||
else:
|
||||
ident
|
||||
|
||||
|
||||
if nimState.includeHeader:
|
||||
if not istype or name != origname:
|
||||
# Add importc pragma since either struct/union or name changed
|
||||
let
|
||||
uors =
|
||||
if not istype:
|
||||
if "union" in pragmas:
|
||||
"union "
|
||||
else:
|
||||
"struct "
|
||||
else:
|
||||
""
|
||||
nimState.addPragma(node, prident[1], "importc", newStrNode(nkStrLit, &"{uors}{origname}"))
|
||||
|
||||
result = newNode(nkTypeDef)
|
||||
result.add prident
|
||||
result.add newNode(nkEmpty)
|
||||
|
|
@ -302,7 +330,7 @@ proc newTypeIdent(nimState: NimState, node: TSNode, kind = nskType, fname = "",
|
|||
# nkIdent(name)
|
||||
# )
|
||||
result = ident
|
||||
|
||||
|
||||
nimState.identifierNodes[name] = result
|
||||
else:
|
||||
necho &"# $1 '{origname}' is duplicate, skipped" % getKeyword(kind)
|
||||
|
|
@ -525,24 +553,37 @@ proc newRecListTree(nimState: NimState, name: string, node: TSNode): PNode =
|
|||
if not field.isNil:
|
||||
result.add field
|
||||
|
||||
proc addTypeObject(nimState: NimState, node: TSNode, typeDef: PNode = nil, fname = "", union = false) =
|
||||
proc addTypeObject(nimState: NimState, node: TSNode, typeDef: PNode = nil, fname = "", istype = false, union = false) =
|
||||
# Add a type of object
|
||||
#
|
||||
# If `typeDef` is set, use it instead of creating new PNode
|
||||
# If `fname` is set, use it as the name when creating new PNode
|
||||
# If `istype` is set, this is a typedef, else struct/union
|
||||
decho("addTypeObject()")
|
||||
let
|
||||
pragmas =
|
||||
if union:
|
||||
@["union", "bycopy"]
|
||||
# Object has fields or not
|
||||
fdlist = node.anyChildInTree("field_declaration_list")
|
||||
|
||||
pragmas = block:
|
||||
var pragmas =
|
||||
if union:
|
||||
@["union"]
|
||||
else:
|
||||
@[]
|
||||
if not fdlist.isNil and fdlist.len > 0:
|
||||
# Object with fields should be bycopy
|
||||
pragmas.add "bycopy"
|
||||
else:
|
||||
@["bycopy"]
|
||||
# Incomplete, might get forward declared
|
||||
pragmas.add "incompleteStruct"
|
||||
|
||||
pragmas
|
||||
|
||||
typeDefExisting = not typeDef.isNil
|
||||
|
||||
typeDef =
|
||||
if typeDef.isNil:
|
||||
nimState.newTypeIdent(node, fname = fname, pragmas = pragmas)
|
||||
nimState.newTypeIdent(node, fname = fname, pragmas = pragmas, istype = istype)
|
||||
else:
|
||||
typeDef
|
||||
|
||||
|
|
@ -567,8 +608,6 @@ proc addTypeObject(nimState: NimState, node: TSNode, typeDef: PNode = nil, fname
|
|||
obj.add newNode(nkEmpty)
|
||||
obj.add newNode(nkEmpty)
|
||||
|
||||
let
|
||||
fdlist = node.anyChildInTree("field_declaration_list")
|
||||
if not fdlist.isNil and fdlist.len > 0:
|
||||
# Add fields to object if present
|
||||
obj.add nimState.newRecListTree(name, fdlist)
|
||||
|
|
@ -580,11 +619,11 @@ proc addTypeObject(nimState: NimState, node: TSNode, typeDef: PNode = nil, fname
|
|||
# If typeDef was passed in, need to add pragmas if any
|
||||
if pragmas.nBl and typeDefExisting:
|
||||
if typeDef[0].kind != nkPragmaExpr:
|
||||
# includeHeader already added impShort
|
||||
let
|
||||
npexpr = nimState.newPragmaExpr(node, typedef[0], pragmas)
|
||||
typedef[0] = npexpr
|
||||
else:
|
||||
# includeHeader already added impShort in newTypeIdent()
|
||||
nimState.addPragma(node, typeDef[0][1], pragmas)
|
||||
|
||||
# nkTypeSection.add
|
||||
|
|
@ -610,6 +649,18 @@ proc addTypeObject(nimState: NimState, node: TSNode, typeDef: PNode = nil, fname
|
|||
# Add fields to existing object
|
||||
def[2][2] = nimState.newRecListTree(name, fdlist)
|
||||
|
||||
# Change incompleteStruct to bycopy pragma
|
||||
if def[0].kind == nkPragmaExpr and def[0].len == 2 and
|
||||
def[0][1].kind == nkPragma and def[0][1].len > 0:
|
||||
for i in 0 ..< def[0][1].len:
|
||||
if $def[0][1][i] == "incompleteStruct":
|
||||
def[0][1][i] = nimState.getIdent(
|
||||
"bycopy", nimState.getLineInfo(node.getAtom()),
|
||||
exported = false
|
||||
)
|
||||
|
||||
nimState.printDebug(def)
|
||||
|
||||
proc addTypeTyped(nimState: NimState, node: TSNode, ftname = "", offset = 0) =
|
||||
# Add a type of a specified type
|
||||
#
|
||||
|
|
@ -622,8 +673,8 @@ proc addTypeTyped(nimState: NimState, node: TSNode, ftname = "", offset = 0) =
|
|||
# Add a type of a specific type
|
||||
let
|
||||
# node[i] = identifer = name
|
||||
typeDef = nimState.newTypeIdent(node[i])
|
||||
|
||||
typeDef = nimState.newTypeIdent(node[i], istype = true)
|
||||
|
||||
if not typeDef.isNil:
|
||||
let
|
||||
name = typeDef.getIdentName()
|
||||
|
|
@ -669,7 +720,7 @@ proc addTypeTyped(nimState: NimState, node: TSNode, ftname = "", offset = 0) =
|
|||
|
||||
nimState.printDebug(typeDef)
|
||||
else:
|
||||
nimState.addTypeObject(node, typeDef = typeDef)
|
||||
nimState.addTypeObject(node, typeDef = typeDef, istype = true)
|
||||
|
||||
proc getTypeArray(nimState: NimState, node: TSNode, name: string): PNode =
|
||||
# Create array type tree
|
||||
|
|
@ -728,7 +779,7 @@ proc addTypeArray(nimState: NimState, node: TSNode) =
|
|||
decho("addTypeArray()")
|
||||
let
|
||||
# node[1] = identifer = name
|
||||
typeDef = nimState.newTypeIdent(node[1])
|
||||
typeDef = nimState.newTypeIdent(node[1], istype = true)
|
||||
|
||||
if not typeDef.isNil:
|
||||
let
|
||||
|
|
@ -809,7 +860,7 @@ proc addTypeProc(nimState: NimState, node: TSNode) =
|
|||
decho("addTypeProc()")
|
||||
let
|
||||
# node[1] = identifier = name
|
||||
typeDef = nimState.newTypeIdent(node[1])
|
||||
typeDef = nimState.newTypeIdent(node[1], istype = true)
|
||||
|
||||
if not typeDef.isNil:
|
||||
let
|
||||
|
|
@ -1018,7 +1069,7 @@ proc addType(nimState: NimState, node: TSNode, union = false) =
|
|||
|
||||
# First add struct as object
|
||||
decho("addType(): case 6")
|
||||
nimState.addTypeObject(node[0], union = union)
|
||||
nimState.addTypeObject(node[0], istype = true, union = union)
|
||||
|
||||
if node.len > 1 and nimState.getNodeVal(node[1]) != "":
|
||||
# Add any additional names
|
||||
|
|
@ -1041,7 +1092,7 @@ proc addType(nimState: NimState, node: TSNode, union = false) =
|
|||
name
|
||||
|
||||
# Now add struct as object with specified name
|
||||
nimState.addTypeObject(node[0], fname = name, union = union)
|
||||
nimState.addTypeObject(node[0], fname = name, istype = true, union = union)
|
||||
|
||||
if name.nBl:
|
||||
# Add any additional names
|
||||
|
|
@ -1068,7 +1119,7 @@ proc addEnum(nimState: NimState, node: TSNode) =
|
|||
# Use Y as name
|
||||
origname = nimState.getNodeVal(node[1].getAtom())
|
||||
offset = 1
|
||||
|
||||
|
||||
if origname.nBl:
|
||||
name = nimState.getIdentifier(origname, nskType)
|
||||
else:
|
||||
|
|
@ -1120,7 +1171,7 @@ proc addEnum(nimState: NimState, node: TSNode) =
|
|||
# Add fields to list of consts after processing enum so that we don't cast
|
||||
# enum field to itself
|
||||
nimState.constIdentifiers.incl fnames
|
||||
|
||||
|
||||
# Add other names
|
||||
if node.getName() == "type_definition" and node.len > 1:
|
||||
nimState.addTypeTyped(node, ftname = name, offset = offset)
|
||||
|
|
@ -1137,7 +1188,7 @@ proc addProc(nimState: NimState, node: TSNode) =
|
|||
let
|
||||
# node[i] = identifier = name
|
||||
ident = nimState.newTypeIdent(node[i], kind = nskProc)
|
||||
|
||||
|
||||
if not ident.isNil:
|
||||
let
|
||||
# Only need the ident tree, not nkTypeDef parent
|
||||
|
|
@ -1270,10 +1321,13 @@ proc searchTree(nimState: NimState, root: TSNode) =
|
|||
break
|
||||
|
||||
proc setupPragmas(nimState: NimState, root: TSNode, fullpath: string) =
|
||||
# Create shortcut pragmas to reduce clutter
|
||||
var
|
||||
hdrPragma: PNode
|
||||
impPragma = newNode(nkPragma)
|
||||
impCPragma = newNode(nkPragma)
|
||||
|
||||
# {.importc.}
|
||||
nimState.addPragma(root, impPragma, "pragma", nimState.getIdent(nimState.impShort))
|
||||
nimState.addPragma(root, impPragma, "importc")
|
||||
|
||||
|
|
@ -1281,19 +1335,29 @@ proc setupPragmas(nimState: NimState, root: TSNode, fullpath: string) =
|
|||
nimState.constSection.add nimState.newConstDef(
|
||||
root, fname = nimState.currentHeader, fval = '"' & fullpath & '"')
|
||||
|
||||
nimState.addPragma(root, impPragma, "header", nimState.getIdent(nimState.currentHeader))
|
||||
# {.header: "xxx".}
|
||||
hdrPragma = nimState.newPragma(root, "pragma", nimState.getIdent(nimState.impShort & "H"))
|
||||
nimState.addPragma(root, hdrPragma, "header", nimState.getIdent(nimState.currentHeader))
|
||||
|
||||
nimState.addPragma(root, impPragma, nimState.impShort & "H")
|
||||
|
||||
# {.importc.} + {.cdecl.} for procs
|
||||
nimState.addPragma(root, impCPragma, "pragma", nimState.getIdent(nimState.impShort & "C"))
|
||||
nimState.addPragma(root, impCPragma, nimState.impShort)
|
||||
nimState.addPragma(root, impCPragma, "cdecl")
|
||||
|
||||
if nimState.gState.dynlib.nBl:
|
||||
# {.dynlib.} for DLLs
|
||||
nimState.addPragma(root, impCPragma, "dynlib", nimState.getIdent(nimState.gState.dynlib))
|
||||
|
||||
# Add all pragma shortcuts to output
|
||||
if not hdrPragma.isNil:
|
||||
nimState.pragmaSection.add hdrPragma
|
||||
nimState.pragmaSection.add impPragma
|
||||
nimState.pragmaSection.add impCPragma
|
||||
|
||||
proc printNimHeader*(gState: State) =
|
||||
# Top level output with context info
|
||||
gecho """# Generated at $1
|
||||
# Command line:
|
||||
# $2 $3
|
||||
|
|
@ -1304,13 +1368,16 @@ import nimterop/types
|
|||
""" % [$now(), getAppFilename(), commandLineParams().join(" ")]
|
||||
|
||||
proc printNim*(gState: State, fullpath: string, root: TSNode) =
|
||||
# Generate Nim from tree-sitter AST root node
|
||||
let
|
||||
nimState = new(NimState)
|
||||
fp = fullpath.replace("\\", "/")
|
||||
|
||||
# Track identifiers already rendered and corresponding PNodes
|
||||
nimState.identifiers = newTable[string, string]()
|
||||
nimState.identifierNodes = newTable[string, PNode]()
|
||||
|
||||
# toast objects
|
||||
nimState.gState = gState
|
||||
nimState.currentHeader = getCurrentHeader(fullpath)
|
||||
nimState.impShort = nimState.currentHeader.replace("header", "imp")
|
||||
|
|
@ -1321,18 +1388,23 @@ proc printNim*(gState: State, fullpath: string, root: TSNode) =
|
|||
nimState.config = newConfigRef()
|
||||
nimstate.graph = newModuleGraph(nimState.identCache, nimState.config)
|
||||
|
||||
# Initialize all section PNodes
|
||||
nimState.pragmaSection = newNode(nkStmtList)
|
||||
nimState.constSection = newNode(nkConstSection)
|
||||
nimState.enumSection = newNode(nkStmtList)
|
||||
nimState.procSection = newNode(nkStmtList)
|
||||
nimState.typeSection = newNode(nkTypeSection)
|
||||
|
||||
# Setup pragmas
|
||||
nimState.setupPragmas(root, fp)
|
||||
|
||||
# Search root node and render Nim
|
||||
nimState.searchTree(root)
|
||||
|
||||
# Add any unused cOverride symbols to output
|
||||
nimState.addAllOverrideFinal()
|
||||
|
||||
# Create output to Nim using Nim compiler renderer
|
||||
var
|
||||
tree = newNode(nkStmtList)
|
||||
tree.add nimState.enumSection
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ import hashes, macros, osproc, sets, strformat, strutils, tables
|
|||
|
||||
import os except findExe, sleep
|
||||
|
||||
import regex
|
||||
|
||||
proc sanitizePath*(path: string, noQuote = false, sep = $DirSep): string =
|
||||
result = path.multiReplace([("\\\\", sep), ("\\", sep), ("/", sep)])
|
||||
if not noQuote:
|
||||
|
|
@ -83,7 +85,7 @@ proc execAction*(cmd: string, retry = 0, die = true, cache = false,
|
|||
else:
|
||||
# Execute command and store results in cache
|
||||
(result.output, result.ret) = gorgeEx(ccmd)
|
||||
if result.ret == 0:
|
||||
if result.ret == 0 or die == false:
|
||||
# mkdir for execCache dir (circular dependency)
|
||||
let dir = cacheFile.parentDir()
|
||||
if not dirExists(dir):
|
||||
|
|
@ -933,7 +935,8 @@ macro getHeader*(header: static[string], giturl: static[string] = "", dlurl: sta
|
|||
## Simply define `proc xxxPreBuild(outdir, header: string)` in the wrapper and it will get called
|
||||
## prior to the build process.
|
||||
var
|
||||
name = header.extractFilename().split(".")[0]
|
||||
origname = header.extractFilename().split(".")[0]
|
||||
name = origname.replace(re"[[:^alnum:]]", "")
|
||||
|
||||
# -d:xxx for this header
|
||||
stdStr = name & "Std"
|
||||
|
|
@ -975,7 +978,7 @@ macro getHeader*(header: static[string], giturl: static[string] = "", dlurl: sta
|
|||
if altNames.len != 0:
|
||||
lre = lre % ("(" & altNames.replace(",", "|") & ")")
|
||||
else:
|
||||
lre = lre % name
|
||||
lre = lre % origname
|
||||
|
||||
result = newNimNode(nnkStmtList)
|
||||
result.add(quote do:
|
||||
|
|
|
|||
|
|
@ -673,6 +673,8 @@ macro c2nImport*(filename: static string, recurse: static bool = false, dynlib:
|
|||
(c2nimout, ret) = execAction(cmd, cache = not gStateCT.nocache,
|
||||
cacheKey = getCacheValue(hpath))
|
||||
|
||||
doAssert ret == 0, "\n\nc2nim codegen limitation or error - " & c2nimout
|
||||
|
||||
var
|
||||
nimout = &"const {header} = \"{fullpath}\"\n\n" & readFile(npath)
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,10 @@ struct A0 {
|
|||
int f1;
|
||||
};
|
||||
|
||||
struct A4 {
|
||||
float f1;
|
||||
};
|
||||
|
||||
typedef char *A9p[3]; //, A9[4];
|
||||
typedef char *A10[3][6];
|
||||
typedef char *(*A11)[3];
|
||||
|
|
@ -127,6 +131,10 @@ struct A0 {
|
|||
int f1;
|
||||
};
|
||||
|
||||
struct A4 {
|
||||
float f1;
|
||||
};
|
||||
|
||||
typedef char *A9p[3]; //, A9[4];
|
||||
typedef char *A10[3][6];
|
||||
typedef char *(*A11)[3];
|
||||
|
|
|
|||
249
tests/tast2.nim
249
tests/tast2.nim
|
|
@ -1,10 +1,30 @@
|
|||
import macros, sets, tables
|
||||
import macros, os, sets, strutils
|
||||
|
||||
import nimterop/[cimport]
|
||||
|
||||
static:
|
||||
cDebug()
|
||||
|
||||
const
|
||||
path = currentSourcePath.parentDir() / "include" / "tast2.h"
|
||||
|
||||
when defined(HEADER):
|
||||
cDefine("HEADER")
|
||||
const
|
||||
flags = " -H -d"
|
||||
pHeader = @["header:" & path]
|
||||
pHeaderImp = @["importc"] & pHeader
|
||||
else:
|
||||
const
|
||||
flags = ""
|
||||
pHeader: seq[string] = @[]
|
||||
pHeaderImp: seq[string] = @[]
|
||||
|
||||
const
|
||||
pHeaderImpBy = @["bycopy"] & pHeaderImp
|
||||
pHeaderBy = @["bycopy"] & pHeader
|
||||
pHeaderInc = @["incompleteStruct"] & pHeader
|
||||
|
||||
cOverride:
|
||||
const
|
||||
A* = 2
|
||||
|
|
@ -12,19 +32,10 @@ cOverride:
|
|||
type
|
||||
A1* = A0
|
||||
|
||||
when defined(HEADER):
|
||||
cDefine("HEADER")
|
||||
const
|
||||
flags = " -H"
|
||||
imp = @["importc", "header:headertast2"]
|
||||
else:
|
||||
const
|
||||
flags = ""
|
||||
imp = @[]
|
||||
|
||||
cImport("include/tast2.h", flags="-d -f:ast2 -ENK_" & flags)
|
||||
cImport(path, flags="-f:ast2 -ENK_" & flags)
|
||||
|
||||
proc getPragmas(n: NimNode): HashSet[string] =
|
||||
# Find all pragmas in AST, return as "name" or "name:value" in set
|
||||
for i in 0 ..< n.len:
|
||||
if n[i].kind == nnkPragma:
|
||||
for j in 0 ..< n[i].len:
|
||||
|
|
@ -35,24 +46,54 @@ proc getPragmas(n: NimNode): HashSet[string] =
|
|||
else:
|
||||
result.incl n[i].getPragmas()
|
||||
|
||||
macro checkPragmas(t: typed, pragmas: static[seq[string]]): untyped =
|
||||
let
|
||||
proc getRecList(n: NimNode): NimNode =
|
||||
# Find nnkRecList in AST
|
||||
for i in 0 ..< n.len:
|
||||
if n[i].kind == nnkRecList:
|
||||
return n[i]
|
||||
elif n[i].len != 0:
|
||||
let
|
||||
rl = getRecList(n[i])
|
||||
if not rl.isNil:
|
||||
return rl
|
||||
|
||||
macro checkPragmas(t: typed, pragmas: static[seq[string]], istype: static[bool] = true): untyped =
|
||||
# Verify that type has expected pragmas defined
|
||||
# `istype` is true when typedef X
|
||||
var
|
||||
ast = t.getImpl()
|
||||
prag = ast.getPragmas()
|
||||
exprag = pragmas.toHashSet()
|
||||
when defined(HEADER):
|
||||
if not istype:
|
||||
if "union" in exprag:
|
||||
exprag.incl "importc:union " & $t
|
||||
else:
|
||||
exprag.incl "importc:struct " & $t
|
||||
doAssert symmetricDifference(prag, exprag).len == 0,
|
||||
"\nWrong number of pragmas in " & $t & "\n" & $prag & " vs " & $exprag
|
||||
|
||||
proc testFields(t: typedesc, fields: Table[string, string] = initTable[string, string]()) =
|
||||
macro testFields(t: typed, fields: static[string] = "") =
|
||||
# Verify that type has expected fields
|
||||
var
|
||||
obj: t
|
||||
count = 0
|
||||
for name, value in obj.fieldPairs():
|
||||
count += 1
|
||||
assert name in fields, $t & "." & name & " invalid"
|
||||
assert $fields[name] == $typeof(value),
|
||||
"typeof(" & $t & ":" & name & ") != " & fields[name] & ", is " & $typeof(value)
|
||||
assert count == fields.len, "Failed for " & $t
|
||||
ast = t.getImpl()
|
||||
rl = ast.getRecList()
|
||||
fsplit = fields.split(":")
|
||||
names = fsplit[0].split("|")
|
||||
types =
|
||||
if fsplit.len > 1:
|
||||
fsplit[1].split("|")
|
||||
else:
|
||||
@[]
|
||||
if not rl.isNil:
|
||||
for i in 0 ..< rl.len:
|
||||
let
|
||||
name = ($rl[i][0]).strip(chars = {'*'})
|
||||
typ = $(rl[i][1].repr())
|
||||
n = names.find(name)
|
||||
assert n != -1, $t & "." & name & " invalid"
|
||||
assert types[n] == typ,
|
||||
"typeof(" & $t & ":" & name & ") != " & types[n] & ", is " & typ
|
||||
|
||||
assert A == 2
|
||||
assert B == 1.0
|
||||
|
|
@ -60,114 +101,190 @@ assert C == 0x10
|
|||
assert D == "hello"
|
||||
assert E == 'c'
|
||||
|
||||
const
|
||||
pragmas = @["bycopy"] & imp
|
||||
|
||||
assert A0 is object
|
||||
testFields(A0, {"f1": "cint"}.toTable())
|
||||
checkPragmas(A0, pragmas)
|
||||
testFields(A0, "f1:cint")
|
||||
checkPragmas(A0, pHeaderBy, istype = false)
|
||||
var a0: A0
|
||||
a0.f1 = 1
|
||||
|
||||
assert A1 is A0
|
||||
testFields(A1, {"f1": "cint"}.toTable())
|
||||
testFields(A1, "f1:cint")
|
||||
var a1: A1
|
||||
a1.f1 = 2
|
||||
|
||||
assert A2 is object
|
||||
testFields(A2)
|
||||
checkPragmas(A2, pragmas)
|
||||
checkPragmas(A2, pHeaderInc, istype = false)
|
||||
when not defined(HEADER):
|
||||
# typedef struct X; is invalid
|
||||
var a2: A2
|
||||
|
||||
assert A3 is object
|
||||
testFields(A3)
|
||||
checkPragmas(A3, pragmas)
|
||||
checkPragmas(A3, pHeaderInc, istype = false)
|
||||
var a3: A3
|
||||
|
||||
assert A4 is object
|
||||
testFields(A4)
|
||||
checkPragmas(A4, pragmas)
|
||||
testFields(A4, "f1:cfloat")
|
||||
checkPragmas(A4, pHeaderImpBy)
|
||||
var a4: A4
|
||||
a4.f1 = 4.1
|
||||
|
||||
assert A4p is ptr A4
|
||||
checkPragmas(A4p, imp)
|
||||
testFields(A4p, "f1:cfloat")
|
||||
checkPragmas(A4p, pHeaderImp)
|
||||
var a4p: A4p
|
||||
a4p = addr a4
|
||||
|
||||
assert A5 is cint
|
||||
checkPragmas(A5, imp)
|
||||
checkPragmas(A5, pHeaderImp)
|
||||
const a5: A5 = 5
|
||||
|
||||
assert A6 is ptr cint
|
||||
checkPragmas(A6, imp)
|
||||
checkPragmas(A6, pHeaderImp)
|
||||
var
|
||||
a6: A6
|
||||
a6i = 6
|
||||
a6 = cast[A6](addr a6i)
|
||||
|
||||
assert A7 is ptr ptr A0
|
||||
checkPragmas(A7, imp)
|
||||
checkPragmas(A7, pHeaderImp)
|
||||
var
|
||||
a7: A7
|
||||
a7a = addr a0
|
||||
a7 = addr a7a
|
||||
|
||||
assert A8 is pointer
|
||||
checkPragmas(A8, imp)
|
||||
checkPragmas(A8, pHeaderImp)
|
||||
var a8: A8
|
||||
a8 = nil
|
||||
|
||||
assert A9p is array[3, cstring]
|
||||
checkPragmas(A9p, imp)
|
||||
checkPragmas(A9p, pHeaderImp)
|
||||
var a9p: A9p
|
||||
a9p[1] = nil
|
||||
a9p[2] = "hello".cstring
|
||||
|
||||
#Not implemented yet
|
||||
#assert A9 is array[4, cchar]
|
||||
#checkPragmas(A9, imp)
|
||||
#checkPragmas(A9, pHeaderImp)
|
||||
#var a9: A9
|
||||
|
||||
assert A10 is array[3, array[6, cstring]]
|
||||
checkPragmas(A10, imp)
|
||||
checkPragmas(A10, pHeaderImp)
|
||||
var a10: A10
|
||||
a10[2][5] = "12345".cstring
|
||||
|
||||
assert A11 is ptr array[3, cstring]
|
||||
checkPragmas(A11, imp)
|
||||
checkPragmas(A11, pHeaderImp)
|
||||
var a11: A11
|
||||
a11 = addr a9p
|
||||
|
||||
assert A111 is array[12, ptr A1]
|
||||
checkPragmas(A111, imp)
|
||||
checkPragmas(A111, pHeaderImp)
|
||||
var a111: A111
|
||||
a111[11] = addr a1
|
||||
|
||||
assert A12 is proc(a1: cint, b: cint, c: ptr cint, a4: ptr cint, count: array[4, ptr cint], `func`: proc(a1: cint, a2: cint): cint): ptr ptr cint
|
||||
checkPragmas(A12, imp)
|
||||
checkPragmas(A12, pHeaderImp)
|
||||
when not defined(HEADER):
|
||||
# Unclear why this fails
|
||||
# request for member ‘ClE_0’ in something not a structure or union
|
||||
var a12: A12
|
||||
|
||||
assert A13 is proc(a1: cint, a2: cint, `func`: proc()): cint
|
||||
checkPragmas(A13, imp)
|
||||
checkPragmas(A13, pHeaderImp)
|
||||
when not defined(HEADER):
|
||||
# Unclear why this fails
|
||||
# request for member ‘ClE_0’ in something not a structure or union
|
||||
var a13: A13
|
||||
|
||||
assert A14 is object
|
||||
testFields(A14, {"a1": "cchar"}.toTable())
|
||||
checkPragmas(A14, pragmas)
|
||||
testFields(A14, "a1:cchar")
|
||||
checkPragmas(A14, pHeaderBy, istype = false)
|
||||
var a14: A14
|
||||
a14.a1 = 'a'
|
||||
|
||||
assert A15 is object
|
||||
testFields(A15, {"a1": "cstring", "a2": "array[0..0, ptr cint]"}.toTable())
|
||||
checkPragmas(A15, pragmas)
|
||||
testFields(A15, "a1|a2:cstring|array[1, ptr cint]")
|
||||
checkPragmas(A15, pHeaderBy, istype = false)
|
||||
var
|
||||
a15: A15
|
||||
a15i = 15.cint
|
||||
a15.a1 = "hello".cstring
|
||||
a15.a2[0] = addr a15i
|
||||
|
||||
assert A16 is object
|
||||
testFields(A16, {"f1": "cchar"}.toTable())
|
||||
checkPragmas(A16, pragmas)
|
||||
testFields(A16, "f1:cchar")
|
||||
checkPragmas(A16, pHeaderImpBy)
|
||||
when not defined(HEADER):
|
||||
# Similar to A2
|
||||
var a16: A16
|
||||
a16.f1 = 's'
|
||||
|
||||
assert A17 is object
|
||||
testFields(A17, {"a1": "cstring", "a2": "array[0..0, ptr cint]"}.toTable())
|
||||
checkPragmas(A17, pragmas)
|
||||
testFields(A17, "a1|a2:cstring|array[1, ptr cint]")
|
||||
checkPragmas(A17, pHeaderImpBy)
|
||||
when not defined(HEADER):
|
||||
# Similar to A2
|
||||
var a17: A17
|
||||
a17.a1 = "hello".cstring
|
||||
a17.a2[0] = addr a15i
|
||||
|
||||
assert A18 is A17
|
||||
checkPragmas(A18, imp)
|
||||
checkPragmas(A18, pHeaderImp)
|
||||
var a18: A18
|
||||
|
||||
assert A18p is ptr A17
|
||||
checkPragmas(A18p, imp)
|
||||
checkPragmas(A18p, pHeaderImp)
|
||||
var a18p: A18p
|
||||
a18p = addr a18
|
||||
|
||||
assert A19 is object
|
||||
testFields(A19, {"a1": "cstring", "a2": "array[0..0, ptr cint]"}.toTable())
|
||||
checkPragmas(A19, pragmas)
|
||||
testFields(A19, "a1|a2:cstring|array[1, ptr cint]")
|
||||
checkPragmas(A19, pHeaderImpBy)
|
||||
var a19: A19
|
||||
a19.a1 = "hello".cstring
|
||||
a19.a2[0] = addr a15i
|
||||
|
||||
assert A19p is ptr A19
|
||||
checkPragmas(A19p, imp)
|
||||
checkPragmas(A19p, pHeaderImp)
|
||||
var a19p: A19p
|
||||
a19p = addr a19
|
||||
|
||||
assert A20 is object
|
||||
testFields(A20, {"a1": "cchar"}.toTable())
|
||||
checkPragmas(A20, pragmas)
|
||||
testFields(A20, "a1:cchar")
|
||||
checkPragmas(A20, pHeaderImpBy)
|
||||
var a20: A20
|
||||
a20.a1 = 'a'
|
||||
|
||||
assert A21 is A20
|
||||
checkPragmas(A21, imp)
|
||||
checkPragmas(A21, pHeaderImp)
|
||||
var a21: A21
|
||||
a21 = a20
|
||||
|
||||
assert A21p is ptr A20
|
||||
checkPragmas(A21p, imp)
|
||||
checkPragmas(A21p, pHeaderImp)
|
||||
var a21p: A21p
|
||||
a21p = addr a20
|
||||
|
||||
assert A22 is object
|
||||
testFields(A22, {"f1": "ptr ptr cint", "f2": "array[0..254, ptr cint]"}.toTable())
|
||||
checkPragmas(A22, pragmas)
|
||||
testFields(A22, "f1|f2:ptr ptr cint|array[123 + 132, ptr cint]")
|
||||
checkPragmas(A22, pHeaderImpBy)
|
||||
var a22: A22
|
||||
a22.f1 = addr a15.a2[0]
|
||||
|
||||
assert U1 is object
|
||||
assert sizeof(U1) == sizeof(cfloat)
|
||||
checkPragmas(U1, pragmas & @["union"])
|
||||
checkPragmas(U1, pHeaderBy & @["union"], istype = false)
|
||||
var u1: U1
|
||||
u1.f1 = 5
|
||||
|
||||
assert U2 is object
|
||||
assert sizeof(U2) == 256 * sizeof(cint)
|
||||
checkPragmas(U2, pragmas & @["union"])
|
||||
checkPragmas(U2, pHeaderImpBy & @["union"])
|
||||
var u2: U2
|
||||
u2.f1 = addr a15.a2[0]
|
||||
|
||||
assert PANEL_WINDOW == 1
|
||||
assert PANEL_GROUP == 2
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue