diff options
author | Andreas Rumpf <andreas@andreas-desktop> | 2010-08-11 23:13:11 +0200 |
---|---|---|
committer | Andreas Rumpf <andreas@andreas-desktop> | 2010-08-11 23:13:11 +0200 |
commit | afbff2d5040ce219c62fa6b9298ed2a8cfc2406b (patch) | |
tree | d853536dd10c18b12aabea3c280a6ecae10e5c7b /rod/c2nim | |
parent | 040c57f0a566b257ab7fcca4c6557be4b3c76f24 (diff) | |
download | Nim-afbff2d5040ce219c62fa6b9298ed2a8cfc2406b.tar.gz |
c2nim: new features & bugfixes
Diffstat (limited to 'rod/c2nim')
-rwxr-xr-x | rod/c2nim/c2nim.nim | 4 | ||||
-rwxr-xr-x | rod/c2nim/cparse.nim | 144 | ||||
-rwxr-xr-x | rod/c2nim/cpp.nim | 24 | ||||
-rwxr-xr-x | rod/c2nim/manual.txt | 47 | ||||
-rwxr-xr-x | rod/c2nim/tests/systest.c | 68 |
5 files changed, 222 insertions, 65 deletions
diff --git a/rod/c2nim/c2nim.nim b/rod/c2nim/c2nim.nim index fd1e227df..f1cb05920 100755 --- a/rod/c2nim/c2nim.nim +++ b/rod/c2nim/c2nim.nim @@ -12,7 +12,7 @@ import clex, cparse const - Version = "0.8.10" + Version = NimrodVersion Usage = """ c2nim - C to Nimrod source converter (c) 2010 Andreas Rumpf @@ -29,6 +29,8 @@ Options: --suffix:SUFFIX strip suffix for the generated Nimrod identifiers (multiple --suffix options are supported) --skipinclude do not convert ``#include`` to ``import`` + --typeprefixes generate ``T`` and ``P`` type prefixes + --skipcomments do not copy comments -v, --version write c2nim's version -h, --help show this help """ diff --git a/rod/c2nim/cparse.nim b/rod/c2nim/cparse.nim index 28cad2805..de2355bba 100755 --- a/rod/c2nim/cparse.nim +++ b/rod/c2nim/cparse.nim @@ -11,7 +11,7 @@ ## It translates a C source file into a Nimrod AST. Then the renderer can be ## used to convert the AST to its text representation. -## XXX cleanup of declaration handling. Standalone enums. +# XXX cleanup of declaration handling. import os, llstream, rnimsyn, clex, idents, strutils, pegs, ast, astalgo, msgs, @@ -22,7 +22,9 @@ type pfRefs, ## use "ref" instead of "ptr" for C's typ* pfCDecl, ## annotate procs with cdecl pfStdCall, ## annotate procs with stdcall - pfSkipInclude ## skip all ``#include`` + pfSkipInclude, ## skip all ``#include`` + pfTypePrefixes, ## all generated types start with 'T' or 'P' + pfSkipComments ## do not generate comments TMacro {.final.} = object name: string @@ -33,8 +35,10 @@ type flags: set[TParserFlag] prefixes, suffixes: seq[string] mangleRules: seq[tuple[pattern: TPeg, frmt: string]] + privateRules: seq[TPeg] dynlibSym, header: string macros: seq[TMacro] + toMangle: PStringTable PParserOptions* = ref TParserOptions TParser* {.final.} = object @@ -53,9 +57,11 @@ proc newParserOptions*(): PParserOptions = result.suffixes = @[] result.macros = @[] result.mangleRules = @[] + result.privateRules = @[] result.flags = {} result.dynlibSym = "" result.header = "" + result.toMangle = newStringTable() proc setOption*(parserOptions: PParserOptions, key: string, val=""): bool = result = true @@ -68,6 +74,8 @@ proc setOption*(parserOptions: PParserOptions, key: string, val=""): bool = of "prefix": parserOptions.prefixes.add(val) of "suffix": parserOptions.suffixes.add(val) of "skipinclude": incl(parserOptions.flags, pfSkipInclude) + of "typeprefixes": incl(parserOptions.flags, pfTypePrefixes) + of "skipcomments": incl(parserOptions.flags, pfSkipComments) else: result = false proc ParseUnit*(p: var TParser): PNode @@ -190,8 +198,9 @@ proc parLineInfo(p: TParser): TLineInfo = proc skipComAux(p: var TParser, n: PNode) = if (n != nil): - if n.comment == nil: n.comment = p.tok.s - else: add(n.comment, "\n" & p.tok.s) + if pfSkipComments notin p.options.flags: + if n.comment == nil: n.comment = p.tok.s + else: add(n.comment, "\n" & p.tok.s) else: parMessage(p, warnCommentXIgnored, p.tok.s) getTok(p) @@ -257,20 +266,31 @@ proc newIdentNodeP(ident: PIdent, p: TParser): PNode = proc newIdentNodeP(ident: string, p: TParser): PNode = result = newIdentNodeP(getIdent(ident), p) +proc mangleRules(s: string, p: TParser): string = + block mangle: + for pattern, frmt in items(p.options.mangleRules): + if s.match(pattern): + result = s.replace(pattern, frmt) + break mangle + block prefixes: + for prefix in items(p.options.prefixes): + if s.startsWith(prefix): + result = s.copy(prefix.len) + break prefixes + result = s + block suffixes: + for suffix in items(p.options.suffixes): + if result.endsWith(suffix): + setLen(result, result.len - suffix.len) + break suffixes + proc mangleName(s: string, p: TParser): string = - for pattern, frmt in items(p.options.mangleRules): - if s.match(pattern): - return s.replace(pattern, frmt) - block prefixes: - for prefix in items(p.options.prefixes): - if s.startsWith(prefix): - result = s.copy(prefix.len) - break prefixes - result = s - for suffix in items(p.options.suffixes): - if result.endsWith(suffix): - setLen(result, result.len - suffix.len) - break + if p.options.toMangle.hasKey(s): result = p.options.toMangle[s] + else: result = mangleRules(s, p) + +proc isPrivate(s: string, p: TParser): bool = + for pattern in items(p.options.privateRules): + if s.match(pattern): return true proc mangledIdent(ident: string, p: TParser): PNode = result = newNodeP(nkIdent, p) @@ -293,16 +313,16 @@ proc addImportToPragma(pragmas: PNode, ident: string, p: TParser) = else: addSon(pragmas, newIdentStrLitPair("header", p.options.header, p)) -proc exportSym(p: TParser, i: PNode): PNode = +proc exportSym(p: TParser, i: PNode, origName: string): PNode = assert i.kind == nkIdent - if p.scopeCounter == 0: + if p.scopeCounter == 0 and not isPrivate(origName, p): result = newNodeI(nkPostfix, i.info) addSon(result, newIdentNode(getIdent("*"), i.info), i) else: result = i proc varIdent(ident: string, p: TParser): PNode = - result = exportSym(p, mangledIdent(ident, p)) + result = exportSym(p, mangledIdent(ident, p), ident) if p.scopeCounter > 0: return if p.options.dynlibSym.len > 0 or p.options.header.len > 0: var a = result @@ -313,7 +333,7 @@ proc varIdent(ident: string, p: TParser): PNode = addImportToPragma(pragmas, ident, p) proc fieldIdent(ident: string, p: TParser): PNode = - result = exportSym(p, mangledIdent(ident, p)) + result = exportSym(p, mangledIdent(ident, p), ident) if p.scopeCounter > 0: return if p.options.header.len > 0: var a = result @@ -340,8 +360,30 @@ proc skipIdent(p: var TParser): PNode = proc skipIdentExport(p: var TParser): PNode = expectIdent(p) - result = exportSym(p, mangledIdent(p.tok.s, p)) + result = exportSym(p, mangledIdent(p.tok.s, p), p.tok.s) getTok(p, result) + +proc skipTypeIdentExport(p: var TParser, prefix='T'): PNode = + expectIdent(p) + var n = prefix & mangleName(p.tok.s, p) + p.options.toMangle[p.tok.s] = n + var i = newNodeP(nkIdent, p) + i.ident = getIdent(n) + result = exportSym(p, i, p.tok.s) + getTok(p, result) + +proc markTypeIdent(p: var TParser, typ: PNode) = + if pfTypePrefixes in p.options.flags: + var prefix = "" + if typ == nil: + prefix = "T" + else: + var t = typ + while t != nil and t.kind in {nkVarTy, nkPtrTy, nkRefTy}: + prefix.add('P') + t = t.sons[0] + expectIdent(p) + p.options.toMangle[p.tok.s] = prefix & mangleRules(p.tok.s, p) # --------------- parser ----------------------------------------------------- # We use this parsing rule: If it looks like a declaration, it is one. This @@ -394,12 +436,13 @@ proc typeAtom(p: var TParser): PNode = elif isIntType(p.tok.s): var x = "c" & p.tok.s getTok(p, nil) - while p.tok.xkind == pxSymbol and (isIntType(p.tok.s) or p.tok.s == "char"): + while p.tok.xkind == pxSymbol and + (isIntType(p.tok.s) or p.tok.s == "char"): add(x, p.tok.s) getTok(p, nil) - result = newIdentNodeP(x, p) + result = mangledIdent(x, p) else: - result = newIdentNodeP(p.tok.s, p) + result = mangledIdent(p.tok.s, p) getTok(p, result) proc newPointerTy(p: TParser, typ: PNode): PNode = @@ -528,7 +571,7 @@ proc parseStructBody(p: var TParser, isUnion: bool, proc structPragmas(p: TParser, name: PNode, origName: string): PNode = assert name.kind == nkIdent result = newNodeP(nkPragmaExpr, p) - addson(result, exportSym(p, name)) + addson(result, exportSym(p, name, origName)) var pragmas = newNodep(nkPragma, p) addSon(pragmas, newIdentNodeP("pure", p), newIdentNodeP("final", p)) if p.options.header.len > 0: @@ -541,7 +584,8 @@ proc enumPragmas(p: TParser, name: PNode): PNode = addson(result, name) var pragmas = newNodep(nkPragma, p) var e = newNodeP(nkExprColonExpr, p) - addSon(e, newIdentNodeP("size", p), newIntNodeP(nkIntLit, 4, p)) + # HACK: sizeof(cint) should be constructed as AST + addSon(e, newIdentNodeP("size", p), newIdentNodeP("sizeof(cint)", p)) addSon(pragmas, e) addSon(result, pragmas) @@ -608,6 +652,7 @@ proc parseFunctionPointerDecl(p: var TParser, rettyp: PNode): PNode = parseCallConv(p, pragmas) if p.tok.xkind == pxStar: getTok(p, params) else: parMessage(p, errTokenExpected, "*") + if p.inTypeDef > 0: markTypeIdent(p, nil) var name = skipIdentExport(p) eat(p, pxParRi, name) parseFormalParams(p, params, pragmas) @@ -633,17 +678,18 @@ proc otherTypeDef(p: var TParser, section, typ: PNode) = case p.tok.xkind of pxParLe: # function pointer: typedef typ (*name)(); - getTok(p, nil) var x = parseFunctionPointerDecl(p, typ) name = x[0] t = x[2] of pxStar: # typedef typ *b; t = pointer(p, typ) + markTypeIdent(p, t) name = skipIdentExport(p) else: # typedef typ name; t = typ + markTypeIdent(p, t) name = skipIdentExport(p) t = parseTypeSuffix(p, t) addTypeDef(section, name, t) @@ -652,6 +698,7 @@ proc parseTrailingDefinedTypes(p: var TParser, section, typ: PNode) = while p.tok.xkind == pxComma: getTok(p, nil) var newTyp = pointer(p, typ) + markTypeIdent(p, newTyp) var newName = skipIdentExport(p) newTyp = parseTypeSuffix(p, newTyp) addTypeDef(section, newName, newTyp) @@ -678,11 +725,13 @@ proc parseTypedefStruct(p: var TParser, result: PNode, isUnion: bool) = if p.tok.xkind == pxCurlyLe: var t = parseStruct(p, isUnion) var origName = p.tok.s + markTypeIdent(p, nil) var name = skipIdent(p) addTypeDef(result, structPragmas(p, name, origName), t) parseTrailingDefinedTypes(p, result, name) elif p.tok.xkind == pxSymbol: # name to be defined or type "struct a", we don't know yet: + markTypeIdent(p, nil) var origName = p.tok.s var nameOrType = skipIdent(p) case p.tok.xkind @@ -691,6 +740,7 @@ proc parseTypedefStruct(p: var TParser, result: PNode, isUnion: bool) = if p.tok.xkind == pxSymbol: # typedef struct tagABC {} abc, *pabc; # --> abc is a better type name than tagABC! + markTypeIdent(p, nil) var origName = p.tok.s var name = skipIdent(p) addTypeDef(result, structPragmas(p, name, origName), t) @@ -716,11 +766,15 @@ proc parseTypedefEnum(p: var TParser, result: PNode) = getTok(p, result) var t = enumFields(p) eat(p, pxCurlyRi, t) + var origName = p.tok.s + markTypeIdent(p, nil) var name = skipIdent(p) - addTypeDef(result, enumPragmas(p, exportSym(p, name)), t) + addTypeDef(result, enumPragmas(p, exportSym(p, name, origName)), t) parseTrailingDefinedTypes(p, result, name) elif p.tok.xkind == pxSymbol: # name to be defined or type "enum a", we don't know yet: + markTypeIdent(p, nil) + var origName = p.tok.s var nameOrType = skipIdent(p) case p.tok.xkind of pxCurlyLe: @@ -730,11 +784,14 @@ proc parseTypedefEnum(p: var TParser, result: PNode) = if p.tok.xkind == pxSymbol: # typedef enum tagABC {} abc, *pabc; # --> abc is a better type name than tagABC! + markTypeIdent(p, nil) + var origName = p.tok.s var name = skipIdent(p) - addTypeDef(result, enumPragmas(p, exportSym(p, name)), t) + addTypeDef(result, enumPragmas(p, exportSym(p, name, origName)), t) parseTrailingDefinedTypes(p, result, name) else: - addTypeDef(result, enumPragmas(p, exportSym(p, nameOrType)), t) + addTypeDef(result, + enumPragmas(p, exportSym(p, nameOrType, origName)), t) of pxSymbol: # typedef enum a a? if mangleName(p.tok.s, p) == nameOrType.ident.s: @@ -862,7 +919,7 @@ proc declaration(p: var TParser): PNode = addSon(pragmas, newIdentNodeP("cdecl", p)) elif pfStdcall in p.options.flags: addSon(pragmas, newIdentNodeP("stdcall", p)) - addSon(result, exportSym(p, name), nil) # no generics + addSon(result, exportSym(p, name, origName), nil) # no generics addSon(result, params, pragmas) case p.tok.xkind of pxSemicolon: @@ -884,9 +941,11 @@ proc createConst(name, typ, val: PNode, p: TParser): PNode = addSon(result, name, typ, val) proc enumSpecifier(p: var TParser): PNode = + saveContext(p) getTok(p, nil) # skip "enum" case p.tok.xkind of pxCurlyLe: + closeContext(p) # make a const section out of it: result = newNodeP(nkConstSection, p) getTok(p, result) @@ -909,17 +968,31 @@ proc enumSpecifier(p: var TParser): PNode = eat(p, pxCurlyRi, result) eat(p, pxSemicolon) of pxSymbol: + var origName = p.tok.s + markTypeIdent(p, nil) result = skipIdent(p) - if p.tok.xkind == pxCurlyLe: + case p.tok.xkind + of pxCurlyLe: + closeContext(p) var name = result # create a type section containing the enum result = newNodeP(nkTypeSection, p) var t = newNodeP(nkTypeDef, p) getTok(p, t) var e = enumFields(p) - addSon(t, exportSym(p, name), nil, e) # nil for generic params + addSon(t, exportSym(p, name, origName), nil, e) # nil for generic params addSon(result, t) + eat(p, pxCurlyRi, result) + eat(p, pxSemicolon) + of pxSemicolon: + # just ignore ``enum X;`` for now. + closeContext(p) + getTok(p, nil) + else: + backtrackContext(p) + result = declaration(p) else: + closeContext(p) parMessage(p, errTokenExpected, "{") # Expressions @@ -1415,9 +1488,10 @@ proc parseStandaloneStruct(p: var TParser, isUnion: bool): PNode = getTok(p, result) # skip "struct" or "union" var origName = "" if p.tok.xkind == pxSymbol: + markTypeIdent(p, nil) origName = p.tok.s getTok(p, result) - if p.tok.xkind == pxCurlyLe: + if p.tok.xkind in {pxCurlyLe, pxSemiColon}: if origName.len > 0: var name = mangledIdent(origName, p) var t = parseStruct(p, isUnion) diff --git a/rod/c2nim/cpp.nim b/rod/c2nim/cpp.nim index dc4e7249e..5f304e1f8 100755 --- a/rod/c2nim/cpp.nim +++ b/rod/c2nim/cpp.nim @@ -25,7 +25,8 @@ proc skipLine(p: var TParser) = 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))): + (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}: @@ -272,22 +273,25 @@ proc parseIfDir(p: var TParser): PNode = eatNewLine(p, nil) parseIfDirAux(p, result) -proc parseMangleDir(p: var TParser) = +proc parsePegLit(p: var TParser): TPeg = var col = getColumn(p.lex) + 2 getTok(p) if p.tok.xkind != pxStrLit: ExpectIdent(p) try: - var pattern = parsePeg( - input = p.tok.s, + 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) - if p.tok.xkind != pxStrLit: ExpectIdent(p) - p.options.mangleRules.add((pattern, p.tok.s)) - 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 = @@ -298,7 +302,7 @@ proc parseDir(p: var TParser): PNode = of "ifdef": result = parseIfdef(p) of "ifndef": result = parseIfndef(p) of "if": result = parseIfDir(p) - of "cdecl", "stdcall", "ref", "skipinclude": + of "cdecl", "stdcall", "ref", "skipinclude", "typeprefixes", "skipcomments": discard setOption(p.options, p.tok.s) getTok(p) eatNewLine(p, nil) @@ -315,6 +319,10 @@ proc parseDir(p: var TParser): PNode = 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) diff --git a/rod/c2nim/manual.txt b/rod/c2nim/manual.txt index bb89c9567..7aa8b557b 100755 --- a/rod/c2nim/manual.txt +++ b/rod/c2nim/manual.txt @@ -1,9 +1,9 @@ ================================= c2nim User's manual ================================= - -:Author: Andreas Rumpf -:Version: 0.8.10 + +:Author: Andreas Rumpf +:Version: |nimrodversion| .. contents:: @@ -211,6 +211,47 @@ identifier should be converted: .. code-block:: C #mangle "'GTK_'{.*}" "TGtk$1" +For convenience the PEG pattern and the replacement can be a single identifiers +too, there is no need to quote them: + +.. code-block:: C + #mangle ssize_t int + // is short for: + #mangle "'ssize_t'" "int" + + +``#private`` directive +---------------------- + +By default c2nim marks every top level identifier (proc name, variable, etc.) +as exported (the export marker is ``*`` in Nimrod). With the ``#private`` +directive identifiers can be marked as private so that the resulting Nimrod +module does not export them. The ``#private`` directive takes a PEG pattern: + +.. code-block:: C + #private "@('_'!.)" // all identifiers ending in '_' are private + +Note: The pattern refers to the original C identifiers, not to the resulting +identifiers after mangling! + + +``#skipcomments`` directive +--------------------------- +**Note**: There is also a ``--skipcomments`` command line option that can be +used for the same purpose. + +The ``#skipcomments`` directive can be put into the C code to make c2nim +ignore comments and not copy them into the generated Nimrod file. + + +``#typeprefixes`` directive +--------------------------- +**Note**: There is also a ``--typeprefixes`` command line option that can be +used for the same purpose. + +The ``#typeprefixes`` directive can be put into the C code to make c2nim +generate the ``T`` or ``P`` prefix for every defined type. + ``#def`` directive ------------------ diff --git a/rod/c2nim/tests/systest.c b/rod/c2nim/tests/systest.c index b2b7646bb..7f689c002 100755 --- a/rod/c2nim/tests/systest.c +++ b/rod/c2nim/tests/systest.c @@ -9,32 +9,64 @@ extern "C" { # endif #endif +#private expatDll + +#if !defined(expatDll) +# if defined(windows) +# define expatDll "expat.dll" +# elif defined(macosx) +# define expatDll "libexpat.dynlib" +# else +# define expatDll "libexpat.so(.1|)" +# endif +#endif + +#mangle "'XML_'{.*}" "$1" +#private "'XML_ParserStruct'" + +#mangle cunsignedint cint + +unsigned int uiVar; + +#private "@('_'!.)" +unsigned int myPrivateVar__; + + +struct XML_ParserStruct; + +#def XMLCALL __cdecl + +typedef void (XMLCALL *XML_ElementDeclHandler) (void *userData, + const XML_Char *name, + XML_Content *model); + + void* x; void* fn(void); void (*fn)(void); void* (*fn)(void); void* (*fn)(void*); -/* - * Very ugly real world code ahead: - */ +/* + * Very ugly real world code ahead: + */ #def JMETHOD(rettype, name, params) rettype (*name) params - -typedef struct cjpeg_source_struct * cjpeg_source_ptr; - -struct cjpeg_source_struct { - JMETHOD(void, start_input, (j_compress_ptr cinfo, - cjpeg_source_ptr sinfo)); - JMETHOD(JDIMENSION, get_pixel_rows, (j_compress_ptr cinfo, - cjpeg_source_ptr sinfo)); - JMETHOD(void, finish_input, (j_compress_ptr cinfo, - cjpeg_source_ptr sinfo)); - - FILE *input_file; - - JSAMPARRAY buffer; - JDIMENSION buffer_height; + +typedef struct cjpeg_source_struct * cjpeg_source_ptr; + +struct cjpeg_source_struct { + JMETHOD(void, start_input, (j_compress_ptr cinfo, + cjpeg_source_ptr sinfo)); + JMETHOD(JDIMENSION, get_pixel_rows, (j_compress_ptr cinfo, + cjpeg_source_ptr sinfo)); + JMETHOD(void, finish_input, (j_compress_ptr cinfo, + cjpeg_source_ptr sinfo)); + + FILE *input_file; + + JSAMPARRAY buffer; + JDIMENSION buffer_height; }; // Test standalone structs: |