c2nim: better parsing of #ifdef C2NIM; #def support

This commit is contained in:
Andreas Rumpf 2010-07-22 19:18:26 +02:00
commit be878a5993
8 changed files with 407 additions and 101 deletions

View file

@ -75,9 +75,11 @@ C file lists the OS, CPU and CC the file has been compiled for.
============== ==============
Nimrod supports the generation of DLLs. However, there must be only one Nimrod supports the generation of DLLs. However, there must be only one
instance of the GC per address space. This instance is contained in instance of the GC per process/address space. This instance is contained in
``nimrtl.dll``. This means that every generated Nimrod DLL depends ``nimrtl.dll``. This means that every generated Nimrod DLL depends
on ``nimrtl.dll``. on ``nimrtl.dll``. To generate the "nimrtl.dll" file, use the command::
nimrod c -d:release lib/nimrtl.nim
Additional Features Additional Features

View file

@ -1,7 +1,7 @@
# #
# #
# Nimrod's Runtime Library # Nimrod's Runtime Library
# (c) Copyright 2009 Andreas Rumpf # (c) Copyright 2010 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -187,6 +187,60 @@ elif defined(nogc):
dest^ = src dest^ = src
include "system/cellsets" include "system/cellsets"
elif appType == "lib":
{.warning: "gc in a library context may not work".}
when hostOS == "windows":
const nimrtl = "nimrtl.dll"
elif hostOS == "macosx":
const nimrtl = "nimrtl.dynlib"
else:
const nimrtl = "libnimrtl.so"
when not defined(includeGC):
# ordinary client; use the GC from nimrtl.dll:
proc initGC() {.cdecl, importc, dynlib: nimrtl.}
proc GC_disable() {.cdecl, importc, dynlib: nimrtl.}
proc GC_enable() {.cdecl, importc, dynlib: nimrtl.}
proc GC_fullCollect() {.cdecl, importc, dynlib: nimrtl.}
proc GC_setStrategy(strategy: TGC_Strategy) {.
cdecl, importc, dynlib: nimrtl.}
proc GC_enableMarkAndSweep() {.cdecl, importc, dynlib: nimrtl.}
proc GC_disableMarkAndSweep() {.cdecl, importc, dynlib: nimrtl.}
proc GC_getStatistics(): string {.cdecl, importc, dynlib: nimrtl.}
proc newObj(typ: PNimType, size: int): pointer {.
compilerproc, cdecl, importc, dynlib: nimrtl.}
proc newSeq(typ: PNimType, len: int): pointer {.
compilerproc, cdecl, importc, dynlib: nimrtl.}
proc growObj(old: pointer, newsize: int): pointer {.
cdecl, importc, dynlib: nimrtl.}
proc setStackBottom(theStackBottom: pointer) {.
compilerproc, cdecl, importc, dynlib: nimrtl.}
proc nimGCref(p: pointer) {.
compilerproc, cdecl, importc, dynlib: nimrtl.}
proc nimGCunref(p: pointer) {.
compilerproc, cdecl, importc, dynlib: nimrtl.}
# The write barrier is performance critical!
# XXX We should ensure that they are inlined here.
# Later implementations will do this.
proc unsureAsgnRef(dest: ppointer, src: pointer) {.
compilerproc, cdecl, importc, dynlib: nimrtl.}
proc asgnRef(dest: ppointer, src: pointer) {.
compilerproc, cdecl, importc, dynlib: nimrtl.}
proc asgnRefNoCycle(dest: ppointer, src: pointer) {.
compilerproc, cdecl, importc, dynlib: nimrtl.}
else:
# include the GC and export it!
include "system/alloc"
include "system/cellsets"
assert(sizeof(TCell) == sizeof(TFreeCell))
include "system/gc"
include "system/cellsets"
else: else:
include "system/alloc" include "system/alloc"
include "system/cellsets" include "system/cellsets"
@ -195,4 +249,3 @@ else:
{.pop.} {.pop.}

View file

