diff options
Diffstat (limited to 'rod/c2nim/cpp.nim')
-rwxr-xr-x | rod/c2nim/cpp.nim | 231 |
1 files changed, 231 insertions, 0 deletions
diff --git a/rod/c2nim/cpp.nim b/rod/c2nim/cpp.nim new file mode 100755 index 000000000..61873628e --- /dev/null +++ b/rod/c2nim/cpp.nim @@ -0,0 +1,231 @@ +# Preprocessor support + +const + c2nimSymbol = "C2NIM" + +proc eatNewLine(p: var TParser, n: PNode) = + if p.tok.xkind == pxLineComment: + skipCom(p, n) + if p.tok.xkind == pxNewLine: getTok(p) + else: + eat(p, pxNewLine) + +proc parseDefineBody(p: var TParser, tmplDef: PNode): string = + if p.tok.xkind == pxCurlyLe or + (p.tok.xkind == pxSymbol and (declKeyword(p.tok.s) or stmtKeyword(p.tok.s))): + addSon(tmplDef, statement(p)) + result = "stmt" + elif p.tok.xkind in {pxLineComment, pxNewLine}: + addSon(tmplDef, buildStmtList(newNodeP(nkNilLit, p))) + result = "stmt" + else: + addSon(tmplDef, buildStmtList(expression(p))) + result = "expr" + +proc parseDefine(p: var TParser): PNode = + if p.tok.xkind == pxDirectiveParLe: + # a macro with parameters: + result = newNodeP(nkTemplateDef, p) + getTok(p) + addSon(result, skipIdent(p)) + eat(p, pxParLe) + var params = newNodeP(nkFormalParams, p) + # return type; not known yet: + addSon(params, nil) + var identDefs = newNodeP(nkIdentDefs, p) + while p.tok.xkind != pxParRi: + addSon(identDefs, skipIdent(p)) + skipStarCom(p, nil) + if p.tok.xkind != pxComma: break + getTok(p) + addSon(identDefs, newIdentNodeP("expr", p)) + addSon(identDefs, nil) + addSon(params, identDefs) + eat(p, pxParRi) + + addSon(result, nil) # no generic parameters + addSon(result, params) + addSon(result, nil) # no pragmas + var kind = parseDefineBody(p, result) + params.sons[0] = newIdentNodeP(kind, p) + eatNewLine(p, result) + else: + # a macro without parameters: + result = newNodeP(nkConstSection, p) + while p.tok.xkind == pxDirective and p.tok.s == "define": + getTok(p) # skip #define + var c = newNodeP(nkConstDef, p) + addSon(c, skipIdent(p)) + addSon(c, nil) + skipStarCom(p, c) + if p.tok.xkind in {pxLineComment, pxNewLine, pxEof}: + addSon(c, newIdentNodeP("true", p)) + else: + addSon(c, expression(p)) + addSon(result, c) + eatNewLine(p, c) + +proc isDir(p: TParser, dir: string): bool = + result = p.tok.xkind in {pxDirectiveParLe, pxDirective} and p.tok.s == dir + +proc parseInclude(p: var TParser): PNode = + result = newNodeP(nkImportStmt, p) + while isDir(p, "include"): + getTok(p) # skip "include" + if p.tok.xkind == pxStrLit: + var file = newStrNodeP(nkStrLit, p.tok.s, p) + addSon(result, file) + getTok(p) + skipStarCom(p, file) + elif p.tok.xkind == pxLt: + while p.tok.xkind notin {pxEof, pxNewLine, pxLineComment}: getTok(p) + else: + parMessage(p, errXExpected, "string literal") + eatNewLine(p, nil) + if sonsLen(result) == 0: + # we only parsed includes that we chose to ignore: + result = nil + +proc definedExprAux(p: var TParser): PNode = + result = newNodeP(nkCall, p) + addSon(result, newIdentNodeP("defined", p)) + addSon(result, skipIdent(p)) + +proc parseStmtList(p: var TParser): PNode = + result = newNodeP(nkStmtList, p) + while true: + case p.tok.xkind + of pxEof: break + of pxDirectiveParLe, pxDirective: + case p.tok.s + of "else", "endif", "elif": break + else: nil + addSon(result, statement(p)) + +proc parseIfDirAux(p: var TParser, result: PNode) = + addSon(result.sons[0], parseStmtList(p)) + while isDir(p, "elif"): + var b = newNodeP(nkElifBranch, p) + getTok(p) + addSon(b, expression(p)) + eatNewLine(p, nil) + addSon(b, parseStmtList(p)) + addSon(result, b) + if isDir(p, "else"): + var s = newNodeP(nkElse, p) + while p.tok.xkind notin {pxEof, pxNewLine, pxLineComment}: getTok(p) + eatNewLine(p, nil) + addSon(s, parseStmtList(p)) + addSon(result, s) + if isDir(p, "endif"): + while p.tok.xkind notin {pxEof, pxNewLine, pxLineComment}: getTok(p) + eatNewLine(p, nil) + else: + parMessage(p, errXExpected, "#endif") + +proc specialIf(p: TParser): bool = + ExpectIdent(p) + result = p.tok.s == c2nimSymbol + +proc chooseBranch(whenStmt: PNode, branch: int): PNode = + var L = sonsLen(whenStmt) + if branch < L: + if L == 2 and whenStmt[1].kind == nkElse or branch == 0: + result = lastSon(whenStmt[branch]) + else: + var b = whenStmt[branch] + assert(b.kind == nkElifBranch) + result = newNodeI(nkWhenStmt, whenStmt.info) + for i in branch .. L-1: + addSon(result, whenStmt[i]) + +proc skipIfdefCPlusPlus(p: var TParser): PNode = + while p.tok.xkind != pxEof: + if isDir(p, "endif"): + while p.tok.xkind notin {pxEof, pxNewLine, pxLineComment}: getTok(p) + eatNewLine(p, nil) + return + getTok(p) + parMessage(p, errXExpected, "#endif") + +proc parseIfdefDir(p: var TParser): PNode = + result = newNodeP(nkWhenStmt, p) + addSon(result, newNodeP(nkElifBranch, p)) + getTok(p) + var special = specialIf(p) + if p.tok.s == "__cplusplus": + return skipIfdefCPlusPlus(p) + addSon(result.sons[0], definedExprAux(p)) + eatNewLine(p, nil) + parseIfDirAux(p, result) + if special: + result = chooseBranch(result, 0) + +proc parseIfndefDir(p: var TParser): PNode = + result = newNodeP(nkWhenStmt, p) + addSon(result, newNodeP(nkElifBranch, p)) + getTok(p) + var special = specialIf(p) + var e = newNodeP(nkCall, p) + addSon(e, newIdentNodeP("not", p)) + addSon(e, definedExprAux(p)) + eatNewLine(p, nil) + addSon(result.sons[0], e) + parseIfDirAux(p, result) + if special: + result = chooseBranch(result, 1) + +proc parseIfDir(p: var TParser): PNode = + result = newNodeP(nkWhenStmt, p) + addSon(result, newNodeP(nkElifBranch, p)) + getTok(p) + addSon(result.sons[0], expression(p)) + eatNewLine(p, nil) + parseIfDirAux(p, result) + +proc parseMangleDir(p: var TParser) = + var col = getColumn(p.lex) + 2 + getTok(p) + if p.tok.xkind != pxStrLit: ExpectIdent(p) + try: + var pattern = parsePeg( + input = p.tok.s, + filename = p.lex.filename, + line = p.lex.linenumber, + col = col) + getTok(p) + if p.tok.xkind != pxStrLit: ExpectIdent(p) + p.options.mangleRules.add((pattern, p.tok.s)) + getTok(p) + except EInvalidPeg: + parMessage(p, errUser, getCurrentExceptionMsg()) + eatNewLine(p, nil) + +proc parseDir(p: var TParser): PNode = + assert(p.tok.xkind in {pxDirective, pxDirectiveParLe}) + case p.tok.s + of "define": result = parseDefine(p) + of "include": result = parseInclude(p) + of "ifdef": result = parseIfdefDir(p) + of "ifndef": result = parseIfndefDir(p) + of "if": result = parseIfDir(p) + of "cdecl", "stdcall", "ref": + discard setOption(p.options, p.tok.s) + getTok(p) + eatNewLine(p, nil) + of "dynlib", "header", "prefix", "suffix", "skip": + var key = p.tok.s + getTok(p) + if p.tok.xkind != pxStrLit: ExpectIdent(p) + discard setOption(p.options, key, p.tok.s) + getTok(p) + eatNewLine(p, nil) + of "mangle": + parseMangleDir(p) + else: + # ignore unimportant/unknown directive ("undef", "pragma", "error") + while true: + getTok(p) + if p.tok.xkind in {pxEof, pxNewLine, pxLineComment}: break + eatNewLine(p, nil) + |