diff options
| author | Ganesh Viswanathan <dev@genotrance.com> | 2018-12-03 14:58:27 -0600 |
|---|---|---|
| committer | Ganesh Viswanathan <dev@genotrance.com> | 2018-12-03 14:58:27 -0600 |
| commit | 33956c36ee837603ed94f94b528a3bbb6bd51890 (patch) | |
| tree | 3e6849e177eb97a2abbe6a94596648fd3ff01c1e | |
| parent | 9c206fea4f93fe2dcc9b5332d08d86f6dc5adb03 (diff) | |
| download | nimterop-33956c36ee837603ed94f94b528a3bbb6bd51890.tar.gz nimterop-33956c36ee837603ed94f94b528a3bbb6bd51890.zip | |
Switch to simpler grammar engine
| -rw-r--r-- | nimterop/ast.nim | 300 | ||||
| -rw-r--r-- | nimterop/astold.nim | 297 | ||||
| -rw-r--r-- | nimterop/getters.nim | 61 | ||||
| -rw-r--r-- | nimterop/globals.nim | 19 | ||||
| -rw-r--r-- | nimterop/grammar.nim | 244 | ||||
| -rw-r--r-- | nimterop/lisp.nim | 35 |
6 files changed, 690 insertions, 266 deletions
diff --git a/nimterop/ast.nim b/nimterop/ast.nim index 069ecba..cce07c3 100644 --- a/nimterop/ast.nim +++ b/nimterop/ast.nim @@ -1,265 +1,83 @@ -import macros, os, strformat, strutils +import strformat, strutils, tables -import treesitter/runtime - -import getters, globals - -# -# Preprocessor -# - -proc pPreprocDef(node: TSNode) = - if node.tsNodeNamedChildCount() == 2: - let - name = getNodeValIf(node.tsNodeNamedChild(0), "identifier") - val = getNodeValIf(node.tsNodeNamedChild(1), "preproc_arg") - - if name.nBl and val.nBl and name notin gStateRT.consts: - gStateRT.consts.add(name) - if val.getLit().nBl: - # #define NAME VALUE - gStateRT.constStr &= &" {name.getIdentifier()}* = {val} # pPreprocDef()\n" - -# -# Types -# - -proc typeScan(node: TSNode, sym, id: string, offset: string): string = - if node.tsNodeIsNull() or $node.tsNodeType() != sym or node.tsNodeNamedChildCount() != 2: - return - - var - name = getNodeValIf(node.tsNodeNamedChild(1), id) - ptyp = getNodeValIf(node.tsNodeNamedChild(0), "primitive_type") - ttyp = getNodeValIf(node.tsNodeNamedChild(0), "type_identifier") - ptrname = false - - if name.len == 0 and $node.tsNodeNamedChild(1).tsNodeType() == "pointer_declarator" and node.tsNodeNamedChild(1).tsNodeNamedChildCount() == 1: - name = getNodeValIf(node.tsNodeNamedChild(1).tsNodeNamedChild(0), id) - ptrname = true - - if name.len == 0: - return - elif ptyp.nBl: - ptyp = ptyp.getType() - if ptyp != "object" and ptrname: - ptyp = &"ptr {ptyp}" - result = &"{offset}{name.getIdentifier()}: {ptyp}" - elif ttyp.nBl: - if ptrname: - ttyp = &"ptr {ttyp}" - result = &"{offset}{name.getIdentifier()}: {ttyp}" - elif $node.tsNodeNamedChild(0).tsNodeType() in ["struct_specifier", "enum_specifier"] and node.tsNodeNamedChild(0).tsNodeNamedChildCount() == 1: - var styp = getNodeValIf(node.tsNodeNamedChild(0).tsNodeNamedChild(0), "type_identifier") - if styp.nBl: - if ptrname: - styp = &"ptr {styp}" - result = &"{offset}{name.getIdentifier()}: {styp}" - -proc pStructSpecifier(node: TSNode, name = "") = - var stmt: string - if node.tsNodeNamedChildCount() == 1 and name notin gStateRT.types: - case $node.tsNodeNamedChild(0).tsNodeType(): - of "type_identifier": - let typ = getNodeValIf(node.tsNodeNamedChild(0), "type_identifier") - if typ.nBl: - gStateRT.types.add(name) - if name != typ: - # typedef struct X Y - gStateRT.typeStr &= &" {name.getIdentifier()}* = {typ} #1 pStructSpecifier()\n" - else: - # typedef struct X X - gStateRT.typeStr &= &" {name.getIdentifier()}* {{.importc: \"{name}\", header: {gStateRT.currentHeader}, bycopy.}} = object #2 pStructSpecifier()\n" - - of "field_declaration_list": - # typedef struct { fields } X - stmt = &" {name.getIdentifier()}* {{.importc: \"{name}\", header: {gStateRT.currentHeader}, bycopy.}} = object #3 pStructSpecifier()\n" - - if node.tsNodeNamedChild(0).tsNodeNamedChildCount() != 0: - for i in 0 .. node.tsNodeNamedChild(0).tsNodeNamedChildCount()-1: - if $node.tsNodeNamedChild(0).tsNodeNamedChild(i).tsNodeType() == "comment": - continue - 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" +import regex - if node.tsNodeNamedChild(1).tsNodeNamedChildCount() != 0: - for i in 0 .. node.tsNodeNamedChild(1).tsNodeNamedChildCount()-1: - if $node.tsNodeNamedChild(1).tsNodeNamedChild(i).tsNodeType() == "comment": - continue - let ts = typeScan(node.tsNodeNamedChild(1).tsNodeNamedChild(i), "field_declaration", "field_identifier", " ") - if ts.len == 0: - return - stmt &= ts & "\n" - - gStateRT.types.add(name) - gStateRT.typeStr &= stmt - -proc pEnumSpecifier(node: TSNode, name = "") = - var - ename: string - elid: uint32 - stmt: string - - if node.tsNodeNamedChildCount() == 1 and $node.tsNodeNamedChild(0).tsNodeType() == "enumerator_list": - # typedef enum { fields } X - ename = name - elid = 0 - stmt = &" {name.getIdentifier()}* = enum #1 pEnumSpecifier()\n" - elif node.tsNodeNamedChildCount() == 2 and $node.tsNodeNamedChild(1).tsNodeType() == "enumerator_list": - if name.len == 0: - ename = getNodeValIf(node.tsNodeNamedChild(0), "type_identifier") - else: - ename = name - elid = 1 - if ename.nBl: - # enum X { fields } - stmt = &" {ename}* = enum #2 pEnumSpecifier()\n" - else: - return - else: - return +import treesitter/runtime - if node.tsNodeNamedChild(elid).tsNodeNamedChildCount() != 0: - for i in 0 .. node.tsNodeNamedChild(elid).tsNodeNamedChildCount()-1: - let field = node.tsNodeNamedChild(elid).tsNodeNamedChild(i) - if $field.tsNodeType() == "comment": - continue - if not field.tsNodeIsNull() and $field.tsNodeType() == "enumerator": - let fname = getNodeValIf(field.tsNodeNamedChild(0), "identifier") - if field.tsNodeNamedChildCount() == 1: - stmt &= &" {fname}\n" - elif field.tsNodeNamedChildCount() == 2 and $field.tsNodeNamedChild(1).tsNodeType() == "number_literal": - let num = getNodeValIf(field.tsNodeNamedChild(1), "number_literal") - stmt &= &" {fname} = {num}\n" - else: - return +import "."/[getters, globals, grammar] - if ename notin gStateRT.types: - gStateRT.types.add(name) - gStateRT.typeStr &= stmt +const gAtoms = @[ + "field_identifier", + "identifier", + "number_literal", + "preproc_arg", + "primitive_type", + "type_identifier" +] -proc pTypeDefinition(node: TSNode) = - if node.tsNodeNamedChildCount() == 2: +proc saveNodeData(node: TSNode): bool = + let name = $node.tsNodeType() + if name in gAtoms: var - name = getNodeValIf(node.tsNodeNamedChild(1), "type_identifier") - ptyp = getNodeValIf(node.tsNodeNamedChild(0), "primitive_type") - ttyp = getNodeValIf(node.tsNodeNamedChild(0), "type_identifier") - ptrname = false + val = node.getNodeVal() - if name.len == 0 and $node.tsNodeNamedChild(1).tsNodeType() == "pointer_declarator" and node.tsNodeNamedChild(1).tsNodeNamedChildCount() == 1: - name = getNodeValIf(node.tsNodeNamedChild(1).tsNodeNamedChild(0), "type_identifier") - ptrname = true + if name == "primitive_type": + val = val.getType() - if name.nBl and name notin gStateRT.types: - if ptyp.nBl: - # typedef int X - gStateRT.types.add(name) - ptyp = ptyp.getType() - if ptyp != "object" and ptrname: - ptyp = &"ptr {ptyp}" - gStateRT.typeStr &= &" {name.getIdentifier()}* = {ptyp} #1 pTypeDefinition()\n" - elif ttyp.nBl: - # typedef X Y - gStateRT.types.add(name) - if ptrname: - ttyp = &"ptr {ttyp}" - gStateRT.typeStr &= &" {name.getIdentifier()}* = {ttyp} #2 pTypeDefinition()\n" - else: - case $node.tsNodeNamedChild(0).tsNodeType(): - of "struct_specifier": - pStructSpecifier(node.tsNodeNamedChild(0), name) - of "enum_specifier": - pEnumSpecifier(node.tsNodeNamedChild(0), name) - else: - discard + if node.tsNodeParent().tsNodeType() == "pointer_declarator": + if gStateRT.data[^1].val != "object": + gStateRT.data[^1].val = "ptr " & gStateRT.data[^1].val -proc pFunctionDeclarator(node: TSNode, typ: string) = - if node.tsNodeNamedChildCount() == 2: - let - name = getNodeValIf(node.tsNodeNamedChild(0), "identifier") - - if name.nBl and name notin gStateRT.procs and $node.tsNodeNamedChild(1).tsNodeType() == "parameter_list": - # typ function(typ param1, ...) - var stmt = &"# pFunctionDeclarator()\nproc {name.getIdentifier()}*(" - - if node.tsNodeNamedChild(1).tsNodeNamedChildCount() != 0: - for i in 0 .. node.tsNodeNamedChild(1).tsNodeNamedChildCount()-1: - let ts = typeScan(node.tsNodeNamedChild(1).tsNodeNamedChild(i), "parameter_declaration", "identifier", "") - if ts.len == 0: - return - stmt &= ts - if i != node.tsNodeNamedChild(1).tsNodeNamedChildCount()-1: - stmt &= ", " - - if typ != "void": - stmt &= &"): {typ.getType()} " - else: - stmt &= ") " + gStateRT.data.add((name, val)) - stmt &= &"{{.importc: \"{name}\", header: {gStateRT.currentHeader}.}}\n" + return true - gStateRT.procs.add(name) - gStateRT.procStr &= stmt +proc searchAstForNode(ast: ref Ast, node: TSNode): bool = + let + childNames = node.getTSNodeNamedChildNames().join() -proc pDeclaration*(node: TSNode) = - if node.tsNodeNamedChildCount() == 2 and $node.tsNodeNamedChild(1).tsNodeType() == "function_declarator": + if ast.children.len != 0: let - ptyp = getNodeValIf(node.tsNodeNamedChild(0), "primitive_type") - ttyp = getNodeValIf(node.tsNodeNamedChild(0), "type_identifier") - - if ptyp.nBl: - pFunctionDeclarator(node.tsNodeNamedChild(1), ptyp.getType()) - elif ttyp.nBl: - pFunctionDeclarator(node.tsNodeNamedChild(1), ttyp) - elif $node.tsNodeNamedChild(0).tsNodeType() == "struct_specifier" and node.tsNodeNamedChild(0).tsNodeNamedChildCount() == 1: - let styp = getNodeValIf(node.tsNodeNamedChild(0).tsNodeNamedChild(0), "type_identifier") - if styp.nBl: - pFunctionDeclarator(node.tsNodeNamedChild(1), styp) + rstr = ast.getRegexForAstChildren() + + if childNames.contains(rstr.toPattern): + if node.getTSNodeNamedChildCountSansComments() != 0: + var flag = true + for i in 0 .. node.tsNodeNamedChildCount()-1: + if node.tsNodeType() != "comment": + let + nodeChild = node.tsNodeNamedChild(i) + astChild = ast.getAstChildByName($nodeChild.tsNodeType()) + if not searchAstForNode(astChild, nodeChild): + flag = false + break + + if flag: + return node.saveNodeData() + else: + return node.saveNodeData() + elif node.getTSNodeNamedChildCountSansComments() == 0: + return node.saveNodeData() -proc genNimAst(root: TSNode) = +proc searchAst(root: TSNode) = var node = root nextnode: TSNode while true: if not node.tsNodeIsNull(): - case $node.tsNodeType(): - of "ERROR": - let (line, col) = getLineCol(node) - let file = gStateRT.sourceFile - echo &"# [toast] Potentially invalid syntax at {file}:{line}:{col}" - of "preproc_def": - pPreprocDef(node) - of "type_definition": - pTypeDefinition(node) - of "declaration": - pDeclaration(node) - of "struct_specifier": - if $node.tsNodeParent().tsNodeType() notin ["type_definition", "declaration"]: - pStructSpecifier(node) - of "enum_specifier": - if $node.tsNodeParent.tsNodeType() notin ["type_definition", "declaration"]: - pEnumSpecifier(node) - else: - # TODO: log - discard + let + name = $node.tsNodeType() + if name in gStateRT.ast: + for ast in gStateRT.ast[name]: + if searchAstForNode(ast, node): + ast.tonim() + break + gStateRT.data = @[] else: return - if node.tsNodeNamedChildCount() != 0: + if $node.tsNodeType() notin gStateRT.ast and node.tsNodeNamedChildCount() != 0: nextnode = node.tsNodeNamedChild(0) else: nextnode = node.tsNodeNextNamedSibling() @@ -279,13 +97,15 @@ proc genNimAst(root: TSNode) = break proc printNim*(fullpath: string, root: TSNode) = + parseGrammar() + echo "{.experimental: \"codeReordering\".}" var fp = fullpath.replace("\\", "/") gStateRT.currentHeader = getCurrentHeader(fullpath) gStateRT.constStr &= &" {gStateRT.currentHeader} = \"{fp}\"\n" - genNimAst(root) + root.searchAst() if gStateRT.constStr.nBl: echo "const\n" & gStateRT.constStr diff --git a/nimterop/astold.nim b/nimterop/astold.nim new file mode 100644 index 0000000..069ecba --- /dev/null +++ b/nimterop/astold.nim @@ -0,0 +1,297 @@ +import macros, os, strformat, strutils + +import treesitter/runtime + +import getters, globals + +# +# Preprocessor +# + +proc pPreprocDef(node: TSNode) = + if node.tsNodeNamedChildCount() == 2: + let + name = getNodeValIf(node.tsNodeNamedChild(0), "identifier") + val = getNodeValIf(node.tsNodeNamedChild(1), "preproc_arg") + + if name.nBl and val.nBl and name notin gStateRT.consts: + gStateRT.consts.add(name) + if val.getLit().nBl: + # #define NAME VALUE + gStateRT.constStr &= &" {name.getIdentifier()}* = {val} # pPreprocDef()\n" + +# +# Types +# + +proc typeScan(node: TSNode, sym, id: string, offset: string): string = + if node.tsNodeIsNull() or $node.tsNodeType() != sym or node.tsNodeNamedChildCount() != 2: + return + + var + name = getNodeValIf(node.tsNodeNamedChild(1), id) + ptyp = getNodeValIf(node.tsNodeNamedChild(0), "primitive_type") + ttyp = getNodeValIf(node.tsNodeNamedChild(0), "type_identifier") + ptrname = false + + if name.len == 0 and $node.tsNodeNamedChild(1).tsNodeType() == "pointer_declarator" and node.tsNodeNamedChild(1).tsNodeNamedChildCount() == 1: + name = getNodeValIf(node.tsNodeNamedChild(1).tsNodeNamedChild(0), id) + ptrname = true + + if name.len == 0: + return + elif ptyp.nBl: + ptyp = ptyp.getType() + if ptyp != "object" and ptrname: + ptyp = &"ptr {ptyp}" + result = &"{offset}{name.getIdentifier()}: {ptyp}" + elif ttyp.nBl: + if ptrname: + ttyp = &"ptr {ttyp}" + result = &"{offset}{name.getIdentifier()}: {ttyp}" + elif $node.tsNodeNamedChild(0).tsNodeType() in ["struct_specifier", "enum_specifier"] and node.tsNodeNamedChild(0).tsNodeNamedChildCount() == 1: + var styp = getNodeValIf(node.tsNodeNamedChild(0).tsNodeNamedChild(0), "type_identifier") + if styp.nBl: + if ptrname: + styp = &"ptr {styp}" + result = &"{offset}{name.getIdentifier()}: {styp}" + +proc pStructSpecifier(node: TSNode, name = "") = + var stmt: string + if node.tsNodeNamedChildCount() == 1 and name notin gStateRT.types: + case $node.tsNodeNamedChild(0).tsNodeType(): + of "type_identifier": + let typ = getNodeValIf(node.tsNodeNamedChild(0), "type_identifier") + if typ.nBl: + gStateRT.types.add(name) + if name != typ: + # typedef struct X Y + gStateRT.typeStr &= &" {name.getIdentifier()}* = {typ} #1 pStructSpecifier()\n" + else: + # typedef struct X X + gStateRT.typeStr &= &" {name.getIdentifier()}* {{.importc: \"{name}\", header: {gStateRT.currentHeader}, bycopy.}} = object #2 pStructSpecifier()\n" + + of "field_declaration_list": + # typedef struct { fields } X + stmt = &" {name.getIdentifier()}* {{.importc: \"{name}\", header: {gStateRT.currentHeader}, bycopy.}} = object #3 pStructSpecifier()\n" + + if node.tsNodeNamedChild(0).tsNodeNamedChildCount() != 0: + for i in 0 .. node.tsNodeNamedChild(0).tsNodeNamedChildCount()-1: + if $node.tsNodeNamedChild(0).tsNodeNamedChild(i).tsNodeType() == "comment": + continue + 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: + if $node.tsNodeNamedChild(1).tsNodeNamedChild(i).tsNodeType() == "comment": + continue + let ts = typeScan(node.tsNodeNamedChild(1).tsNodeNamedChild(i), "field_declaration", "field_identifier", " ") + if ts.len == 0: + return + stmt &= ts & "\n" + + gStateRT.types.add(name) + gStateRT.typeStr &= stmt + +proc pEnumSpecifier(node: TSNode, name = "") = + var + ename: string + elid: uint32 + stmt: string + + if node.tsNodeNamedChildCount() == 1 and $node.tsNodeNamedChild(0).tsNodeType() == "enumerator_list": + # typedef enum { fields } X + ename = name + elid = 0 + stmt = &" {name.getIdentifier()}* = enum #1 pEnumSpecifier()\n" + elif node.tsNodeNamedChildCount() == 2 and $node.tsNodeNamedChild(1).tsNodeType() == "enumerator_list": + if name.len == 0: + ename = getNodeValIf(node.tsNodeNamedChild(0), "type_identifier") + else: + ename = name + elid = 1 + if ename.nBl: + # enum X { fields } + stmt = &" {ename}* = enum #2 pEnumSpecifier()\n" + else: + return + else: + return + + if node.tsNodeNamedChild(elid).tsNodeNamedChildCount() != 0: + for i in 0 .. node.tsNodeNamedChild(elid).tsNodeNamedChildCount()-1: + let field = node.tsNodeNamedChild(elid).tsNodeNamedChild(i) + if $field.tsNodeType() == "comment": + continue + if not field.tsNodeIsNull() and $field.tsNodeType() == "enumerator": + let fname = getNodeValIf(field.tsNodeNamedChild(0), "identifier") + if field.tsNodeNamedChildCount() == 1: + stmt &= &" {fname}\n" + elif field.tsNodeNamedChildCount() == 2 and $field.tsNodeNamedChild(1).tsNodeType() == "number_literal": + let num = getNodeValIf(field.tsNodeNamedChild(1), "number_literal") + stmt &= &" {fname} = {num}\n" + else: + return + + if ename notin gStateRT.types: + gStateRT.types.add(name) + gStateRT.typeStr &= stmt + +proc pTypeDefinition(node: TSNode) = + if node.tsNodeNamedChildCount() == 2: + var + name = getNodeValIf(node.tsNodeNamedChild(1), "type_identifier") + ptyp = getNodeValIf(node.tsNodeNamedChild(0), "primitive_type") + ttyp = getNodeValIf(node.tsNodeNamedChild(0), "type_identifier") + ptrname = false + + if name.len == 0 and $node.tsNodeNamedChild(1).tsNodeType() == "pointer_declarator" and node.tsNodeNamedChild(1).tsNodeNamedChildCount() == 1: + name = getNodeValIf(node.tsNodeNamedChild(1).tsNodeNamedChild(0), "type_identifier") + ptrname = true + + if name.nBl and name notin gStateRT.types: + if ptyp.nBl: + # typedef int X + gStateRT.types.add(name) + ptyp = ptyp.getType() + if ptyp != "object" and ptrname: + ptyp = &"ptr {ptyp}" + gStateRT.typeStr &= &" {name.getIdentifier()}* = {ptyp} #1 pTypeDefinition()\n" + elif ttyp.nBl: + # typedef X Y + gStateRT.types.add(name) + if ptrname: + ttyp = &"ptr {ttyp}" + gStateRT.typeStr &= &" {name.getIdentifier()}* = {ttyp} #2 pTypeDefinition()\n" + else: + case $node.tsNodeNamedChild(0).tsNodeType(): + of "struct_specifier": + pStructSpecifier(node.tsNodeNamedChild(0), name) + of "enum_specifier": + pEnumSpecifier(node.tsNodeNamedChild(0), name) + else: + discard + +proc pFunctionDeclarator(node: TSNode, typ: string) = + if node.tsNodeNamedChildCount() == 2: + let + name = getNodeValIf(node.tsNodeNamedChild(0), "identifier") + + if name.nBl and name notin gStateRT.procs and $node.tsNodeNamedChild(1).tsNodeType() == "parameter_list": + # typ function(typ param1, ...) + var stmt = &"# pFunctionDeclarator()\nproc {name.getIdentifier()}*(" + + if node.tsNodeNamedChild(1).tsNodeNamedChildCount() != 0: + for i in 0 .. node.tsNodeNamedChild(1).tsNodeNamedChildCount()-1: + let ts = typeScan(node.tsNodeNamedChild(1).tsNodeNamedChild(i), "parameter_declaration", "identifier", "") + if ts.len == 0: + return + stmt &= ts + if i != node.tsNodeNamedChild(1).tsNodeNamedChildCount()-1: + stmt &= ", " + + if typ != "void": + stmt &= &"): {typ.getType()} " + else: + stmt &= ") " + + stmt &= &"{{.importc: \"{name}\", header: {gStateRT.currentHeader}.}}\n" + + gStateRT.procs.add(name) + gStateRT.procStr &= stmt + +proc pDeclaration*(node: TSNode) = + if node.tsNodeNamedChildCount() == 2 and $node.tsNodeNamedChild(1).tsNodeType() == "function_declarator": + let + ptyp = getNodeValIf(node.tsNodeNamedChild(0), "primitive_type") + ttyp = getNodeValIf(node.tsNodeNamedChild(0), "type_identifier") + + if ptyp.nBl: + pFunctionDeclarator(node.tsNodeNamedChild(1), ptyp.getType()) + elif ttyp.nBl: + pFunctionDeclarator(node.tsNodeNamedChild(1), ttyp) + elif $node.tsNodeNamedChild(0).tsNodeType() == "struct_specifier" and node.tsNodeNamedChild(0).tsNodeNamedChildCount() == 1: + let styp = getNodeValIf(node.tsNodeNamedChild(0).tsNodeNamedChild(0), "type_identifier") + if styp.nBl: + pFunctionDeclarator(node.tsNodeNamedChild(1), styp) + +proc genNimAst(root: TSNode) = + var + node = root + nextnode: TSNode + + while true: + if not node.tsNodeIsNull(): + case $node.tsNodeType(): + of "ERROR": + let (line, col) = getLineCol(node) + let file = gStateRT.sourceFile + echo &"# [toast] Potentially invalid syntax at {file}:{line}:{col}" + of "preproc_def": + pPreprocDef(node) + of "type_definition": + pTypeDefinition(node) + of "declaration": + pDeclaration(node) + of "struct_specifier": + if $node.tsNodeParent().tsNodeType() notin ["type_definition", "declaration"]: + pStructSpecifier(node) + of "enum_specifier": + if $node.tsNodeParent.tsNodeType() notin ["type_definition", "declaration"]: + pEnumSpecifier(node) + else: + # TODO: log + discard + else: + return + + if node.tsNodeNamedChildCount() != 0: + 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 diff --git a/nimterop/getters.nim b/nimterop/getters.nim index daf01ef..ff8a361 100644 --- a/nimterop/getters.nim +++ b/nimterop/getters.nim @@ -21,11 +21,14 @@ proc getLit*(str: string): string = str.contains(re"^0x[\d]+$"): return str +proc getNodeVal*(node: TSNode): string = + return gStateRT.code[node.tsNodeStartByte() .. node.tsNodeEndByte()-1].strip() + proc getNodeValIf*(node: TSNode, esym: string): string = if esym != $node.tsNodeType(): return - return gStateRT.code[node.tsNodeStartByte() .. node.tsNodeEndByte()-1].strip() + return node.getNodeVal() proc getLineCol*(node: TSNode): tuple[line, col: int] = result.line = 1 @@ -81,3 +84,59 @@ proc getPreprocessor*(fullpath: string, mode = "cpp"): string = .replace(re"\(\(__format__[\s]*\(__[gnu_]*printf__, [\d]+, [\d]+\)\)\);", ";") ) return rdata.join("\n") + +converter toString*(kind: Kind): string = + return case kind: + of exactlyOne: + "" + of oneOrMore: + "+" + of zeroOrMore: + "*" + of zeroOrOne: + "?" + +converter toKind*(kind: string): Kind = + return case kind: + of "+": + oneOrMore + of "*": + zeroOrMore + of "?": + zeroOrOne + else: + exactlyOne + +proc getNameKind*(name: string): tuple[name: string, kind: Kind] = + result.name = name + result.kind = $name[^1] + + if result.kind != exactlyOne: + result.name = name[0 .. ^2] + +proc getTSNodeNamedChildCountSansComments*(node: TSNode): int = + if node.tsNodeNamedChildCount() != 0: + for i in 0 .. node.tsNodeNamedChildCount()-1: + if $node.tsNodeType() != "comment": + result += 1 + +proc getTSNodeNamedChildNames*(node: TSNode): seq[string] = + if node.tsNodeNamedChildCount() != 0: + for i in 0 .. node.tsNodeNamedChildCount()-1: + let + name = $node.tsNodeNamedChild(i).tsNodeType() + + if name != "comment": + result.add(name) + +proc getRegexForAstChildren*(ast: ref Ast): string = + result = "^" + for i in 0 .. ast.children.len-1: + let kind: string = ast.children[i].kind + result &= &"(?:{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] diff --git a/nimterop/globals.nim b/nimterop/globals.nim index f1ba151..39969bc 100644 --- a/nimterop/globals.nim +++ b/nimterop/globals.nim @@ -1,4 +1,18 @@ +import tables + type + Kind* = enum + exactlyOne + oneOrMore # + + zeroOrMore # * + zeroOrOne # ? + + Ast* = object + name*: string + kind*: Kind + children*: seq[ref Ast] + tonim*: proc () {.closure, locks: 0.} + State* = object compile*, defines*, headers*, includeDirs*, searchDirs*: seq[string] @@ -9,6 +23,10 @@ type code*, constStr*, currentHeader*, mode*, procStr*, typeStr*: string sourceFile*: string # eg, C or C++ source or header file + ast*: Table[string, seq[ref Ast]] + data*: seq[tuple[name, val: string]] + grammar*: seq[tuple[grammar: string, call: proc() {.locks: 0.}]] + var gStateCT* {.compiletime.}: State gStateRT*: State @@ -16,7 +34,6 @@ var template nBl*(s: typed): untyped = (s.len != 0) - type CompileMode* = enum c, cpp, diff --git a/nimterop/grammar.nim b/nimterop/grammar.nim new file mode 100644 index 0000000..4e819d9 --- /dev/null +++ b/nimterop/grammar.nim @@ -0,0 +1,244 @@ +import strformat, tables + +import "."/[getters, globals, lisp] + +proc initGrammar() = + # #define X Y + gStateRT.grammar.add((""" + (preproc_def + (identifier) + (preproc_arg) + ) + """, + proc () {.closure, locks: 0.} = + let + name = gStateRT.data[0].val.getIdentifier() + val = gStateRT.data[1].val + + if name notin gStateRT.consts: + gStateRT.consts.add(name) + gStateRT.constStr &= &" {name}* = {val}\n" + )) + + # typedef int X + # typedef X Y + # typedef struct X Y + # typedef ?* Y + gStateRT.grammar.add((""" + (type_definition + (primitive_type|type_identifier?) + (struct_specifier? + (type_identifier) + ) + (type_identifier?) + (pointer_declarator? + (type_identifier) + ) + ) + """, + proc () {.closure, locks: 0.} = + var + name = gStateRT.data[1].val.getIdentifier() + typ = gStateRT.data[0].val + + if name notin gStateRT.types: + gStateRT.types.add(name) + gStateRT.typeStr &= &" {name}* = {typ}\n" + )) + + proc pStructCommon(name: string, fstart, fend: int, prefix="") = + let + nname = name.getIdentifier() + if nname notin gStateRT.types: + gStateRT.types.add(nname) + gStateRT.typeStr &= &" {nname}* {{.importc: \"{prefix}{name}\", header: {gStateRT.currentHeader}, bycopy.}} = object\n" + + for i in fstart .. gStateRT.data.len-fend: + let + ftyp = gStateRT.data[i].val + fname = gStateRT.data[i+1].val.getIdentifier() + gStateRT.typeStr &= &" {fname}*: {ftyp}\n" + + # struct X {} + gStateRT.grammar.add((""" + (struct_specifier + (type_identifier) + (field_declaration_list + (field_declaration+ + (primitive_type|type_identifier?) + (struct_specifier? + (type_identifier) + ) + (field_identifier?) + (pointer_declarator? + (field_identifier) + ) + ) + ) + ) + """, + proc () {.closure, locks: 0.} = + pStructCommon(gStateRT.data[0].val, 1, 2, "struct ") + )) + + # typedef struct X {} + gStateRT.grammar.add((""" + (type_definition + (struct_specifier + (field_declaration_list + (field_declaration+ + (primitive_type|type_identifier?) + (struct_specifier? + (type_identifier) + ) + (field_identifier?) + (pointer_declarator? + (field_identifier) + ) + ) + ) + ) + (type_identifier) + ) + """, + proc () {.closure, locks: 0.} = + pStructCommon(gStateRT.data[^1].val, 0, 3) + )) + + proc pEnumCommon(name: string, fstart, fend: int, prefix="") = + let + nname = name.getIdentifier() + if nname notin gStateRT.types: + gStateRT.types.add(nname) + gStateRT.typeStr &= &" {nname}* = enum\n" + + var + i = fstart + while i < gStateRT.data.len-fend: + let + fname = gStateRT.data[i].val.getIdentifier() + + if i+1 < gStateRT.data.len-fend and gStateRT.data[i+1].name == "number_literal": + gStateRT.typeStr &= &" {fname} = {gStateRT.data[i+1].val}\n" + i += 2 + else: + gStateRT.typeStr &= &" {fname}\n" + i += 1 + + # enum X {} + gStateRT.grammar.add((""" + (enum_specifier + (type_identifier) + (enumerator_list + (enumerator+ + (identifier) + (number_literal?) + ) + ) + ) + """, + proc () {.closure, locks: 0.} = + pEnumCommon(gStateRT.data[0].val, 1, 0) + )) + + # typedef enum {} X + gStateRT.grammar.add((""" + (type_definition + (enum_specifier + (enumerator_list + (enumerator+ + (identifier) + (number_literal?) + ) + ) + ) + (type_identifier) + ) + """, + proc () {.closure, locks: 0.} = + pEnumCommon(gStateRT.data[^1].val, 0, 1) + )) + + # typ function(typ param1, ...) + gStateRT.grammar.add((""" + (declaration + (primitive_type|type_identifier?) + (struct_specifier? + (type_identifier) + ) + (function_declarator? + (identifier) + (parameter_list + (parameter_declaration* + (primitive_type|type_identifier?) + (struct_specifier? + (type_identifier) + ) + (enum_specifier? + (type_identifier) + ) + (identifier) + ) + ) + ) + (pointer_declarator? + (function_declarator? + (identifier) + (parameter_list + (parameter_declaration* + (primitive_type|type_identifier?) + (struct_specifier? + (type_identifier) + ) + (enum_specifier? + (type_identifier) + ) + (identifier) + ) + ) + ) + ) + ) + """, + proc () {.closure, locks: 0.} = + let + ftyp = gStateRT.data[0].val + fname = gStateRT.data[1].val + fnname = fname.getIdentifier() + + if fnname notin gStateRT.procs: + var + pout = "" + i = 2 + if gStateRT.data.len > 2: + while i < gStateRT.data.len-1: + let + ptyp = gStateRT.data[i].val + pname = gStateRT.data[i+1].val.getIdentifier() + pout &= &"{pname}: {ptyp}," + i += 2 + if pout.len != 0 and pout[^1] == ',': + pout = pout[0 .. ^2] + + gStateRT.procStr &= &"proc {fnname}({pout}): {ftyp} {{.importc: \"{fname}\", header: {gStateRT.currentHeader}.}}\n" + + )) + +proc parseGrammar*() = + initGrammar() + + gStateRT.ast = initTable[string, seq[ref Ast]]() + for i in 0 .. gStateRT.grammar.len-1: + var + ast = gStateRT.grammar[i].grammar.parseLisp() + + ast.tonim = gStateRT.grammar[i].call + if ast.name notin gStateRT.ast: + gStateRT.ast[ast.name] = @[ast] + else: + gStateRT.ast[ast.name].add(ast) + +proc printGrammar*() = + for name in gStateRT.ast.keys(): + for ast in gStateRT.ast[name]: + echo ast.printAst() diff --git a/nimterop/lisp.nim b/nimterop/lisp.nim index 8fa73e0..3d3c34e 100644 --- a/nimterop/lisp.nim +++ b/nimterop/lisp.nim @@ -1,22 +1,18 @@ import strutils import strformat -import globals +import "."/[getters, globals] var - gTokens {.compiletime.}: seq[string] - idx {.compiletime.} = 0 + gTokens: seq[string] + idx = 0 -proc tokenize(fullpath: string) = +proc tokenize(tree: string) = var collect = "" gTokens = @[] idx = 0 - # TODO: consider calling API directly - const cmd = &"toast --past --pretty:false --source:{fullpath.quoteShell}" - var (output, exitCode) = gorgeEx cmd - doAssert exitCode == 0, $exitCode - for i in output: + for i in tree: case i: of ' ', '\n', '\r', '(', ')': if collect.nBl: @@ -27,10 +23,6 @@ proc tokenize(fullpath: string) = else: collect &= $i - 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: echo "Bad AST" @@ -42,18 +34,12 @@ proc readFromTokens(): ref Ast = quit(1) if gTokens[idx+1] != "comment": result = new(Ast) - try: - result.sym = parseEnum[Sym](gTokens[idx+1]) - except: - result.sym = IGNORED - result.start = gTokens[idx+2].parseInt() - result.stop = gTokens[idx+3].parseInt() + (result.name, result.kind) = gTokens[idx+1].getNameKind() result.children = @[] - idx += 4 + idx += 2 while gTokens[idx] != ")": var res = readFromTokens() if not res.isNil(): - res.parent = result result.children.add(res) elif gTokens[idx] == ")": echo "Poor AST" @@ -62,7 +48,8 @@ proc readFromTokens(): ref Ast = idx += 1 proc printAst*(node: ref Ast, offset=""): string = - result = offset & "(" & $node.sym & " " & $node.start & " " & $node.stop + result = offset & "(" & node.name & node.kind.toString() + if node.children.len != 0: result &= "\n" for child in node.children: @@ -71,7 +58,7 @@ proc printAst*(node: ref Ast, offset=""): string = else: result &= ")\n" -proc parseLisp*(fullpath: string): ref Ast = - tokenize(fullpath) +proc parseLisp*(tree: string): ref Ast = + tokenize(tree) return readFromTokens() |
