Move AST to Nim processing into toast

This commit is contained in:
Ganesh Viswanathan 2018-11-28 11:08:45 -06:00
commit 7344042029
5 changed files with 267 additions and 274 deletions

View file

@ -1,53 +1,41 @@
import macros, os, strformat import macros, os, strformat, strutils
import regex import treesitter/runtime
import getters, globals import getters, globals
proc addReorder*(): NimNode =
result = newNimNode(nnkStmtList)
if not gReorder:
gReorder = true
result.add parseStmt(
"{.experimental: \"codeReordering\".}"
)
proc addHeader*(fullpath: string) =
gCurrentHeader = ("header" & fullpath.splitFile().name.replace(re"[-.]+", ""))
gConstStr &= &" {gCurrentHeader} = \"{fullpath}\" # addHeader()\n"
# #
# Preprocessor # Preprocessor
# #
proc pPreprocDef(node: ref Ast) = proc pPreprocDef(node: TSNode) =
if node.children.len == 2: if node.tsNodeNamedChildCount() == 2:
let let
name = getNodeValIf(node.children[0], identifier) name = getNodeValIf(node.tsNodeNamedChild(0), "identifier")
val = getNodeValIf(node.children[1], preproc_arg) val = getNodeValIf(node.tsNodeNamedChild(1), "preproc_arg")
if name.nBl and val.nBl and name notin gConsts: if name.nBl and val.nBl and name notin gStateRT.consts:
gConsts.add(name) gStateRT.consts.add(name)
if val.getLit().nBl: if val.getLit().nBl:
# #define NAME VALUE # #define NAME VALUE
gConstStr &= &" {name.getIdentifier()}* = {val} # pPreprocDef()\n" gStateRT.constStr &= &" {name.getIdentifier()}* = {val} # pPreprocDef()\n"
# #
# Types # Types
# #
proc typeScan(node: ref Ast, sym, id: Sym, offset: string): string = proc typeScan(node: TSNode, sym, id: string, offset: string): string =
if node.sym != sym or node.children.len != 2: if node.tsNodeIsNull() or $node.tsNodeType() != sym or node.tsNodeNamedChildCount() != 2:
return return
var var
name = getNodeValIf(node.children[1], id) name = getNodeValIf(node.tsNodeNamedChild(1), id)
ptyp = getNodeValIf(node.children[0], primitive_type) ptyp = getNodeValIf(node.tsNodeNamedChild(0), "primitive_type")
ttyp = getNodeValIf(node.children[0], type_identifier) ttyp = getNodeValIf(node.tsNodeNamedChild(0), "type_identifier")
ptrname = false 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.tsNodeNamedChild(1).tsNodeType() == "pointer_declarator" and node.tsNodeNamedChild(1).tsNodeNamedChildCount() == 1:
name = getNodeValIf(node.children[1].children[0], id) name = getNodeValIf(node.tsNodeNamedChild(1).tsNodeNamedChild(0), id)
ptrname = true ptrname = true
if name.len == 0: if name.len == 0:
@ -61,194 +49,238 @@ proc typeScan(node: ref Ast, sym, id: Sym, offset: string): string =
if ptrname: if ptrname:
ttyp = &"ptr {ttyp}" ttyp = &"ptr {ttyp}"
result = &"{offset}{name.getIdentifier()}: {ttyp}" result = &"{offset}{name.getIdentifier()}: {ttyp}"
elif node.children[0].sym in [struct_specifier, enum_specifier] and node.children[0].children.len == 1: elif $node.tsNodeNamedChild(0).tsNodeType() in ["struct_specifier", "enum_specifier"] and node.tsNodeNamedChild(0).tsNodeNamedChildCount() == 1:
var styp = getNodeValIf(node.children[0].children[0], type_identifier) var styp = getNodeValIf(node.tsNodeNamedChild(0).tsNodeNamedChild(0), "type_identifier")
if styp.nBl: if styp.nBl:
if ptrname: if ptrname:
styp = &"ptr {styp}" styp = &"ptr {styp}"
result = &"{offset}{name.getIdentifier()}: {styp}" result = &"{offset}{name.getIdentifier()}: {styp}"
else:
return
proc pStructSpecifier(node: ref Ast, name = "") = proc pStructSpecifier(node: TSNode, name = "") =
var stmt: string var stmt: string
if node.children.len == 1 and name notin gTypes: if node.tsNodeNamedChildCount() == 1 and name notin gStateRT.types:
case node.children[0].sym: case $node.tsNodeNamedChild(0).tsNodeType():
of type_identifier: of "type_identifier":
let typ = getNodeValIf(node.children[0], type_identifier) let typ = getNodeValIf(node.tsNodeNamedChild(0), "type_identifier")
if typ.nBl: if typ.nBl:
gTypes.add(name) gStateRT.types.add(name)
if name != typ: if name != typ:
# typedef struct X Y # typedef struct X Y
gTypeStr &= &" {name.getIdentifier()}* = {typ} #1 pStructSpecifier()\n" gStateRT.typeStr &= &" {name.getIdentifier()}* = {typ} #1 pStructSpecifier()\n"
else: else:
# typedef struct X X # typedef struct X X
gTypeStr &= &" {name.getIdentifier()}* {{.importc: \"{name}\", header: {gCurrentHeader}, bycopy.}} = object #2 pStructSpecifier()\n" gStateRT.typeStr &= &" {name.getIdentifier()}* {{.importc: \"{name}\", header: {gStateRT.currentHeader}, bycopy.}} = object #2 pStructSpecifier()\n"
of field_declaration_list: of "field_declaration_list":
# typedef struct { fields } X # typedef struct { fields } X
stmt = &" {name.getIdentifier()}* {{.importc: \"{name}\", header: {gCurrentHeader}, bycopy.}} = object #3 pStructSpecifier()\n" stmt = &" {name.getIdentifier()}* {{.importc: \"{name}\", header: {gStateRT.currentHeader}, bycopy.}} = object #3 pStructSpecifier()\n"
for field in node.children[0].children: if node.tsNodeNamedChild(0).tsNodeNamedChildCount() != 0:
let ts = typeScan(field, field_declaration, field_identifier, " ") for i in 0 .. node.tsNodeNamedChild(0).tsNodeNamedChildCount()-1:
let ts = typeScan(node.tsNodeNamedChild(0).tsNodeNamedChild(i), "field_declaration", "field_identifier", " ")
if ts.len == 0:
return
stmt &= ts & "\n"
gStateRT.types.add(name)
gStateRT.typeStr &= stmt
else:
discard
elif name.len == 0 and node.tsNodeNamedChildCount() == 2 and $node.tsNodeNamedChild(1).tsNodeType() == "field_declaration_list":
let ename = getNodeValIf(node.tsNodeNamedChild(0), "type_identifier")
if ename.nBl and ename notin gStateRT.types:
# struct X { fields }
stmt &= &" {ename}* {{.importc: \"struct {ename}\", header: {gStateRT.currentHeader}, bycopy.}} = object #4 pStructSpecifier()\n"
if node.tsNodeNamedChild(1).tsNodeNamedChildCount() != 0:
for i in 0 .. node.tsNodeNamedChild(1).tsNodeNamedChildCount()-1:
let ts = typeScan(node.tsNodeNamedChild(1).tsNodeNamedChild(i), "field_declaration", "field_identifier", " ")
if ts.len == 0: if ts.len == 0:
return return
stmt &= ts & "\n" stmt &= ts & "\n"
gTypes.add(name) gStateRT.types.add(name)
gTypeStr &= stmt gStateRT.typeStr &= stmt
else:
discard
elif name.len == 0 and node.children.len == 2 and node.children[1].sym == field_declaration_list: proc pEnumSpecifier(node: TSNode, name = "") =
let ename = getNodeValIf(node.children[0], type_identifier)
if ename.nBl and ename notin gTypes:
# struct X { fields }
stmt &= &" {ename}* {{.importc: \"struct {ename}\", header: {gCurrentHeader}, bycopy.}} = object #4 pStructSpecifier()\n"
for field in node.children[1].children:
let ts = typeScan(field, field_declaration, field_identifier, " ")
if ts.len == 0:
return
stmt &= ts & "\n"
gTypes.add(name)
gTypeStr &= stmt
proc pEnumSpecifier(node: ref Ast, name = "") =
var var
ename: string ename: string
elid: int elid: uint32
stmt: string stmt: string
if node.children.len == 1 and node.children[0].sym == enumerator_list: if node.tsNodeNamedChildCount() == 1 and $node.tsNodeNamedChild(0).tsNodeType() == "enumerator_list":
# typedef enum { fields } X # typedef enum { fields } X
ename = name ename = name
elid = 0 elid = 0
stmt = &" {name.getIdentifier()}* = enum #1 pEnumSpecifier()\n" stmt = &" {name.getIdentifier()}* = enum #1 pEnumSpecifier()\n"
elif name.len == 0 and node.children.len == 2 and node.children[1].sym == enumerator_list: elif name.len == 0 and node.tsNodeNamedChildCount() == 2 and $node.tsNodeNamedChild(1).tsNodeType() == "enumerator_list":
ename = getNodeValIf(node.children[0], type_identifier) ename = getNodeValIf(node.tsNodeNamedChild(0), "type_identifier")
elid = 1 elid = 1
if ename.nBl: if ename.nBl:
# enum X { fields } # enum X { fields }
stmt = &" {ename}* = enum #2 pEnumSpecifier()\n" stmt = &" {ename}* = enum #2 pEnumSpecifier()\n"
else: else:
return return
else:
return
for field in node.children[elid].children: if node.tsNodeNamedChild(elid).tsNodeNamedChildCount() != 0:
if field.sym == enumerator: for i in 0 .. node.tsNodeNamedChild(elid).tsNodeNamedChildCount()-1:
let fname = getNodeValIf(field.children[0], identifier) let field = node.tsNodeNamedChild(elid).tsNodeNamedChild(i)
if field.children.len == 1: if not field.tsNodeIsNull() and $field.tsNodeType() == "enumerator":
stmt &= &" {fname}\n" let fname = getNodeValIf(field.tsNodeNamedChild(0), "identifier")
elif field.children.len == 2 and field.children[1].sym == number_literal: if field.tsNodeNamedChildCount() == 1:
let num = getNodeValIf(field.children[1], number_literal) stmt &= &" {fname}\n"
stmt &= &" {fname} = {num}\n" elif field.tsNodeNamedChildCount() == 2 and $field.tsNodeNamedChild(1).tsNodeType() == "number_literal":
else: let num = getNodeValIf(field.tsNodeNamedChild(1), "number_literal")
return stmt &= &" {fname} = {num}\n"
else:
return
if ename notin gTypes: if ename notin gStateRT.types:
gTypes.add(name) gStateRT.types.add(name)
gTypeStr &= stmt gStateRT.typeStr &= stmt
proc pTypeDefinition(node: ref Ast) = proc pTypeDefinition(node: TSNode) =
if node.children.len == 2: if node.tsNodeNamedChildCount() == 2:
var var
name = getNodeValIf(node.children[1], type_identifier) name = getNodeValIf(node.tsNodeNamedChild(1), "type_identifier")
ptyp = getNodeValIf(node.children[0], primitive_type) ptyp = getNodeValIf(node.tsNodeNamedChild(0), "primitive_type")
ttyp = getNodeValIf(node.children[0], type_identifier) ttyp = getNodeValIf(node.tsNodeNamedChild(0), "type_identifier")
ptrname = false 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.tsNodeNamedChild(1).tsNodeType() == "pointer_declarator" and node.tsNodeNamedChild(1).tsNodeNamedChildCount() == 1:
name = getNodeValIf(node.children[1].children[0], type_identifier) name = getNodeValIf(node.tsNodeNamedChild(1).tsNodeNamedChild(0), "type_identifier")
ptrname = true ptrname = true
if name.nBl and name notin gTypes: if name.nBl and name notin gStateRT.types:
if ptyp.nBl: if ptyp.nBl:
# typedef int X # typedef int X
gTypes.add(name) gStateRT.types.add(name)
ptyp = ptyp.getType() ptyp = ptyp.getType()
if ptyp != "object" and ptrname: if ptyp != "object" and ptrname:
ptyp = &"ptr {ptyp}" ptyp = &"ptr {ptyp}"
gTypeStr &= &" {name.getIdentifier()}* = {ptyp} #1 pTypeDefinition()\n" gStateRT.typeStr &= &" {name.getIdentifier()}* = {ptyp} #1 pTypeDefinition()\n"
elif ttyp.nBl: elif ttyp.nBl:
# typedef X Y # typedef X Y
gTypes.add(name) gStateRT.types.add(name)
if ptrname: if ptrname:
ttyp = &"ptr {ttyp}" ttyp = &"ptr {ttyp}"
gTypeStr &= &" {name.getIdentifier()}* = {ttyp} #2 pTypeDefinition()\n" gStateRT.typeStr &= &" {name.getIdentifier()}* = {ttyp} #2 pTypeDefinition()\n"
else: else:
case node.children[0].sym: case $node.tsNodeNamedChild(0).tsNodeType():
of struct_specifier: of "struct_specifier":
pStructSpecifier(node.children[0], name) pStructSpecifier(node.tsNodeNamedChild(0), name)
of enum_specifier: of "enum_specifier":
pEnumSpecifier(node.children[0], name) pEnumSpecifier(node.tsNodeNamedChild(0), name)
else: else:
discard discard
proc pFunctionDeclarator(node: ref Ast, typ: string) = proc pFunctionDeclarator(node: TSNode, typ: string) =
if node.children.len == 2: if node.tsNodeNamedChildCount() == 2:
let let
name = getNodeValIf(node.children[0], identifier) name = getNodeValIf(node.tsNodeNamedChild(0), "identifier")
if name.nBl and name notin gProcs and node.children[1].sym == parameter_list: if name.nBl and name notin gStateRT.procs and $node.tsNodeNamedChild(1).tsNodeType() == "parameter_list":
# typ function(typ param1, ...) # typ function(typ param1, ...)
var stmt = &"# pFunctionDeclarator()\nproc {name.getIdentifier()}*(" var stmt = &"# pFunctionDeclarator()\nproc {name.getIdentifier()}*("
for i in 0 .. node.children[1].children.len-1: if node.tsNodeNamedChild(1).tsNodeNamedChildCount() != 0:
let ts = typeScan(node.children[1].children[i], parameter_declaration, identifier, "") for i in 0 .. node.tsNodeNamedChild(1).tsNodeNamedChildCount()-1:
if ts.len == 0: let ts = typeScan(node.tsNodeNamedChild(1).tsNodeNamedChild(i), "parameter_declaration", "identifier", "")
return if ts.len == 0:
stmt &= ts return
if i != node.children[1].children.len-1: stmt &= ts
stmt &= ", " if i != node.tsNodeNamedChild(1).tsNodeNamedChildCount()-1:
stmt &= ", "
if typ != "void": if typ != "void":
stmt &= &"): {typ.getType()} " stmt &= &"): {typ.getType()} "
else: else:
stmt &= ") " stmt &= ") "
stmt &= &"{{.importc: \"{name}\", header: {gCurrentHeader}.}}\n" stmt &= &"{{.importc: \"{name}\", header: {gStateRT.currentHeader}.}}\n"
gProcs.add(name) gStateRT.procs.add(name)
gProcStr &= stmt gStateRT.procStr &= stmt
proc pDeclaration*(node: ref Ast) = proc pDeclaration*(node: TSNode) =
if node.children.len == 2 and node.children[1].sym == function_declarator: if node.tsNodeNamedChildCount() == 2 and $node.tsNodeNamedChild(1).tsNodeType() == "function_declarator":
let let
ptyp = getNodeValIf(node.children[0], primitive_type) ptyp = getNodeValIf(node.tsNodeNamedChild(0), "primitive_type")
ttyp = getNodeValIf(node.children[0], type_identifier) ttyp = getNodeValIf(node.tsNodeNamedChild(0), "type_identifier")
if ptyp.nBl: if ptyp.nBl:
pFunctionDeclarator(node.children[1], ptyp.getType()) pFunctionDeclarator(node.tsNodeNamedChild(1), ptyp.getType())
elif ttyp.nBl: elif ttyp.nBl:
pFunctionDeclarator(node.children[1], ttyp) pFunctionDeclarator(node.tsNodeNamedChild(1), ttyp)
elif node.children[0].sym == struct_specifier and node.children[0].children.len == 1: elif $node.tsNodeNamedChild(0).tsNodeType() == "struct_specifier" and node.tsNodeNamedChild(0).tsNodeNamedChildCount() == 1:
let styp = getNodeValIf(node.children[0].children[0], type_identifier) let styp = getNodeValIf(node.tsNodeNamedChild(0).tsNodeNamedChild(0), "type_identifier")
if styp.nBl: if styp.nBl:
pFunctionDeclarator(node.children[1], styp) pFunctionDeclarator(node.tsNodeNamedChild(1), styp)
proc genNimAst*(node: ref Ast) = proc genNimAst(root: TSNode) =
if node.isNil: var
return node = root
nextnode: TSNode
case node.sym: while true:
of ERROR: if not node.tsNodeIsNull():
let (line, col) = getLineCol(node) case $node.tsNodeType():
echo &"Potentially invalid syntax at line {line} column {col}" of "ERROR":
of preproc_def: let (line, col) = getLineCol(node)
pPreprocDef(node) echo &"Potentially invalid syntax at line {line} column {col}"
of type_definition: of "preproc_def":
pTypeDefinition(node) pPreprocDef(node)
of declaration: of "type_definition":
pDeclaration(node) pTypeDefinition(node)
of struct_specifier: of "declaration":
if node.parent.sym notin [type_definition, declaration]: pDeclaration(node)
pStructSpecifier(node) of "struct_specifier":
of enum_specifier: if $node.tsNodeParent().tsNodeType() notin ["type_definition", "declaration"]:
if node.parent.sym notin [type_definition, declaration]: pStructSpecifier(node)
pEnumSpecifier(node) of "enum_specifier":
if $node.tsNodeParent.tsNodeType() notin ["type_definition", "declaration"]:
pEnumSpecifier(node)
else:
discard
else: else:
discard return
for child in node.children: if node.tsNodeNamedChildCount() != 0:
genNimAst(child) nextnode = node.tsNodeNamedChild(0)
else:
nextnode = node.tsNodeNextNamedSibling()
if nextnode.tsNodeIsNull():
while true:
node = node.tsNodeParent()
if node == root:
break
if not node.tsNodeNextNamedSibling().tsNodeIsNull():
node = node.tsNodeNextNamedSibling()
break
else:
node = nextnode
if node == root:
break
proc printNim*(fullpath: string, root: TSNode) =
echo "{.experimental: \"codeReordering\".}"
var fp = fullpath.replace("\\", "/")
gStateRT.currentHeader = getCurrentHeader(fullpath)
gStateRT.constStr &= &" {gStateRT.currentHeader} = \"{fp}\"\n"
genNimAst(root)
if gStateRT.constStr.nBl:
echo "const\n" & gStateRT.constStr
if gStateRT.typeStr.nBl:
echo "type\n" & gStateRT.typeStr
if gStateRT.procStr.nBl:
echo gStateRT.procStr

View file

@ -1,6 +1,6 @@
import macros, os, strformat, strutils import macros, os, strformat, strutils
import ast, getters, globals, lisp import getters, globals
proc interpPath(dir: string): string= proc interpPath(dir: string): string=
# TODO: more robust: needs a DirSep after "$projpath" # TODO: more robust: needs a DirSep after "$projpath"
@ -24,19 +24,31 @@ proc findPath(path: string, fail = true): string =
else: else:
return "" return ""
proc getToast(fullpath: string): string =
var
cmd = "toast -n -p "
for i in gStateCT.defines:
cmd &= &"-D\"{i}\" "
for i in gStateCT.includeDirs:
cmd &= &"-I\"{i}\" "
result = staticExec(cmd & fullpath)
proc cSearchPath*(path: string): string {.compileTime.}= proc cSearchPath*(path: string): string {.compileTime.}=
result = findPath(path, fail = false) result = findPath(path, fail = false)
if result.len == 0: if result.len == 0:
var found = false var found = false
for inc in gSearchDirs: for inc in gStateCT.searchDirs:
result = (inc & "/" & path).replace("\\", "/") result = (inc & "/" & path).replace("\\", "/")
if fileExists(result) or dirExists(result): if fileExists(result) or dirExists(result):
found = true found = true
break break
doAssert found, "File or directory not found: " & path & " gSearchDirs: " & $gSearchDirs doAssert found, "File or directory not found: " & path & " gStateCT.searchDirs: " & $gStateCT.searchDirs
macro cDebug*(): untyped = macro cDebug*(): untyped =
gDebug = true gStateCT.debug = true
macro cDefine*(name: static string, val: static string = ""): untyped = macro cDefine*(name: static string, val: static string = ""): untyped =
result = newNimNode(nnkStmtList) result = newNimNode(nnkStmtList)
@ -45,21 +57,21 @@ macro cDefine*(name: static string, val: static string = ""): untyped =
if val.nBl: if val.nBl:
str &= &"=\"{val}\"" str &= &"=\"{val}\""
if str notin gDefines: if str notin gStateCT.defines:
gDefines.add(str) gStateCT.defines.add(str)
str = "-D" & str str = "-D" & str
result.add(quote do: result.add(quote do:
{.passC: `str`.} {.passC: `str`.}
) )
if gDebug: if gStateCT.debug:
echo result.repr echo result.repr
macro cAddSearchDir*(dir: static string): untyped = macro cAddSearchDir*(dir: static string): untyped =
var dir = interpPath(dir) var dir = interpPath(dir)
if dir notin gSearchDirs: if dir notin gStateCT.searchDirs:
gSearchDirs.add(dir) gStateCT.searchDirs.add(dir)
macro cIncludeDir*(dir: static string): untyped = macro cIncludeDir*(dir: static string): untyped =
var dir = interpPath(dir) var dir = interpPath(dir)
@ -69,14 +81,14 @@ macro cIncludeDir*(dir: static string): untyped =
fullpath = findPath(dir) fullpath = findPath(dir)
str = &"-I\"{fullpath}\"" str = &"-I\"{fullpath}\""
if fullpath notin gIncludeDirs: if fullpath notin gStateCT.includeDirs:
gIncludeDirs.add(fullpath) gStateCT.includeDirs.add(fullpath)
result.add(quote do: result.add(quote do:
{.passC: `str`.} {.passC: `str`.}
) )
if gDebug: if gStateCT.debug:
echo result.repr echo result.repr
macro cAddStdDir*(mode = "c"): untyped = macro cAddStdDir*(mode = "c"): untyped =
@ -109,11 +121,11 @@ macro cCompile*(path: static string): untyped =
var var
ufn = fn ufn = fn
uniq = 1 uniq = 1
while ufn in gCompile: while ufn in gStateCT.compile:
ufn = fn & $uniq ufn = fn & $uniq
uniq += 1 uniq += 1
gCompile.add(ufn) gStateCT.compile.add(ufn)
if fn == ufn: if fn == ufn:
return "{.compile: \"$#\".}" % file.replace("\\", "/") return "{.compile: \"$#\".}" % file.replace("\\", "/")
else: else:
@ -138,44 +150,18 @@ macro cCompile*(path: static string): untyped =
result.add stmt.parseStmt() result.add stmt.parseStmt()
if gDebug: if gStateCT.debug:
echo result.repr echo result.repr
macro cImport*(filename: static string): untyped = macro cImport*(filename: static string): untyped =
result = newNimNode(nnkStmtList) result = newNimNode(nnkStmtList)
result.add addReorder()
let let
fullpath = findPath(filename) fullpath = findPath(filename)
root = parseLisp(fullpath)
echo "Importing " & fullpath echo "Importing " & fullpath
gCode = staticRead(fullpath) result.add parseStmt(getToast(fullpath))
gConstStr = ""
gTypeStr = ""
addHeader(fullpath) if gStateCT.debug:
genNimAst(root)
if gConstStr.nBl:
if gDebug:
echo "const\n" & gConstStr
result.add parseStmt(
"const\n" & gConstStr
)
if gTypeStr.nBl:
if gDebug:
echo "type\n" & gTypeStr
result.add parseStmt(
"type\n" & gTypeStr
)
if gProcStr.nBl:
if gDebug:
echo gProcStr
result.add gProcStr.parseStmt()
if gDebug:
echo result.repr echo result.repr

