diff options
-rwxr-xr-x | rod/c2nim/c2nim.nim | 1 | ||||
-rwxr-xr-x | rod/c2nim/cparse.nim | 261 | ||||
-rwxr-xr-x | rod/c2nim/cpp.nim | 14 | ||||
-rw-r--r-- | rod/c2nim/manual.txt | 20 | ||||
-rw-r--r-- | rod/c2nim/tests/systest.c | 60 |
5 files changed, 270 insertions, 86 deletions
diff --git a/rod/c2nim/c2nim.nim b/rod/c2nim/c2nim.nim index 118ff608d..fd1e227df 100755 --- a/rod/c2nim/c2nim.nim +++ b/rod/c2nim/c2nim.nim @@ -28,6 +28,7 @@ Options: (multiple --prefix options are supported) --suffix:SUFFIX strip suffix for the generated Nimrod identifiers (multiple --suffix options are supported) + --skipinclude do not convert ``#include`` to ``import`` -v, --version write c2nim's version -h, --help show this help """ diff --git a/rod/c2nim/cparse.nim b/rod/c2nim/cparse.nim index 1a0215735..a96aece3c 100755 --- a/rod/c2nim/cparse.nim +++ b/rod/c2nim/cparse.nim @@ -11,8 +11,6 @@ ## 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 standalone structs and unions! - import os, llstream, rnimsyn, clex, idents, strutils, pegs, ast, astalgo, msgs, options, strtabs @@ -21,7 +19,8 @@ type TParserFlag = enum pfRefs, ## use "ref" instead of "ptr" for C's typ* pfCDecl, ## annotate procs with cdecl - pfStdCall ## annotate procs with stdcall + pfStdCall, ## annotate procs with stdcall + pfSkipInclude ## skip all ``#include`` TMacro {.final.} = object name: string @@ -66,6 +65,7 @@ proc setOption*(parserOptions: PParserOptions, key: string, val=""): bool = of "stdcall": incl(parserOptions.flags, pfStdCall) of "prefix": parserOptions.prefixes.add(val) of "suffix": parserOptions.suffixes.add(val) + of "skipinclude": incl(parserOptions.flags, pfSkipInclude) else: result = false proc ParseUnit*(p: var TParser): PNode @@ -87,7 +87,7 @@ proc parMessage(p: TParser, msg: TMsgKind, arg = "") = lexMessage(p.lex, msg, arg) proc CloseParser(p: var TParser) = CloseLexer(p.lex) -proc safeContext(p: var TParser) = p.backtrack.add(p.tok) +proc saveContext(p: var TParser) = p.backtrack.add(p.tok) proc closeContext(p: var TParser) = discard p.backtrack.pop() proc backtrackContext(p: var TParser) = p.tok = p.backtrack.pop() @@ -120,7 +120,7 @@ proc parseMacroArguments(p: var TParser): seq[seq[ref TToken]] = result.add(@[]) var i: array[pxParLe..pxCurlyLe, int] var L = 0 - safeContext(p) + saveContext(p) while true: var kind = p.tok.xkind case kind @@ -166,9 +166,11 @@ proc expandMacro(p: var TParser, m: TMacro) = for tok in items(m.body): if tok.xkind == pxMacroParam: for t in items(arguments[int(tok.iNumber)]): + #echo "t: ", t^ lastTok.next = t lastTok = t else: + #echo "tok: ", tok^ lastTok.next = tok lastTok = tok lastTok.next = p.tok @@ -184,23 +186,19 @@ proc getTok(p: var TParser) = proc parLineInfo(p: TParser): TLineInfo = result = getLineInfo(p.lex) +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) + else: + parMessage(p, warnCommentXIgnored, p.tok.s) + getTok(p) + proc skipCom(p: var TParser, n: PNode) = - while p.tok.xkind in {pxLineComment, pxStarComment}: - if (n != nil): - 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) + while p.tok.xkind in {pxLineComment, pxStarComment}: skipcomAux(p, n) proc skipStarCom(p: var TParser, n: PNode) = - while p.tok.xkind == pxStarComment: - if (n != nil): - 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) + while p.tok.xkind == pxStarComment: skipComAux(p, n) proc getTok(p: var TParser, n: PNode) = getTok(p) @@ -382,7 +380,6 @@ proc skipConst(p: var TParser) = getTok(p, nil) proc typeAtom(p: var TParser): PNode = - if p.tok.xkind != pxSymbol: return nil skipConst(p) ExpectIdent(p) case p.tok.s @@ -428,51 +425,99 @@ proc pointer(p: var TParser, a: PNode): PNode = result = newIdentNodeP("pointer", p) for j in 1..i-1: result = newPointerTy(p, result) +proc newProcPragmas(p: TParser): PNode = + result = newNodeP(nkPragma, p) + if pfCDecl in p.options.flags: + addSon(result, newIdentNodeP("cdecl", p)) + elif pfStdCall in p.options.flags: + addSon(result, newIdentNodeP("stdcall", p)) + +proc addPragmas(father, pragmas: PNode) = + if sonsLen(pragmas) > 0: addSon(father, pragmas) + else: addSon(father, nil) + +proc addReturnType(params, rettyp: PNode) = + if rettyp == nil or rettyp.kind != nkNilLit: addSon(params, rettyp) + else: addson(params, nil) + +proc parseFormalParams(p: var TParser, params, pragmas: PNode) + proc parseTypeSuffix(p: var TParser, typ: PNode): PNode = result = typ - while p.tok.xkind == pxBracketLe: - getTok(p, result) - skipConst(p) # POSIX contains: ``int [restrict]`` - if p.tok.xkind != pxBracketRi: - var tmp = result - var index = expression(p) - # array type: - result = newNodeP(nkBracketExpr, p) - addSon(result, newIdentNodeP("array", p)) - var r = newNodeP(nkRange, p) - addSon(r, newIntNodeP(nkIntLit, 0, p)) - addSon(r, newBinary("-", index, newIntNodeP(nkIntLit, 1, p), p)) - addSon(result, r) - addSon(result, tmp) - else: - # pointer type: - var tmp = result - if pfRefs in p.options.flags: - result = newNodeP(nkRefTy, p) + while true: + case p.tok.xkind + of pxBracketLe: + getTok(p, result) + skipConst(p) # POSIX contains: ``int [restrict]`` + if p.tok.xkind != pxBracketRi: + var tmp = result + var index = expression(p) + # array type: + result = newNodeP(nkBracketExpr, p) + addSon(result, newIdentNodeP("array", p)) + var r = newNodeP(nkRange, p) + addSon(r, newIntNodeP(nkIntLit, 0, p)) + addSon(r, newBinary("-", index, newIntNodeP(nkIntLit, 1, p), p)) + addSon(result, r) + addSon(result, tmp) else: - result = newNodeP(nkPtrTy, p) - result.addSon(tmp) - eat(p, pxBracketRi, result) + # pointer type: + var tmp = result + if pfRefs in p.options.flags: + result = newNodeP(nkRefTy, p) + else: + result = newNodeP(nkPtrTy, p) + result.addSon(tmp) + eat(p, pxBracketRi, result) + of pxParLe: + # function pointer: + var procType = newNodeP(nkProcTy, p) + var pragmas = newProcPragmas(p) + var params = newNodeP(nkFormalParams, p) + addReturnType(params, result) + parseFormalParams(p, params, pragmas) + addSon(procType, params) + addPragmas(procType, pragmas) + result = procType + else: break proc typeDesc(p: var TParser): PNode = - result = typeAtom(p) - if result != nil: - result = pointer(p, result) + #result = typeAtom(p) + #if result != nil: + # result = pointer(p, result) + result = pointer(p, typeAtom(p)) -proc parseStructBody(p: var TParser): PNode = - result = newNodeP(nkRecList, p) +proc parseField(p: var TParser, kind: TNodeKind): PNode = + if p.tok.xkind == pxParLe: + getTok(p, nil) + while p.tok.xkind == pxStar: getTok(p, nil) + result = parseField(p, kind) + eat(p, pxParRi, result) + else: + expectIdent(p) + if kind == nkRecList: result = fieldIdent(p.tok.s, p) + else: result = mangledIdent(p.tok.s, p) + getTok(p, result) + +proc takeOnlyFirstField(p: TParser, isUnion: bool): bool = + # if we generate an interface to a header file, *all* fields can be + # generated: + result = isUnion and p.options.header.len == 0 + +proc parseStructBody(p: var TParser, isUnion: bool, + kind: TNodeKind = nkRecList): PNode = + result = newNodeP(kind, p) eat(p, pxCurlyLe, result) while p.tok.xkind notin {pxEof, pxCurlyRi}: var baseTyp = typeAtom(p) while true: var def = newNodeP(nkIdentDefs, p) var t = pointer(p, baseTyp) - expectIdent(p) - var i = fieldIdent(p.tok.s, p) - getTok(p, i) + var i = parseField(p, kind) t = parseTypeSuffix(p, t) addSon(def, i, t, nil) - addSon(result, def) + if not takeOnlyFirstField(p, isUnion) or sonsLen(result) < 1: + addSon(result, def) if p.tok.xkind != pxComma: break getTok(p, def) eat(p, pxSemicolon, lastSon(result)) @@ -500,12 +545,12 @@ proc enumPragmas(p: TParser, name: PNode): PNode = addSon(pragmas, e) addSon(result, pragmas) -proc parseStruct(p: var TParser): PNode = +proc parseStruct(p: var TParser, isUnion: bool): PNode = result = newNodeP(nkObjectTy, p) addSon(result, nil) # no pragmas addSon(result, nil) # no inheritance if p.tok.xkind == pxCurlyLe: - addSon(result, parseStructBody(p)) + addSon(result, parseStructBody(p, isUnion)) else: addSon(result, newNodeP(nkRecList, p)) @@ -558,14 +603,10 @@ proc parseCallConv(p: var TParser, pragmas: PNode) = proc parseFunctionPointerDecl(p: var TParser, rettyp: PNode): PNode = var procType = newNodeP(nkProcTy, p) - var pragmas = newNodeP(nkPragma, p) - if pfCDecl in p.options.flags: - addSon(pragmas, newIdentNodeP("cdecl", p)) - elif pfStdCall in p.options.flags: - addSon(pragmas, newIdentNodeP("stdcall", p)) + var pragmas = newProcPragmas(p) var params = newNodeP(nkFormalParams, p) eat(p, pxParLe, params) - addSon(params, rettyp) + addReturnType(params, rettyp) parseCallConv(p, pragmas) if p.tok.xkind == pxStar: getTok(p, params) else: parMessage(p, errTokenExpected, "*") @@ -573,7 +614,7 @@ proc parseFunctionPointerDecl(p: var TParser, rettyp: PNode): PNode = eat(p, pxParRi, name) parseFormalParams(p, params, pragmas) addSon(procType, params) - addSon(procType, pragmas) + addPragmas(procType, pragmas) if p.inTypeDef == 0: result = newNodeP(nkVarSection, p) @@ -639,10 +680,10 @@ proc enumFields(p: var TParser): PNode = if p.tok.xkind != pxComma: break getTok(p, e) -proc parseTypedefStruct(p: var TParser, result: PNode) = +proc parseTypedefStruct(p: var TParser, result: PNode, isUnion: bool) = getTok(p, result) if p.tok.xkind == pxCurlyLe: - var t = parseStruct(p) + var t = parseStruct(p, isUnion) var origName = p.tok.s var name = skipIdent(p) addTypeDef(result, structPragmas(p, name, origName), t) @@ -653,7 +694,7 @@ proc parseTypedefStruct(p: var TParser, result: PNode) = var nameOrType = skipIdent(p) case p.tok.xkind of pxCurlyLe: - var t = parseStruct(p) + var t = parseStruct(p, isUnion) if p.tok.xkind == pxSymbol: # typedef struct tagABC {} abc, *pabc; # --> abc is a better type name than tagABC! @@ -721,7 +762,8 @@ proc parseTypeDef(p: var TParser): PNode = inc(p.inTypeDef) expectIdent(p) case p.tok.s - of "struct", "union": parseTypedefStruct(p, result) + of "struct": parseTypedefStruct(p, result, isUnion=false) + of "union": parseTypedefStruct(p, result, isUnion=true) of "enum": parseTypedefEnum(p, result) else: var t = typeAtom(p) @@ -773,7 +815,24 @@ proc parseVarDecl(p: var TParser, baseTyp, typ: PNode, addSon(def, parseTypeSuffix(p, t)) addInitializer(p, def) addSon(result, def) - eat(p, pxSemicolon, result) + eat(p, pxSemicolon) + +when false: + proc declaration(p: var TParser, father: PNode) = + # general syntax to parse is:: + # + # baseType ::= typeIdent | ((struct|union|enum) ident ("{" body "}" )? + # | "{" body "}") + # declIdent ::= "(" "*" ident ")" formalParams ("=" exprNoComma)? + # | ident ((formalParams ("{" statements "}")?)|"=" + # exprNoComma|(typeSuffix("=" exprNoComma)? ))? + # declaration ::= baseType (pointers)? declIdent ("," declIdent)* + var pragmas = newNodeP(nkPragma, p) + + skipDeclarationSpecifiers(p) + parseCallConv(p, pragmas) + skipDeclarationSpecifiers(p) + expectIdent(p) proc declaration(p: var TParser): PNode = result = newNodeP(nkProcDef, p) @@ -785,7 +844,6 @@ proc declaration(p: var TParser): PNode = expectIdent(p) var baseTyp = typeAtom(p) var rettyp = pointer(p, baseTyp) - if rettyp != nil and rettyp.kind == nkNilLit: rettyp = nil skipDeclarationSpecifiers(p) parseCallConv(p, pragmas) skipDeclarationSpecifiers(p) @@ -794,7 +852,7 @@ proc declaration(p: var TParser): PNode = # Function pointer declaration: This is of course only a heuristic, but the # best we can do here. result = parseFunctionPointerDecl(p, rettyp) - eat(p, pxSemicolon, result) + eat(p, pxSemicolon) return ExpectIdent(p) var origName = p.tok.s @@ -804,7 +862,7 @@ proc declaration(p: var TParser): PNode = # really a function! var name = mangledIdent(origName, p) var params = newNodeP(nkFormalParams, p) - addSon(params, rettyp) + addReturnType(params, rettyp) parseFormalParams(p, params, pragmas) if pfCDecl in p.options.flags: @@ -901,7 +959,7 @@ proc isDefinitelyAType(p: var TParser): bool = proc castExpression(p: var TParser): PNode = if p.tok.xkind == pxParLe: - SafeContext(p) + saveContext(p) result = newNodeP(nkCast, p) getTok(p, result) var t = isDefinitelyAType(p) @@ -1128,7 +1186,7 @@ proc incdec(p: var TParser, opr: string, a: PNode): PNode = addSon(result, b) proc assignmentExpression(p: var TParser): PNode = - safeContext(p) + saveContext(p) var a = lvalue(p) case p.tok.xkind of pxAsgn: @@ -1325,7 +1383,7 @@ proc declarationOrStatement(p: var TParser): PNode = result = declaration(p) else: # ordinary identifier: - safeContext(p) + saveContext(p) getTok(p) # skip identifier to look ahead case p.tok.xkind of pxSymbol, pxStar: @@ -1345,6 +1403,49 @@ proc declarationOrStatement(p: var TParser): PNode = backtrackContext(p) result = expressionStatement(p) +proc parseTuple(p: var TParser, isUnion: bool): PNode = + result = parseStructBody(p, isUnion, nkTupleTy) + +proc parseTrailingDefinedIdents(p: var TParser, result, baseTyp: PNode) = + var varSection = newNodeP(nkVarSection, p) + while p.tok.xkind notin {pxEof, pxSemicolon}: + var t = pointer(p, baseTyp) + expectIdent(p) + var def = newNodeP(nkIdentDefs, p) + addSon(def, varIdent(p.tok.s, p)) + getTok(p, def) + addSon(def, parseTypeSuffix(p, t)) + addInitializer(p, def) + addSon(varSection, def) + if p.tok.xkind != pxComma: break + getTok(p, def) + eat(p, pxSemicolon) + if sonsLen(varSection) > 0: + addSon(result, varSection) + +proc parseStandaloneStruct(p: var TParser, isUnion: bool): PNode = + result = newNodeP(nkStmtList, p) + saveContext(p) + getTok(p, result) # skip "struct" or "union" + var origName = "" + if p.tok.xkind == pxSymbol: + origName = p.tok.s + getTok(p, result) + if p.tok.xkind == pxCurlyLe: + if origName.len > 0: + var name = mangledIdent(origName, p) + var t = parseStruct(p, isUnion) + var typeSection = newNodeP(nkTypeSection, p) + addTypeDef(typeSection, structPragmas(p, name, origName), t) + addSon(result, typeSection) + parseTrailingDefinedIdents(p, result, name) + else: + var t = parseTuple(p, isUnion) + parseTrailingDefinedIdents(p, result, t) + else: + backtrackContext(p) + result = declaration(p) + proc parseFor(p: var TParser, result: PNode) = # 'for' '(' expression_statement expression_statement expression? ')' # statement @@ -1425,11 +1526,21 @@ proc parseSwitch(p: var TParser): PNode = if b.kind == nkElse: break eat(p, pxCurlyRi) +proc addStmt(sl, a: PNode) = + # merge type sections is possible: + if a.kind != nkTypeSection or sonsLen(sl) == 0 or + lastSon(sl).kind != nkTypeSection: + addSon(sl, a) + else: + var ts = lastSon(sl) + for i in 0..sonsLen(a)-1: addSon(ts, a.sons[i]) + proc embedStmts(sl, a: PNode) = if a.kind != nkStmtList: - addSon(sl, a) + addStmt(sl, a) else: - for i in 0..sonsLen(a)-1: addSon(sl, a[i]) + for i in 0..sonsLen(a)-1: + if a[i] != nil: addStmt(sl, a[i]) proc compoundStatement(p: var TParser): PNode = result = newNodeP(nkStmtList, p) @@ -1493,6 +1604,10 @@ proc statement(p: var TParser): PNode = result = enumSpecifier(p) of "typedef": result = parseTypeDef(p) + of "struct": + result = parseStandaloneStruct(p, isUnion=false) + of "union": + result = parseStandaloneStruct(p, isUnion=true) else: result = declarationOrStatement(p) of pxCurlyLe: diff --git a/rod/c2nim/cpp.nim b/rod/c2nim/cpp.nim index 860b1c063..dc4e7249e 100755 --- a/rod/c2nim/cpp.nim +++ b/rod/c2nim/cpp.nim @@ -83,7 +83,7 @@ proc parseDefBody(p: var TParser, m: var TMacro, params: seq[string]) = # 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) + saveContext(p) while p.tok.xkind notin {pxEof, pxNewLine, pxLineComment}: case p.tok.xkind of pxSymbol: @@ -136,16 +136,14 @@ 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) + 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) - elif p.tok.xkind == pxLt: - while p.tok.xkind notin {pxEof, pxNewLine, pxLineComment}: getTok(p) + eatNewLine(p, nil) else: - parMessage(p, errXExpected, "string literal") - eatNewLine(p, nil) + skipLine(p) if sonsLen(result) == 0: # we only parsed includes that we chose to ignore: result = nil @@ -300,7 +298,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": + of "cdecl", "stdcall", "ref", "skipinclude": discard setOption(p.options, p.tok.s) getTok(p) eatNewLine(p, nil) diff --git a/rod/c2nim/manual.txt b/rod/c2nim/manual.txt index 3aca188b4..bb89c9567 100644 --- a/rod/c2nim/manual.txt +++ b/rod/c2nim/manual.txt @@ -84,6 +84,16 @@ is generated. They should be put into a ``#ifdef C2NIM`` section so that ordinary C compilers ignore them. +``#skipinclude`` directive +-------------------------- +**Note**: There is also a ``--skipinclude`` command line option that can be +used for the same purpose. + +By default, c2nim translates an ``#include`` that is not followed by ``<`` +(like in ``#include <stdlib>``) to a Nimrod ``import`` statement. This +directive tells c2nim to just skip any ``#include``. + + ``#stdcall`` and ``#cdecl`` directives -------------------------------------- **Note**: There are also ``--stdcall`` and ``--cdecl`` command line options @@ -225,17 +235,17 @@ that ``EXPORT`` is a macro that should be expanded by c2nim too: EXTERN(int) f(void); EXTERN(int) g(void); -``#def`` is very similar to C's ``#define``. - +``#def`` is very similar to C's ``#define``, so in general the macro definition +can be copied and pasted into a ``#def`` directive. Limitations =========== * C's ``,`` operator (comma operator) is not supported. -* C's ``union`` has no equivalent in Nimrod. -* Standalone ``struct x {}`` declarations are not implemented. Put them into - a ``typedef``. +* C's ``union`` are translated to Nimrod's objects and only the first field + is included in the object type. This way there is a high chance that it is + binary compatible to the union. * The condition in a ``do while(condition)`` statement must be ``0``. * Lots of other small issues... diff --git a/rod/c2nim/tests/systest.c b/rod/c2nim/tests/systest.c index 2d195ec0f..b2b7646bb 100644 --- a/rod/c2nim/tests/systest.c +++ b/rod/c2nim/tests/systest.c @@ -9,6 +9,66 @@ extern "C" { # endif #endif +void* x; +void* fn(void); +void (*fn)(void); +void* (*fn)(void); +void* (*fn)(void*); + +/* + * 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; +}; + +// Test standalone structs: + +union myunion { + char x, y, *z; + myint a, b; +} u; + +struct mystruct { + char x, y, *z; + myint a, b; +}; + +struct mystruct fn(i32 x, i64 y); + +struct mystruct { + char x, y, *z; + myint a, b; +} *myvar = NULL, **myvar2 = NULL; + +// anonymous struct: + +struct { + char x, y, *z; + myint a, b; +} varX, **varY; + +// empty anonymous struct: + +struct { + +} varX, **varY; + // Test C2NIM skipping: #define MASK(x) ((x) & 0xff) |