@ -28,7 +28,6 @@ Options:
(multiple --prefix options are supported) (multiple --prefix options are supported)
--suffix:SUFFIX strip suffix for the generated Nimrod identifiers --suffix:SUFFIX strip suffix for the generated Nimrod identifiers
(multiple --suffix options are supported) (multiple --suffix options are supported)
--skip:IDENT skip IDENT in the input file
-v, --version write c2nim's version -v, --version write c2nim's version
-h, --help show this help -h, --help show this help
""" """

View file

@ -24,6 +24,7 @@ const
type type
TTokKind* = enum TTokKind* = enum
pxInvalid, pxEof, pxInvalid, pxEof,
pxMacroParam, # fake token: macro parameter (with its index)
pxStarComment, # /* */ comment pxStarComment, # /* */ comment
pxLineComment, # // comment pxLineComment, # // comment
pxDirective, # #define, etc. pxDirective, # #define, etc.
@ -78,10 +79,9 @@ type
pxIntLit, pxIntLit,
pxInt64Lit, # long constant like 0x70fffffff or out of int range pxInt64Lit, # long constant like 0x70fffffff or out of int range
pxFloatLit, pxFloatLit,
pxParLe, pxParRi, pxParLe, pxBracketLe, pxCurlyLe, # this order is important
pxBracketLe, pxBracketRi, pxParRi, pxBracketRi, pxCurlyRi, # for macro argument parsing!
pxComma, pxSemiColon, pxColon, pxComma, pxSemiColon, pxColon,
pxCurlyLe, pxCurlyRi
TTokKinds* = set[TTokKind] TTokKinds* = set[TTokKind]
type type
@ -89,7 +89,8 @@ type
TToken* = object TToken* = object
xkind*: TTokKind # the type of the token xkind*: TTokKind # the type of the token
s*: string # parsed symbol, char or string literal s*: string # parsed symbol, char or string literal
iNumber*: BiggestInt # the parsed integer literal iNumber*: BiggestInt # the parsed integer literal;
# if xkind == pxMacroParam: parameter's position
fNumber*: BiggestFloat # the parsed floating point literal fNumber*: BiggestFloat # the parsed floating point literal
base*: TNumericalBase # the numerical base; only valid for int base*: TNumericalBase # the numerical base; only valid for int
# or float literals # or float literals
@ -99,7 +100,6 @@ type
filename*: string filename*: string
inDirective: bool inDirective: bool
proc getTok*(L: var TLexer, tok: var TToken) proc getTok*(L: var TLexer, tok: var TToken)
proc PrintTok*(tok: TToken) proc PrintTok*(tok: TToken)
proc `$`*(tok: TToken): string proc `$`*(tok: TToken): string
@ -140,6 +140,7 @@ proc TokKindToStr*(k: TTokKind): string =
case k case k
of pxEof: result = "[EOF]" of pxEof: result = "[EOF]"
of pxInvalid: result = "[invalid]" of pxInvalid: result = "[invalid]"
of pxMacroParam: result = "[macro param]"
of pxStarComment, pxLineComment: result = "[comment]" of pxStarComment, pxLineComment: result = "[comment]"
of pxStrLit: result = "[string literal]" of pxStrLit: result = "[string literal]"
of pxCharLit: result = "[char literal]" of pxCharLit: result = "[char literal]"

View file

@ -7,9 +7,9 @@
# distribution, for details about the copyright. # distribution, for details about the copyright.
# #
# This module implements an Ansi C parser. ## This module implements an Ansi C parser.
# It transfers a C source file into a Nimrod AST. Then the renderer can be ## It translates a C source file into a Nimrod AST. Then the renderer can be
# used to convert the AST to its text representation. ## used to convert the AST to its text representation.
# XXX standalone structs and unions! # XXX standalone structs and unions!
# XXX header pragma for struct and union fields! # XXX header pragma for struct and union fields!
@ -20,16 +20,22 @@ import
options, strtabs options, strtabs
type type
TParserFlag* = enum TParserFlag = enum
pfRefs, ## use "ref" instead of "ptr" for C's typ* pfRefs, ## use "ref" instead of "ptr" for C's typ*
pfCDecl, ## annotate procs with cdecl pfCDecl, ## annotate procs with cdecl
pfStdCall ## annotate procs with stdcall pfStdCall ## annotate procs with stdcall
TMacro {.final.} = object
name: string
params: int # number of parameters
body: seq[ref TToken] # can contain pxMacroParam tokens
TParserOptions {.final.} = object TParserOptions {.final.} = object
flags: set[TParserFlag] flags: set[TParserFlag]
prefixes, suffixes, skipWords: seq[string] prefixes, suffixes: seq[string]
mangleRules: seq[tuple[pattern: TPeg, frmt: string]] mangleRules: seq[tuple[pattern: TPeg, frmt: string]]
dynlibSym, header: string dynlibSym, header: string
macros: seq[TMacro]
PParserOptions* = ref TParserOptions PParserOptions* = ref TParserOptions
TParser* {.final.} = object TParser* {.final.} = object
@ -46,7 +52,7 @@ proc newParserOptions*(): PParserOptions =
new(result) new(result)
result.prefixes = @[] result.prefixes = @[]
result.suffixes = @[] result.suffixes = @[]
result.skipWords = @[] result.macros = @[]
result.mangleRules = @[] result.mangleRules = @[]
result.flags = {} result.flags = {}
result.dynlibSym = "" result.dynlibSym = ""
@ -62,7 +68,6 @@ proc setOption*(parserOptions: PParserOptions, key: string, val=""): bool =
of "stdcall": incl(parserOptions.flags, pfStdCall) of "stdcall": incl(parserOptions.flags, pfStdCall)
of "prefix": parserOptions.prefixes.add(val) of "prefix": parserOptions.prefixes.add(val)
of "suffix": parserOptions.suffixes.add(val) of "suffix": parserOptions.suffixes.add(val)
of "skip": parserOptions.skipWords.add(val)
else: result = false else: result = false
proc ParseUnit*(p: var TParser): PNode proc ParseUnit*(p: var TParser): PNode
@ -71,7 +76,6 @@ proc openParser*(p: var TParser, filename: string, inputStream: PLLStream,
proc closeParser*(p: var TParser) proc closeParser*(p: var TParser)
proc exSymbol*(n: var PNode) proc exSymbol*(n: var PNode)
proc fixRecordDef*(n: var PNode) proc fixRecordDef*(n: var PNode)
# XXX: move these two to an auxiliary module
# implementation # implementation
@ -81,7 +85,11 @@ proc OpenParser(p: var TParser, filename: string,
p.options = options p.options = options
p.backtrack = @[] p.backtrack = @[]
new(p.tok) new(p.tok)
proc parMessage(p: TParser, msg: TMsgKind, arg = "") =
#assert false
lexMessage(p.lex, msg, arg)
proc CloseParser(p: var TParser) = CloseLexer(p.lex) proc CloseParser(p: var TParser) = CloseLexer(p.lex)
proc safeContext(p: var TParser) = p.backtrack.add(p.tok) proc safeContext(p: var TParser) = p.backtrack.add(p.tok)
proc closeContext(p: var TParser) = discard p.backtrack.pop() proc closeContext(p: var TParser) = discard p.backtrack.pop()
@ -102,18 +110,81 @@ proc rawGetTok(p: var TParser) =
p.tok.next = t p.tok.next = t
p.tok = t p.tok = t
proc isSkipWord(p: TParser): bool = proc findMacro(p: TParser): int =
for s in items(p.options.skipWords): for i in 0..high(p.options.macros):
if p.tok.s == s: return true if p.tok.s == p.options.macros[i].name: return i
return -1
proc rawEat(p: var TParser, xkind: TTokKind) =
if p.tok.xkind == xkind: rawGetTok(p)
else: parMessage(p, errTokenExpected, TokKindToStr(xkind))
proc parseMacroArguments(p: var TParser): seq[seq[ref TToken]] =
result = @[]
result.add(@[])
var i: array[pxParLe..pxCurlyLe, int]
var L = 0
safeContext(p)
while true:
var kind = p.tok.xkind
case kind
of pxEof: rawEat(p, pxParRi)
of pxParLe, pxBracketLe, pxCurlyLe:
inc(i[kind])
result[L].add(p.tok)
of pxParRi:
# end of arguments?
if i[pxParLe] == 0: break
dec(i[pxParLe])
result[L].add(p.tok)
of pxBracketRi, pxCurlyRi:
kind = pred(kind, 3)
if i[kind] > 0: dec(i[kind])
result[L].add(p.tok)
of pxComma:
if i[pxParLe] == 0 and i[pxBracketLe] == 0 and i[pxCurlyLe] == 0:
# next argument: comma is not part of the argument
rawGetTok(p)
result.add(@[])
inc(L)
else:
# comma does not separate different arguments:
result[L].add(p.tok)
else:
result[L].add(p.tok)
rawGetTok(p)
closeContext(p)
proc expandMacro(p: var TParser, m: TMacro) =
rawGetTok(p) # skip macro name
var arguments: seq[seq[ref TToken]]
if m.params > 0:
rawEat(p, pxParLe)
arguments = parseMacroArguments(p)
if arguments.len != m.params: parMessage(p, errWrongNumberOfArguments)
rawEat(p, pxParRi)
# insert into the token list:
if m.body.len > 0:
var newList: ref TToken
new(newList)
var lastTok = newList
for tok in items(m.body):
if tok.xkind == pxMacroParam:
for t in items(arguments[int(tok.iNumber)]):
lastTok.next = t
lastTok = t
else:
lastTok.next = tok
lastTok = tok
lastTok.next = p.tok
p.tok = newList.next
proc getTok(p: var TParser) = proc getTok(p: var TParser) =
while true: rawGetTok(p)
rawGetTok(p) if p.tok.xkind == pxSymbol:
if p.tok.xkind != pxSymbol or not isSkipWord(p): break var idx = findMacro(p)
if idx >= 0:
proc parMessage(p: TParser, msg: TMsgKind, arg = "") = expandMacro(p, p.options.macros[idx])
#assert false
lexMessage(p.lex, msg, arg)
proc parLineInfo(p: TParser): TLineInfo = proc parLineInfo(p: TParser): TLineInfo =
result = getLineInfo(p.lex) result = getLineInfo(p.lex)

View file

@ -1,3 +1,12 @@
#
#
# 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 # Preprocessor support
const const
@ -9,6 +18,10 @@ proc eatNewLine(p: var TParser, n: PNode) =
if p.tok.xkind == pxNewLine: getTok(p) if p.tok.xkind == pxNewLine: getTok(p)
else: else:
eat(p, pxNewLine) 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 = proc parseDefineBody(p: var TParser, tmplDef: PNode): string =
if p.tok.xkind == pxCurlyLe or if p.tok.xkind == pxCurlyLe or
@ -65,6 +78,57 @@ proc parseDefine(p: var TParser): PNode =
addSon(result, c) addSon(result, c)
eatNewLine(p, 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.
safeContext(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 = proc isDir(p: TParser, dir: string): bool =
result = p.tok.xkind in {pxDirectiveParLe, pxDirective} and p.tok.s == dir result = p.tok.xkind in {pxDirectiveParLe, pxDirective} and p.tok.s == dir
@ -102,6 +166,12 @@ proc parseStmtList(p: var TParser): PNode =
else: nil else: nil
addSon(result, statement(p)) 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) = proc parseIfDirAux(p: var TParser, result: PNode) =
addSon(result.sons[0], parseStmtList(p)) addSon(result.sons[0], parseStmtList(p))
while isDir(p, "elif"): while isDir(p, "elif"):
@ -113,67 +183,133 @@ proc parseIfDirAux(p: var TParser, result: PNode) =
addSon(result, b) addSon(result, b)
if isDir(p, "else"): if isDir(p, "else"):
var s = newNodeP(nkElse, p) var s = newNodeP(nkElse, p)
while p.tok.xkind notin {pxEof, pxNewLine, pxLineComment}: getTok(p) skipLine(p)
eatNewLine(p, nil)
addSon(s, parseStmtList(p)) addSon(s, parseStmtList(p))
addSon(result, s) addSon(result, s)
if isDir(p, "endif"): eatEndif(p)
while p.tok.xkind notin {pxEof, pxNewLine, pxLineComment}: getTok(p)
eatNewLine(p, nil)
else:
parMessage(p, errXExpected, "#endif")
proc specialIf(p: TParser): bool = when false:
ExpectIdent(p) proc specialIf(p: TParser): bool =
result = p.tok.s == c2nimSymbol 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 chooseBranch(whenStmt: PNode, branch: int): PNode = proc skipUntilEndif(p: var TParser) =
var L = sonsLen(whenStmt) var nested = 1
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: while p.tok.xkind != pxEof:
if isDir(p, "endif"): if isDir(p, "ifdef") or isDir(p, "ifndef") or isDir(p, "if"):
while p.tok.xkind notin {pxEof, pxNewLine, pxLineComment}: getTok(p) inc(nested)
eatNewLine(p, nil) elif isDir(p, "endif"):
return dec(nested)
if nested <= 0:
skipLine(p)
return
getTok(p) getTok(p)
parMessage(p, errXExpected, "#endif") parMessage(p, errXExpected, "#endif")
proc parseIfdefDir(p: var TParser): PNode = type
result = newNodeP(nkWhenStmt, p) TEndifMarker = enum
addSon(result, newNodeP(nkElifBranch, p)) emElif, emElse, emEndif
getTok(p)
var special = specialIf(p) proc skipUntilElifElseEndif(p: var TParser): TEndifMarker =
if p.tok.s == "__cplusplus": var nested = 1
return skipIfdefCPlusPlus(p) while p.tok.xkind != pxEof:
addSon(result.sons[0], definedExprAux(p)) if isDir(p, "ifdef") or isDir(p, "ifndef") or isDir(p, "if"):
eatNewLine(p, nil) inc(nested)
parseIfDirAux(p, result) elif isDir(p, "elif") and nested <= 1:
if special: return emElif
result = chooseBranch(result, 0) 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)
when false:
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 = proc parseIfndefDir(p: var TParser): PNode =
result = newNodeP(nkWhenStmt, p) result = newNodeP(nkWhenStmt, p)
addSon(result, newNodeP(nkElifBranch, p)) addSon(result, newNodeP(nkElifBranch, p))
getTok(p) getTok(p)
var special = specialIf(p) var special = specialIf(p)
var e = newNodeP(nkCall, p) var e = newNodeP(nkCall, p)
addSon(e, newIdentNodeP("not", p)) addSon(e, newIdentNodeP("not", p))
addSon(e, definedExprAux(p)) addSon(e, definedExprAux(p))
eatNewLine(p, nil) eatNewLine(p, nil)
addSon(result.sons[0], e) addSon(result.sons[0], e)
parseIfDirAux(p, result) parseIfDirAux(p, result)
if special: if special:
result = chooseBranch(result, 1) result = chooseBranch(result, 1)
proc parseIfDir(p: var TParser): PNode = proc parseIfDir(p: var TParser): PNode =
result = newNodeP(nkWhenStmt, p) result = newNodeP(nkWhenStmt, p)
@ -206,14 +342,14 @@ proc parseDir(p: var TParser): PNode =
case p.tok.s case p.tok.s
of "define": result = parseDefine(p) of "define": result = parseDefine(p)
of "include": result = parseInclude(p) of "include": result = parseInclude(p)
of "ifdef": result = parseIfdefDir(p) of "ifdef": result = parseIfdef(p)
of "ifndef": result = parseIfndefDir(p) of "ifndef": result = parseIfndef(p)
of "if": result = parseIfDir(p) of "if": result = parseIfDir(p)
of "cdecl", "stdcall", "ref": of "cdecl", "stdcall", "ref":
discard setOption(p.options, p.tok.s) discard setOption(p.options, p.tok.s)
getTok(p) getTok(p)
eatNewLine(p, nil) eatNewLine(p, nil)
of "dynlib", "header", "prefix", "suffix", "skip": of "dynlib", "header", "prefix", "suffix":
var key = p.tok.s var key = p.tok.s
getTok(p) getTok(p)
if p.tok.xkind != pxStrLit: ExpectIdent(p) if p.tok.xkind != pxStrLit: ExpectIdent(p)
@ -222,10 +358,11 @@ proc parseDir(p: var TParser): PNode =
eatNewLine(p, nil) eatNewLine(p, nil)
of "mangle": of "mangle":
parseMangleDir(p) parseMangleDir(p)
of "def":
var L = p.options.macros.len
setLen(p.options.macros, L+1)
parseDef(p, p.options.macros[L])
else: else:
# ignore unimportant/unknown directive ("undef", "pragma", "error") # ignore unimportant/unknown directive ("undef", "pragma", "error")
while true: skipLine(p)
getTok(p)
if p.tok.xkind in {pxEof, pxNewLine, pxLineComment}: break
eatNewLine(p, nil)

View file

@ -202,27 +202,31 @@ identifier should be converted:
#mangle "'GTK_'{.*}" "TGtk$1" #mangle "'GTK_'{.*}" "TGtk$1"
``#skip`` directive ``#def`` directive
------------------- ------------------
**Note**: There is also ``--skip`` command line option that can be used for the
same purpose.
Often C code contains special macros that affect the declaration of a function Often C code contains special macros that affect the declaration of a function
prototype but confuse c2nim's parser: prototype but confuse c2nim's parser:
.. code-block:: C .. code-block:: C
// does not parse! // does not parse!
EXPORT int f(void); EXTERN(int) f(void);
EXPORT int g(void); EXTERN(int) g(void);
Instead of removing ``EXPORT`` from the input source file, one can tell c2nim Instead of removing ``EXTERN()`` from the input source file (which cannot be
to skip special identifiers: done reliably even with a regular expression!), one can tell c2nim
that ``EXPORT`` is a macro that should be expanded by c2nim too:
.. code-block:: C .. code-block:: C
#skip EXPORT #ifdef C2NIM
// does parse now! # def EXTERN(x) static x
EXPORT int f(void); #endif
EXPORT int g(void); // parses now!
EXTERN(int) f(void);
EXTERN(int) g(void);
``#def`` is very similar to C's ``#define``.
Limitations Limitations

View file

@ -4,12 +4,43 @@
*/ */
#ifdef __cplusplus #ifdef __cplusplus
# ifdef __SOME_OTHER_CRAP
extern "C" { extern "C" {
# endif
#endif #endif
// Test C2NIM skipping:
#ifndef C2NIM
#if someNestedCond
This is an invalid text that should generate a parser error, if not
#endif
skipped correctly.
#endif
#ifndef C2NIM
#if someNestedCond
This is an invalid text that should generate a parser error, if not
#endif
skipped correctly.
#else
typedef char gchar; typedef char gchar;
typedef unsigned int gunsignedint; typedef unsigned int gunsignedint;
typedef unsigned char guchar; typedef unsigned char guchar;
#endif
#ifdef C2NIM
# mangle "'those'" "these"
int those;
#elif abc
#if someNestedCond
This is an invalid text that should generate a parser error, if not
#else
skipped correctly.
#endif
#else
Another crappy input line.
#endif
point* newPoint(void) { point* newPoint(void) {
for (int i = 0; i < 89; ++i) echo("test" " string " "concatenation"); for (int i = 0; i < 89; ++i) echo("test" " string " "concatenation");
@ -97,7 +128,15 @@ int IupConvertXYToPos(PIhandle ih, int x, int y);
#endif #endif
#skip EXPORT #ifdef C2NIM
# def EXTERN(x) static x
#endif
// parses now!
EXTERN(int) f(void);
EXTERN(int) g(void);
#def EXPORT
// does parse now! // does parse now!
EXPORT int f(void); EXPORT int f(void);
EXPORT int g(void); EXPORT int g(void);