View file

@ -2,6 +2,8 @@ import macros, ospaths, strformat, strutils
import regex import regex
import treesitter/runtime
import git, globals import git, globals
proc sanitizePath*(path: string): string = proc sanitizePath*(path: string): string =
@ -19,21 +21,24 @@ proc getLit*(str: string): string =
str.contains(re"^0x[\d]+$"): str.contains(re"^0x[\d]+$"):
return str return str
proc getNodeValIf*(node: ref Ast, esym: Sym): string = proc getNodeValIf*(node: TSNode, esym: string): string =
if esym != node.sym: if esym != $node.tsNodeType():
return return
return gCode[node.start .. node.stop-1].strip() return gStateRT.code[node.tsNodeStartByte() .. node.tsNodeEndByte()-1].strip()
proc getLineCol*(node: ref Ast): tuple[line, col: int] = proc getLineCol*(node: TSNode): tuple[line, col: int] =
result.line = 1 result.line = 1
result.col = 1 result.col = 1
for i in 0 .. node.start-1: for i in 0 .. node.tsNodeStartByte()-1:
if gCode[i] == '\n': if gStateRT.code[i] == '\n':
result.col = 0 result.col = 0
result.line += 1 result.line += 1
result.col += 1 result.col += 1
proc getCurrentHeader*(fullpath: string): string =
("header" & fullpath.splitFile().name.replace(re"[-.]+", ""))
proc getGccPaths*(mode = "c"): string = proc getGccPaths*(mode = "c"): string =
var var
nul = when defined(Windows): "nul" else: "/dev/null" nul = when defined(Windows): "nul" else: "/dev/null"
@ -45,23 +50,15 @@ proc getPreprocessor*(fullpath: string, mode = "cpp"): string =
var var
mmode = if mode == "cpp": "c++" else: mode mmode = if mode == "cpp": "c++" else: mode
cmd = &"gcc -E -dD -x{mmode} " cmd = &"gcc -E -dD -x{mmode} "
gdef, gdir: seq[string]
rdata: seq[string] = @[] rdata: seq[string] = @[]
start = false start = false
sfile = fullpath.sanitizePath sfile = fullpath.sanitizePath
when nimvm: for inc in gStateRT.includeDirs:
gdef = gDefines
gdir = gIncludeDirs
else:
gdef = gDefinesRT
gdir = gIncludeDirsRT
for inc in gdir:
cmd &= &"-I\"{inc}\" " cmd &= &"-I\"{inc}\" "
for def in gdef: for def in gStateRT.defines:
cmd &= &"-D{def} " cmd &= &"-D{def} "
cmd &= &"\"{fullpath}\"" cmd &= &"\"{fullpath}\""

