Move AST to Nim processing into toast
This commit is contained in:
parent
d7ca43f49f
commit
7344042029
5 changed files with 267 additions and 274 deletions
318
nimterop/ast.nim
318
nimterop/ast.nim
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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}\""
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
78
toast.nim
78
toast.nim
|
|
@ -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()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue