nimterop/nimterop/toast.nim
2019-05-10 18:51:46 -05:00

180 lines
4.5 KiB
Nim

import os, strformat, strutils
import "."/treesitter/[api, c, cpp]
import "."/[ast, globals, getters, grammar]
proc printLisp(gState: State, root: TSNode) =
var
node = root
nextnode: TSNode
depth = 0
while true:
if not node.tsNodeIsNull() and depth > -1:
if gState.pretty:
stdout.write spaces(depth)
let
(line, col) = gState.getLineCol(node)
stdout.write &"({$node.tsNodeType()} {line} {col} {node.tsNodeEndByte() - node.tsNodeStartByte()}"
else:
break
if node.tsNodeNamedChildCount() != 0:
if gState.pretty:
echo ""
nextnode = node.tsNodeNamedChild(0)
depth += 1
else:
if gState.pretty:
echo ")"
else:
stdout.write ")"
nextnode = node.tsNodeNextNamedSibling()
if nextnode.tsNodeIsNull():
while true:
node = node.tsNodeParent()
depth -= 1
if depth == -1:
break
if gState.pretty:
echo spaces(depth) & ")"
else:
stdout.write ")"
if node == root:
break
if not node.tsNodeNextNamedSibling().tsNodeIsNull():
node = node.tsNodeNextNamedSibling()
break
else:
node = nextnode
if node == root:
break
proc process(gState: State, path: string, astTable: AstTable) =
doAssert existsFile(path), "Invalid path " & path
var
parser = tsParserNew()
ext = path.splitFile().ext
defer:
parser.tsParserDelete()
if gState.mode.len == 0:
if ext in [".h", ".c"]:
gState.mode = "c"
elif ext in [".hxx", ".hpp", ".hh", ".H", ".h++", ".cpp", ".cxx", ".cc", ".C", ".c++"]:
gState.mode = "cpp"
if gState.preprocess:
gState.code = gState.getPreprocessor(path)
else:
gState.code = readFile(path)
doAssert gState.code.len != 0, "Empty file or preprocessor error"
if gState.mode == "c":
doAssert parser.tsParserSetLanguage(treeSitterC()), "Failed to load C parser"
elif gState.mode == "cpp":
doAssert parser.tsParserSetLanguage(treeSitterCpp()), "Failed to load C++ parser"
else:
doAssert false, "Invalid parser " & gState.mode
var
tree = parser.tsParserParseString(nil, gState.code.cstring, gState.code.len.uint32)
root = tree.tsTreeRootNode()
defer:
tree.tsTreeDelete()
if gState.past:
gState.printLisp(root)
elif gState.pnim:
gState.printNim(path, root, astTable)
elif gState.preprocess:
echo gState.code
proc main(
preprocess = false,
past = false,
pnim = false,
recurse = false,
nocomments = false,
defines: seq[string] = @[],
includeDirs: seq[string] = @[],
dynlib: string = "",
symOverride: seq[string] = @[],
nim: string = "nim",
pluginSourcePath: string = "",
debug = false,
mode = modeDefault,
pgrammar = false,
source: seq[string]
) =
var gState = State(
preprocess: preprocess,
past: past,
pnim: pnim,
recurse: recurse,
nocomments: nocomments,
defines: defines,
includeDirs: includeDirs,
dynlib: dynlib,
symOverride: symOverride,
nim: nim,
pluginSourcePath: pluginSourcePath,
debug: debug,
mode: mode,
pretty: true
)
gState.symOverride = gState.symOverride.getSplitComma()
if pluginSourcePath.nBl:
gState.loadPlugin(pluginSourcePath)
let
astTable = parseGrammar()
if pgrammar:
astTable.printGrammar()
elif source.len != 0:
if gState.pnim:
printNimHeader()
for src in source:
gState.process(src, astTable)
when isMainModule:
import cligen
dispatch(main, help = {
"preprocess": "run preprocessor on header",
"past": "print AST output",
"pnim": "print Nim output",
"recurse": "process #include files",
"nocomments": "exclude top-level comments from output",
"defines": "definitions to pass to preprocessor",
"includeDirs": "include directory to pass to preprocessor",
"dynlib": "Import symbols from library in specified Nim string",
"symOverride": "skip generating specified symbols",
"nim": "use a particular Nim executable (default: $PATH/nim)",
"pluginSourcePath": "Nim file to build and load as a plugin",
"debug": "enable debug output",
"mode": "language parser: c or cpp",
"pgrammar": "print grammar",
"source" : "C/C++ source/header"
}, short = {
"preprocess": 'p',
"past": 'a',
"pnim": 'n',
"recurse": 'r',
"nocomments": 'c',
"defines": 'D',
"includeDirs": 'I',
"dynlib": 'l',
"symOverride": 'O',
"debug": 'd',
"pgrammar": 'g'
})