View file

@ -1,42 +1,16 @@
import macros
type type
Sym* = enum State* = object
ERROR, IGNORED, compile*, defines*, headers*, includeDirs*, searchDirs*: seq[string]
enumerator, enumerator_list, enum_specifier,
declaration,
field_declaration, field_declaration_list, field_identifier, function_declarator,
identifier,
number_literal,
parameter_declaration, parameter_list, pointer_declarator, preproc_arg, preproc_def, primitive_type,
struct_specifier,
type_definition, type_identifier
Ast* = object debug*, past*, preprocess*, pnim*, pretty*: bool
sym*: Sym
start*, stop*: int consts*, procs*, types*: seq[string]
parent*: ref Ast
children*: seq[ref Ast] code*, constStr*, currentHeader*, mode*, procStr*, typeStr*: string
var var
gDefines* {.compiletime.}: seq[string] gStateCT* {.compiletime.}: State
gDefinesRT*: seq[string] gStateRT*: State
gCompile* {.compiletime.}: seq[string]
gConsts* {.compiletime.}: seq[string]
gHeaders* {.compiletime.}: seq[string]
gIncludeDirs* {.compiletime.}: seq[string]
gIncludeDirsRT*: seq[string]
gProcs* {.compiletime.}: seq[string]
gSearchDirs* {.compiletime.}: seq[string]
gTypes* {.compiletime.}: seq[string]
gCode* {.compiletime.}: string
gConstStr* {.compiletime.}: string
gCurrentHeader* {.compiletime.}: string
gDebug* {.compiletime.}: bool
gReorder* {.compiletime.}: bool
gProcStr* {.compiletime.}: string
gTypeStr* {.compiletime.}: string
template nBl*(s: untyped): untyped = template nBl*(s: untyped): untyped =
(s.len != 0) (s.len != 0)

View file

@ -2,18 +2,20 @@ import os, strutils
import treesitter/[runtime, c, cpp] import treesitter/[runtime, c, cpp]
import nimterop/[globals, getters] import nimterop/[ast, globals, getters]
const HELP = """ const HELP = """
> toast header.h > toast header.h
-a print AST output -a print AST output
-c C mode - CPP is default
-m print minimized AST output - non-pretty (implies -a) -m print minimized AST output - non-pretty (implies -a)
-n print Nim output
-c C mode - CPP is default
-p run preprocessor on header -p run preprocessor on header
-D definitions to pass to preprocessor -D definitions to pass to preprocessor
-I include directory to pass to preprocessor""" -I include directory to pass to preprocessor"""
proc printLisp(root: TSNode, data: var string, pretty = true) = proc printLisp(root: TSNode) =
var var
node = root node = root
nextnode: TSNode nextnode: TSNode
@ -21,19 +23,19 @@ proc printLisp(root: TSNode, data: var string, pretty = true) =
while true: while true:
if not node.tsNodeIsNull(): if not node.tsNodeIsNull():
if pretty: if gStateRT.pretty:
stdout.write spaces(depth) stdout.write spaces(depth)
stdout.write "(" & $node.tsNodeType() & " " & $node.tsNodeStartByte() & " " & $node.tsNodeEndByte() stdout.write "(" & $node.tsNodeType() & " " & $node.tsNodeStartByte() & " " & $node.tsNodeEndByte()
else: else:
return return
if node.tsNodeNamedChildCount() != 0: if node.tsNodeNamedChildCount() != 0:
if pretty: if gStateRT.pretty:
echo "" echo ""
nextnode = node.tsNodeNamedChild(0) nextnode = node.tsNodeNamedChild(0)
depth += 1 depth += 1
else: else:
if pretty: if gStateRT.pretty:
echo ")" echo ")"
else: else:
stdout.write ")" stdout.write ")"
@ -45,7 +47,7 @@ proc printLisp(root: TSNode, data: var string, pretty = true) =
depth -= 1 depth -= 1
if depth == -1: if depth == -1:
break break
if pretty: if gStateRT.pretty:
echo spaces(depth) & ")" echo spaces(depth) & ")"
else: else:
stdout.write ")" stdout.write ")"
@ -60,7 +62,7 @@ proc printLisp(root: TSNode, data: var string, pretty = true) =
if node == root: if node == root:
break break
proc process(path: string, mode="cpp", past, pretty, preprocess: bool) = proc process(path: string) =
if not existsFile(path): if not existsFile(path):
echo "Invalid path " & path echo "Invalid path " & path
return return
@ -68,54 +70,54 @@ proc process(path: string, mode="cpp", past, pretty, preprocess: bool) =
var var
parser = tsParserNew() parser = tsParserNew()
ext = path.splitFile().ext ext = path.splitFile().ext
pmode = ""
data = ""
defer: defer:
parser.tsParserDelete() parser.tsParserDelete()
if mode.len != 0: if gStateRT.mode.len != 0:
pmode = mode gStateRT.mode = "cpp"
elif ext in [".h", ".c"]: elif ext in [".h", ".c"]:
pmode = "c" gStateRT.mode = "c"
elif ext in [".hxx", ".hpp", ".hh", ".H", ".h++", ".cpp", ".cxx", ".cc", ".C", ".c++"]: elif ext in [".hxx", ".hpp", ".hh", ".H", ".h++", ".cpp", ".cxx", ".cc", ".C", ".c++"]:
pmode = "cpp" gStateRT.mode = "cpp"
if preprocess: if gStateRT.preprocess:
data = getPreprocessor(path) gStateRT.code = getPreprocessor(path)
else: else:
data = readFile(path) gStateRT.code = readFile(path)
if pmode == "c": if gStateRT.mode == "c":
if not parser.tsParserSetLanguage(treeSitterC()): if not parser.tsParserSetLanguage(treeSitterC()):
echo "Failed to load C parser" echo "Failed to load C parser"
quit() quit()
elif pmode == "cpp": elif gStateRT.mode == "cpp":
if not parser.tsParserSetLanguage(treeSitterCpp()): if not parser.tsParserSetLanguage(treeSitterCpp()):
echo "Failed to load C++ parser" echo "Failed to load C++ parser"
quit() quit()
else: else:
echo "Invalid parser " & mode echo "Invalid parser " & gStateRT.mode
quit() quit()
var var
tree = parser.tsParserParseString(nil, data.cstring, data.len.uint32) tree = parser.tsParserParseString(nil, gStateRT.code.cstring, gStateRT.code.len.uint32)
root = tree.tsTreeRootNode() root = tree.tsTreeRootNode()
defer: defer:
tree.tsTreeDelete() tree.tsTreeDelete()
if past: if gStateRT.past:
printLisp(root, data, pretty) printLisp(root)
elif gStateRT.pnim:
printNim(path, root)
proc parseCli() = proc parseCli() =
var var params = commandLineParams()
mode = "cpp"
params = commandLineParams()
past = false gStateRT.mode = "cpp"
pretty = true gStateRT.past = false
preprocess = false gStateRT.pnim = false
gStateRT.pretty = true
gStateRT.preprocess = false
for param in params: for param in params:
let flag = if param.len() <= 2: param else: param[0..<2] let flag = if param.len() <= 2: param else: param[0..<2]
@ -124,19 +126,21 @@ proc parseCli() =
echo HELP echo HELP
quit() quit()
elif flag == "-a": elif flag == "-a":
past = true gStateRT.past = true
elif flag == "-c": elif flag == "-c":
mode = "c" gStateRT.mode = "c"
elif flag == "-m": elif flag == "-m":
past = true gStateRT.past = true
pretty = false gStateRT.pretty = false
elif flag == "-n":
gStateRT.pnim = true
elif flag == "-p": elif flag == "-p":
preprocess = true gStateRT.preprocess = true
elif flag == "-D": elif flag == "-D":
gDefinesRT.add(param[2..^1]) gStateRT.defines.add(param[2..^1].strip(chars={'"'}))
elif flag == "-I": elif flag == "-I":
gIncludeDirsRT.add(param[2..^1]) gStateRT.includeDirs.add(param[2..^1].strip(chars={'"'}))
else: else:
process(param, mode, past, pretty, preprocess) process(param)
parseCli() parseCli()