# # # c2nim - C to Nimrod source converter # (c) Copyright 2010 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # # 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 skipLine(p: var TParser) = while p.tok.xkind notin {pxEof, pxNewLine, pxLineComment}: getTok(p) eatNewLine(p, nil) 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, skipIdentExport(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, skipIdentExport(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 parseDefBody(p: var TParser, m: var TMacro, params: seq[string]) = m.body = @[] # A little hack: We safe the context, so that every following token will be # put into a newly allocated TToken object. Thus we can just save a # reference to the token in the macro's body. saveContext(p) while p.tok.xkind notin {pxEof, pxNewLine, pxLineComment}: case p.tok.xkind of pxSymbol: # is it a parameter reference? var tok = p.tok for i in 0..high(params): if params[i] == p.tok.s: new(tok) tok.xkind = pxMacroParam tok.iNumber = i break m.body.add(tok) of pxDirConc: # just ignore this token: this implements token merging correctly nil else: m.body.add(p.tok) # we do not want macro expansion here: rawGetTok(p) eatNewLine(p, nil) closeContext(p) # newline token might be overwritten, but this is not # part of the macro body, so it is safe. proc parseDef(p: var TParser, m: var TMacro) = var hasParams = p.tok.xkind == pxDirectiveParLe getTok(p) expectIdent(p) m.name = p.tok.s getTok(p) var params: seq[string] = @[] # parse parameters: if hasParams: eat(p, pxParLe) while p.tok.xkind != pxParRi: expectIdent(p) params.add(p.tok.s) getTok(p) skipStarCom(p, nil) if p.tok.xkind != pxComma: break getTok(p) eat(p, pxParRi) m.params = params.len parseDefBody(p, m, params) 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 and pfSkipInclude notin p.options.flags: var file = newStrNodeP(nkStrLit, changeFileExt(p.tok.s, ""), p) addSon(result, file) getTok(p) skipStarCom(p, file) eatNewLine(p, nil) else: skipLine(p) 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 eatEndif(p: var TParser) = if isDir(p, "endif"): skipLine(p) else: parMessage(p, errXExpected, "#endif") 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) skipLine(p) addSon(s, parseStmtList(p)) addSon(result, s) eatEndif(p) proc skipUntilEndif(p: var TParser) = var nested = 1 while p.tok.xkind != pxEof: if isDir(p, "ifdef") or isDir(p, "ifndef") or isDir(p, "if"): inc(nested) elif isDir(p, "endif"): dec(nested) if nested <= 0: skipLine(p) return getTok(p) parMessage(p, errXExpected, "#endif") type TEndifMarker = enum emElif, emElse, emEndif proc skipUntilElifElseEndif(p: var TParser): TEndifMarker = var nested = 1 while p.tok.xkind != pxEof: if isDir(p, "ifdef") or isDir(p, "ifndef") or isDir(p, "if"): inc(nested) elif isDir(p, "elif") and nested <= 1: return emElif elif isDir(p, "else") and nested <= 1: return emElse elif isDir(p, "endif"): dec(nested) if nested <= 0: return emEndif getTok(p) parMessage(p, errXExpected, "#endif") proc parseIfdef(p: var TParser): PNode = getTok(p) # skip #ifdef ExpectIdent(p) case p.tok.s of "__cplusplus": skipUntilEndif(p) of c2nimSymbol: skipLine(p) result = parseStmtList(p) skipUntilEndif(p) else: result = newNodeP(nkWhenStmt, p) addSon(result, newNodeP(nkElifBranch, p)) addSon(result.sons[0], definedExprAux(p)) eatNewLine(p, nil) parseIfDirAux(p, result) proc parseIfndef(p: var TParser): PNode = getTok(p) # skip #ifndef ExpectIdent(p) if p.tok.s == c2nimSymbol: skipLine(p) case skipUntilElifElseEndif(p) of emElif: result = newNodeP(nkWhenStmt, p) addSon(result, newNodeP(nkElifBranch, p)) getTok(p) addSon(result.sons[0], expression(p)) eatNewLine(p, nil) parseIfDirAux(p, result) of emElse: skipLine(p) result = parseStmtList(p) eatEndif(p) of emEndif: skipLine(p) else: result = newNodeP(nkWhenStmt, p) addSon(result, newNodeP(nkElifBranch, 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) 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 parsePegLit(p: var TParser): TPeg = var col = getColumn(p.lex) + 2 getTok(p) if p.tok.xkind != pxStrLit: ExpectIdent(p) try: result = parsePeg( input = if p.tok.xkind == pxStrLit: p.tok.s else: escapePeg(p.tok.s), filename = p.lex.filename, line = p.lex.linenumber, col = col) getTok(p) except EInvalidPeg: parMessage(p, errUser, getCurrentExceptionMsg()) proc parseMangleDir(p: var TParser) = var pattern = parsePegLit(p) if p.tok.xkind != pxStrLit: ExpectIdent(p) p.options.mangleRules.add((pattern, p.tok.s)) getTok(p) 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 = parseIfdef(p) of "ifndef": result = parseIfndef(p) of "if": result = parseIfDir(p) of "cdecl", "stdcall", "ref", "skipinclude", "typeprefixes", "skipcomments": discard setOption(p.options, p.tok.s) getTok(p) eatNewLine(p, nil) of "dynlib", "header", "prefix", "suffix": 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) of "def": var L = p.options.macros.len setLen(p.options.macros, L+1) parseDef(p, p.options.macros[L]) of "private": var pattern = parsePegLit(p) p.options.privateRules.add(pattern) eatNewLine(p, nil) else: # ignore unimportant/unknown directive ("undef", "pragma", "error") skipLine(p)