Remove legacy backend
This commit is contained in:
parent
a8ea96055d
commit
d126e9944f
17 changed files with 33 additions and 1295 deletions
|
|
@ -19,6 +19,8 @@ https://github.com/nimterop/nimterop/compare/v0.5.9...v0.6.0
|
|||
|
||||
### Breaking changes
|
||||
|
||||
- The legacy algorithm has been removed as promised. `ast2` is now the default and wrappers no longer need to explicitly specify `-f:ast2` in order to use it.
|
||||
|
||||
- All shared libraries installed by `getHeader()` will now get copied into the `libdir` parameter specified. If left blank, `libdir` will default to the directory where the executable binary gets created (outdir). While this is not really a breaking change, it is a change in behavior compared to older versions of nimterop. Note that `Std` libraries are not copied over. [#154](i154)
|
||||
|
||||
- `git.nim` has been removed. This module was an artifact from the early days and was renamed to `build.nim` back in v0.2.0.
|
||||
|
|
|
|||
|
|
@ -177,13 +177,13 @@ Now that this is understood, a user might want any combination of the above in t
|
|||
- By default, generated wrappers will include the `{.header, importc.}` pragmas for types and procs. This can be disabled with the `--noHeader | -H` flag to `toast` or `flags = "-H"` param to `cImport()` which will remove `{.header}` for both and `{.importc.}` for types only.
|
||||
- By default, generated wrappers will assume that the user will link the library implementation themselves. The `--dynlib | -l` flag to `toast` or `dynlib = "headerLPath"` param to `cImport()` will configure the wrapper to generate `{.dynlib.}` pragmas for procs.
|
||||
|
||||
This results in four cases:
|
||||
This results in four supported cases:
|
||||
1. Default: `{.header, importc.}` for both types and procs
|
||||
2. With `--noHeader`, types will be pure Nim and procs will be just `{.importc.}`
|
||||
3. With `--dynlib`, types will still be `{.header, importc.}` but procs will be `{.dynlib, importc.}`
|
||||
4. With `--dynlib` and `--noHeader`, types will be pure Nim, procs will be `{.dynlib, importc.}`
|
||||
|
||||
While `ast2` supports all these modes, the legacy backend does not support the third mixed case and will infer `--noHeader` when `--dynlib` is specified (case 4). Creation of a standalone wrapper (case 4) which does not require the header or library at compile time will require an explicit `--noHeader` and `--dynlib` for `ast2`.
|
||||
Creation of a standalone wrapper (case 4) which does not require the header or library at compile time will require an explicit `--noHeader` and `--dynlib`.
|
||||
|
||||
More documentation on on these pragmas can be found in the Nim manual:
|
||||
- [{.importc.}](https://nim-lang.org/docs/manual.html#foreign-function-interface-importc-pragma)
|
||||
|
|
@ -218,7 +218,7 @@ Options:
|
|||
-d, --debug bool false enable debug output
|
||||
-D=, --defines= strings {} definitions to pass to preprocessor
|
||||
-l=, --dynlib= string "" {.dynlib.} pragma to import symbols - Nim const string or file path
|
||||
-f=, --feature= Features ast1 flags to enable experimental features
|
||||
-f=, --feature= Features {} flags to enable experimental features
|
||||
-I=, --includeDirs= strings {} include directory to pass to preprocessor
|
||||
-m=, --mode= string "" language parser: c or cpp
|
||||
--nim= string "nim" use a particular Nim executable
|
||||
|
|
@ -226,12 +226,11 @@ Options:
|
|||
-H, --noHeader bool false skip {.header.} pragma in wrapper
|
||||
-o=, --output= string "" file to output content - default: stdout
|
||||
-a, --past bool false print AST output
|
||||
-g, --pgrammar bool false print grammar
|
||||
--pluginSourcePath= string "" nim file to build and load as a plugin
|
||||
-n, --pnim bool false print Nim output
|
||||
-E=, --prefix= strings {} strip prefix from identifiers
|
||||
-p, --preprocess bool false run preprocessor on header
|
||||
-r, --recurse bool false process #include files, implies --preprocess
|
||||
-r, --recurse bool false process #include files - implies --preprocess
|
||||
-G=, --replace= strings {} replace X with Y in identifiers, X1=Y1,X2=Y2, @X for regex
|
||||
-s, --stub bool false stub out undefined type references as objects
|
||||
-F=, --suffix= strings {} strip suffix from identifiers
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Package
|
||||
|
||||
version = "0.5.9"
|
||||
version = "0.6.0"
|
||||
author = "genotrance"
|
||||
description = "C/C++ interop for Nim"
|
||||
license = "MIT"
|
||||
|
|
@ -55,15 +55,12 @@ task test, "Test":
|
|||
execTest "tests/tast2.nim", "-d:NOHEADER"
|
||||
|
||||
execTest "tests/tnimterop_c.nim"
|
||||
execTest "tests/tnimterop_c.nim", "-d:FLAGS=\"-f:ast2\""
|
||||
execTest "tests/tnimterop_c.nim", "-d:FLAGS=\"-f:ast2 -H\""
|
||||
execTest "tests/tnimterop_c.nim", "-d:FLAGS=\"-H\""
|
||||
|
||||
execCmd "nim cpp --hints:off -f -r tests/tnimterop_cpp.nim"
|
||||
execCmd "./nimterop/toast tests/toast.cfg tests/include/toast.h"
|
||||
execCmd "./nimterop/toast tests/toast.cfg -f:ast2 tests/include/toast.h"
|
||||
|
||||
execTest "tests/tpcre.nim"
|
||||
execTest "tests/tpcre.nim", "-d:FLAGS=\"-f:ast2\""
|
||||
|
||||
when defined(Linux):
|
||||
execTest "tests/rsa.nim"
|
||||
|
|
@ -72,12 +69,10 @@ task test, "Test":
|
|||
# Platform specific tests
|
||||
when defined(Windows):
|
||||
execTest "tests/tmath.nim"
|
||||
execTest "tests/tmath.nim", "-d:FLAGS=\"-f:ast2\""
|
||||
execTest "tests/tmath.nim", "-d:FLAGS=\"-f:ast2 -H\""
|
||||
execTest "tests/tmath.nim", "-d:FLAGS=\"-H\""
|
||||
if defined(OSX) or defined(Windows) or not existsEnv("TRAVIS"):
|
||||
execTest "tests/tsoloud.nim"
|
||||
execTest "tests/tsoloud.nim", "-d:FLAGS=\"-f:ast2\""
|
||||
execTest "tests/tsoloud.nim", "-d:FLAGS=\"-f:ast2 -H\""
|
||||
execTest "tests/tsoloud.nim", "-d:FLAGS=\"-H\""
|
||||
|
||||
# getHeader tests
|
||||
withDir("tests"):
|
||||
|
|
|
|||
|
|
@ -14,12 +14,6 @@ var
|
|||
gNimExe* = ""
|
||||
|
||||
# Misc helpers
|
||||
proc echoDebug(str: string) =
|
||||
let str = "\n# " & str.strip().replace("\n", "\n# ")
|
||||
when nimvm:
|
||||
if gDebugCT: echo str
|
||||
else:
|
||||
if gDebug: echo str
|
||||
|
||||
proc sanitizePath*(path: string, noQuote = false, sep = $DirSep): string =
|
||||
result = path.multiReplace([("\\\\", sep), ("\\", sep), ("/", sep)])
|
||||
|
|
@ -34,7 +28,7 @@ proc getCurrentNimCompiler*(): string =
|
|||
else:
|
||||
result = gNimExe
|
||||
|
||||
template fixOutDir() {.dirty.} =
|
||||
template fixOutDir() {.dirty, used.} =
|
||||
let
|
||||
outdir = if outdir.isAbsolute(): outdir else: getProjectDir() / outdir
|
||||
|
||||
|
|
|
|||
|
|
@ -178,7 +178,6 @@ proc getLocalPath(header, outdir: string): string =
|
|||
proc buildLibrary(lname, outdir, conFlags, cmakeFlags, makeFlags: string, buildTypes: openArray[BuildType]): string =
|
||||
var
|
||||
lpath = findFile(lname, outdir, regex = true)
|
||||
makeFlagsProc = &"-j {getNumProcs()} {makeFlags}"
|
||||
makePath = outdir
|
||||
|
||||
if lpath.len != 0:
|
||||
|
|
@ -200,7 +199,7 @@ proc buildLibrary(lname, outdir, conFlags, cmakeFlags, makeFlags: string, buildT
|
|||
let libraryExists = findFile(lname, buildStatus.buildPath, regex = true).len > 0
|
||||
|
||||
if not libraryExists and fileExists(buildStatus.buildPath / "Makefile"):
|
||||
make(buildStatus.buildPath, lname, makeFlagsProc, regex = true)
|
||||
make(buildStatus.buildPath, lname, makeFlags, regex = true)
|
||||
buildStatus.built = true
|
||||
|
||||
let error = if buildStatus.error.len > 0: buildStatus.error else: "No build files found in " & outdir
|
||||
|
|
|
|||
|
|
@ -455,7 +455,8 @@ proc linkLibs*(names: openArray[string], staticLink = true): string =
|
|||
for res in resSet:
|
||||
result &= " " & res
|
||||
|
||||
proc getNumProcs(): string =
|
||||
proc getNumProcs*(): string =
|
||||
## Get number of processors
|
||||
when defined(Windows):
|
||||
getEnv("NUMBER_OF_PROCESSORS").strip()
|
||||
elif defined(linux):
|
||||
|
|
|
|||
|
|
@ -9,6 +9,13 @@ type
|
|||
buildPath: string
|
||||
error: string
|
||||
|
||||
proc echoDebug(str: string) =
|
||||
let str = "\n# " & str.strip().replace("\n", "\n# ")
|
||||
when nimvm:
|
||||
if gDebugCT: echo str
|
||||
else:
|
||||
if gDebug: echo str
|
||||
|
||||
proc configure*(path, check: string, flags = "") =
|
||||
## Run the GNU `configure` command to generate all Makefiles or other
|
||||
## build scripts in the specified path
|
||||
|
|
@ -193,7 +200,7 @@ proc make*(path, check: string, flags = "", regex = false) =
|
|||
cpFile(cmd, cmd.replace("mingw32-make", "make"))
|
||||
doAssert cmd.len != 0, "Make not found"
|
||||
|
||||
cmd = &"cd {path.sanitizePath} && make"
|
||||
cmd = &"cd {path.sanitizePath} && make -j {getNumProcs()}"
|
||||
if flags.len != 0:
|
||||
cmd &= &" {flags}"
|
||||
|
||||
|
|
|
|||
|
|
@ -3,8 +3,6 @@ import tables
|
|||
when defined(TOAST):
|
||||
import sets, sequtils, strutils
|
||||
|
||||
import regex
|
||||
|
||||
import "."/plugin
|
||||
|
||||
import compiler/[ast, idents, modulegraphs, options]
|
||||
|
|
@ -13,7 +11,7 @@ when defined(TOAST):
|
|||
|
||||
type
|
||||
Feature* = enum
|
||||
ast1, ast2
|
||||
ast2
|
||||
|
||||
State* = ref object
|
||||
# Command line arguments to toast - some forwarded from cimport.nim
|
||||
|
|
@ -72,12 +70,6 @@ type
|
|||
# Controls whether or not the current expression
|
||||
# should validate idents against currently defined idents
|
||||
skipIdentValidation*: bool
|
||||
|
||||
# Legacy AST fields, remove when ast2 becomes default
|
||||
constStr*, enumStr*, procStr*, typeStr*: string
|
||||
commentStr*, debugStr*, skipStr*: string
|
||||
data*: seq[tuple[name, val: string]]
|
||||
nodeBranch*: seq[string]
|
||||
else:
|
||||
# cimport.nim specific
|
||||
compile*: seq[string] # `cCompile()` list of files already processed
|
||||
|
|
@ -114,23 +106,6 @@ when defined(TOAST):
|
|||
].concat(toSeq(gExpressions.items))
|
||||
|
||||
type
|
||||
Kind* = enum
|
||||
exactlyOne
|
||||
oneOrMore # +
|
||||
zeroOrMore # *
|
||||
zeroOrOne # ?
|
||||
orWithNext # !
|
||||
|
||||
Ast* = object
|
||||
name*: string
|
||||
kind*: Kind
|
||||
recursive*: bool
|
||||
children*: seq[ref Ast]
|
||||
tonim*: proc (ast: ref Ast, node: TSNode, gState: State)
|
||||
regex*: Regex
|
||||
|
||||
AstTable* {.used.} = TableRef[string, seq[ref Ast]]
|
||||
|
||||
Status* = enum
|
||||
success, unknown, error
|
||||
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ import "."/treesitter/[api, c, cpp]
|
|||
|
||||
import "."/[build, globals]
|
||||
|
||||
import "."/toastlib/[ast, ast2, getters, grammar, tshelp]
|
||||
import "."/toastlib/[ast2, getters, tshelp]
|
||||
|
||||
proc process(gState: State, path: string, astTable: AstTable) =
|
||||
proc process(gState: State, path: string) =
|
||||
doAssert existsFile(path), &"Invalid path {path}"
|
||||
|
||||
if gState.mode.Bl:
|
||||
|
|
@ -21,10 +21,7 @@ proc process(gState: State, path: string, astTable: AstTable) =
|
|||
if gState.past:
|
||||
gecho gState.printLisp(root)
|
||||
elif gState.pnim:
|
||||
if Feature.ast2 in gState.feature:
|
||||
ast2.parseNim(gState, path, root)
|
||||
elif Feature.ast1 in gState.feature:
|
||||
ast.parseNim(gState, path, root, astTable)
|
||||
parseNim(gState, path, root)
|
||||
elif gState.preprocess:
|
||||
gecho gState.code
|
||||
|
||||
|
|
@ -43,7 +40,6 @@ proc main(
|
|||
noHeader = false,
|
||||
output = "",
|
||||
past = false,
|
||||
pgrammar = false,
|
||||
pluginSourcePath: string = "",
|
||||
pnim = false,
|
||||
prefix: seq[string] = @[],
|
||||
|
|
@ -84,10 +80,6 @@ proc main(
|
|||
build.gDebug = gState.debug
|
||||
build.gNimExe = gState.nim
|
||||
|
||||
# Default `ast` mode
|
||||
if gState.feature.Bl:
|
||||
gState.feature.add Feature.ast1
|
||||
|
||||
# Split some arguments with ,
|
||||
gState.symOverride = gState.symOverride.getSplitComma()
|
||||
gState.prefix = gState.prefix.getSplitComma()
|
||||
|
|
@ -137,31 +129,14 @@ proc main(
|
|||
if gState.debug:
|
||||
echo &"# Writing output to {outputFile}\n"
|
||||
|
||||
# Process grammar into AST
|
||||
let
|
||||
astTable =
|
||||
if Feature.ast1 in gState.feature:
|
||||
parseGrammar()
|
||||
else:
|
||||
nil
|
||||
|
||||
if pgrammar:
|
||||
if Feature.ast1 in gState.feature:
|
||||
# Print AST of grammar
|
||||
gState.printGrammar(astTable)
|
||||
elif source.nBl:
|
||||
if source.nBl:
|
||||
# Print source after preprocess or Nim output
|
||||
if gState.pnim:
|
||||
gState.initNim()
|
||||
for src in source:
|
||||
gState.process(src.expandSymlinkAbs(), astTable)
|
||||
gState.process(src.expandSymlinkAbs())
|
||||
if gState.pnim:
|
||||
if Feature.ast2 in gState.feature:
|
||||
ast2.printNim(gState)
|
||||
elif Feature.ast1 in gState.feature:
|
||||
ast.printNim(gState)
|
||||
gecho """{.hint: "The legacy wrapper generation algorithm is deprecated and will be removed in the next release of Nimterop.".}"""
|
||||
gecho """{.hint: "Refer to CHANGES.md for details on migrating to the new backend.".}"""
|
||||
printNim(gState)
|
||||
|
||||
# Close outputFile
|
||||
if outputFile.len != 0:
|
||||
|
|
@ -249,7 +224,6 @@ when isMainModule:
|
|||
"noHeader": "skip {.header.} pragma in wrapper",
|
||||
"output": "file to output content - default: stdout",
|
||||
"past": "print AST output",
|
||||
"pgrammar": "print grammar",
|
||||
"pluginSourcePath": "nim file to build and load as a plugin",
|
||||
"pnim": "print Nim output",
|
||||
"prefix": "strip prefix from identifiers",
|
||||
|
|
@ -273,7 +247,6 @@ when isMainModule:
|
|||
"noHeader": 'H',
|
||||
"output": 'o',
|
||||
"past": 'a',
|
||||
"pgrammar": 'g',
|
||||
"pnim": 'n',
|
||||
"prefix": 'E',
|
||||
"preprocess": 'p',
|
||||
|
|
|
|||
|
|
@ -1,253 +0,0 @@
|
|||
import hashes, macros, os, sets, strformat, strutils, tables
|
||||
|
||||
import regex
|
||||
|
||||
import ".."/[globals, treesitter/api]
|
||||
import "."/[getters, tshelp]
|
||||
|
||||
proc getHeaderPragma*(gState: State): string =
|
||||
result =
|
||||
if not gState.noHeader and gState.dynlib.Bl:
|
||||
&", header: {gState.currentHeader}"
|
||||
else:
|
||||
""
|
||||
|
||||
proc getDynlib*(gState: State): string =
|
||||
result =
|
||||
if gState.dynlib.nBl:
|
||||
&", dynlib: {gState.dynlib}"
|
||||
else:
|
||||
""
|
||||
|
||||
proc getImportC*(gState: State, origName, nimName: string): string =
|
||||
if nimName != origName:
|
||||
result = &"importc: \"{origName}\"{gState.getHeaderPragma()}"
|
||||
else:
|
||||
result = gState.impShort
|
||||
|
||||
proc getPragma*(gState: State, pragmas: varargs[string]): string =
|
||||
result = ""
|
||||
for pragma in pragmas.items():
|
||||
if pragma.nBl:
|
||||
result &= pragma & ", "
|
||||
if result.nBl:
|
||||
result = " {." & result[0 .. ^3] & ".}"
|
||||
|
||||
result = result.replace(gState.impShort & ", cdecl", gState.impShort & "C")
|
||||
|
||||
let
|
||||
dy = gState.getDynlib()
|
||||
|
||||
if ", cdecl" in result and dy.nBl:
|
||||
result = result.replace(".}", dy & ".}")
|
||||
|
||||
proc saveNodeData(node: TSNode, gState: State): bool =
|
||||
let name = $node.tsNodeType()
|
||||
|
||||
# Atoms are nodes whose values are to be saved
|
||||
if name in gAtoms:
|
||||
let
|
||||
pname = node.getPxName(1)
|
||||
ppname = node.getPxName(2)
|
||||
pppname = node.getPxName(3)
|
||||
ppppname = node.getPxName(4)
|
||||
|
||||
var
|
||||
val = gState.getNodeVal(node)
|
||||
|
||||
# Skip since value already obtained from parent atom
|
||||
if name == "primitive_type" and pname == "sized_type_specifier":
|
||||
return true
|
||||
|
||||
# Skip since value already obtained from parent expression
|
||||
if name in ["number_literal", "identifier"] and pname in gExpressions:
|
||||
return true
|
||||
|
||||
# Add reference point in saved data for bitfield_clause
|
||||
if name in ["number_literal"] and pname == "bitfield_clause":
|
||||
gState.data.add(("bitfield_clause", val))
|
||||
return true
|
||||
|
||||
# Process value as a type
|
||||
if name in ["primitive_type", "sized_type_specifier"]:
|
||||
val = val.getType()
|
||||
|
||||
if node.tsNodePrevNamedSibling().tsNodeIsNull():
|
||||
if pname == "pointer_declarator":
|
||||
if ppname notin ["function_declarator", "array_declarator"]:
|
||||
gState.data.add(("pointer_declarator", ""))
|
||||
elif ppname == "array_declarator":
|
||||
gState.data.add(("array_pointer_declarator", ""))
|
||||
|
||||
# Double pointer
|
||||
if ppname == "pointer_declarator":
|
||||
gState.data.add(("pointer_declarator", ""))
|
||||
elif pname in ["function_declarator", "array_declarator"]:
|
||||
if ppname == "pointer_declarator":
|
||||
gState.data.add(("pointer_declarator", ""))
|
||||
if pppname == "pointer_declarator":
|
||||
gState.data.add(("pointer_declarator", ""))
|
||||
|
||||
gState.data.add((name, val))
|
||||
|
||||
if pname == "pointer_declarator" and
|
||||
ppname == "function_declarator":
|
||||
if name == "field_identifier":
|
||||
if pppname == "pointer_declarator":
|
||||
gState.data.insert(("pointer_declarator", ""), gState.data.len-1)
|
||||
if ppppname == "pointer_declarator":
|
||||
gState.data.insert(("pointer_declarator", ""), gState.data.len-1)
|
||||
gState.data.add(("function_declarator", ""))
|
||||
elif name == "identifier":
|
||||
gState.data.add(("pointer_declarator", ""))
|
||||
|
||||
# Save node value for a top-level expression
|
||||
elif name in gExpressions and name != "escape_sequence":
|
||||
if $node.tsNodeParent.tsNodeType() notin gExpressions:
|
||||
gState.data.add((name, gState.getNodeVal(node)))
|
||||
|
||||
elif name in ["abstract_pointer_declarator", "enumerator", "field_declaration", "function_declarator"]:
|
||||
gState.data.add((name.replace("abstract_", ""), ""))
|
||||
|
||||
return true
|
||||
|
||||
proc searchAstForNode(ast: ref Ast, node: TSNode, gState: State): bool =
|
||||
let
|
||||
childNames = node.getTSNodeNamedChildNames().join()
|
||||
|
||||
if ast.isNil:
|
||||
return
|
||||
|
||||
if gState.debug:
|
||||
gState.nodeBranch.add $node.tsNodeType()
|
||||
gecho "#" & spaces(gState.nodeBranch.len * 2) & gState.nodeBranch[^1]
|
||||
|
||||
if ast.children.nBl:
|
||||
if childNames.contains(ast.regex) or
|
||||
(childNames.Bl and ast.recursive):
|
||||
if node.getTSNodeNamedChildCountSansComments() != 0:
|
||||
var flag = true
|
||||
|
||||
for i in 0 .. node.tsNodeNamedChildCount()-1:
|
||||
if $node.tsNodeNamedChild(i).tsNodeType() != "comment":
|
||||
let
|
||||
nodeChild = node.tsNodeNamedChild(i)
|
||||
astChild =
|
||||
if not ast.recursive:
|
||||
ast.getAstChildByName($nodeChild.tsNodeType())
|
||||
else:
|
||||
ast
|
||||
|
||||
if not searchAstForNode(astChild, nodeChild, gState):
|
||||
flag = false
|
||||
break
|
||||
|
||||
if flag:
|
||||
result = node.saveNodeData(gState)
|
||||
else:
|
||||
result = node.saveNodeData(gState)
|
||||
else:
|
||||
if gState.debug:
|
||||
gecho "#" & spaces(gState.nodeBranch.len * 2) & &" {ast.getRegexForAstChildren()} !=~ {childNames}"
|
||||
elif node.getTSNodeNamedChildCountSansComments() == 0:
|
||||
result = node.saveNodeData(gState)
|
||||
|
||||
if gState.debug:
|
||||
discard gState.nodeBranch.pop()
|
||||
if gState.nodeBranch.Bl:
|
||||
gecho ""
|
||||
|
||||
proc searchAst(root: TSNode, astTable: AstTable, gState: State) =
|
||||
var
|
||||
node = root
|
||||
nextnode: TSNode
|
||||
depth = 0
|
||||
|
||||
while true:
|
||||
if not node.tsNodeIsNull() and depth > -1:
|
||||
let
|
||||
name = $node.tsNodeType()
|
||||
if name in astTable:
|
||||
for ast in astTable[name]:
|
||||
if gState.debug:
|
||||
gecho "\n# " & gState.getNodeVal(node).replace("\n", "\n# ") & "\n"
|
||||
if searchAstForNode(ast, node, gState):
|
||||
ast.tonim(ast, node, gState)
|
||||
if gState.debug:
|
||||
gState.debugStr &= "\n# " & gState.data.join("\n# ") & "\n"
|
||||
break
|
||||
gState.data = @[]
|
||||
else:
|
||||
break
|
||||
|
||||
if $node.tsNodeType() notin astTable and node.tsNodeNamedChildCount() != 0:
|
||||
nextnode = node.tsNodeNamedChild(0)
|
||||
depth += 1
|
||||
else:
|
||||
nextnode = node.tsNodeNextNamedSibling()
|
||||
|
||||
if nextnode.tsNodeIsNull():
|
||||
while true:
|
||||
node = node.tsNodeParent()
|
||||
depth -= 1
|
||||
if depth == -1:
|
||||
break
|
||||
if node == root:
|
||||
break
|
||||
if not node.tsNodeNextNamedSibling().tsNodeIsNull():
|
||||
node = node.tsNodeNextNamedSibling()
|
||||
break
|
||||
else:
|
||||
node = nextnode
|
||||
|
||||
if node == root:
|
||||
break
|
||||
|
||||
proc parseNim*(gState: State, fullpath: string, root: TSNode, astTable: AstTable) =
|
||||
# Generate Nim from tree-sitter AST root node
|
||||
var
|
||||
fp = fullpath.replace("\\", "/")
|
||||
|
||||
gState.currentHeader = getCurrentHeader(fullpath)
|
||||
gState.impShort = gState.currentHeader.replace("header", "imp")
|
||||
gState.sourceFile = fullpath
|
||||
|
||||
if not gState.noHeader and gState.dynlib.Bl:
|
||||
gState.constStr &= &"\n {gState.currentHeader} {{.used.}} = \"{fp}\""
|
||||
|
||||
root.searchAst(astTable, gState)
|
||||
|
||||
proc printNim*(gState: State) =
|
||||
# Print Nim generated by parseNim()
|
||||
if gState.enumStr.nBl:
|
||||
gecho &"{gState.enumStr}\n"
|
||||
|
||||
gState.constStr = gState.getOverrideFinal(nskConst) & gState.constStr
|
||||
if gState.constStr.nBl:
|
||||
gecho &"const{gState.constStr}\n"
|
||||
|
||||
gecho &"""
|
||||
{{.pragma: {gState.impShort}, importc{gState.getHeaderPragma()}.}}
|
||||
{{.pragma: {gState.impShort}C, {gState.impShort}, cdecl{gState.getDynlib()}.}}
|
||||
"""
|
||||
|
||||
gState.typeStr = gState.getOverrideFinal(nskType) & gState.typeStr
|
||||
if gState.typeStr.nBl:
|
||||
gecho &"type{gState.typeStr}\n"
|
||||
|
||||
gState.procStr = gState.getOverrideFinal(nskProc) & gState.procStr
|
||||
if gState.procStr.nBl:
|
||||
gecho &"{gState.procStr}\n"
|
||||
|
||||
gecho "{.pop.}"
|
||||
|
||||
if gState.debug:
|
||||
if gState.debugStr.nBl:
|
||||
gecho gState.debugStr
|
||||
|
||||
if gState.skipStr.nBl:
|
||||
let
|
||||
hash = gState.skipStr.hash().abs()
|
||||
sname = getTempDir() / &"nimterop_{$hash}.h"
|
||||
gecho &"# Writing skipped definitions to {sname}\n"
|
||||
writeFile(sname, gState.skipStr)
|
||||
|
|
@ -304,131 +304,6 @@ proc getPreprocessor*(gState: State, fullpath: string) =
|
|||
rdata.add line
|
||||
gState.code = rdata.join("\n")
|
||||
|
||||
converter toString*(kind: Kind): string =
|
||||
return case kind:
|
||||
of exactlyOne:
|
||||
""
|
||||
of oneOrMore:
|
||||
"+"
|
||||
of zeroOrMore:
|
||||
"*"
|
||||
of zeroOrOne:
|
||||
"?"
|
||||
of orWithNext:
|
||||
"!"
|
||||
|
||||
converter toKind*(kind: string): Kind =
|
||||
return case kind:
|
||||
of "+":
|
||||
oneOrMore
|
||||
of "*":
|
||||
zeroOrMore
|
||||
of "?":
|
||||
zeroOrOne
|
||||
of "!":
|
||||
orWithNext
|
||||
else:
|
||||
exactlyOne
|
||||
|
||||
proc getNameKind*(name: string): tuple[name: string, kind: Kind, recursive: bool] =
|
||||
if name[0] == '^':
|
||||
result.recursive = true
|
||||
result.name = name[1 .. ^1]
|
||||
else:
|
||||
result.name = name
|
||||
result.kind = $name[^1]
|
||||
|
||||
if result.kind != exactlyOne:
|
||||
result.name = result.name[0 .. ^2]
|
||||
|
||||
proc getRegexForAstChildren*(ast: ref Ast): string =
|
||||
result = "^"
|
||||
for i in 0 .. ast.children.len-1:
|
||||
let
|
||||
kind: string = ast.children[i].kind
|
||||
begin = if result[^1] == '|': "" else: "(?:"
|
||||
case kind:
|
||||
of "!":
|
||||
result &= &"{begin}{ast.children[i].name}|"
|
||||
else:
|
||||
result &= &"{begin}{ast.children[i].name}){kind}"
|
||||
result &= "$"
|
||||
|
||||
proc getAstChildByName*(ast: ref Ast, name: string): ref Ast =
|
||||
for i in 0 .. ast.children.len-1:
|
||||
if name in ast.children[i].name.split("|"):
|
||||
return ast.children[i]
|
||||
|
||||
if ast.children.len == 1 and ast.children[0].name == ".":
|
||||
return ast.children[0]
|
||||
|
||||
proc getNimExpression*(gState: State, expr: string, name = ""): string =
|
||||
# Convert C/C++ expression into Nim - cast identifiers to `name` if specified
|
||||
var
|
||||
clean = expr.multiReplace([("\n", " "), ("\r", "")])
|
||||
ident = ""
|
||||
gen = ""
|
||||
hex = false
|
||||
|
||||
for i in 0 .. clean.len:
|
||||
if i != clean.len:
|
||||
if clean[i] in IdentChars:
|
||||
if clean[i] in Digits and ident.Bl:
|
||||
# Identifiers cannot start with digits
|
||||
gen = $clean[i]
|
||||
elif clean[i] in HexDigits and hex == true:
|
||||
# Part of a hex number
|
||||
gen = $clean[i]
|
||||
elif i > 0 and i < clean.len-1 and clean[i] in ['x', 'X'] and
|
||||
clean[i-1] == '0' and clean[i+1] in HexDigits:
|
||||
# Found a hex number
|
||||
gen = $clean[i]
|
||||
hex = true
|
||||
else:
|
||||
# Part of an identifier
|
||||
ident &= clean[i]
|
||||
hex = false
|
||||
else:
|
||||
gen = (block:
|
||||
if (i == 0 or clean[i-1] != '\'') or
|
||||
(i == clean.len - 1 or clean[i+1] != '\''):
|
||||
# If unquoted, convert logical ops to Nim
|
||||
case clean[i]
|
||||
of '^': " xor "
|
||||
of '&': " and "
|
||||
of '|': " or "
|
||||
of '~': " not "
|
||||
else: $clean[i]
|
||||
else:
|
||||
$clean[i]
|
||||
)
|
||||
hex = false
|
||||
|
||||
if i == clean.len or gen.nBl:
|
||||
# Process identifier
|
||||
if ident.nBl:
|
||||
# Issue #178
|
||||
if ident != "_":
|
||||
ident = gState.getIdentifier(ident, nskConst, name)
|
||||
if name.nBl and ident in gState.constIdentifiers:
|
||||
ident = ident & "." & name
|
||||
result &= ident
|
||||
ident = ""
|
||||
result &= gen
|
||||
gen = ""
|
||||
|
||||
# Convert shift ops to Nim
|
||||
result = result.multiReplace([
|
||||
("<<", " shl "), (">>", " shr ")
|
||||
])
|
||||
|
||||
proc getComments*(gState: State, strip = false): string =
|
||||
if not gState.noComments and gState.commentStr.nBl:
|
||||
result = "\n" & gState.commentStr
|
||||
if strip:
|
||||
result = result.replace("\n ", "\n")
|
||||
gState.commentStr = ""
|
||||
|
||||
# Plugin related
|
||||
|
||||
proc dll*(path: string): string =
|
||||
|
|
|
|||
|
|
@ -1,768 +0,0 @@
|
|||
import macros, strformat, strutils, tables
|
||||
|
||||
import regex
|
||||
|
||||
import ".."/[globals, treesitter/api]
|
||||
import "."/[ast, getters, lisp, tshelp]
|
||||
|
||||
type
|
||||
Grammar = seq[tuple[grammar: string, call: proc(ast: ref Ast, node: TSNode, gState: State) {.nimcall.}]]
|
||||
|
||||
proc getPtrType(str: string): string =
|
||||
result = case str:
|
||||
of "ptr cchar":
|
||||
"cstring"
|
||||
of "ptr ptr cchar":
|
||||
"ptr cstring"
|
||||
of "ptr object":
|
||||
"pointer"
|
||||
of "ptr ptr object":
|
||||
"ptr pointer"
|
||||
of "ptr FILE":
|
||||
"File"
|
||||
else:
|
||||
str
|
||||
|
||||
proc getLit(str: string): string =
|
||||
# Used to convert #define literals into const
|
||||
let
|
||||
str = str.replace(re"/[/*].*?(?:\*/)?$", "").strip()
|
||||
|
||||
if str.contains(re"^[\-]?[\d]*[.]?[\d]+$") or # decimal
|
||||
str.contains(re"^0x[\da-fA-F]+$") or # hexadecimal
|
||||
str.contains(re"^'[[:ascii:]]'$") or # char
|
||||
str.contains(re"""^"[[:ascii:]]+"$"""): # char *
|
||||
return str
|
||||
|
||||
proc initGrammar(): Grammar =
|
||||
# #define X Y
|
||||
result.add(("""
|
||||
(preproc_def
|
||||
(identifier)
|
||||
(preproc_arg)
|
||||
)
|
||||
""",
|
||||
proc (ast: ref Ast, node: TSNode, gState: State) =
|
||||
if gState.debug:
|
||||
gState.debugStr &= "\n# define X Y"
|
||||
|
||||
let
|
||||
name = gState.data[0].val
|
||||
nname = gState.getIdentifier(name, nskConst)
|
||||
val = gState.data[1].val.getLit()
|
||||
|
||||
if not nname.nBl:
|
||||
let
|
||||
override = gState.getOverride(name, nskConst)
|
||||
if override.nBl:
|
||||
gState.constStr &= &"{gState.getComments()}\n{override}"
|
||||
else:
|
||||
gState.constStr &= &"{gState.getComments()}\n # Const '{name}' skipped"
|
||||
if gState.debug:
|
||||
gState.skipStr &= &"\n{gState.getNodeVal(node)}"
|
||||
elif val.nBl and gState.addNewIdentifer(nname):
|
||||
gState.constStr &= &"{gState.getComments()}\n {nname}* = {val}"
|
||||
))
|
||||
|
||||
let
|
||||
typeGrammar = """
|
||||
(type_qualifier?)
|
||||
(primitive_type|type_identifier?)
|
||||
(type_qualifier?)
|
||||
(sized_type_specifier?
|
||||
(primitive_type?)
|
||||
)
|
||||
(struct_specifier|union_specifier|enum_specifier?
|
||||
(type_identifier)
|
||||
)
|
||||
"""
|
||||
|
||||
arrGrammar = &"""
|
||||
(array_declarator!
|
||||
(pointer_declarator!
|
||||
(pointer_declarator!
|
||||
(type_identifier)
|
||||
)
|
||||
(type_identifier)
|
||||
)
|
||||
(type_identifier|identifier)
|
||||
(identifier|number_literal)
|
||||
)
|
||||
"""
|
||||
|
||||
paramListGrammar = &"""
|
||||
(parameter_list
|
||||
(parameter_declaration*
|
||||
{typeGrammar}
|
||||
(identifier|type_identifier?)
|
||||
(pointer_declarator?
|
||||
(type_qualifier?)
|
||||
(pointer_declarator!
|
||||
(type_qualifier?)
|
||||
{arrGrammar}
|
||||
(identifier|type_identifier)
|
||||
)
|
||||
{arrGrammar}
|
||||
(identifier|type_identifier)
|
||||
)
|
||||
{arrGrammar}
|
||||
(abstract_pointer_declarator?
|
||||
(abstract_pointer_declarator?)
|
||||
)
|
||||
)
|
||||
)
|
||||
"""
|
||||
|
||||
funcGrammar = &"""
|
||||
(function_declarator*
|
||||
(identifier|type_identifier!)
|
||||
(pointer_declarator
|
||||
(pointer_declarator!
|
||||
(type_identifier)
|
||||
)
|
||||
(type_identifier|identifier)
|
||||
)
|
||||
{paramListGrammar}
|
||||
(noexcept|throw_specifier?)
|
||||
)
|
||||
"""
|
||||
|
||||
template funcParamCommon(fname, pname, ptyp, pptr, pout, count, i, flen: untyped): untyped =
|
||||
ptyp = gState.getIdentifier(gState.data[i].val, nskType, fname).getType()
|
||||
|
||||
pptr = ""
|
||||
while i+1 < gState.data.len and gState.data[i+1].name == "pointer_declarator":
|
||||
pptr &= "ptr "
|
||||
i += 1
|
||||
|
||||
if i+1 < gState.data.len and gState.data[i+1].name == "identifier":
|
||||
pname = gState.getIdentifier(gState.data[i+1].val, nskParam, fname)
|
||||
i += 2
|
||||
else:
|
||||
pname = "a" & $count
|
||||
count += 1
|
||||
i += 1
|
||||
|
||||
if i < gState.data.len and gState.data[i].name in ["identifier", "number_literal"]:
|
||||
flen = gState.data[i].val
|
||||
if gState.data[i].name == "identifier":
|
||||
flen = gState.getIdentifier(flen, nskConst, fname)
|
||||
|
||||
pout &= &"{pname}: array[{flen}, {getPtrType(pptr&ptyp)}], "
|
||||
i += 1
|
||||
elif pptr.nBl or ptyp != "object":
|
||||
pout &= &"{pname}: {getPtrType(pptr&ptyp)}, "
|
||||
|
||||
# typedef int X
|
||||
# typedef X Y
|
||||
# typedef struct X Y
|
||||
# typedef ?* Y
|
||||
result.add((&"""
|
||||
(type_definition
|
||||
{typeGrammar}
|
||||
(type_identifier!)
|
||||
{arrGrammar}
|
||||
(pointer_declarator!
|
||||
(pointer_declarator!
|
||||
(type_identifier!)
|
||||
{arrGrammar}
|
||||
{funcGrammar}
|
||||
)
|
||||
(type_identifier!)
|
||||
{arrGrammar}
|
||||
{funcGrammar}
|
||||
)
|
||||
{funcGrammar}
|
||||
)
|
||||
""",
|
||||
proc (ast: ref Ast, node: TSNode, gState: State) =
|
||||
if gState.debug:
|
||||
gState.debugStr &= "\n# typedef X Y"
|
||||
|
||||
var
|
||||
i = 0
|
||||
typ = gState.getIdentifier(gState.data[i].val, nskType, "IgnoreSkipSymbol").getType()
|
||||
name = ""
|
||||
nname = ""
|
||||
tptr = ""
|
||||
aptr = ""
|
||||
pragmas: seq[string] = @[]
|
||||
|
||||
i += 1
|
||||
while i < gState.data.len and "pointer" in gState.data[i].name:
|
||||
case gState.data[i].name:
|
||||
of "pointer_declarator":
|
||||
tptr &= "ptr "
|
||||
i += 1
|
||||
of "array_pointer_declarator":
|
||||
aptr &= "ptr "
|
||||
i += 1
|
||||
|
||||
if i < gState.data.len:
|
||||
name = gState.data[i].val
|
||||
nname = gState.getIdentifier(name, nskType)
|
||||
i += 1
|
||||
|
||||
if not gState.noHeader and gState.dynlib.Bl:
|
||||
pragmas.add gState.getImportC(name, nname)
|
||||
|
||||
let
|
||||
pragma = gState.getPragma(pragmas)
|
||||
|
||||
if not nname.nBl:
|
||||
let
|
||||
override = gState.getOverride(name, nskType)
|
||||
if override.nBl:
|
||||
gState.typeStr &= &"{gState.getComments()}\n{override}"
|
||||
elif nname notin gTypeMap and typ.nBl and gState.addNewIdentifer(nname):
|
||||
if i < gState.data.len and gState.data[^1].name == "function_declarator":
|
||||
var
|
||||
fname = nname
|
||||
pout, pname, ptyp, pptr = ""
|
||||
count = 1
|
||||
flen = ""
|
||||
|
||||
while i < gState.data.len:
|
||||
if gState.data[i].name == "function_declarator":
|
||||
break
|
||||
|
||||
funcParamCommon(fname, pname, ptyp, pptr, pout, count, i, flen)
|
||||
|
||||
if pout.nBl and pout[^2 .. ^1] == ", ":
|
||||
pout = pout[0 .. ^3]
|
||||
|
||||
if tptr.nBl or typ != "object":
|
||||
gState.typeStr &= &"{gState.getComments()}\n {nname}*{pragma} = proc({pout}): {getPtrType(tptr&typ)} {{.cdecl.}}"
|
||||
else:
|
||||
gState.typeStr &= &"{gState.getComments()}\n {nname}*{pragma} = proc({pout}) {{.cdecl.}}"
|
||||
else:
|
||||
if i < gState.data.len and gState.data[i].name in ["identifier", "number_literal"]:
|
||||
var
|
||||
flen = gState.data[i].val
|
||||
if gState.data[i].name == "identifier":
|
||||
flen = gState.getIdentifier(flen, nskConst, nname)
|
||||
|
||||
gState.typeStr &= &"{gState.getComments()}\n {nname}*{pragma} = {aptr}array[{flen}, {getPtrType(tptr&typ)}]"
|
||||
else:
|
||||
if nname == typ:
|
||||
pragmas.add "incompleteStruct"
|
||||
let
|
||||
pragma = gState.getPragma(pragmas)
|
||||
gState.typeStr &= &"{gState.getComments()}\n {nname}*{pragma} = object"
|
||||
else:
|
||||
gState.typeStr &= &"{gState.getComments()}\n {nname}*{pragma} = {getPtrType(tptr&typ)}"
|
||||
))
|
||||
|
||||
proc pDupTypeCommon(nname: string, fend: int, gState: State, isEnum=false) =
|
||||
if gState.debug:
|
||||
gState.debugStr &= "\n# pDupTypeCommon()"
|
||||
|
||||
var
|
||||
dname = gState.data[^1].val
|
||||
ndname = gState.getIdentifier(dname, nskType)
|
||||
dptr =
|
||||
if fend == 2:
|
||||
"ptr "
|
||||
else:
|
||||
""
|
||||
|
||||
if ndname.nBl and ndname != nname:
|
||||
if isEnum:
|
||||
if gState.addNewIdentifer(ndname):
|
||||
gState.enumStr &= &"{gState.getComments(true)}\ntype {ndname}* = {dptr}{nname}"
|
||||
else:
|
||||
if gState.addNewIdentifer(ndname):
|
||||
let
|
||||
pragma = gState.getPragma(gState.getImportc(dname, ndname), "bycopy")
|
||||
gState.typeStr &=
|
||||
&"{gState.getComments()}\n {ndname}*{pragma} = {dptr}{nname}"
|
||||
|
||||
proc pStructCommon(ast: ref Ast, node: TSNode, name: string, fstart, fend: int, gState: State) =
|
||||
if gState.debug:
|
||||
gState.debugStr &= "\n# pStructCommon"
|
||||
|
||||
var
|
||||
nname = gState.getIdentifier(name, nskType)
|
||||
prefix = ""
|
||||
union = ""
|
||||
|
||||
case $node.tsNodeType():
|
||||
of "struct_specifier":
|
||||
prefix = "struct "
|
||||
of "union_specifier":
|
||||
prefix = "union "
|
||||
union = ", union"
|
||||
of "type_definition":
|
||||
if node.getTSNodeNamedChildCountSansComments() != 0:
|
||||
for i in 0 .. node.tsNodeNamedChildCount()-1:
|
||||
let
|
||||
nchild = $node.tsNodeNamedChild(i).tsNodeType()
|
||||
if nchild != "comment":
|
||||
case nchild:
|
||||
of "struct_specifier":
|
||||
if fstart == 1:
|
||||
prefix = "struct "
|
||||
of "union_specifier":
|
||||
if fstart == 1:
|
||||
prefix = "union "
|
||||
union = ", union"
|
||||
break
|
||||
|
||||
if not nname.nBl:
|
||||
let
|
||||
override = gState.getOverride(name, nskType)
|
||||
if override.nBl:
|
||||
gState.typeStr &= &"{gState.getComments()}\n{override}"
|
||||
elif gState.addNewIdentifer(nname):
|
||||
if gState.data.len == 1:
|
||||
gState.typeStr &= &"{gState.getComments()}\n {nname}* {{.bycopy{union}.}} = object"
|
||||
else:
|
||||
var
|
||||
pragmas: seq[string] = @[]
|
||||
if not gState.noHeader and gState.dynlib.Bl:
|
||||
pragmas.add gState.getImportC(prefix & name, nname)
|
||||
pragmas.add "bycopy"
|
||||
if union.nBl:
|
||||
pragmas.add "union"
|
||||
|
||||
let
|
||||
pragma = gState.getPragma(pragmas)
|
||||
|
||||
gState.typeStr &= &"{gState.getComments()}\n {nname}*{pragma} = object"
|
||||
|
||||
var
|
||||
i = fstart
|
||||
ftyp, fname: string
|
||||
fptr = ""
|
||||
aptr = ""
|
||||
flen = ""
|
||||
while i < gState.data.len-fend:
|
||||
fptr = ""
|
||||
aptr = ""
|
||||
if gState.data[i].name == "field_declaration":
|
||||
i += 1
|
||||
continue
|
||||
|
||||
if gState.data[i].name notin ["field_identifier", "pointer_declarator", "array_pointer_declarator"]:
|
||||
ftyp = gState.getIdentifier(gState.data[i].val, nskType, nname).getType()
|
||||
i += 1
|
||||
|
||||
while i < gState.data.len-fend and "pointer" in gState.data[i].name:
|
||||
case gState.data[i].name:
|
||||
of "pointer_declarator":
|
||||
fptr &= "ptr "
|
||||
i += 1
|
||||
of "array_pointer_declarator":
|
||||
aptr &= "ptr "
|
||||
i += 1
|
||||
|
||||
fname = gState.getIdentifier(gState.data[i].val, nskField, nname)
|
||||
|
||||
if i+1 < gState.data.len-fend and gState.data[i+1].name in gEnumVals:
|
||||
# Struct field is an array where size is an expression
|
||||
var
|
||||
flen = gState.getNimExpression(gState.data[i+1].val)
|
||||
if "/" in flen:
|
||||
flen = &"({flen}).int"
|
||||
gState.typeStr &= &"{gState.getComments()}\n {fname}*: {aptr}array[{flen}, {getPtrType(fptr&ftyp)}]"
|
||||
i += 2
|
||||
elif i+1 < gState.data.len-fend and gState.data[i+1].name == "bitfield_clause":
|
||||
let
|
||||
size = gState.data[i+1].val
|
||||
gState.typeStr &= &"{gState.getComments()}\n {fname}* {{.bitsize: {size}.}} : {getPtrType(fptr&ftyp)} "
|
||||
i += 2
|
||||
elif i+1 < gState.data.len-fend and gState.data[i+1].name == "function_declarator":
|
||||
var
|
||||
pout, pname, ptyp, pptr = ""
|
||||
count = 1
|
||||
|
||||
i += 2
|
||||
while i < gState.data.len-fend:
|
||||
if gState.data[i].name == "function_declarator":
|
||||
i += 1
|
||||
continue
|
||||
|
||||
if gState.data[i].name == "field_declaration":
|
||||
break
|
||||
|
||||
funcParamCommon(fname, pname, ptyp, pptr, pout, count, i, flen)
|
||||
|
||||
if pout.nBl and pout[^2 .. ^1] == ", ":
|
||||
pout = pout[0 .. ^3]
|
||||
if fptr.nBl or ftyp != "object":
|
||||
gState.typeStr &= &"{gState.getComments()}\n {fname}*: proc({pout}): {getPtrType(fptr&ftyp)} {{.cdecl.}}"
|
||||
else:
|
||||
gState.typeStr &= &"{gState.getComments()}\n {fname}*: proc({pout}) {{.cdecl.}}"
|
||||
i += 1
|
||||
else:
|
||||
if ftyp == "object":
|
||||
gState.typeStr &= &"{gState.getComments()}\n {fname}*: pointer"
|
||||
else:
|
||||
gState.typeStr &= &"{gState.getComments()}\n {fname}*: {getPtrType(fptr&ftyp)}"
|
||||
i += 1
|
||||
|
||||
if node.tsNodeType() == "type_definition" and
|
||||
gState.data[^1].name == "type_identifier" and gState.data[^1].val.nBl:
|
||||
pDupTypeCommon(nname, fend, gState, false)
|
||||
|
||||
let
|
||||
fieldGrammar = &"""
|
||||
(field_identifier!)
|
||||
(bitfield_clause!
|
||||
(number_literal)
|
||||
)
|
||||
(array_declarator!
|
||||
(field_identifier!)
|
||||
(pointer_declarator
|
||||
(pointer_declarator!
|
||||
(field_identifier)
|
||||
)
|
||||
(field_identifier)
|
||||
)
|
||||
(^$1+)
|
||||
)
|
||||
(function_declarator+
|
||||
(pointer_declarator
|
||||
(pointer_declarator!
|
||||
(field_identifier)
|
||||
)
|
||||
(field_identifier)
|
||||
)
|
||||
{paramListGrammar}
|
||||
)
|
||||
""" % gEnumVals.join("|")
|
||||
|
||||
fieldListGrammar = &"""
|
||||
(field_declaration_list?
|
||||
(field_declaration+
|
||||
{typeGrammar}
|
||||
(pointer_declarator!
|
||||
(pointer_declarator!
|
||||
{fieldGrammar}
|
||||
)
|
||||
{fieldGrammar}
|
||||
)
|
||||
{fieldGrammar}
|
||||
)
|
||||
)
|
||||
"""
|
||||
|
||||
# struct X {}
|
||||
result.add((&"""
|
||||
(struct_specifier|union_specifier
|
||||
(type_identifier)
|
||||
{fieldListGrammar}
|
||||
)
|
||||
""",
|
||||
proc (ast: ref Ast, node: TSNode, gState: State) =
|
||||
if gState.debug:
|
||||
gState.debugStr &= "\n# struct X {}"
|
||||
|
||||
pStructCommon(ast, node, gState.data[0].val, 1, 1, gState)
|
||||
))
|
||||
|
||||
# typedef struct X {}
|
||||
result.add((&"""
|
||||
(type_definition
|
||||
(struct_specifier|union_specifier
|
||||
(type_identifier?)
|
||||
{fieldListGrammar}
|
||||
)
|
||||
(type_identifier!)
|
||||
(pointer_declarator
|
||||
(pointer_declarator!
|
||||
(type_identifier)
|
||||
)
|
||||
(type_identifier)
|
||||
)
|
||||
)
|
||||
""",
|
||||
proc (ast: ref Ast, node: TSNode, gState: State) =
|
||||
if gState.debug:
|
||||
gState.debugStr &= "\n# typedef struct X {}"
|
||||
|
||||
var
|
||||
fstart = 0
|
||||
fend = 1
|
||||
|
||||
if gState.data[^2].name == "pointer_declarator":
|
||||
fend = 2
|
||||
|
||||
if gState.data.len > 1 and
|
||||
gState.data[0].name == "type_identifier" and
|
||||
gState.data[1].name notin ["field_identifier", "pointer_declarator"]:
|
||||
|
||||
fstart = 1
|
||||
pStructCommon(ast, node, gState.data[0].val, fstart, fend, gState)
|
||||
else:
|
||||
pStructCommon(ast, node, gState.data[^1].val, fstart, fend, gState)
|
||||
))
|
||||
|
||||
proc pEnumCommon(ast: ref Ast, node: TSNode, name: string, fstart, fend: int, gState: State) =
|
||||
if gState.debug:
|
||||
gState.debugStr &= "\n# pEnumCommon()"
|
||||
|
||||
let nname =
|
||||
if name.Bl:
|
||||
getUniqueIdentifier(gState, "Enum")
|
||||
else:
|
||||
gState.getIdentifier(name, nskType)
|
||||
|
||||
if nname.nBl and gState.addNewIdentifer(nname):
|
||||
gState.enumStr &= &"{gState.getComments(true)}\ndefineEnum({nname})"
|
||||
|
||||
var
|
||||
i = fstart
|
||||
count = 0
|
||||
while i < gState.data.len-fend:
|
||||
if gState.data[i].name == "enumerator":
|
||||
i += 1
|
||||
continue
|
||||
|
||||
let
|
||||
fname = gState.getIdentifier(gState.data[i].val, nskEnumField)
|
||||
|
||||
if i+1 < gState.data.len-fend and
|
||||
gState.data[i+1].name in gEnumVals:
|
||||
if fname.nBl and gState.addNewIdentifer(fname):
|
||||
gState.constStr &= &"{gState.getComments()}\n {fname}* = ({gState.getNimExpression(gState.data[i+1].val)}).{nname}"
|
||||
try:
|
||||
count = gState.data[i+1].val.parseInt() + 1
|
||||
except:
|
||||
count += 1
|
||||
i += 2
|
||||
else:
|
||||
if fname.nBl and gState.addNewIdentifer(fname):
|
||||
gState.constStr &= &"{gState.getComments()}\n {fname}* = {count}.{nname}"
|
||||
i += 1
|
||||
count += 1
|
||||
|
||||
if node.tsNodeType() == "type_definition" and
|
||||
gState.data[^1].name == "type_identifier" and gState.data[^1].val.nBl:
|
||||
pDupTypeCommon(nname, fend, gState, true)
|
||||
|
||||
# enum X {}
|
||||
result.add(("""
|
||||
(enum_specifier
|
||||
(type_identifier?)
|
||||
(enumerator_list
|
||||
(enumerator+
|
||||
(identifier?)
|
||||
(^$1+)
|
||||
)
|
||||
)
|
||||
)
|
||||
""" % gEnumVals.join("|"),
|
||||
proc (ast: ref Ast, node: TSNode, gState: State) =
|
||||
if gState.debug:
|
||||
gState.debugStr &= "\n# enum X {}"
|
||||
|
||||
var
|
||||
name = ""
|
||||
offset = 0
|
||||
|
||||
if gState.data[0].name == "type_identifier":
|
||||
name = gState.data[0].val
|
||||
offset = 1
|
||||
|
||||
pEnumCommon(ast, node, name, offset, 0, gState)
|
||||
))
|
||||
|
||||
# typedef enum {} X
|
||||
result.add((&"""
|
||||
(type_definition
|
||||
{result[^1].grammar}
|
||||
(type_identifier!)
|
||||
(pointer_declarator
|
||||
(pointer_declarator!
|
||||
(type_identifier)
|
||||
)
|
||||
(type_identifier)
|
||||
)
|
||||
)
|
||||
""",
|
||||
proc (ast: ref Ast, node: TSNode, gState: State) =
|
||||
if gState.debug:
|
||||
gState.debugStr &= "\n# typedef enum {}"
|
||||
|
||||
var
|
||||
fstart = 0
|
||||
fend = 1
|
||||
|
||||
if gState.data[^2].name == "pointer_declarator":
|
||||
fend = 2
|
||||
|
||||
if gState.data[0].name == "type_identifier":
|
||||
fstart = 1
|
||||
|
||||
pEnumCommon(ast, node, gState.data[0].val, fstart, fend, gState)
|
||||
else:
|
||||
pEnumCommon(ast, node, gState.data[^1].val, fstart, fend, gState)
|
||||
))
|
||||
|
||||
# typ function(typ param1, ...)
|
||||
result.add((&"""
|
||||
(declaration
|
||||
(storage_class_specifier?)
|
||||
{typeGrammar}
|
||||
(pointer_declarator!
|
||||
(pointer_declarator!
|
||||
{funcGrammar}
|
||||
)
|
||||
{funcGrammar}
|
||||
)
|
||||
{funcGrammar}
|
||||
)
|
||||
""",
|
||||
proc (ast: ref Ast, node: TSNode, gState: State) =
|
||||
if gState.debug:
|
||||
gState.debugStr &= "\n# typ function"
|
||||
|
||||
var
|
||||
fptr = ""
|
||||
i = 1
|
||||
|
||||
while i < gState.data.len:
|
||||
if gState.data[i].name == "function_declarator":
|
||||
i += 1
|
||||
continue
|
||||
|
||||
fptr = ""
|
||||
while i < gState.data.len and gState.data[i].name == "pointer_declarator":
|
||||
fptr &= "ptr "
|
||||
i += 1
|
||||
|
||||
var
|
||||
fname = gState.data[i].val
|
||||
fnname = gState.getIdentifier(fname, nskProc)
|
||||
pout, pname, ptyp, pptr = ""
|
||||
count = 1
|
||||
flen = ""
|
||||
fVar = false
|
||||
|
||||
i += 1
|
||||
if i < gState.data.len and gState.data[i].name == "pointer_declarator":
|
||||
fVar = true
|
||||
i += 1
|
||||
|
||||
while i < gState.data.len:
|
||||
if gState.data[i].name == "function_declarator":
|
||||
break
|
||||
|
||||
funcParamCommon(fnname, pname, ptyp, pptr, pout, count, i, flen)
|
||||
|
||||
if pout.nBl and pout[^2 .. ^1] == ", ":
|
||||
pout = pout[0 .. ^3]
|
||||
|
||||
if not fnname.nBl:
|
||||
let
|
||||
override = gState.getOverride(fname, nskProc)
|
||||
if override.nBl:
|
||||
gState.typeStr &= &"{gState.getComments()}\n{override}"
|
||||
elif gState.addNewIdentifer(fnname):
|
||||
let
|
||||
ftyp = gState.getIdentifier(gState.data[0].val, nskType, fnname).getType()
|
||||
pragma = gState.getPragma(gState.getImportC(fname, fnname), "cdecl")
|
||||
|
||||
if fptr.nBl or ftyp != "object":
|
||||
if fVar:
|
||||
gState.procStr &= &"{gState.getComments(true)}\nvar {fnname}*: proc ({pout}): {getPtrType(fptr&ftyp)}{{.cdecl.}}"
|
||||
else:
|
||||
gState.procStr &= &"{gState.getComments(true)}\nproc {fnname}*({pout}): {getPtrType(fptr&ftyp)}{pragma}"
|
||||
else:
|
||||
if fVar:
|
||||
gState.procStr &= &"{gState.getComments(true)}\nvar {fnname}*: proc ({pout}){{.cdecl.}}"
|
||||
else:
|
||||
gState.procStr &= &"{gState.getComments(true)}\nproc {fnname}*({pout}){pragma}"
|
||||
))
|
||||
|
||||
# // comment
|
||||
result.add((&"""
|
||||
(comment
|
||||
)
|
||||
""",
|
||||
proc (ast: ref Ast, node: TSNode, gState: State) =
|
||||
let
|
||||
cmt = $gState.getNodeVal(node)
|
||||
|
||||
for line in cmt.splitLines():
|
||||
let
|
||||
line = line.multiReplace([("//", ""), ("/*", ""), ("*/", "")])
|
||||
|
||||
gState.commentStr &= &"\n # {line.strip(leading=false)}"
|
||||
))
|
||||
|
||||
# // unknown
|
||||
result.add((&"""
|
||||
(type_definition|struct_specifier|union_specifier|enum_specifier|declaration
|
||||
(^.*)
|
||||
)
|
||||
""",
|
||||
proc (ast: ref Ast, node: TSNode, gState: State) =
|
||||
var
|
||||
done = false
|
||||
for i in gState.data:
|
||||
case $node.tsNodeType()
|
||||
of "declaration":
|
||||
if i.name == "identifier":
|
||||
let
|
||||
override = gState.getOverride(i.val, nskProc)
|
||||
|
||||
if override.nBl:
|
||||
gState.procStr &= &"{gState.getComments(true)}\n{override}"
|
||||
done = true
|
||||
break
|
||||
else:
|
||||
gState.procStr &= &"{gState.getComments(true)}\n# Declaration '{i.val}' skipped"
|
||||
|
||||
else:
|
||||
if i.name == "type_identifier":
|
||||
let
|
||||
override = gState.getOverride(i.val, nskType)
|
||||
|
||||
if override.nBl:
|
||||
gState.typeStr &= &"{gState.getComments()}\n{override}"
|
||||
done = true
|
||||
break
|
||||
else:
|
||||
gState.typeStr &= &"{gState.getComments()}\n # Type '{i.val}' skipped"
|
||||
|
||||
if gState.debug and not done:
|
||||
gState.skipStr &= &"\n{gState.getNodeVal(node)}"
|
||||
))
|
||||
|
||||
proc initRegex(ast: ref Ast) =
|
||||
if ast.children.nBl:
|
||||
if not ast.recursive:
|
||||
for child in ast.children:
|
||||
child.initRegex()
|
||||
|
||||
var
|
||||
reg: string
|
||||
try:
|
||||
reg = ast.getRegexForAstChildren()
|
||||
ast.regex = reg.re()
|
||||
except:
|
||||
echo reg
|
||||
raise newException(Exception, getCurrentExceptionMsg())
|
||||
|
||||
proc parseGrammar*(): AstTable =
|
||||
const grammars = initGrammar()
|
||||
|
||||
result = newTable[string, seq[ref Ast]]()
|
||||
for i in 0 .. grammars.len-1:
|
||||
var
|
||||
ast = grammars[i].grammar.parseLisp()
|
||||
|
||||
ast.tonim = grammars[i].call
|
||||
ast.initRegex()
|
||||
for n in ast.name.split("|"):
|
||||
if n notin result:
|
||||
result[n] = @[ast]
|
||||
else:
|
||||
result[n].add(ast)
|
||||
|
||||
proc printGrammar*(gState: State, astTable: AstTable) =
|
||||
for name in astTable.keys():
|
||||
for ast in astTable[name]:
|
||||
gecho ast.printAst()
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
import ".."/globals
|
||||
import "."/getters
|
||||
|
||||
var
|
||||
gTokens: seq[string]
|
||||
idx = 0
|
||||
|
||||
proc tokenize(tree: string) =
|
||||
var collect = ""
|
||||
|
||||
gTokens = @[]
|
||||
idx = 0
|
||||
for i in tree:
|
||||
case i:
|
||||
of ' ', '\n', '\r', '(', ')':
|
||||
if collect.nBl:
|
||||
gTokens.add(collect)
|
||||
collect = ""
|
||||
if i in ['(', ')']:
|
||||
gTokens.add($i)
|
||||
else:
|
||||
collect &= $i
|
||||
|
||||
proc readFromTokens(): ref Ast =
|
||||
if idx == gTokens.len:
|
||||
doAssert false, "Bad AST " & $(idx: idx)
|
||||
|
||||
if gTokens[idx] == "(":
|
||||
if gTokens.len - idx < 2:
|
||||
doAssert false, "Corrupt AST " & $(gTokensLen: gTokens.len, idx: idx)
|
||||
result = new(Ast)
|
||||
(result.name, result.kind, result.recursive) = gTokens[idx+1].getNameKind()
|
||||
result.children = @[]
|
||||
if result.recursive:
|
||||
result.children.add(result)
|
||||
idx += 2
|
||||
while gTokens[idx] != ")":
|
||||
var res = readFromTokens()
|
||||
if not res.isNil:
|
||||
result.children.add(res)
|
||||
elif gTokens[idx] == ")":
|
||||
doAssert false, "Poor AST " & $(idx: idx)
|
||||
|
||||
idx += 1
|
||||
|
||||
proc printAst*(node: ref Ast, offset=""): string =
|
||||
result = offset & "(" & (if node.recursive: "^" else: "") & node.name & node.kind.toString()
|
||||
|
||||
if node.children.nBl and not node.recursive:
|
||||
result &= "\n"
|
||||
for child in node.children:
|
||||
result &= printAst(child, offset & " ")
|
||||
result &= offset & ")\n"
|
||||
else:
|
||||
result &= ")\n"
|
||||
|
||||
proc parseLisp*(tree: string): ref Ast =
|
||||
tokenize(tree)
|
||||
|
||||
return readFromTokens()
|
||||
|
|
@ -2,7 +2,6 @@ import sets, strformat, strutils
|
|||
|
||||
import ".."/treesitter/[api, c, cpp]
|
||||
import ".."/globals
|
||||
import "."/getters
|
||||
|
||||
template withCodeAst*(code: string, mode: string, body: untyped): untyped =
|
||||
## A simple template to inject the TSNode into a body of code
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ proc testCall(cmd, output: string, exitCode: int, delete = true) =
|
|||
doAssert outp.contains(output), outp
|
||||
|
||||
var
|
||||
cmd = "nim c -f --hints:off -d:FLAGS=\"-f:ast2\" -d:checkAbi"
|
||||
cmd = "nim c -f --hints:off -d:checkAbi"
|
||||
lrcmd = " -r lzma.nim"
|
||||
zrcmd = " -r zlib.nim"
|
||||
sshcmd = " -r libssh2.nim"
|
||||
|
|
|
|||
|
|
@ -17,12 +17,12 @@ cOverride:
|
|||
SOCKET = object
|
||||
|
||||
when not libssh2Static:
|
||||
cImport(libssh2Path, recurse = true, dynlib = "libssh2LPath", flags = "-f:ast2 -c -E_ -F_")
|
||||
cImport(libssh2Path, recurse = true, dynlib = "libssh2LPath", flags = "-c -E_ -F_")
|
||||
|
||||
when not defined(Windows) and not isDefined(libssh2JBB):
|
||||
proc zlibVersion(): cstring {.importc, dynlib: libssh2LPath.}
|
||||
else:
|
||||
cImport(libssh2Path, recurse = true, flags = "-f:ast2 -c -E_ -F_")
|
||||
cImport(libssh2Path, recurse = true, flags = "-c -E_ -F_")
|
||||
|
||||
when not defined(Windows) and not isDefined(libssh2JBB):
|
||||
proc zlibVersion(): cstring {.importc.}
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ cOverride:
|
|||
cImport(@[
|
||||
basePath / "rsa.h",
|
||||
basePath / "err.h",
|
||||
], recurse = true, flags = "-f:ast2 -s -c " & FLAGS)
|
||||
], recurse = true, flags = "-s -c " & FLAGS)
|
||||
|
||||
{.passL: cryptoLPath.}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue