Add preliminary expression parsing

This commit is contained in:
Joey Yakimowich-Payne 2020-04-16 20:49:06 -06:00
commit 879c7e3c78
3 changed files with 253 additions and 71 deletions

View file

@ -1,37 +1,10 @@
import macros, os, sequtils, sets, strformat, strutils, tables, times
import regex
import compiler/[ast, idents, lineinfos, modulegraphs, msgs, options, parser, renderer]
import "."/treesitter/[api, c, cpp]
import "."/treesitter/api
import "."/[globals, getters]
proc getCCodeAst*(gState: State, code: string): string =
var parser = tsParserNew()
var code = code
defer:
parser.tsParserDelete()
doAssert code.nBl, "Empty code"
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, code.cstring, code.len.uint32)
root = tree.tsTreeRootNode()
defer:
tree.tsTreeDelete()
return code.printLisp(root)
import "."/[globals, getters, exprparser]
proc getPtrType*(str: string): string =
result = case str:
@ -59,42 +32,8 @@ proc parseString(gState: State, str: string): PNode =
except:
decho getCurrentExceptionMsg()
proc getLit*(gState: State, str: string, expression = false): PNode =
# Used to convert #define literals into const and expressions
# in array sizes
#
# `expression` is true when `str` should be converted into a Nim expression
let
str = str.replace(re"/[/*].*?(?:\*/)?$", "").strip()
if str.contains(re"^[\-]?[\d]+$"): # decimal
result = newIntNode(nkIntLit, parseInt(str))
elif str.contains(re"^[\-]?[\d]*[.]?[\d]+$"): # float
result = newFloatNode(nkFloatLit, parseFloat(str))
elif str.contains(re"^0x[\da-fA-F]+$"): # hexadecimal
result = gState.parseString(str)
elif str.contains(re"^'[[:ascii:]]'$"): # char
result = newNode(nkCharLit)
result.intVal = str[1].int64
elif str.contains(re"""^"[[:ascii:]]+"$"""): # char *
result = newStrNode(nkStrLit, str[1 .. ^2])
else:
decho "Macro AST:"
decho str
decho nimState.gState.getCCodeAst(str)
let
str =
if expression: gState.getNimExpression(str)
else: str
result = gState.parseString(str)
if result.isNil:
result = newNode(nkNilLit)
proc getLit*(nimState: NimState, str: string, expression = false): PNode =
result = nimState.codeToNode(str)
proc getOverrideOrSkip(gState: State, node: TSNode, origname: string, kind: NimSymKind): PNode =
# Check if symbol `origname` of `kind` and `origname` has any cOverride defined
@ -181,11 +120,9 @@ proc newConstDef(gState: State, node: TSNode, fname = "", fval = ""): PNode =
if name.Bl:
# Name skipped or overridden since blank
result = gState.getOverrideOrSkip(node, origname, nskConst)
elif valident.kind in {nkCharLit .. nkStrLit} or
(valident.kind == nkStmtList and valident.len > 0 and
valident[0].kind in {nkCharLit .. nkStrLit}):
if gState.addNewIdentifer(name):
result = nimState.getOverrideOrSkip(node, origname, nskConst)
elif valident.kind != nkNilLit:
if nimState.addNewIdentifer(name):
# const X* = Y
#
# nkConstDef(

245
nimterop/exprparser.nim Normal file
View file

