summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rwxr-xr-xrod/c2nim/c2nim.nim1
-rwxr-xr-xrod/c2nim/cparse.nim261
-rwxr-xr-xrod/c2nim/cpp.nim14
-rw-r--r--rod/c2nim/manual.txt20
-rw-r--r--rod/c2nim/tests/systest.c60
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)