Add string and char support

Update some comments

Rename exprparser main proc

Don't export parse procs

Add missing utils module

Try to fix array type test

Try fix cast test error

Disable cast test for now

Revert back comment test. Have to figure out how to test without vm
This commit is contained in:
Joey Yakimowich-Payne 2020-04-19 19:18:04 -06:00
commit 173e6d625c
5 changed files with 120 additions and 23 deletions

View file

@ -18,7 +18,7 @@ proc getPtrType*(str: string): string =
str str
proc getLit*(nimState: NimState, str: string, expression = false): PNode = proc getLit*(nimState: NimState, str: string, expression = false): PNode =
result = nimState.codeToNode(str) 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
@ -164,7 +164,7 @@ proc addPragma(gState: State, node: TSNode, pragma: PNode, name: string, value:
if value.isNil: if value.isNil:
pragma.add pident pragma.add pident
else: else:
var let
colExpr = newNode(nkExprColonExpr) colExpr = newNode(nkExprColonExpr)
colExpr.add pident colExpr.add pident
colExpr.add value colExpr.add value
@ -1386,7 +1386,7 @@ proc addEnum(gState: State, node: TSNode) =
if en.len > 1 and en[1].getName() in gEnumVals: if en.len > 1 and en[1].getName() in gEnumVals:
# Explicit value # Explicit value
fval = "(" & gState.getNimExpression(gState.getNodeVal(en[1]), name) & ")." & name fval = "(" & $gState.parseCExpression(gState.getNodeVal(en[1]), name) & ")." & name
# Cannot use newConstDef() since parseString(fval) adds backticks to and/or # Cannot use newConstDef() since parseString(fval) adds backticks to and/or
gState.constSection.add gState.parseString(&"const {fname}* = {fval}")[0][0] gState.constSection.add gState.parseString(&"const {fname}* = {fval}")[0][0]

View file

@ -1,4 +1,4 @@
import strformat, strutils, macros import strformat, strutils, macros, sets
import regex import regex
@ -12,11 +12,12 @@ type
ExprParser* = ref object ExprParser* = ref object
state*: NimState state*: NimState
code*: string code*: string
name*: string
ExprParseError* = object of CatchableError ExprParseError* = object of CatchableError
proc newExprParser*(state: NimState, code: string): ExprParser = proc newExprParser*(state: NimState, code: string, name = ""): ExprParser =
ExprParser(state: state, code: code) ExprParser(state: state, code: code, name: name)
template techo(msg: varargs[string, `$`]) = template techo(msg: varargs[string, `$`]) =
if exprParser.state.gState.debug: if exprParser.state.gState.debug:
@ -38,6 +39,8 @@ 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:
ident = ident & "." & exprParser.name
if ident != "": if ident != "":
result = exprParser.state.getIdent(ident) result = exprParser.state.getIdent(ident)
@ -70,6 +73,58 @@ template withCodeAst(exprParser: ExprParser, body: untyped): untyped =
defer: defer:
tree.tsTreeDelete() tree.tsTreeDelete()
proc parseChar(charStr: string): uint8 {.inline.} =
## Parses a character literal out of a string. This is needed
## because treesitter gives unescaped characters when parsing
## strings.
if charStr.len == 1:
return charStr[0].uint8
# Handle octal, hex, unicode?
if charStr.startsWith("\\x"):
result = parseHexInt(charStr.replace("\\x", "0x")).uint8
elif charStr.len == 4: # Octal
result = parseOctInt("0o" & charStr[1 ..< charStr.len]).uint8
if result == 0:
case charStr
of "\\0":
result = ord('\0')
of "\\a":
result = 0x07
of "\\b":
result = 0x08
of "\\e":
result = 0x1B
of "\\f":
result = 0x0C
of "\\n":
result = '\n'.uint8
of "\\r":
result = 0x0D
of "\\t":
result = 0x09
of "\\v":
result = 0x0B
of "\\\\":
result = 0x5C
of "\\'":
result = '\''.uint8
of "\\\"":
result = '\"'.uint8
of "\\?":
result = 0x3F
else:
discard
if result > uint8.high:
result = uint8.high
proc getCharLit(charStr: string): PNode {.inline.} =
## Convert a character string into a proper Nim char lit node
result = newNode(nkCharLit)
result.intVal = parseChar(charStr).int64
proc getNumNode(number, suffix: string): PNode {.inline.} = proc getNumNode(number, suffix: string): PNode {.inline.} =
## Convert a C number to a Nim number PNode ## Convert a C number to a Nim number PNode
result = newNode(nkNone) result = newNode(nkNone)
@ -117,7 +172,7 @@ proc getNumNode(number, suffix: string): PNode {.inline.} =
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
result = newNode(nkNone) result = newNode(nkNone)
let nodeVal = node.val let nodeVal = node.val
@ -141,17 +196,29 @@ proc processNumberLiteral*(exprParser: ExprParser, node: TSNode): PNode =
else: else:
raise newException(ExprParseError, &"Could not find a number in number_literal: \"{nodeVal}\"") raise newException(ExprParseError, &"Could not find a number in number_literal: \"{nodeVal}\"")
proc processCharacterLiteral*(exprParser: ExprParser, node: TSNode): PNode = proc processCharacterLiteral(exprParser: ExprParser, node: TSNode): PNode =
result = newNode(nkCharLit) let val = node.val
result.intVal = node.val[1].int64 result = getCharLit(val[1 ..< val.len - 1])
proc processStringLiteral*(exprParser: ExprParser, node: TSNode): PNode = proc processStringLiteral(exprParser: ExprParser, node: TSNode): PNode =
let nodeVal = node.val let
result = newStrNode(nkStrLit, nodeVal[1 ..< nodeVal.len - 1]) nodeVal = node.val
strVal = nodeVal[1 ..< nodeVal.len - 1]
proc processTSNode*(exprParser: ExprParser, node: TSNode, typeofNode: var PNode): PNode const
str = "(\\\\x[[:xdigit:]]{2}|\\\\\\d{3}|\\\\0|\\\\a|\\\\b|\\\\e|\\\\f|\\\\n|\\\\r|\\\\t|\\\\v|\\\\\\\\|\\\\'|\\\\\"|[[:ascii:]])"
reg = re(str)
proc processShiftExpression*(exprParser: ExprParser, node: TSNode, typeofNode: var PNode): PNode = # Convert the c string escape sequences/etc to Nim chars
var nimStr = newStringOfCap(nodeVal.len)
for m in strVal.findAll(reg):
nimStr.add(parseChar(strVal[m.group(0)[0]]).chr)
result = newStrNode(nkStrLit, nimStr)
proc processTSNode(exprParser: ExprParser, node: TSNode, typeofNode: var PNode): PNode
proc processShiftExpression(exprParser: ExprParser, node: TSNode, typeofNode: var PNode): PNode =
result = newNode(nkInfix) result = newNode(nkInfix)
let let
left = node[0] left = node[0]
@ -185,18 +252,18 @@ proc processShiftExpression*(exprParser: ExprParser, node: TSNode, typeofNode: v
rightNode rightNode
) )
proc processParenthesizedExpr*(exprParser: ExprParser, node: TSNode, typeofNode: var PNode): PNode = proc processParenthesizedExpr(exprParser: ExprParser, node: TSNode, typeofNode: var PNode): PNode =
result = newNode(nkPar) result = newNode(nkPar)
for i in 0 ..< node.len(): for i in 0 ..< node.len():
result.add(exprParser.processTSNode(node[i], typeofNode)) result.add(exprParser.processTSNode(node[i], typeofNode))
proc processCastExpression*(exprParser: ExprParser, node: TSNode, typeofNode: var PNode): PNode = proc processCastExpression(exprParser: ExprParser, node: TSNode, typeofNode: var PNode): PNode =
result = nkCast.newTree( result = nkCast.newTree(
exprParser.processTSNode(node[0], typeofNode), exprParser.processTSNode(node[0], typeofNode),
exprParser.processTSNode(node[1], typeofNode) exprParser.processTSNode(node[1], typeofNode)
) )
proc processLogicalExpression*(exprParser: ExprParser, node: TSNode, typeofNode: var PNode): PNode = proc processLogicalExpression(exprParser: ExprParser, node: TSNode, typeofNode: var PNode): PNode =
result = newNode(nkPar) result = newNode(nkPar)
let child = node[0] let child = node[0]
var nimSym = "" var nimSym = ""
@ -415,13 +482,13 @@ proc processTSNode(exprParser: ExprParser, node: TSNode, typeofNode: var PNode):
techo "NODE RESULT: ", result techo "NODE RESULT: ", result
proc codeToNode*(state: NimState, code: string): PNode = 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
var tnode: PNode = nil var tnode: PNode = nil
let exprParser = newExprParser(state, code) let exprParser = newExprParser(state, code, name)
try: try:
withCodeAst(exprParser): withCodeAst(exprParser):
result = exprParser.processTSNode(root, tnode) result = exprParser.processTSNode(root, tnode)

18
nimterop/utils.nim Normal file
View file

@ -0,0 +1,18 @@
import compiler/[ast, lineinfos, msgs, options, parser, renderer]
import "."/[globals, getters]
proc handleError*(conf: ConfigRef, info: TLineInfo, msg: TMsgKind, arg: string) =
# Raise exception in parseString() instead of exiting for errors
if msg < warnMin:
raise newException(Exception, msgKindToString(msg))
proc parseString*(nimState: NimState, str: string): PNode =
# Parse a string into Nim AST - use custom error handler that raises
# an exception rather than exiting on failure
try:
result = parseString(
str, nimState.identCache, nimState.config, errorHandler = handleError
)
except:
decho getCurrentExceptionMsg()

View file

@ -23,6 +23,12 @@ extern "C" {
#define BOOL true #define BOOL true
#define MATHEXPR (1 + 2/3*20 - 100) #define MATHEXPR (1 + 2/3*20 - 100)
#define ANDEXPR (100 & 11000) #define ANDEXPR (100 & 11000)
#define CASTEXPR (int) 34
#define NULLCHAR '\0'
#define OCTCHAR '\012'
#define HEXCHAR '\xFE'
#define TRICKYSTR "\x4E\034\nfoo\0\'\"\r\v\a\b\e\f\t\\\?bar"
#define ALLSHL (SHL1 | SHL2 | SHL3) #define ALLSHL (SHL1 | SHL2 | SHL3)

View file

@ -93,11 +93,11 @@ macro testFields(t: typed, fields: static[string] = "") =
for i in 0 ..< rl.len: for i in 0 ..< rl.len:
let let
name = ($rl[i][0]).strip(chars = {'*'}) name = ($rl[i][0]).strip(chars = {'*'})
typ = ($(rl[i][1].repr())).replace("\n", "").replace(" ", "") typ = ($(rl[i][1].repr())).replace("\n", "").replace(" ", "").replace("typeof", "type")
n = names.find(name) n = names.find(name)
assert n != -1, $t & "." & name & " invalid" assert n != -1, $t & "." & name & " invalid"
assert types[n] == typ, assert types[n].replace("typeof", "type") == typ,
"typeof(" & $t & ":" & name & ") != " & types[n] & ", is " & typ "typeof(" & $t & ":" & name & ") != " & types[n].replace("typeof", "type") & ", is " & typ
assert A == 2 assert A == 2
assert B == 1.0 assert B == 1.0
@ -118,6 +118,12 @@ assert BINEXPR == 5
assert BOOL == true assert BOOL == true
assert MATHEXPR == -99 assert MATHEXPR == -99
assert ANDEXPR == 96 assert ANDEXPR == 96
assert CASTEXPR == 34.chr
assert TRICKYSTR == "N\x1C\nfoo\x00\'\"\c\v\a\b\e\f\t\\\\?bar"
assert NULLCHAR == '\0'
assert OCTCHAR == '\n'
assert HEXCHAR.int == 0xFE
assert SHL1 == (1.uint shl 1) assert SHL1 == (1.uint shl 1)
assert SHL2 == (1.uint shl 2) assert SHL2 == (1.uint shl 2)