@ -0,0 +1,245 @@
import strformat, strutils, macros
import regex
import compiler/[ast, renderer]
import "."/treesitter/[api, c, cpp]
import "."/[globals, getters]
type
ExprParser* = ref object
state*: NimState
code*: string
proc newExprParser*(state: NimState, code: string): ExprParser =
ExprParser(state: state, code: code)
template decho(msg: varargs[string, `$`]) =
if exprParser.state.gState.debug:
let nimState {.inject.} = exprParser.state
necho "# " & join(msg, "")
template val*(node: TSNode): string =
exprParser.code.getNodeVal(node)
proc mode*(exprParser: ExprParser): string =
exprParser.state.gState.mode
template withCodeAst(exprParser: ExprParser, body: untyped): untyped =
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 getNumNode(number, suffix: string): PNode {.inline.} =
result = newNode(nkNilLit)
if number.contains("."):
let floatSuffix = number[result.len-1]
case floatSuffix
of 'l', 'L':
# TODO: handle long double (128 bits)
# result = newNode(nkFloat128Lit)
result = newFloatNode(nkFloat64Lit, parseFloat(number[0 ..< number.len - 1]))
of 'f', 'F':
result = newFloatNode(nkFloat64Lit, parseFloat(number[0 ..< number.len - 1]))
else:
discard
return
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)
if number.contains(re"0[xX]"):
result.intVal = parseHexInt(number)
result.flags = {nfBase16}
elif number.contains(re"0[bB]"):
result.intVal = parseBinInt(number)
result.flags = {nfBase2}
elif number.contains(re"0[oO]"):
result.intVal = parseOctInt(number)
result.flags = {nfBase8}
else:
result.intVal = parseInt(number)
proc processNumberLiteral*(exprParser: ExprParser, node: TSNode): PNode =
result = newNode(nkNilLit)
let nodeVal = node.val
var match: RegexMatch
const reg = re"(\-)?(0\d+|0[xX][0-9a-fA-F]+|0[bB][01]+|\d+\.?\d*[fFlL]?|\d*\.?\d+[fFlL]?|\d+)([ulUL]*)"
let found = nodeVal.find(reg, match)
if found:
let
prefix = if match.group(0).len > 0: nodeVal[match.group(0)[0]] else: ""
number = nodeVal[match.group(1)[0]]
suffix = nodeVal[match.group(2)[0]]
result = getNumNode(number, suffix)
if result.kind != nkNilLit and prefix == "-":
result = nkPrefix.newTree(
exprParser.state.getIdent("-"),
result
)
proc processCharacterLiteral*(exprParser: ExprParser, node: TSNode): PNode =
result = newNode(nkCharLit)
result.intVal = node.val[1].int64
proc processStringLiteral*(exprParser: ExprParser, node: TSNode): PNode =
let nodeVal = node.val
result = newStrNode(nkStrLit, nodeVal[1 ..< nodeVal.len - 1])
proc processTSNode*(exprParser: ExprParser, node: TSNode): PNode
proc processShiftExpression*(exprParser: ExprParser, node: TSNode): PNode =
result = newNode(nkInfix)
let
left = node[0]
right = node[1]
var shiftSym = exprParser.code[left.tsNodeEndByte() ..< right.tsNodeStartByte()].strip()
case shiftSym
of "<<":
result.add exprParser.state.getIdent("shl")
of ">>":
result.add exprParser.state.getIdent("shr")
else:
discard
result.add exprParser.processTSNode(left)
result.add exprParser.processTSNode(right)
proc processParenthesizedExpr*(exprParser: ExprParser, node: TSNode): PNode =
result = newNode(nkPar)
for i in 0 ..< node.len():
result.add(exprParser.processTSNode(node[i]))
proc processLogicalExpression*(exprParser: ExprParser, node: TSNode): PNode =
result = newNode(nkPar)
let child = node[0]
var nimSym = ""
var binarySym = exprParser.code[node.tsNodeStartByte() ..< child.tsNodeStartByte()].strip()
decho "LOG SYM: ", binarySym
case binarySym
of "!":
nimSym = "not"
else:
return newNode(nkNilLit)
decho "LOG CHILD: ", child.val, ", nim: ", nimSym
result.add nkPrefix.newTree(
exprParser.state.getIdent(nimSym),
exprParser.processTSNode(child)
)
proc processBitwiseExpression*(exprParser: ExprParser, node: TSNode): PNode =
if node.len() > 1:
result = newNode(nkInfix)
let left = node[0]
let right = node[1]
var nimSym = ""
var binarySym = exprParser.code[left.tsNodeEndByte() ..< right.tsNodeStartByte()].strip()
decho "# BIN SYM: ", binarySym
case binarySym
of "|", "||":
nimSym = "or"
of "&", "&&":
nimSym = "and"
of "^":
nimSym = "xor"
else:
return newNode(nkNilLit)
result.add exprParser.state.getIdent(nimSym)
result.add exprParser.processTSNode(left)
result.add exprParser.processTSNode(right)
elif node.len() == 1:
result = newNode(nkPar)
let child = node[0]
var nimSym = ""
var binarySym = exprParser.code[node.tsNodeStartByte() ..< child.tsNodeStartByte()].strip()
decho "# BIN SYM: ", binarySym
case binarySym
of "~":
nimSym = "not"
else:
return newNode(nkNilLit)
result.add nkPrefix.newTree(
exprParser.state.getIdent(nimSym),
exprParser.processTSNode(child)
)
proc processTSNode*(exprParser: ExprParser, node: TSNode): PNode =
result = newNode(nkNilLit)
decho "# NODE: ", node.getName(), ", VAL: ", node.val
case node.getName()
of "number_literal":
result = exprParser.processNumberLiteral(node)
of "string_literal":
result = exprParser.processStringLiteral(node)
of "char_literal":
result = exprParser.processCharacterLiteral(node)
of "expression_statement", "ERROR", "translation_unit":
# This may be wrong. What can be in an expression?
result = exprParser.processTSNode(node[0])
of "parenthesized_expression":
result = exprParser.processParenthesizedExpr(node)
of "bitwise_expression":
result = exprParser.processBitwiseExpression(node)
of "shift_expression":
result = exprParser.processShiftExpression(node)
of "logical_expression":
result = exprParser.processLogicalExpression(node)
of "identifier":
var ident = node.val
if ident != "_":
ident = exprParser.state.getIdentifier(ident, nskConst)
result = exprParser.state.getIdent(ident)
else:
result = newNode(nkNilLit)
decho "# NODERES: ", result
proc codeToNode*(state: NimState, code: string): PNode =
let exprParser = newExprParser(state, code)
withCodeAst(exprParser):
result = exprParser.processTSNode(root)

View file

@ -221,7 +221,7 @@ proc len*(node: TSNode): int =
result = node.tsNodeNamedChildCount().int
proc `[]`*(node: TSNode, i: SomeInteger): TSNode =
if i < node.len:
if i < type(i)(node.len()):
result = node.tsNodeNamedChild(i.uint32)
proc getName*(node: TSNode): string {.inline.} =