Update based on comments from review. Need to add more docs and reorg to use gstate
This commit is contained in:
parent
9bd23ab716
commit
0d9f1ddc87
7 changed files with 249 additions and 246 deletions
|
|
@ -1,10 +1,12 @@
|
||||||
import macros, os, sequtils, sets, strformat, strutils, tables, times
|
import macros, os, sequtils, sets, strformat, strutils, tables, times
|
||||||
|
|
||||||
|
import options as opts
|
||||||
|
|
||||||
import compiler/[ast, idents, lineinfos, modulegraphs, msgs, options, renderer]
|
import compiler/[ast, idents, lineinfos, modulegraphs, msgs, options, renderer]
|
||||||
|
|
||||||
import "."/treesitter/api
|
import "."/treesitter/api
|
||||||
|
|
||||||
import "."/[globals, getters, exprparser, utils]
|
import "."/[globals, getters, exprparser, comphelp]
|
||||||
|
|
||||||
proc getPtrType*(str: string): string =
|
proc getPtrType*(str: string): string =
|
||||||
result = case str:
|
result = case str:
|
||||||
|
|
@ -17,9 +19,6 @@ proc getPtrType*(str: string): string =
|
||||||
else:
|
else:
|
||||||
str
|
str
|
||||||
|
|
||||||
proc getLit*(nimState: NimState, str: string, expression = false): PNode =
|
|
||||||
result = nimState.parseCExpression(str)
|
|
||||||
|
|
||||||
proc getOverrideOrSkip(gState: State, node: TSNode, origname: string, kind: NimSymKind): PNode =
|
proc getOverrideOrSkip(gState: State, node: TSNode, origname: string, kind: NimSymKind): PNode =
|
||||||
# Check if symbol `origname` of `kind` and `origname` has any cOverride defined
|
# Check if symbol `origname` of `kind` and `origname` has any cOverride defined
|
||||||
# and use that if present
|
# and use that if present
|
||||||
|
|
@ -101,7 +100,7 @@ proc newConstDef(gState: State, node: TSNode, fname = "", fval = ""): PNode =
|
||||||
else:
|
else:
|
||||||
gState.getNodeVal(node[1])
|
gState.getNodeVal(node[1])
|
||||||
valident =
|
valident =
|
||||||
gState.getLit(val)
|
gState.parseCExpression(val)
|
||||||
|
|
||||||
if name.Bl:
|
if name.Bl:
|
||||||
# Name skipped or overridden since blank
|
# Name skipped or overridden since blank
|
||||||
|
|
@ -962,7 +961,7 @@ proc getTypeArray(gState: State, node: TSNode, tident: PNode, name: string): PNo
|
||||||
# type name[X] => array[X, type]
|
# type name[X] => array[X, type]
|
||||||
let
|
let
|
||||||
# Size of array could be a Nim expression
|
# Size of array could be a Nim expression
|
||||||
size = gState.getLit(gState.getNodeVal(cnode[1]), expression = true)
|
size = gState.parseCExpression(gState.getNodeVal(cnode[1]))
|
||||||
if size.kind != nkNone:
|
if size.kind != nkNone:
|
||||||
result = gState.newArrayTree(cnode, result, size)
|
result = gState.newArrayTree(cnode, result, size)
|
||||||
cnode = cnode[0]
|
cnode = cnode[0]
|
||||||
|
|
@ -1367,6 +1366,7 @@ proc addEnum(gState: State, node: TSNode) =
|
||||||
# Create const for fields
|
# Create const for fields
|
||||||
var
|
var
|
||||||
fnames: HashSet[string]
|
fnames: HashSet[string]
|
||||||
|
fvalSections: seq[tuple[fname: string, fval: string, cexpr: Option[TSNode]]]
|
||||||
for i in 0 .. enumlist.len - 1:
|
for i in 0 .. enumlist.len - 1:
|
||||||
let
|
let
|
||||||
en = enumlist[i]
|
en = enumlist[i]
|
||||||
|
|
@ -1385,20 +1385,25 @@ proc addEnum(gState: State, node: TSNode) =
|
||||||
fval = &"({prev} + 1).{name}"
|
fval = &"({prev} + 1).{name}"
|
||||||
|
|
||||||
if en.len > 1 and en[1].getName() in gEnumVals:
|
if en.len > 1 and en[1].getName() in gEnumVals:
|
||||||
# Explicit value
|
fvalSections.add((fname, "", some(en[1])))
|
||||||
fval = "(" & $gState.parseCExpression(gState.getNodeVal(en[1]), name) & ")." & name
|
else:
|
||||||
|
fvalSections.add((fname, fval, none(TSNode)))
|
||||||
# Cannot use newConstDef() since parseString(fval) adds backticks to and/or
|
|
||||||
gState.constSection.add gState.parseString(&"const {fname}* = {fval}")[0][0]
|
|
||||||
|
|
||||||
fnames.incl fname
|
fnames.incl fname
|
||||||
|
|
||||||
prev = fname
|
prev = fname
|
||||||
|
|
||||||
# Add fields to list of consts after processing enum so that we don't cast
|
# Add fields to list of consts after processing enum so that we don't cast
|
||||||
# enum field to itself
|
# enum field to itself
|
||||||
gState.constIdentifiers.incl fnames
|
gState.constIdentifiers.incl fnames
|
||||||
|
|
||||||
|
# parseCExpression requires all const identifiers to be present for the enum
|
||||||
|
for (fname, fval, cexprNode) in fvalSections:
|
||||||
|
var fval = fval
|
||||||
|
if cexprNode.isSome:
|
||||||
|
fval = "(" & $nimState.parseCExpression(nimState.getNodeVal(cexprNode.get()), name) & ")." & name
|
||||||
|
# Cannot use newConstDef() since parseString(fval) adds backticks to and/or
|
||||||
|
nimState.constSection.add nimState.parseString(&"const {fname}* = {fval}")[0][0]
|
||||||
|
|
||||||
# Add other names
|
# Add other names
|
||||||
if node.getName() == "type_definition" and node.len > 1:
|
if node.getName() == "type_definition" and node.len > 1:
|
||||||
gState.addTypeTyped(node, ftname = name, offset = offset)
|
gState.addTypeTyped(node, ftname = name, offset = offset)
|
||||||
|
|
@ -1486,7 +1491,6 @@ proc addProc(gState: State, node, rnode: TSNode) =
|
||||||
# Parameter list
|
# Parameter list
|
||||||
plist = node.anyChildInTree("parameter_list")
|
plist = node.anyChildInTree("parameter_list")
|
||||||
|
|
||||||
var
|
|
||||||
procDef = newNode(nkProcDef)
|
procDef = newNode(nkProcDef)
|
||||||
|
|
||||||
# proc X(a1: Y, a2: Z): P {.pragma.}
|
# proc X(a1: Y, a2: Z): P {.pragma.}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import compiler/[ast, renderer]
|
||||||
|
|
||||||
import "."/treesitter/[api, c, cpp]
|
import "."/treesitter/[api, c, cpp]
|
||||||
|
|
||||||
import "."/[globals, getters, utils]
|
import "."/[globals, getters, comphelp, tshelp]
|
||||||
|
|
||||||
# This version of exprparser should be able to handle:
|
# This version of exprparser should be able to handle:
|
||||||
#
|
#
|
||||||
|
|
@ -41,9 +41,9 @@ proc newExprParser*(state: NimState, code: string, name = ""): ExprParser =
|
||||||
ExprParser(state: state, code: code, name: name)
|
ExprParser(state: state, code: code, name: name)
|
||||||
|
|
||||||
template techo(msg: varargs[string, `$`]) =
|
template techo(msg: varargs[string, `$`]) =
|
||||||
if exprParser.state.gState.debug:
|
block:
|
||||||
let nimState {.inject.} = exprParser.state
|
let nimState {.inject.} = exprParser.state
|
||||||
necho join(msg, "").getCommented
|
decho join(msg, "")
|
||||||
|
|
||||||
template val(node: TSNode): string =
|
template val(node: TSNode): string =
|
||||||
exprParser.code.getNodeVal(node)
|
exprParser.code.getNodeVal(node)
|
||||||
|
|
@ -60,9 +60,11 @@ proc getIdent(exprParser: ExprParser, identName: string, kind = nskConst, parent
|
||||||
if ident != "_":
|
if ident != "_":
|
||||||
# Process the identifier through cPlugin
|
# Process the identifier through cPlugin
|
||||||
ident = exprParser.state.getIdentifier(ident, kind, parent)
|
ident = exprParser.state.getIdentifier(ident, kind, parent)
|
||||||
if exprParser.name.nBl and ident in exprParser.state.constIdentifiers:
|
if kind == nskType:
|
||||||
ident = ident & "." & exprParser.name
|
result = exprParser.state.getIdent(ident)
|
||||||
if ident != "":
|
elif ident.nBl and ident in exprParser.state.constIdentifiers:
|
||||||
|
if exprParser.name.nBl:
|
||||||
|
ident = ident & "." & exprParser.name
|
||||||
result = exprParser.state.getIdent(ident)
|
result = exprParser.state.getIdent(ident)
|
||||||
|
|
||||||
proc getIdent(exprParser: ExprParser, node: TSNode, kind = nskConst, parent = ""): PNode =
|
proc getIdent(exprParser: ExprParser, node: TSNode, kind = nskConst, parent = ""): PNode =
|
||||||
|
|
@ -71,29 +73,6 @@ proc getIdent(exprParser: ExprParser, node: TSNode, kind = nskConst, parent = ""
|
||||||
## Returns PNode(nkNone) if the identifier is blank
|
## Returns PNode(nkNone) if the identifier is blank
|
||||||
exprParser.getIdent(node.val, kind, parent)
|
exprParser.getIdent(node.val, kind, parent)
|
||||||
|
|
||||||
template withCodeAst(exprParser: ExprParser, body: untyped): untyped =
|
|
||||||
## A simple template to inject the TSNode into a body of code
|
|
||||||
var parser = tsParserNew()
|
|
||||||
defer:
|
|
||||||
parser.tsParserDelete()
|
|
||||||
|
|
||||||
doAssert exprParser.code.nBl, "Empty code"
|
|
||||||
if exprParser.mode == "c":
|
|
||||||
doAssert parser.tsParserSetLanguage(treeSitterC()), "Failed to load C parser"
|
|
||||||
elif exprParser.mode == "cpp":
|
|
||||||
doAssert parser.tsParserSetLanguage(treeSitterCpp()), "Failed to load C++ parser"
|
|
||||||
else:
|
|
||||||
doAssert false, &"Invalid parser {exprParser.mode}"
|
|
||||||
|
|
||||||
var
|
|
||||||
tree = parser.tsParserParseString(nil, exprParser.code.cstring, exprParser.code.len.uint32)
|
|
||||||
root {.inject.} = tree.tsTreeRootNode()
|
|
||||||
|
|
||||||
body
|
|
||||||
|
|
||||||
defer:
|
|
||||||
tree.tsTreeDelete()
|
|
||||||
|
|
||||||
proc parseChar(charStr: string): uint8 {.inline.} =
|
proc parseChar(charStr: string): uint8 {.inline.} =
|
||||||
## Parses a character literal out of a string. This is needed
|
## Parses a character literal out of a string. This is needed
|
||||||
## because treesitter gives unescaped characters when parsing
|
## because treesitter gives unescaped characters when parsing
|
||||||
|
|
@ -161,37 +140,36 @@ proc getNumNode(number, suffix: string): PNode {.inline.} =
|
||||||
result = newFloatNode(nkFloat64Lit, parseFloat(number[0 ..< number.len - 1]))
|
result = newFloatNode(nkFloat64Lit, parseFloat(number[0 ..< number.len - 1]))
|
||||||
else:
|
else:
|
||||||
result = newFloatNode(nkFloatLit, parseFloat(number))
|
result = newFloatNode(nkFloatLit, parseFloat(number))
|
||||||
return
|
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise newException(ExprParseError, &"Could not parse float value \"{number}\".")
|
raise newException(ExprParseError, &"Could not parse float value \"{number}\".")
|
||||||
|
|
||||||
case suffix
|
|
||||||
of "u", "U":
|
|
||||||
result = newNode(nkUintLit)
|
|
||||||
of "l", "L":
|
|
||||||
result = newNode(nkInt32Lit)
|
|
||||||
of "ul", "UL":
|
|
||||||
result = newNode(nkUint32Lit)
|
|
||||||
of "ll", "LL":
|
|
||||||
result = newNode(nkInt64Lit)
|
|
||||||
of "ull", "ULL":
|
|
||||||
result = newNode(nkUint64Lit)
|
|
||||||
else:
|
else:
|
||||||
result = newNode(nkIntLit)
|
case suffix
|
||||||
|
of "u", "U":
|
||||||
|
result = newNode(nkUintLit)
|
||||||
|
of "l", "L":
|
||||||
|
result = newNode(nkInt32Lit)
|
||||||
|
of "ul", "UL":
|
||||||
|
result = newNode(nkUint32Lit)
|
||||||
|
of "ll", "LL":
|
||||||
|
result = newNode(nkInt64Lit)
|
||||||
|
of "ull", "ULL":
|
||||||
|
result = newNode(nkUint64Lit)
|
||||||
|
else:
|
||||||
|
result = newNode(nkIntLit)
|
||||||
|
|
||||||
# I realize these regex are wasteful on performance, but
|
# I realize these regex are wasteful on performance, but
|
||||||
# couldn't come up with a better idea.
|
# couldn't come up with a better idea.
|
||||||
if number.contains(re"0[xX]"):
|
if number.contains(re"0[xX]"):
|
||||||
result.intVal = parseHexInt(number)
|
result.intVal = parseHexInt(number)
|
||||||
result.flags = {nfBase16}
|
result.flags = {nfBase16}
|
||||||
elif number.contains(re"0[bB]"):
|
elif number.contains(re"0[bB]"):
|
||||||
result.intVal = parseBinInt(number)
|
result.intVal = parseBinInt(number)
|
||||||
result.flags = {nfBase2}
|
result.flags = {nfBase2}
|
||||||
elif number.contains(re"0[oO]"):
|
elif number.contains(re"0[oO]"):
|
||||||
result.intVal = parseOctInt(number)
|
result.intVal = parseOctInt(number)
|
||||||
result.flags = {nfBase8}
|
result.flags = {nfBase8}
|
||||||
else:
|
else:
|
||||||
result.intVal = parseInt(number)
|
result.intVal = parseInt(number)
|
||||||
|
|
||||||
proc processNumberLiteral(exprParser: ExprParser, node: TSNode): PNode =
|
proc processNumberLiteral(exprParser: ExprParser, node: TSNode): PNode =
|
||||||
## Parse a number literal from a TSNode. Can be a float, hex, long, etc
|
## Parse a number literal from a TSNode. Can be a float, hex, long, etc
|
||||||
|
|
@ -285,168 +263,112 @@ proc processCastExpression(exprParser: ExprParser, node: TSNode, typeofNode: var
|
||||||
exprParser.processTSNode(node[1], typeofNode)
|
exprParser.processTSNode(node[1], typeofNode)
|
||||||
)
|
)
|
||||||
|
|
||||||
proc processLogicalExpression(exprParser: ExprParser, node: TSNode, typeofNode: var PNode): PNode =
|
proc getNimUnarySym(csymbol: string): string =
|
||||||
result = newNode(nkPar)
|
## Get the Nim equivalent of a unary C symbol
|
||||||
let child = node[0]
|
##
|
||||||
var nimSym = ""
|
## TODO: Add ++, --,
|
||||||
|
case csymbol
|
||||||
let binarySym = node.tsNodeChild(0).val.strip()
|
of "+", "-":
|
||||||
techo "LOG SYM: ", binarySym
|
result = csymbol
|
||||||
|
of "~", "!":
|
||||||
case binarySym
|
result = "not"
|
||||||
of "!":
|
|
||||||
nimSym = "not"
|
|
||||||
else:
|
else:
|
||||||
raise newException(ExprParseError, &"Unsupported logical symbol \"{binarySym}\"")
|
raise newException(ExprParseError, &"Unsupported unary symbol \"{csymbol}\"")
|
||||||
|
|
||||||
techo "LOG CHILD: ", child.val, ", nim: ", nimSym
|
proc getNimBinarySym(csymbol: string): string =
|
||||||
result.add nkPrefix.newTree(
|
case csymbol
|
||||||
exprParser.state.getIdent(nimSym),
|
of "|", "||":
|
||||||
exprParser.processTSNode(child, typeofNode)
|
result = "or"
|
||||||
|
of "&", "&&":
|
||||||
|
result = "and"
|
||||||
|
of "^":
|
||||||
|
result = "xor"
|
||||||
|
of "==", "!=",
|
||||||
|
"+", "-", "/", "*",
|
||||||
|
">", "<", ">=", "<=":
|
||||||
|
result = csymbol
|
||||||
|
of "%":
|
||||||
|
result = "mod"
|
||||||
|
else:
|
||||||
|
raise newException(ExprParseError, &"Unsupported binary symbol \"{csymbol}\"")
|
||||||
|
|
||||||
|
proc processBinaryExpression(exprParser: ExprParser, node: TSNode, typeofNode: var PNode): PNode =
|
||||||
|
# Node has left and right children ie: (2 + 7)
|
||||||
|
result = newNode(nkInfix)
|
||||||
|
|
||||||
|
let
|
||||||
|
left = node[0]
|
||||||
|
right = node[1]
|
||||||
|
binarySym = node.tsNodeChild(1).val.strip()
|
||||||
|
nimSym = getNimBinarySym(binarySym)
|
||||||
|
|
||||||
|
result.add exprParser.state.getIdent(nimSym)
|
||||||
|
let leftNode = exprParser.processTSNode(left, typeofNode)
|
||||||
|
|
||||||
|
if typeofNode.isNil:
|
||||||
|
typeofNode = nkCall.newTree(
|
||||||
|
exprParser.state.getIdent("typeof"),
|
||||||
|
leftNode
|
||||||
|
)
|
||||||
|
|
||||||
|
let rightNode = exprParser.processTSNode(right, typeofNode)
|
||||||
|
|
||||||
|
result.add leftNode
|
||||||
|
result.add nkCall.newTree(
|
||||||
|
typeofNode,
|
||||||
|
rightNode
|
||||||
)
|
)
|
||||||
|
|
||||||
proc processMathExpression(exprParser: ExprParser, node: TSNode, typeofNode: var PNode): PNode =
|
proc processUnaryExpression(exprParser: ExprParser, node: TSNode, typeofNode: var PNode): PNode =
|
||||||
|
result = newNode(nkPar)
|
||||||
|
|
||||||
|
let
|
||||||
|
child = node[0]
|
||||||
|
unarySym = node.tsNodeChild(0).val.strip()
|
||||||
|
nimSym = getNimUnarySym(unarySym)
|
||||||
|
|
||||||
|
if nimSym == "-":
|
||||||
|
# Special case. The minus symbol must be in front of an integer,
|
||||||
|
# so we have to make a gentle cast here to coerce it to one.
|
||||||
|
# Might be bad because we are overwriting the type
|
||||||
|
# There's probably a better way of doing this
|
||||||
|
if typeofNode.isNil:
|
||||||
|
typeofNode = exprParser.state.getIdent("int64")
|
||||||
|
|
||||||
|
result.add nkPrefix.newTree(
|
||||||
|
exprParser.state.getIdent(unarySym),
|
||||||
|
nkPar.newTree(
|
||||||
|
nkCall.newTree(
|
||||||
|
exprParser.state.getIdent("int64"),
|
||||||
|
exprParser.processTSNode(child, typeofNode)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
result.add nkPrefix.newTree(
|
||||||
|
exprParser.state.getIdent(nimSym),
|
||||||
|
exprParser.processTSNode(child, typeofNode)
|
||||||
|
)
|
||||||
|
|
||||||
|
proc processUnaryOrBinaryExpression(exprParser: ExprParser, node: TSNode, typeofNode: var PNode): PNode =
|
||||||
if node.len > 1:
|
if node.len > 1:
|
||||||
# Node has left and right children ie: (2 + 7)
|
# Node has left and right children ie: (2 + 7)
|
||||||
var
|
|
||||||
res = newNode(nkInfix)
|
|
||||||
let
|
|
||||||
left = node[0]
|
|
||||||
right = node[1]
|
|
||||||
|
|
||||||
let mathSym = node.tsNodeChild(1).val.strip()
|
|
||||||
techo "MATH SYM: ", mathSym
|
|
||||||
|
|
||||||
res.add exprParser.state.getIdent(mathSym)
|
|
||||||
let leftNode = exprParser.processTSNode(left, typeofNode)
|
|
||||||
|
|
||||||
# If the typeofNode is nil, set it
|
|
||||||
# to be the leftNode because C's type coercion
|
|
||||||
# happens left to right, and we want to emulate it
|
|
||||||
if typeofNode.isNil:
|
|
||||||
typeofNode = nkCall.newTree(
|
|
||||||
exprParser.state.getIdent("typeof"),
|
|
||||||
leftNode
|
|
||||||
)
|
|
||||||
|
|
||||||
let rightNode = exprParser.processTSNode(right, typeofNode)
|
|
||||||
|
|
||||||
res.add leftNode
|
|
||||||
res.add nkCall.newTree(
|
|
||||||
typeofNode,
|
|
||||||
rightNode
|
|
||||||
)
|
|
||||||
|
|
||||||
# Make sure the statement is of the same type as the left
|
# Make sure the statement is of the same type as the left
|
||||||
# hand argument, since some expressions return a differing
|
# hand argument, since some expressions return a differing
|
||||||
# type than the input types (2/3 == float)
|
# type than the input types (2/3 == float)
|
||||||
|
let binExpr = processBinaryExpression(exprParser, node, typeofNode)
|
||||||
|
# Note that this temp var binExpr is needed for some reason, or else we get a segfault
|
||||||
result = nkCall.newTree(
|
result = nkCall.newTree(
|
||||||
typeofNode,
|
typeofNode,
|
||||||
res
|
binexpr
|
||||||
)
|
)
|
||||||
|
|
||||||
elif node.len() == 1:
|
elif node.len() == 1:
|
||||||
# Node has only one child, ie -(20 + 7)
|
# Node has only one child, ie -(20 + 7)
|
||||||
result = newNode(nkPar)
|
result = processUnaryExpression(exprParser, node, typeofNode)
|
||||||
let child = node[0]
|
|
||||||
var nimSym = ""
|
|
||||||
|
|
||||||
let unarySym = node.tsNodeChild(0).val.strip()
|
|
||||||
techo "MATH SYM: ", unarySym
|
|
||||||
|
|
||||||
case unarySym
|
|
||||||
of "+":
|
|
||||||
nimSym = "+"
|
|
||||||
of "-":
|
|
||||||
# Special case. The minus symbol must be in front of an integer,
|
|
||||||
# so we have to make a gental cast here to coerce it to one.
|
|
||||||
# Might be bad because we are overwriting the type
|
|
||||||
# There's probably a better way of doing this
|
|
||||||
if typeofNode.isNil:
|
|
||||||
typeofNode = exprParser.state.getIdent("int64")
|
|
||||||
result.add nkPrefix.newTree(
|
|
||||||
exprParser.state.getIdent(unarySym),
|
|
||||||
nkPar.newTree(
|
|
||||||
nkCall.newTree(
|
|
||||||
exprParser.state.getIdent("int64"),
|
|
||||||
exprParser.processTSNode(child, typeofNode)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
raise newException(ExprParseError, &"Unsupported unary symbol \"{unarySym}\"")
|
|
||||||
|
|
||||||
result.add nkPrefix.newTree(
|
|
||||||
exprParser.state.getIdent(nimSym),
|
|
||||||
exprParser.processTSNode(child, typeofNode)
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
raise newException(ExprParseError, &"Invalid bitwise_expression \"{node.val}\"")
|
raise newException(ExprParseError, &"Invalid {node.getName()} \"{node.val}\"")
|
||||||
|
|
||||||
proc processBitwiseExpression(exprParser: ExprParser, node: TSNode, typeofNode: var PNode): PNode =
|
|
||||||
if node.len() > 1:
|
|
||||||
result = newNode(nkInfix)
|
|
||||||
|
|
||||||
let
|
|
||||||
left = node[0]
|
|
||||||
right = node[1]
|
|
||||||
|
|
||||||
var nimSym = ""
|
|
||||||
|
|
||||||
let binarySym = node.tsNodeChild(1).val.strip()
|
|
||||||
techo "BIN SYM: ", binarySym
|
|
||||||
|
|
||||||
case binarySym
|
|
||||||
of "|", "||":
|
|
||||||
nimSym = "or"
|
|
||||||
of "&", "&&":
|
|
||||||
nimSym = "and"
|
|
||||||
of "^":
|
|
||||||
nimSym = "xor"
|
|
||||||
of "==", "!=":
|
|
||||||
nimSym = binarySym
|
|
||||||
else:
|
|
||||||
raise newException(ExprParseError, &"Unsupported binary symbol \"{binarySym}\"")
|
|
||||||
|
|
||||||
result.add exprParser.state.getIdent(nimSym)
|
|
||||||
let leftNode = exprParser.processTSNode(left, typeofNode)
|
|
||||||
|
|
||||||
if typeofNode.isNil:
|
|
||||||
typeofNode = nkCall.newTree(
|
|
||||||
exprParser.state.getIdent("typeof"),
|
|
||||||
leftNode
|
|
||||||
)
|
|
||||||
|
|
||||||
let rightNode = exprParser.processTSNode(right, typeofNode)
|
|
||||||
|
|
||||||
result.add leftNode
|
|
||||||
result.add nkCall.newTree(
|
|
||||||
typeofNode,
|
|
||||||
rightNode
|
|
||||||
)
|
|
||||||
|
|
||||||
elif node.len() == 1:
|
|
||||||
result = newNode(nkPar)
|
|
||||||
let child = node[0]
|
|
||||||
var nimSym = ""
|
|
||||||
|
|
||||||
let unarySym = node.tsNodeChild(0).val.strip()
|
|
||||||
techo "BIN SYM: ", unarySym
|
|
||||||
|
|
||||||
# TODO: Support more symbols here. ++, --, &
|
|
||||||
case unarySym
|
|
||||||
of "~":
|
|
||||||
nimSym = "not"
|
|
||||||
else:
|
|
||||||
raise newException(ExprParseError, &"Unsupported unary symbol \"{unarySym}\"")
|
|
||||||
|
|
||||||
result.add nkPrefix.newTree(
|
|
||||||
exprParser.state.getIdent(nimSym),
|
|
||||||
exprParser.processTSNode(child, typeofNode)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
raise newException(ExprParseError, &"Invalid bitwise_expression \"{node.val}\"")
|
|
||||||
|
|
||||||
proc processSizeofExpression(exprParser: ExprParser, node: TSNode, typeofNode: var PNode): PNode =
|
proc processSizeofExpression(exprParser: ExprParser, node: TSNode, typeofNode: var PNode): PNode =
|
||||||
result = nkCall.newTree(
|
result = nkCall.newTree(
|
||||||
|
|
@ -464,45 +386,85 @@ proc processTSNode(exprParser: ExprParser, node: TSNode, typeofNode: var PNode):
|
||||||
|
|
||||||
case nodeName
|
case nodeName
|
||||||
of "number_literal":
|
of "number_literal":
|
||||||
|
# Input -> 0x1234FE, 1231, 123u, 123ul, 123ull, 1.334f
|
||||||
|
# Output -> 0x1234FE, 1231, 123'u, 123'u32, 123'u64, 1.334
|
||||||
result = exprParser.processNumberLiteral(node)
|
result = exprParser.processNumberLiteral(node)
|
||||||
of "string_literal":
|
of "string_literal":
|
||||||
|
# Input -> "foo\0\x42"
|
||||||
|
# Output -> "foo\0"
|
||||||
result = exprParser.processStringLiteral(node)
|
result = exprParser.processStringLiteral(node)
|
||||||
of "char_literal":
|
of "char_literal":
|
||||||
|
# Input -> 'F', '\034' // Octal, '\x5A' // Hex, '\r' // escape sequences
|
||||||
|
# Output ->
|
||||||
result = exprParser.processCharacterLiteral(node)
|
result = exprParser.processCharacterLiteral(node)
|
||||||
of "expression_statement", "ERROR", "translation_unit":
|
of "expression_statement", "ERROR", "translation_unit":
|
||||||
# This may be wrong. What can be in an expression?
|
# Note that we're parsing partial expressions, so the TSNode might contain
|
||||||
if node.len > 0:
|
# an ERROR node. If that's the case, they usually contain children with
|
||||||
|
# partial results, which will contain parsed expressions
|
||||||
|
#
|
||||||
|
# Input (top level statement) -> ((1 + 3 - IDENT) - (int)400.0)
|
||||||
|
# Output -> (1 + typeof(1)(3) - typeof(1)(IDENT) - typeof(1)(cast[int](400.0))) # Type casting in case some args differ
|
||||||
|
if node.len == 1:
|
||||||
result = exprParser.processTSNode(node[0], typeofNode)
|
result = exprParser.processTSNode(node[0], typeofNode)
|
||||||
|
elif node.len > 1:
|
||||||
|
result = newNode(nkStmtListExpr)
|
||||||
|
for i in 0 ..< node.len:
|
||||||
|
result.add exprParser.processTSNode(node[i], typeofNode)
|
||||||
else:
|
else:
|
||||||
raise newException(ExprParseError, &"Node type \"{nodeName}\" has no children")
|
raise newException(ExprParseError, &"Node type \"{nodeName}\" has no children")
|
||||||
of "parenthesized_expression":
|
of "parenthesized_expression":
|
||||||
|
# Input -> (IDENT - OTHERIDENT)
|
||||||
|
# Output -> (IDENT - typeof(IDENT)(OTHERIDENT)) # Type casting in case OTHERIDENT is a slightly different type (uint vs int)
|
||||||
result = exprParser.processParenthesizedExpr(node, typeofNode)
|
result = exprParser.processParenthesizedExpr(node, typeofNode)
|
||||||
of "sizeof_expression":
|
of "sizeof_expression":
|
||||||
|
# Input -> sizeof(char)
|
||||||
|
# Output -> sizeof(cchar)
|
||||||
result = exprParser.processSizeofExpression(node, typeofNode)
|
result = exprParser.processSizeofExpression(node, typeofNode)
|
||||||
# binary_expression from the new treesitter upgrade should work here
|
# binary_expression from the new treesitter upgrade should work here
|
||||||
# once we upgrade
|
# once we upgrade
|
||||||
of "bitwise_expression", "equality_expression", "binary_expression":
|
of "math_expression", "logical_expression", "relational_expression",
|
||||||
result = exprParser.processBitwiseExpression(node, typeofNode)
|
"bitwise_expression", "equality_expression", "binary_expression":
|
||||||
of "math_expression":
|
# Input -> a == b, a != b, !a, ~a, a < b, a > b, a <= b, a >= b
|
||||||
result = exprParser.processMathExpression(node, typeofNode)
|
# Output ->
|
||||||
|
# typeof(a)(a == typeof(a)(b))
|
||||||
|
# typeof(a)(a != typeof(a)(b))
|
||||||
|
# (not a)
|
||||||
|
# (not a)
|
||||||
|
# typeof(a)(a < typeof(a)(b))
|
||||||
|
# typeof(a)(a > typeof(a)(b))
|
||||||
|
# typeof(a)(a <= typeof(a)(b))
|
||||||
|
# typeof(a)(a >= typeof(a)(b))
|
||||||
|
result = exprParser.processUnaryOrBinaryExpression(node, typeofNode)
|
||||||
of "shift_expression":
|
of "shift_expression":
|
||||||
|
# Input -> a >> b, a << b
|
||||||
|
# Output -> a shr typeof(a)(b), a shl typeof(a)(b)
|
||||||
result = exprParser.processShiftExpression(node, typeofNode)
|
result = exprParser.processShiftExpression(node, typeofNode)
|
||||||
of "cast_expression":
|
of "cast_expression":
|
||||||
|
# Input -> (int) a
|
||||||
|
# Output -> cast[cint](a)
|
||||||
result = exprParser.processCastExpression(node, typeofNode)
|
result = exprParser.processCastExpression(node, typeofNode)
|
||||||
of "logical_expression":
|
|
||||||
result = exprParser.processLogicalExpression(node, typeofNode)
|
|
||||||
# Why are these node types named true/false?
|
# Why are these node types named true/false?
|
||||||
of "true", "false":
|
of "true", "false":
|
||||||
|
# Input -> true, false
|
||||||
|
# Output -> true, false
|
||||||
result = exprParser.state.parseString(node.val)
|
result = exprParser.state.parseString(node.val)
|
||||||
of "type_descriptor", "sized_type_specifier":
|
of "type_descriptor", "sized_type_specifier":
|
||||||
|
# Input -> int, unsigned int, long int, etc
|
||||||
|
# Output -> cint, cuint, clong, etc
|
||||||
let ty = getType(node.val)
|
let ty = getType(node.val)
|
||||||
result = exprParser.getIdent(ty, nskType, parent=node.getName())
|
if ty.len > 0:
|
||||||
if result.kind == nkNone:
|
# If ty is not empty, one of C's builtin types has been found
|
||||||
result = exprParser.state.getIdent(ty)
|
result = exprParser.getIdent(ty, nskType, parent=node.getName())
|
||||||
|
else:
|
||||||
|
result = exprParser.getIdent(node.val, nskType, parent=node.getName())
|
||||||
|
if result.kind == nkNone:
|
||||||
|
raise newException(ExprParseError, &"Missing type specifier \"{node.val}\"")
|
||||||
of "identifier":
|
of "identifier":
|
||||||
|
# Input -> IDENT
|
||||||
|
# Output -> IDENT (if found in sym table, else error)
|
||||||
result = exprParser.getIdent(node, parent=node.getName())
|
result = exprParser.getIdent(node, parent=node.getName())
|
||||||
if result.kind == nkNone:
|
if result.kind == nkNone:
|
||||||
raise newException(ExprParseError, &"Could not get identifier \"{node.val}\"")
|
raise newException(ExprParseError, &"Missing identifier \"{node.val}\"")
|
||||||
else:
|
else:
|
||||||
raise newException(ExprParseError, &"Unsupported node type \"{nodeName}\" for node \"{node.val}\"")
|
raise newException(ExprParseError, &"Unsupported node type \"{nodeName}\" for node \"{node.val}\"")
|
||||||
|
|
||||||
|
|
@ -512,15 +474,15 @@ proc parseCExpression*(state: NimState, code: string, name = ""): PNode =
|
||||||
## Convert the C string to a nim PNode tree
|
## Convert the C string to a nim PNode tree
|
||||||
result = newNode(nkNone)
|
result = newNode(nkNone)
|
||||||
# This is used for keeping track of the type of the first
|
# This is used for keeping track of the type of the first
|
||||||
# symbol
|
# symbol used for type casting
|
||||||
var tnode: PNode = nil
|
var tnode: PNode = nil
|
||||||
let exprParser = newExprParser(state, code, name)
|
let exprParser = newExprParser(state, code, name)
|
||||||
try:
|
try:
|
||||||
withCodeAst(exprParser):
|
withCodeAst(exprParser.code, exprParser.mode):
|
||||||
result = exprParser.processTSNode(root, tnode)
|
result = exprParser.processTSNode(root, tnode)
|
||||||
except ExprParseError as e:
|
except ExprParseError as e:
|
||||||
techo e.msg
|
techo e.msg
|
||||||
result = newNode(nkNone)
|
result = newNode(nkNone)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
techo "UNEXPECTED EXCEPTION: ", e.msg
|
techo "UNEXPECTED EXCEPTION: ", e.msg
|
||||||
result = newNode(nkNone)
|
result = newNode(nkNone)
|
||||||
|
|
@ -401,7 +401,7 @@ proc printLisp*(code: var string, root: TSNode): string =
|
||||||
|
|
||||||
if node.len() != 0:
|
if node.len() != 0:
|
||||||
result &= "\n"
|
result &= "\n"
|
||||||
nextnode = node.tsNodeNamedChild(0)
|
nextnode = node[0]
|
||||||
depth += 1
|
depth += 1
|
||||||
else:
|
else:
|
||||||
result &= ")\n"
|
result &= ")\n"
|
||||||
|
|
@ -432,7 +432,7 @@ proc getCommented*(str: string): string =
|
||||||
"\n# " & str.strip().replace("\n", "\n# ")
|
"\n# " & str.strip().replace("\n", "\n# ")
|
||||||
|
|
||||||
proc printTree*(gState: State, pnode: PNode, offset = ""): string =
|
proc printTree*(gState: State, pnode: PNode, offset = ""): string =
|
||||||
if gState.debug and pnode.kind != nkNone:
|
if not pnode.isNil and gState.debug and pnode.kind != nkNone:
|
||||||
result &= "\n# " & offset & $pnode.kind & "("
|
result &= "\n# " & offset & $pnode.kind & "("
|
||||||
case pnode.kind
|
case pnode.kind
|
||||||
of nkCharLit:
|
of nkCharLit:
|
||||||
|
|
@ -459,17 +459,17 @@ proc printTree*(gState: State, pnode: PNode, offset = ""): string =
|
||||||
if offset.len == 0:
|
if offset.len == 0:
|
||||||
result &= "\n"
|
result &= "\n"
|
||||||
|
|
||||||
proc printDebug*(nimState: NimState, node: TSNode) =
|
proc printDebug*(gState: State, node: TSNode) =
|
||||||
# This causes random segfaults for some reason on macOS Catalina
|
# This causes random segfaults for some reason on macOS Catalina
|
||||||
if nimState.gState.debug:
|
if gState.debug:
|
||||||
necho ("Input => " & nimState.getNodeVal(node)).getCommented()
|
gecho ("Input => " & gState.getNodeVal(node)).getCommented()
|
||||||
necho nimState.gState.printLisp(node).getCommented()
|
gecho gState.printLisp(node).getCommented()
|
||||||
|
|
||||||
proc printDebug*(nimState: NimState, pnode: PNode) =
|
proc printDebug*(gState: State, pnode: PNode) =
|
||||||
# This causes random segfaults for some reason on macOS Catalina
|
# This causes random segfaults for some reason on macOS Catalina
|
||||||
if nimState.gState.debug and pnode.kind != nkNone:
|
if gState.debug and pnode.kind != nkNone:
|
||||||
necho ("Output => " & $pnode).getCommented()
|
gecho ("Output => " & $pnode).getCommented()
|
||||||
necho nimState.printTree(pnode).getCommented()
|
gecho gState.printTree(pnode).getCommented()
|
||||||
|
|
||||||
# Compiler shortcuts
|
# Compiler shortcuts
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,16 +2,11 @@ import os, osproc, strformat, strutils, tables, times
|
||||||
|
|
||||||
import "."/treesitter/[api, c, cpp]
|
import "."/treesitter/[api, c, cpp]
|
||||||
|
|
||||||
import "."/[ast, ast2, globals, getters, grammar, build]
|
import "."/[ast, ast2, globals, getters, grammar, build, tshelp]
|
||||||
|
|
||||||
proc process(gState: State, path: string, astTable: AstTable) =
|
proc process(gState: State, path: string, astTable: AstTable) =
|
||||||
doAssert existsFile(path), &"Invalid path {path}"
|
doAssert existsFile(path), &"Invalid path {path}"
|
||||||
|
|
||||||
var parser = tsParserNew()
|
|
||||||
|
|
||||||
defer:
|
|
||||||
parser.tsParserDelete()
|
|
||||||
|
|
||||||
if gState.mode.Bl:
|
if gState.mode.Bl:
|
||||||
gState.mode = getCompilerMode(path)
|
gState.mode = getCompilerMode(path)
|
||||||
|
|
||||||
|
|
@ -20,6 +15,7 @@ proc process(gState: State, path: string, astTable: AstTable) =
|
||||||
else:
|
else:
|
||||||
gState.code = readFile(path)
|
gState.code = readFile(path)
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
doAssert gState.code.nBl, "Empty file or preprocessor error"
|
doAssert gState.code.nBl, "Empty file or preprocessor error"
|
||||||
|
|
||||||
if gState.mode == "c":
|
if gState.mode == "c":
|
||||||
|
|
@ -45,6 +41,18 @@ proc process(gState: State, path: string, astTable: AstTable) =
|
||||||
ast.parseNim(gState, path, root, astTable)
|
ast.parseNim(gState, path, root, astTable)
|
||||||
elif gState.preprocess:
|
elif gState.preprocess:
|
||||||
gecho gState.code
|
gecho gState.code
|
||||||
|
=======
|
||||||
|
withCodeAst(gState.code, gState.mode):
|
||||||
|
if gState.past:
|
||||||
|
gecho gState.printLisp(root)
|
||||||
|
elif gState.pnim:
|
||||||
|
if Feature.ast2 in gState.feature:
|
||||||
|
ast2.printNim(gState, path, root)
|
||||||
|
else:
|
||||||
|
ast.printNim(gState, path, root, astTable)
|
||||||
|
elif gState.preprocess:
|
||||||
|
gecho gState.code
|
||||||
|
>>>>>>> Update based on comments from review. Need to add more docs and reorg to use gstate
|
||||||
|
|
||||||
# CLI processing with default values
|
# CLI processing with default values
|
||||||
proc main(
|
proc main(
|
||||||
|
|
|
||||||
30
nimterop/tshelp.nim
Normal file
30
nimterop/tshelp.nim
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
template withCodeAst*(inputCode: string, inputMode: string, body: untyped): untyped =
|
||||||
|
## A simple template to inject the TSNode into a body of code
|
||||||
|
|
||||||
|
# This section is needed to be able to reference
|
||||||
|
# mode in strformat calls
|
||||||
|
let
|
||||||
|
code = inputCode
|
||||||
|
mode {.inject.} = inputMode
|
||||||
|
|
||||||
|
var parser = tsParserNew()
|
||||||
|
defer:
|
||||||
|
parser.tsParserDelete()
|
||||||
|
|
||||||
|
doAssert code.nBl, "Empty code or preprocessor error"
|
||||||
|
|
||||||
|
if mode == "c":
|
||||||
|
doAssert parser.tsParserSetLanguage(treeSitterC()), "Failed to load C parser"
|
||||||
|
elif mode == "cpp":
|
||||||
|
doAssert parser.tsParserSetLanguage(treeSitterCpp()), "Failed to load C++ parser"
|
||||||
|
else:
|
||||||
|
doAssert false, &"Invalid parser {mode}"
|
||||||
|
|
||||||
|
var
|
||||||
|
tree = parser.tsParserParseString(nil, code.cstring, code.len.uint32)
|
||||||
|
root {.inject.} = tree.tsTreeRootNode()
|
||||||
|
|
||||||
|
body
|
||||||
|
|
||||||
|
defer:
|
||||||
|
tree.tsTreeDelete()
|
||||||
|
|
@ -12,7 +12,6 @@ static:
|
||||||
const
|
const
|
||||||
path = currentSourcePath.parentDir() / "include" / "tast2.h"
|
path = currentSourcePath.parentDir() / "include" / "tast2.h"
|
||||||
|
|
||||||
|
|
||||||
when defined(HEADER):
|
when defined(HEADER):
|
||||||
cDefine("HEADER")
|
cDefine("HEADER")
|
||||||
const
|
const
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue