More pointer support, cpp mode default in toast, cleanup

This commit is contained in:
Ganesh Viswanathan 2018-11-21 23:45:12 -06:00
commit b9b5174759
11 changed files with 132 additions and 89 deletions

View file

@ -65,6 +65,8 @@ Detailed documentation is still forthcoming.
`cAddSearchDir("XXX")` - add directory XXX to search path in calls to `cSearchPath()`
`cAddStdDir("XXX")` - add standard "c" [default] or "cpp" include paths to search path
`cSearchPath("header.h")` - return a file or directory found in search path configured using `cSearchPath()` - can be used in `cCompile()`, `cIncludeDir()` and `cImport()` calls
`gitPull()` - pull a git repository prior to C/C++ interop

View file

@ -21,7 +21,7 @@ proc addHeader*(fullpath: string) =
#
proc pPreprocDef(node: ref Ast) =
if node.children.len() == 2:
if node.children.len == 2:
let
name = getNodeValIf(node.children[0], identifier)
val = getNodeValIf(node.children[1], preproc_arg)
@ -37,30 +37,42 @@ proc pPreprocDef(node: ref Ast) =
#
proc typeScan(node: ref Ast, sym, id: Sym, offset: string): string =
if node.sym != sym or node.children.len() != 2:
if node.sym != sym or node.children.len != 2:
return
let
pname = getNodeValIf(node.children[1], id)
var
name = getNodeValIf(node.children[1], id)
ptyp = getNodeValIf(node.children[0], primitive_type)
ttyp = getNodeValIf(node.children[0], type_identifier)
ptrname = false
if pname.len() == 0:
if name.len == 0 and node.children[1].sym == pointer_declarator and node.children[1].children.len == 1:
name = getNodeValIf(node.children[1].children[0], id)
ptrname = true
if name.len == 0:
return
elif ptyp.nBl:
result = &"{offset}{pname.getIdentifier()}: {ptyp.getType()}"
ptyp = ptyp.getType()
if ptyp != "object" and ptrname:
ptyp = &"ptr {ptyp}"
result = &"{offset}{name.getIdentifier()}: {ptyp}"
elif ttyp.nBl:
result = &"{offset}{pname.getIdentifier()}: {ttyp}"
elif node.children[0].sym in [struct_specifier, enum_specifier] and node.children[0].children.len() == 1:
let styp = getNodeValIf(node.children[0].children[0], type_identifier)
if ptrname:
ttyp = &"ptr {ttyp}"
result = &"{offset}{name.getIdentifier()}: {ttyp}"
elif node.children[0].sym in [struct_specifier, enum_specifier] and node.children[0].children.len == 1:
var styp = getNodeValIf(node.children[0].children[0], type_identifier)
if styp.nBl:
result = &"{offset}{pname.getIdentifier()}: {styp}"
if ptrname:
styp = &"ptr {styp}"
result = &"{offset}{name.getIdentifier()}: {styp}"
else:
return
proc pStructSpecifier(node: ref Ast, name = "") =
var stmt: string
if node.children.len() == 1 and name notin gTypes:
if node.children.len == 1 and name notin gTypes:
case node.children[0].sym:
of type_identifier:
let typ = getNodeValIf(node.children[0], type_identifier)
@ -68,18 +80,18 @@ proc pStructSpecifier(node: ref Ast, name = "") =
gTypes.add(name)
if name != typ:
# typedef struct X Y
gTypeStr &= &" {name}* = {typ} #1 pStructSpecifier()\n"
gTypeStr &= &" {name.getIdentifier()}* = {typ} #1 pStructSpecifier()\n"
else:
# typedef struct X X
gTypeStr &= &" {name}* {{.importc: \"{name}\", header: {gCurrentHeader}, bycopy.}} = object #2 pStructSpecifier()\n"
gTypeStr &= &" {name.getIdentifier()}* {{.importc: \"{name}\", header: {gCurrentHeader}, bycopy.}} = object #2 pStructSpecifier()\n"
of field_declaration_list:
# typedef struct { fields } X
stmt = &" {name}* {{.importc: \"{name}\", header: {gCurrentHeader}, bycopy.}} = object #3 pStructSpecifier()\n"
stmt = &" {name.getIdentifier()}* {{.importc: \"{name}\", header: {gCurrentHeader}, bycopy.}} = object #3 pStructSpecifier()\n"
for field in node.children[0].children:
let ts = typeScan(field, field_declaration, field_identifier, " ")
if ts.len() == 0:
if ts.len == 0:
return
stmt &= ts & "\n"
@ -88,7 +100,7 @@ proc pStructSpecifier(node: ref Ast, name = "") =
else:
discard
elif name.len() == 0 and node.children.len() == 2 and node.children[1].sym == field_declaration_list:
elif name.len == 0 and node.children.len == 2 and node.children[1].sym == field_declaration_list:
let ename = getNodeValIf(node.children[0], type_identifier)
if ename.nBl and ename notin gTypes:
# struct X { fields }
@ -96,7 +108,7 @@ proc pStructSpecifier(node: ref Ast, name = "") =
for field in node.children[1].children:
let ts = typeScan(field, field_declaration, field_identifier, " ")
if ts.len() == 0:
if ts.len == 0:
return
stmt &= ts & "\n"
@ -109,12 +121,12 @@ proc pEnumSpecifier(node: ref Ast, name = "") =
elid: int
stmt: string
if node.children.len() == 1 and node.children[0].sym == enumerator_list:
if node.children.len == 1 and node.children[0].sym == enumerator_list:
# typedef enum { fields } X
ename = name
elid = 0
stmt = &" {name}* = enum #1 pEnumSpecifier()\n"
elif name.len() == 0 and node.children.len() == 2 and node.children[1].sym == enumerator_list:
stmt = &" {name.getIdentifier()}* = enum #1 pEnumSpecifier()\n"
elif name.len == 0 and node.children.len == 2 and node.children[1].sym == enumerator_list:
ename = getNodeValIf(node.children[0], type_identifier)
elid = 1
if ename.nBl:
@ -126,9 +138,9 @@ proc pEnumSpecifier(node: ref Ast, name = "") =
for field in node.children[elid].children:
if field.sym == enumerator:
let fname = getNodeValIf(field.children[0], identifier)
if field.children.len() == 1:
if field.children.len == 1:
stmt &= &" {fname}\n"
elif field.children.len() == 2 and field.children[1].sym == number_literal:
elif field.children.len == 2 and field.children[1].sym == number_literal:
let num = getNodeValIf(field.children[1], number_literal)
stmt &= &" {fname} = {num}\n"
else:
@ -139,25 +151,31 @@ proc pEnumSpecifier(node: ref Ast, name = "") =
gTypeStr &= stmt
proc pTypeDefinition(node: ref Ast) =
if node.children.len() == 2:
if node.children.len == 2:
var
name = getNodeValIf(node.children[1], type_identifier)
pname = getNodeValIf(node.children[1], pointer_declarator)
ptyp = getNodeValIf(node.children[0], primitive_type)
ttyp = getNodeValIf(node.children[0], type_identifier)
ptrname = false
if name.len() == 0 and node.children[1].sym == pointer_declarator and node.children[1].children.len() == 1:
if name.len == 0 and node.children[1].sym == pointer_declarator and node.children[1].children.len == 1:
name = getNodeValIf(node.children[1].children[0], type_identifier)
ptrname = true
if name.nBl and name notin gTypes:
if ptyp.nBl:
# typedef int X
gTypes.add(name)
gTypeStr &= &" {name}* = {ptyp.getType()} #1 pTypeDefinition()\n"
ptyp = ptyp.getType()
if ptyp != "object" and ptrname:
ptyp = &"ptr {ptyp}"
gTypeStr &= &" {name.getIdentifier()}* = {ptyp} #1 pTypeDefinition()\n"
elif ttyp.nBl:
# typedef X Y
gTypes.add(name)
gTypeStr &= &" {name}* = {ttyp} #2 pTypeDefinition()\n"
if ptrname:
ttyp = &"ptr {ttyp}"
gTypeStr &= &" {name.getIdentifier()}* = {ttyp} #2 pTypeDefinition()\n"
else:
case node.children[0].sym:
of struct_specifier:
@ -168,20 +186,20 @@ proc pTypeDefinition(node: ref Ast) =
discard
proc pFunctionDeclarator(node: ref Ast, typ: string) =
if node.children.len() == 2:
if node.children.len == 2:
let
name = getNodeValIf(node.children[0], identifier)
if name.nBl and name notin gProcs and node.children[1].sym == parameter_list:
# typ function(typ param1, ...)
var stmt = &"# pFunctionDeclarator()\nproc {name}*("
var stmt = &"# pFunctionDeclarator()\nproc {name.getIdentifier()}*("
for i in 0 .. node.children[1].children.len()-1:
for i in 0 .. node.children[1].children.len-1:
let ts = typeScan(node.children[1].children[i], parameter_declaration, identifier, "")
if ts.len() == 0:
if ts.len == 0:
return
stmt &= ts
if i != node.children[1].children.len()-1:
if i != node.children[1].children.len-1:
stmt &= ", "
if typ != "void":
@ -195,7 +213,7 @@ proc pFunctionDeclarator(node: ref Ast, typ: string) =
gProcStr &= stmt
proc pDeclaration*(node: ref Ast) =
if node.children.len() == 2 and node.children[1].sym == function_declarator:
if node.children.len == 2 and node.children[1].sym == function_declarator:
let
ptyp = getNodeValIf(node.children[0], primitive_type)
ttyp = getNodeValIf(node.children[0], type_identifier)
@ -204,7 +222,7 @@ proc pDeclaration*(node: ref Ast) =
pFunctionDeclarator(node.children[1], ptyp.getType())
elif ttyp.nBl:
pFunctionDeclarator(node.children[1], ttyp)
elif node.children[0].sym == struct_specifier and node.children[0].children.len() == 1:
elif node.children[0].sym == struct_specifier and node.children[0].children.len == 1:
let styp = getNodeValIf(node.children[0].children[0], type_identifier)
if styp.nBl:
pFunctionDeclarator(node.children[1], styp)

View file

@ -17,7 +17,7 @@ proc findPath(path: string, fail = true): string =
proc cSearchPath*(path: string): string =
result = findPath(path, fail = false)
if result.len() == 0:
if result.len == 0:
var found = false
for inc in gSearchDirs:
result = (inc & "/" & path).replace("\\", "/")
@ -31,12 +31,12 @@ proc cSearchPath*(path: string): string =
macro cDebug*(): untyped =
gDebug = true
macro cDefine*(name: static[string], val: static[string] = ""): untyped =
macro cDefine*(name: static string, val: static string = ""): untyped =
result = newNimNode(nnkStmtList)
var str = "-D" & name
if val.nBl:
str &= "=\"" & val & "\""
str &= &"=\"{val}\""
result.add(quote do:
{.passC: `str`.}
@ -45,36 +45,37 @@ macro cDefine*(name: static[string], val: static[string] = ""): untyped =
if gDebug:
echo result.repr
macro cAddSearchDir*(dir: static[string]): untyped =
macro cAddSearchDir*(dir: static string): untyped =
result = newNimNode(nnkStmtList)
let fullpath = cSearchPath(dir)
if fullpath notin gSearchDirs:
gSearchDirs.add(fullpath)
macro cIncludeDir*(dir: static[string]): untyped =
macro cIncludeDir*(dir: static string): untyped =
result = newNimNode(nnkStmtList)
let
fullpath = findPath(dir)
str = "-I\"" & fullpath & "\""
str = &"-I\"{fullpath}\""
if fullpath notin gIncludeDirs:
gIncludeDirs.add(fullpath)
result.add(quote do:
{.passC: `str`.}
)
result.add(quote do:
{.passC: `str`.}
)
if gDebug:
echo result.repr
macro cIncludeC*(): untyped =
macro cAddStdDir*(mode = "c"): untyped =
result = newNimNode(nnkStmtList)
var
inc = false
for line in getGccPaths().splitLines():
for line in getGccPaths(mode.strVal()).splitLines():
if "#include <...> search starts here" in line:
inc = true
continue
@ -82,10 +83,11 @@ macro cIncludeC*(): untyped =
break
if inc:
result = quote do:
cIncludeDir(line)
let sline = line.strip()
result.add quote do:
cAddSearchDir(`sline`)
macro cCompile*(path: static[string]): untyped =
macro cCompile*(path: static string): untyped =
result = newNimNode(nnkStmtList)
var
@ -129,7 +131,7 @@ macro cCompile*(path: static[string]): untyped =
if gDebug:
echo result.repr
macro cImport*(filename: static[string]): untyped =
macro cImport*(filename: static string): untyped =
result = newNimNode(nnkStmtList)
result.add addReorder()

View file

@ -23,10 +23,11 @@ proc getNodeValIf*(node: ref Ast, esym: Sym): string =
return gCode[node.start .. node.stop-1].strip()
proc getGccPaths*(mode = "c"): string =
let
var
nul = when defined(Windows): "nul" else: "/dev/null"
mmode = if mode == "cpp": "c++" else: mode
return staticExec("gcc -Wp,-v -x" & mode & " " & nul)
return staticExec("gcc -Wp,-v -x" & mmode & " " & nul)
proc getLineCol*(node: ref Ast): tuple[line, col: int] =
result.line = 1

View file

@ -18,7 +18,7 @@ proc execAction*(cmd: string): string =
echo result
quit(1)
macro extractZip*(zipfile, outdir: static[string]): untyped =
macro extractZip*(zipfile, outdir: static string): untyped =
var cmd = "unzip -o $#"
if defined(Windows):
cmd = "powershell -nologo -noprofile -command \"& { Add-Type -A " &
@ -28,7 +28,7 @@ macro extractZip*(zipfile, outdir: static[string]): untyped =
echo "Extracting " & zipfile
discard execAction(&"cd \"{getProjectPath()/outdir}\" && " & cmd % zipfile)
macro downloadUrl*(url, outdir: static[string]): untyped =
macro downloadUrl*(url, outdir: static string): untyped =
let
file = url.extractFilename()
ext = file.splitFile().ext.toLowerAscii()
@ -45,7 +45,7 @@ macro downloadUrl*(url, outdir: static[string]): untyped =
discard quote do:
extractZip(`file`, `outdir`)
macro gitReset*(outdir: static[string]): untyped =
macro gitReset*(outdir: static string): untyped =
echo "Resetting " & outdir
let cmd = &"cd \"{getProjectPath()/outdir}\" && git reset --hard"
@ -53,7 +53,7 @@ macro gitReset*(outdir: static[string]): untyped =
sleep(1000)
echo " Retrying ..."
macro gitCheckout*(file, outdir: static[string]): untyped =
macro gitCheckout*(file, outdir: static string): untyped =
echo "Resetting " & file
let cmd = &"cd \"{getProjectPath()/outdir}\" && git checkout $#" % file.replace(outdir & "/", "")
@ -61,7 +61,7 @@ macro gitCheckout*(file, outdir: static[string]): untyped =
sleep(500)
echo " Retrying ..."
macro gitPull*(url: static[string], outdirN = "", plistN = "", checkoutN = ""): untyped =
macro gitPull*(url: static string, outdirN = "", plistN = "", checkoutN = ""): untyped =
let
outdir = getProjectPath()/outdirN.strVal()
plist = plistN.strVal()
@ -78,14 +78,14 @@ macro gitPull*(url: static[string], outdirN = "", plistN = "", checkoutN = ""):
discard execAction(&"cd \"{outdir}\" && git init .")
discard execAction(&"cd \"{outdir}\" && git remote add origin " & url)
if plist.len() != 0:
if plist.len != 0:
let sparsefile = &"{outdir}/.git/info/sparse-checkout"
discard execAction(&"cd \"{outdir}\" && git config core.sparsecheckout true")
writeFile(sparsefile, plist)
echo "Wrote"
if checkout.len() != 0:
if checkout.len != 0:
echo "Checking out " & checkout
discard execAction(&"cd \"{outdir}\" && git pull --tags origin master")
discard execAction(&"cd \"{outdir}\" && git checkout {checkout}")

View file

@ -37,4 +37,4 @@ var
gTypeStr* {.compiletime.}: string
template nBl*(s: untyped): untyped =
(s.len() != 0)
(s.len != 0)

View file

@ -13,7 +13,7 @@ proc tokenize(fullpath: string) =
gTokens = @[]
idx = 0
for i in staticExec("toast -u " & fullpath):
for i in staticExec("toast -m " & fullpath):
case i:
of ' ', '\n', '\r', '(', ')':
if collect.nBl:
@ -24,17 +24,17 @@ proc tokenize(fullpath: string) =
else:
collect &= $i
if gTokens.len() == 0:
if gTokens.len == 0:
echo "toast binary not installed - nimble install nimterop to force build"
quit(1)
proc readFromTokens(): ref Ast =
if idx == gTokens.len():
if idx == gTokens.len:
echo "Bad AST"
quit(1)
if gTokens[idx] == "(":
if gTokens.len() - idx < 2:
if gTokens.len - idx < 2:
echo "Corrupt AST"
quit(1)
if gTokens[idx+1] != "comment":
@ -60,7 +60,7 @@ proc readFromTokens(): ref Ast =
proc printAst*(node: ref Ast, offset=""): string =
result = offset & "(" & $node.sym & " " & $node.start & " " & $node.stop
if node.children.len() != 0:
if node.children.len != 0:
result &= "\n"
for child in node.children:
result &= printAst(child, offset & " ")

View file

@ -4,13 +4,15 @@ int test_call_int() {
return 5;
}
struct STRUCT1 test_call_int_param(int param1) {
#ifdef FORCE
struct STRUCT1 _test_call_int_param_(int param1) {
struct STRUCT1 s;
s.field1 = param1;
return s;
}
#endif
STRUCT2 test_call_int_param2(int param1, STRUCT2 param2) {
STRUCT2 s;

View file

@ -29,8 +29,15 @@ typedef enum {
enum6
} ENUM2;
typedef void * VOIDPTR;
typedef int * INTPTR;
typedef struct {
int *field;
} STRUCT4;
int test_call_int();
struct STRUCT1 test_call_int_param(int param1);
struct STRUCT1 _test_call_int_param_(int param1);
STRUCT2 test_call_int_param2(int param1, STRUCT2 param2);
STRUCT2 test_call_int_param3(int param1, struct STRUCT1 param2);
ENUM2 test_call_int_param4(enum ENUM param1);

View file

@ -2,6 +2,7 @@ import nimterop/cimport
cDebug()
cDefine("FORCE")
cIncludeDir "include"
cAddSearchDir "include"
cCompile cSearchPath("test.c")
@ -18,10 +19,14 @@ var
s: STRUCT1
s2: STRUCT2
s3: STRUCT3
s4: STRUCT4
e: ENUM
e2: ENUM2 = enum5
vptr: VOIDPTR
iptr: INTPTR
pt = 3
ct = 4
@ -31,9 +36,11 @@ s3.field1 = 7
e = enum1
e2 = enum4
doAssert test_call_int() == 5
doAssert test_call_int_param(5).field1 == 5
doAssert test_call_int_param2(5, s2).field1 == 11
doAssert test_call_int_param3(5, s).field1 == 10
doAssert test_call_int_param4(e) == e2
cAddStdDir()

View file

@ -6,30 +6,28 @@ import treesitter/[runtime, c, cpp]
const HELP = """
> toast header.h
"""
-m minimized output - non-pretty
-c C mode - CPP is default"""
var
gPretty = true
proc printLisp(root: TSNode, data: var string) =
proc printLisp(root: TSNode, data: var string, pretty = true) =
var
node = root
nextnode: TSNode
depth = 0
while true:
if not node.tsNodeIsNull():
if gPretty:
if pretty:
stdout.write spaces(depth)
stdout.write "(" & $node.tsNodeType() & " " & $node.tsNodeStartByte() & " " & $node.tsNodeEndByte()
if node.tsNodeNamedChildCount() != 0:
if gPretty:
if pretty:
echo ""
nextnode = node.tsNodeNamedChild(0)
depth += 1
else:
if gPretty:
if pretty:
echo ")"
else:
stdout.write ")"
@ -39,7 +37,7 @@ proc printLisp(root: TSNode, data: var string) =
while true:
node = node.tsNodeParent()
depth -= 1
if gPretty:
if pretty:
echo spaces(depth) & ")"
else:
stdout.write ")"
@ -54,11 +52,11 @@ proc printLisp(root: TSNode, data: var string) =
if node == root:
break
proc process(path: string, mode="") =
proc process(path: string, mode="cpp", pretty = true) =
if not existsFile(path):
echo "Invalid path " & path
return
var
parser = tsParserNew()
ext = path.splitFile().ext
@ -67,14 +65,14 @@ proc process(path: string, mode="") =
defer:
parser.tsParserDelete()
if mode.len() != 0:
if mode.len != 0:
pmode = mode
elif ext in [".h", ".c"]:
pmode = "c"
elif ext in [".hxx", ".hpp", ".hh", ".H", ".h++", ".cpp", ".cxx", ".cc", ".C", ".c++"]:
pmode = "cpp"
if "cplusplus" in data or "extern \"C\"" in data:
pmode = "cpp"
@ -91,23 +89,29 @@ proc process(path: string, mode="") =
quit()
var
tree = parser.tsParserParseString(nil, data.cstring, data.len().uint32)
tree = parser.tsParserParseString(nil, data.cstring, data.len.uint32)
root = tree.tsTreeRootNode()
defer:
tree.tsTreeDelete()
printLisp(root, data)
printLisp(root, data, pretty)
proc parseCli() =
let params = commandLineParams()
var
mode = "cpp"
params = commandLineParams()
pretty = true
for param in params:
if param in ["-h", "--help", "-?", "/?", "/h"]:
echo HELP
quit()
elif param == "-u":
gPretty = false
elif param == "-c":
mode = "c"
elif param == "-m":
pretty = false
else:
process(param)
process(param, mode, pretty)
parseCli()