diff --git a/nimterop/all.nim b/nimterop/all.nim index 6be6d11..20ab4e8 100644 --- a/nimterop/all.nim +++ b/nimterop/all.nim @@ -2,4 +2,4 @@ The following modules are available to users of Nimterop. ]## -import "."/[docs, cimport, build, types, plugin] +import "."/[build, cimport, docs, plugin] diff --git a/nimterop/cimport.nim b/nimterop/cimport.nim index edfdeb9..b5c62a2 100644 --- a/nimterop/cimport.nim +++ b/nimterop/cimport.nim @@ -17,8 +17,7 @@ All `{.compileTime.}` procs must be used in a compile time context, e.g. using: import hashes, macros, os, strformat, strutils -import "."/[build, globals, paths, types] -export types +import "."/[build, globals, paths] proc interpPath(dir: string): string= # TODO: more robust: needs a DirSep after "$projpath" @@ -153,7 +152,7 @@ proc getToast(fullpaths: seq[string], recurse: bool = false, dynlib: string = "" # see https://github.com/nimterop/nimterop/issues/69 (result, ret) = execAction(cmd, die = false, cache = (not gStateCT.nocache), - cacheKey = getCacheValue(fullpaths)) + cacheKey = getCacheValue(toastExe) & getCacheValue(fullpaths)) doAssert ret == 0, getToastError(result) macro cOverride*(body): untyped = diff --git a/nimterop/enumtype.nim b/nimterop/enumtype.nim new file mode 100644 index 0000000..3c20b79 --- /dev/null +++ b/nimterop/enumtype.nim @@ -0,0 +1,42 @@ +import macros + +macro defineEnum(typ: untyped): untyped = + result = newNimNode(nnkStmtList) + + # Enum mapped to distinct cint + result.add quote do: + type `typ`* = distinct cint + + for i in ["+", "-", "*", "div", "mod", "shl", "shr", "or", "and", "xor", "<", "<=", "==", ">", ">="]: + let + ni = newIdentNode(i) + typout = if i[0] in "<=>": newIdentNode("bool") else: typ # comparisons return bool + if i[0] == '>': # cannot borrow `>` and `>=` from templates + let + nopp = if i.len == 2: newIdentNode("<=") else: newIdentNode("<") + result.add quote do: + proc `ni`*(x: `typ`, y: cint): `typout` = `nopp`(y, x) + proc `ni`*(x: cint, y: `typ`): `typout` = `nopp`(y, x) + proc `ni`*(x, y: `typ`): `typout` = `nopp`(y, x) + else: + result.add quote do: + proc `ni`*(x: `typ`, y: cint): `typout` {.borrow.} + proc `ni`*(x: cint, y: `typ`): `typout` {.borrow.} + proc `ni`*(x, y: `typ`): `typout` {.borrow.} + result.add quote do: + proc `ni`*(x: `typ`, y: int): `typout` = `ni`(x, y.cint) + proc `ni`*(x: int, y: `typ`): `typout` = `ni`(x.cint, y) + + let + divop = newIdentNode("/") # `/`() + dlrop = newIdentNode("$") # `$`() + notop = newIdentNode("not") # `not`() + result.add quote do: + proc `divop`*(x, y: `typ`): `typ` = `typ`((x.float / y.float).cint) + proc `divop`*(x: `typ`, y: cint): `typ` = `divop`(x, `typ`(y)) + proc `divop`*(x: cint, y: `typ`): `typ` = `divop`(`typ`(x), y) + proc `divop`*(x: `typ`, y: int): `typ` = `divop`(x, y.cint) + proc `divop`*(x: int, y: `typ`): `typ` = `divop`(x.cint, y) + + proc `dlrop`*(x: `typ`): string {.borrow.} + proc `notop`*(x: `typ`): `typ` {.borrow.} diff --git a/nimterop/globals.nim b/nimterop/globals.nim index c831589..d1e22a6 100644 --- a/nimterop/globals.nim +++ b/nimterop/globals.nim @@ -54,7 +54,7 @@ type constIdentifiers*: HashSet[string] # Const names for enum casting identifiers*: TableRef[string, string] # Symbols that have been declared so far indexed by nimName skippedSyms*: HashSet[string] # Symbols that have been skipped due to being unwrappable or - # the user provided override is blank + # the user provided override is blank # Nim compiler objects constSection*, enumSection*, pragmaSection*, procSection*, typeSection*, varSection*: PNode @@ -70,6 +70,9 @@ type # Controls whether or not the current expression # should validate idents against currently defined idents skipIdentValidation*: bool + + # Top level header for wrapper output - include imported types, pragmas and other info + wrapperHeader*: string else: # cimport.nim specific compile*: seq[string] # `cCompile()` list of files already processed diff --git a/nimterop/toastlib/ast2.nim b/nimterop/toastlib/ast2.nim index 783b6e4..0147463 100644 --- a/nimterop/toastlib/ast2.nim +++ b/nimterop/toastlib/ast2.nim @@ -1481,6 +1481,11 @@ proc addEnum(gState: State, node: TSNode) = if node.getName() == "type_definition" and node.len > 1: gState.addTypeTyped(node, ftname = name, offset = offset) + if gEnumMacro.nBl: + # Add enum generation macro once + gState.wrapperHeader &= gEnumMacro + gEnumMacro = "" + proc addProc(gState: State, node, rnode: TSNode, commentNodes: seq[TSNode]) = # Add a proc # @@ -1822,10 +1827,7 @@ proc setupPragmas(gState: State, root: TSNode, fullpath: string) = proc initNim*(gState: State) = # Initialize for parseNim() one time - gecho """import nimterop/types - -{.push hint[ConvFromXtoItselfNotNeeded]: off.} -""" + gState.wrapperHeader = "{.push hint[ConvFromXtoItselfNotNeeded]: off.}\n" # Track identifiers already rendered and corresponding PNodes gState.identifiers = newTable[string, string]() @@ -1876,6 +1878,7 @@ proc printNim*(gState: State) = tree.add gState.varSection tree.add gState.procSection + gecho gState.wrapperHeader gecho tree.renderTree() gecho "{.pop.}" \ No newline at end of file diff --git a/nimterop/toastlib/comphelp.nim b/nimterop/toastlib/comphelp.nim index d404c42..642fbbd 100644 --- a/nimterop/toastlib/comphelp.nim +++ b/nimterop/toastlib/comphelp.nim @@ -95,7 +95,7 @@ proc getNameInfo*(gState: State, node: TSNode, kind: NimSymKind, parent = ""): result.name = gState.getIdentifier(result.origname, kind, parent) if result.name.nBl: if kind == nskType: - result.name = result.name.getType() + result.name = gState.getType(result.name, parent) result.info = gState.getLineInfo(node) proc getPtrType*(str: string): string = diff --git a/nimterop/toastlib/exprparser.nim b/nimterop/toastlib/exprparser.nim index 13b1a98..a77dbf9 100644 --- a/nimterop/toastlib/exprparser.nim +++ b/nimterop/toastlib/exprparser.nim @@ -580,7 +580,7 @@ proc processTSNode(gState: State, node: TSNode, typeofNode: var PNode): PNode = of "sized_type_specifier", "primitive_type", "type_identifier": # Input -> int, unsigned int, long int, etc # Output -> cint, cuint, clong, etc - let ty = getType(node.val) + let ty = gState.getType(node.val, parent = node.getName()) if ty.len > 0: # If ty is not empty, one of C's builtin types has been found result = gState.getExprIdent(ty, nskType, parent=node.getName()) diff --git a/nimterop/toastlib/getters.nim b/nimterop/toastlib/getters.nim index c3f48ae..43a4f6f 100644 --- a/nimterop/toastlib/getters.nim +++ b/nimterop/toastlib/getters.nim @@ -29,7 +29,13 @@ yield""".split(Whitespace).toHashSet() # Types related +const + # Enum macro read from file - written into wrapper when required + gEnumMacroConst = staticRead(currentSourcePath.parentDir().parentDir() / "enumtype.nim") + var + gEnumMacro* = gEnumMacroConst + gTypeMap* = { # char "char": "cchar", @@ -107,13 +113,40 @@ var "long double": "clongdouble", # Misc Nim types - "Bool": "bool" + "Bool": "bool", + "ptrdiff_t": "ByteAddress" }.toTable() # Nim type names that shouldn't need to be wrapped again gTypeMapValues* = toSeq(gTypeMap.values).toHashSet() -proc getType*(str: string): string = + # Types to import from C/Nim if used in wrapper + gTypeImport* = { + "time_t": """ +import std/time_t as std_time_t +type time_t* = Time +""", + + "time64_t": """ +import std/time_t as std_time64_t +type time64_t* = Time +""", + + "wchar_t": """ +when defined(cpp): + # http://www.cplusplus.com/reference/cwchar/wchar_t/ + # In C++, wchar_t is a distinct fundamental type (and thus it is + # not defined in nor any other header). + type wchar_t* {.importc.} = object +else: + type wchar_t* {.importc, header:"".} = object +""", + + "va_list": """ +type va_list* {.importc, header:"".} = object +"""}.toTable() + +proc getType*(gState: State, str, parent: string): string = if str == "void": return "object" @@ -121,6 +154,10 @@ proc getType*(str: string): string = if gTypeMap.hasKey(result): result = gTypeMap[result] + elif parent.nBl and gTypeImport.hasKey(result) and not gState.identifierNodes.hasKey(result): + # Include C/Nim type imports once if a field/param and not already declared + gState.wrapperHeader &= "\n" & gTypeImport[result] + gTypeImport.del result # Identifier related diff --git a/nimterop/treesitter/api.nim b/nimterop/treesitter/api.nim index 116bc10..753178c 100644 --- a/nimterop/treesitter/api.nim +++ b/nimterop/treesitter/api.nim @@ -2,7 +2,8 @@ import strutils, os -import ".."/[setup, paths, types] +include ".."/enumtype +import ".."/[paths, setup] static: treesitterSetup() diff --git a/nimterop/types.nim b/nimterop/types.nim deleted file mode 100644 index 38f95d1..0000000 --- a/nimterop/types.nim +++ /dev/null @@ -1,74 +0,0 @@ -# see https://github.com/nimterop/nimterop/issues/79 - -import std/time_t as time_t_temp -type - time_t* = time_t_temp.Time - time64_t* = time_t_temp.Time - -when defined(cpp): - # http://www.cplusplus.com/reference/cwchar/wchar_t/ - # In C++, wchar_t is a distinct fundamental type (and thus it is - # not defined in nor any other header). - type - wchar_t* {.importc.} = object -else: - type - wchar_t* {.importc, header:"".} = object - -type - ptrdiff_t* = ByteAddress - -type - va_list* {.importc, header:"".} = object - -template enumOp*(op, typ, typout) = - proc op*(x: typ, y: cint): typout {.borrow.} - proc op*(x: cint, y: typ): typout {.borrow.} - proc op*(x, y: typ): typout {.borrow.} - - proc op*(x: typ, y: int): typout = op(x, y.cint) - proc op*(x: int, y: typ): typout = op(x.cint, y) - -template defineEnum*(typ) = - # Create a `distinct cint` type for C enums since Nim enums - # need to be in order and cannot have duplicates. - type - typ* = distinct cint - - # Enum operations allowed - enumOp(`+`, typ, typ) - enumOp(`-`, typ, typ) - enumOp(`*`, typ, typ) - enumOp(`<`, typ, bool) - enumOp(`<=`, typ, bool) - enumOp(`==`, typ, bool) - enumOp(`div`, typ, typ) - enumOp(`mod`, typ, typ) - - # These don't work with `enumOp()` for some reason - proc `shl`*(x: typ, y: cint): typ {.borrow.} - proc `shl`*(x: cint, y: typ): typ {.borrow.} - proc `shl`*(x, y: typ): typ {.borrow.} - - proc `shr`*(x: typ, y: cint): typ {.borrow.} - proc `shr`*(x: cint, y: typ): typ {.borrow.} - proc `shr`*(x, y: typ): typ {.borrow.} - - proc `or`*(x: typ, y: cint): typ {.borrow.} - proc `or`*(x: cint, y: typ): typ {.borrow.} - proc `or`*(x, y: typ): typ {.borrow.} - - proc `and`*(x: typ, y: cint): typ {.borrow.} - proc `and`*(x: cint, y: typ): typ {.borrow.} - proc `and`*(x, y: typ): typ {.borrow.} - - proc `xor`*(x: typ, y: cint): typ {.borrow.} - proc `xor`*(x: cint, y: typ): typ {.borrow.} - proc `xor`*(x, y: typ): typ {.borrow.} - - proc `/`*(x, y: typ): typ = - return (x.float / y.float).cint.typ - proc `/`*(x: typ, y: cint): typ = `/`(x, y.typ) - proc `/`*(x: cint, y: typ): typ = `/`(x.typ, y) - - proc `$`*(x: typ): string {.borrow.}