summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/c2nim/c2nim.nim3
-rw-r--r--compiler/c2nim/clex.nim3
-rw-r--r--compiler/c2nim/cparse.nim515
-rw-r--r--compiler/c2nim/cpp.nim2
-rw-r--r--compiler/c2nim/tests/matrix.h241
-rw-r--r--doc/apis.txt1
-rw-r--r--doc/manual.txt2
-rw-r--r--web/news.txt8
8 files changed, 720 insertions, 55 deletions
diff --git a/compiler/c2nim/c2nim.nim b/compiler/c2nim/c2nim.nim
index 029f9ecda..df1e42f23 100644
--- a/compiler/c2nim/c2nim.nim
+++ b/compiler/c2nim/c2nim.nim
@@ -19,6 +19,7 @@ c2nim - C to Nimrod source converter
 Usage: c2nim [options] inputfile [options]
 Options:
   -o, --out:FILE         set output filename
+  --cpp                  process C++ input file
   --dynlib:SYMBOL        import from dynlib: SYMBOL will be used for the import
   --header:HEADER_FILE   import from a HEADER_FILE (discouraged!)
   --cdecl                annotate procs with ``{.cdecl.}``
@@ -31,6 +32,8 @@ Options:
   --skipinclude          do not convert ``#include`` to ``import``
   --typeprefixes         generate ``T`` and ``P`` type prefixes
   --skipcomments         do not copy comments
+  --ignoreRValueRefs     translate C++'s ``T&&`` to ``T`` instead ``of var T``
+  --keepBodies           keep C++'s method bodies
   -v, --version          write c2nim's version
   -h, --help             show this help
 """
diff --git a/compiler/c2nim/clex.nim b/compiler/c2nim/clex.nim
index 5b648392f..f949b97cb 100644
--- a/compiler/c2nim/clex.nim
+++ b/compiler/c2nim/clex.nim
@@ -82,6 +82,8 @@ type
     pxParLe, pxBracketLe, pxCurlyLe, # this order is important 
     pxParRi, pxBracketRi, pxCurlyRi, # for macro argument parsing!
     pxComma, pxSemiColon, pxColon,
+    pxAngleRi                 # '>' but determined to be the end of a
+                              # template's angle bracket
   TTokKinds* = set[TTokKind]
 
 type
@@ -202,6 +204,7 @@ proc TokKindToStr*(k: TTokKind): string =
   of pxColon: result = ":"
   of pxCurlyLe: result = "{"
   of pxCurlyRi: result = "}"
+  of pxAngleRi: result = "> [end of template]"
 
 proc `$`(tok: TToken): string = 
   case tok.xkind
diff --git a/compiler/c2nim/cparse.nim b/compiler/c2nim/cparse.nim
index b964ed976..6bb4451e4 100644
--- a/compiler/c2nim/cparse.nim
+++ b/compiler/c2nim/cparse.nim
@@ -11,7 +11,11 @@
 ## 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.
+# TODO
+# - implement handling of '::'
+# - C++'s "operator" still needs some love
+# - ignore 'using' statements
+# - support '#if' in classes
 
 import 
   os, llstream, renderer, clex, idents, strutils, pegs, ast, astalgo, msgs,
@@ -24,14 +28,17 @@ type
     pfStdCall,          ## annotate procs with stdcall
     pfSkipInclude,      ## skip all ``#include``
     pfTypePrefixes,     ## all generated types start with 'T' or 'P'
-    pfSkipComments      ## do not generate comments
+    pfSkipComments,     ## do not generate comments
+    pfCpp,              ## process C++
+    pfIgnoreRValueRefs, ## transform C++'s 'T&&' to 'T'
+    pfKeepBodies        ## do not skip C++ method bodies
   
-  TMacro {.final.} = object
+  TMacro = object
     name: string
     params: int           # number of parameters
     body: seq[ref TToken] # can contain pxMacroParam tokens
   
-  TParserOptions {.final.} = object
+  TParserOptions = object
     flags: set[TParserFlag]
     prefixes, suffixes: seq[string]
     mangleRules: seq[tuple[pattern: TPeg, frmt: string]]
@@ -41,7 +48,7 @@ type
     toMangle: PStringTable
   PParserOptions* = ref TParserOptions
   
-  TParser* {.final.} = object
+  TParser* = object
     lex: TLexer
     tok: ref TToken       # current token
     options: PParserOptions
@@ -49,6 +56,7 @@ type
     inTypeDef: int
     scopeCounter: int
     hasDeadCodeElimPragma: bool
+    currentClass: PNode   # type that needs to be added as 'this' parameter
   
   TReplaceTuple* = array[0..1, string]
 
@@ -66,7 +74,7 @@ proc newParserOptions*(): PParserOptions =
 
 proc setOption*(parserOptions: PParserOptions, key: string, val=""): bool = 
   result = true
-  case key
+  case key.normalize
   of "ref": incl(parserOptions.flags, pfRefs)
   of "dynlib": parserOptions.dynlibSym = val
   of "header": parserOptions.header = val
@@ -77,6 +85,9 @@ proc setOption*(parserOptions: PParserOptions, key: string, val=""): bool =
   of "skipinclude": incl(parserOptions.flags, pfSkipInclude)
   of "typeprefixes": incl(parserOptions.flags, pfTypePrefixes)
   of "skipcomments": incl(parserOptions.flags, pfSkipComments)
+  of "cpp": incl(parserOptions.flags, pfCpp)
+  of "keepbodies": incl(parserOptions.flags, pfKeepBodies)
+  of "ignorervaluerefs": incl(parserOptions.flags, pfIgnoreRValueRefs)
   else: result = false
 
 proc ParseUnit*(p: var TParser): PNode
@@ -94,6 +105,7 @@ proc OpenParser(p: var TParser, filename: string,
   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)
@@ -116,6 +128,13 @@ proc rawGetTok(p: var TParser) =
     p.tok.next = t
     p.tok = t
 
+proc insertAngleRi(currentToken: ref TToken) = 
+  var t: ref TToken
+  new(t)
+  t.xkind = pxAngleRi
+  t.next = currentToken.next
+  currentToken.next = t
+
 proc findMacro(p: TParser): int =
   for i in 0..high(p.options.macros):
     if p.tok.s == p.options.macros[i].name: return i
@@ -346,6 +365,14 @@ proc DoImport(ident: string, pragmas: PNode, p: TParser) =
   if p.options.dynlibSym.len > 0 or p.options.header.len > 0: 
     addImportToPragma(pragmas, ident, p)
 
+proc DoImportCpp(ident: string, pragmas: PNode, p: TParser) = 
+  if p.options.dynlibSym.len > 0 or p.options.header.len > 0:
+    addSon(pragmas, newIdentStrLitPair("importcpp", ident, p))
+    if p.options.dynlibSym.len > 0:
+      addSon(pragmas, newIdentPair("dynlib", p.options.dynlibSym, p))
+    else:
+      addSon(pragmas, newIdentStrLitPair("header", p.options.header, p))
+
 proc newBinary(opr: string, a, b: PNode, p: TParser): PNode =
   result = newNodeP(nkInfix, p)
   addSon(result, newIdentNodeP(getIdent(opr), p))
@@ -396,7 +423,7 @@ proc assignmentExpression(p: var TParser): PNode
 proc compoundStatement(p: var TParser): PNode
 proc statement(p: var TParser): PNode
 
-proc declKeyword(s: string): bool = 
+proc declKeyword(p: TParser, s: string): bool = 
   # returns true if it is a keyword that introduces a declaration
   case s
   of  "extern", "static", "auto", "register", "const", "volatile", "restrict",
@@ -404,6 +431,8 @@ proc declKeyword(s: string): bool =
       "__safecall", "void", "struct", "union", "enum", "typedef",
       "short", "int", "long", "float", "double", "signed", "unsigned", "char": 
     result = true
+  of "class":
+    result = p.options.flags.contains(pfCpp)
 
 proc stmtKeyword(s: string): bool =
   case s
@@ -423,6 +452,59 @@ proc skipConst(p: var TParser) =
       (p.tok.s == "const" or p.tok.s == "volatile" or p.tok.s == "restrict"): 
     getTok(p, nil)
 
+proc isTemplateAngleBracket(p: var TParser): bool =
+  if pfCpp notin p.options.flags: return false
+  saveContext(p)
+  getTok(p, nil) # skip "<"
+  var i: array[pxParLe..pxCurlyLe, int]
+  var angles = 0
+  while true:
+    let kind = p.tok.xkind
+    case kind
+    of pxEof: break
+    of pxParLe, pxBracketLe, pxCurlyLe: inc(i[kind])
+    of pxGt, pxAngleRi:
+      # end of arguments?
+      if i[pxParLe] == 0 and i[pxBracketLe] == 0 and i[pxCurlyLe] == 0 and
+          angles == 0:
+        # mark as end token:
+        p.tok.xkind = pxAngleRi
+        result = true; 
+        break
+      if angles > 0: dec(angles)
+    of pxShr:
+      # >> can end a template too:
+      if i[pxParLe] == 0 and i[pxBracketLe] == 0 and i[pxCurlyLe] == 0 and
+          angles == 1:
+        p.tok.xkind = pxAngleRi
+        insertAngleRi(p.tok)
+        result = true
+        break
+      if angles > 1: dec(angles, 2)
+    of pxLt: inc(angles)
+    of pxParRi, pxBracketRi, pxCurlyRi:
+      let kind = pred(kind, 3)
+      if i[kind] > 0: dec(i[kind])
+      else: break
+    of pxSemicolon: break
+    else: discard
+    getTok(p, nil)
+  backtrackContext(p)
+
+proc optAngle(p: var TParser, n: PNode): PNode =
+  if p.tok.xkind == pxLt and isTemplateAngleBracket(p):
+    getTok(p)
+    result = newNodeP(nkBracketExpr, p)
+    result.add(n)
+    while true:
+      let a = assignmentExpression(p)
+      if not a.isNil: result.add(a)
+      if p.tok.xkind != pxComma: break
+      getTok(p)
+    eat(p, pxAngleRi)
+  else:
+    result = n
+
 proc typeAtom(p: var TParser): PNode = 
   skipConst(p)
   ExpectIdent(p)
@@ -451,6 +533,7 @@ proc typeAtom(p: var TParser): PNode =
   else:
     result = mangledIdent(p.tok.s, p)
     getTok(p, result)
+    result = optAngle(p, result)
     
 proc newPointerTy(p: TParser, typ: PNode): PNode =
   if pfRefs in p.options.flags: 
@@ -458,16 +541,31 @@ proc newPointerTy(p: TParser, typ: PNode): PNode =
   else:
     result = newNodeP(nkPtrTy, p)
   result.addSon(typ)
-  
+
 proc pointer(p: var TParser, a: PNode): PNode = 
   result = a
   var i = 0
   skipConst(p)
-  while p.tok.xkind == pxStar:
-    inc(i)
-    getTok(p, result)
-    skipConst(p)
-    result = newPointerTy(p, result)
+  while true:
+    if p.tok.xkind == pxStar:
+      inc(i)
+      getTok(p, result)
+      skipConst(p)
+      result = newPointerTy(p, result)
+    elif p.tok.xkind == pxAmp and pfCpp in p.options.flags:
+      getTok(p, result)
+      skipConst(p)
+      let b = result
+      result = newNodeP(nkVarTy, p)
+      result.add(b)
+    elif p.tok.xkind == pxAmpAmp and pfCpp in p.options.flags:
+      getTok(p, result)
+      skipConst(p)
+      if pfIgnoreRvalueRefs notin p.options.flags:
+        let b = result
+        result = newNodeP(nkVarTy, p)
+        result.add(b)
+    else: break
   if a.kind == nkIdent and a.ident.s == "char": 
     if i >= 2: 
       result = newIdentNodeP("cstringArray", p)
@@ -553,9 +651,9 @@ 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 = 
+                     kind: TNodeKind = nkRecList): PNode =
   result = newNodeP(kind, p)
   eat(p, pxCurlyLe, result)
   while p.tok.xkind notin {pxEof, pxCurlyRi}:
@@ -682,7 +780,7 @@ proc addTypeDef(section, name, t: PNode) =
 proc otherTypeDef(p: var TParser, section, typ: PNode) = 
   var name: PNode
   var t = typ
-  if p.tok.xkind == pxStar:
+  if p.tok.xkind in {pxStar, pxAmp, pxAmpAmp}:
     t = pointer(p, t)
   if p.tok.xkind == pxParLe: 
     # function pointer: typedef typ (*name)();
@@ -819,6 +917,12 @@ proc parseTypeDef(p: var TParser): PNode =
     of "struct": parseTypedefStruct(p, result, isUnion=false)
     of "union": parseTypedefStruct(p, result, isUnion=true)
     of "enum": parseTypedefEnum(p, result)
+    of "class":
+      if pfCpp in p.options.flags:
+        parseTypedefStruct(p, result, isUnion=false)
+      else:
+        var t = typeAtom(p)
+        otherTypeDef(p, result, t)
     else: 
       var t = typeAtom(p)
       otherTypeDef(p, result, t)
@@ -871,22 +975,16 @@ proc parseVarDecl(p: var TParser, baseTyp, typ: PNode,
     addSon(result, def)
   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 declarationName(p: var TParser): string =
+  expectIdent(p)
+  result = p.tok.s
+  getTok(p) # skip identifier
+  while p.tok.xkind == pxScope and pfCpp in p.options.flags:
+    getTok(p) # skip "::"
+    expectIdent(p)
+    result.add("::")
+    result.add(p.tok.s)
+    getTok(p)
 
 proc declaration(p: var TParser): PNode = 
   result = newNodeP(nkProcDef, p)
@@ -908,17 +1006,17 @@ proc declaration(p: var TParser): PNode =
     result = parseFunctionPointerDecl(p, rettyp)
     eat(p, pxSemicolon)
     return
-  ExpectIdent(p)
-  var origName = p.tok.s
-  getTok(p) # skip identifier
-  case p.tok.xkind 
-  of pxParLe: 
+  var origName = declarationName(p)
+  case p.tok.xkind
+  of pxParLe:
     # really a function!
     var name = mangledIdent(origName, p)
     var params = newNodeP(nkFormalParams, p)
     addReturnType(params, rettyp)
     parseFormalParams(p, params, pragmas)
-    
+    if pfCpp in p.options.flags and p.tok.xkind == pxSymbol and
+        p.tok.s == "const":
+      addSon(pragmas, newIdentNodeP("noSideEffect", p))
     if pfCDecl in p.options.flags:
       addSon(pragmas, newIdentNodeP("cdecl", p))
     elif pfStdcall in p.options.flags:
@@ -1027,10 +1125,10 @@ proc isDefinitelyAType(p: var TParser): bool =
   while true:
     case p.tok.xkind 
     of pxSymbol:
-      if declKeyword(p.tok.s): return true
+      if declKeyword(p, p.tok.s): return true
       elif starFound: return false
       else: inc(words)
-    of pxStar:
+    of pxStar, pxAmp, pxAmpAmp:
       starFound = true
     of pxParRi: return words == 0 or words > 1 or starFound
     else: return false
@@ -1205,6 +1303,10 @@ proc postfixExpression(p: var TParser): PNode =
       addSon(result, newIdentNodeP("dec", p))
       gettok(p, result)
       addSon(result, a)
+    of pxLt:
+      if isTemplateAngleBracket(p):
+        result = optAngle(p, result)
+      else: break
     else: break
 
 proc unaryExpression(p: var TParser): PNode =
@@ -1222,9 +1324,24 @@ proc unaryExpression(p: var TParser): PNode =
       result = newNodeP(nkCall, p)
       addSon(result, newIdentNodeP("sizeof", p))
       getTok(p, result)
-      if p.tok.xkind == pxParLe: 
+      if p.tok.xkind == pxParLe:
         getTok(p, result)
-        addson(result, typeDesc(p))
+        addSon(result, typeDesc(p))
+        eat(p, pxParRi, result)
+      else:
+        addSon(result, unaryExpression(p))
+    elif p.tok.s == "new" or p.tok.s == "delete" and pfCpp in p.options.flags:
+      var opr = p.tok.s
+      result = newNodeP(nkCall, p)
+      getTok(p, result)
+      if p.tok.xkind == pxBracketLe:
+        getTok(p)
+        eat(p, pxBracketRi)
+        opr.add("Array")
+      addSon(result, newIdentNodeP(opr, p))
+      if p.tok.xkind == pxParLe:
+        getTok(p, result)
+        addSon(result, typeDesc(p))
         eat(p, pxParRi, result)
       else:
         addSon(result, unaryExpression(p))
@@ -1455,14 +1572,14 @@ proc parseDoWhile(p: var TParser): PNode =
 proc declarationOrStatement(p: var TParser): PNode = 
   if p.tok.xkind != pxSymbol:
     result = expressionStatement(p)
-  elif declKeyword(p.tok.s): 
+  elif declKeyword(p, p.tok.s): 
     result = declaration(p)
   else:
     # ordinary identifier:
     saveContext(p)
     getTok(p) # skip identifier to look ahead
-    case p.tok.xkind 
-    of pxSymbol, pxStar: 
+    case p.tok.xkind
+    of pxSymbol, pxStar, pxLt, pxAmp, pxAmpAmp:
       # we parse 
       # a b
       # a * b
@@ -1500,7 +1617,7 @@ proc parseTrailingDefinedIdents(p: var TParser, result, baseTyp: PNode) =
   if sonsLen(varSection) > 0:
     addSon(result, varSection)
 
-proc parseStandaloneStruct(p: var TParser, isUnion: bool): PNode = 
+proc parseStandaloneStruct(p: var TParser, isUnion: bool): PNode =
   result = newNodeP(nkStmtList, p)
   saveContext(p)
   getTok(p, result) # skip "struct" or "union"
@@ -1630,11 +1747,288 @@ proc compoundStatement(p: var TParser): PNode =
     if a.kind == nkEmpty: break
     embedStmts(result, a)
   if sonsLen(result) == 0:
-    # translate ``{}`` to Nimrod's ``nil`` statement
-    result = newNodeP(nkNilLit, p)
+    # translate ``{}`` to Nimrod's ``discard`` statement
+    result = newNodeP(nkDiscardStmt, p)
+    result.add(ast.emptyNode)
   dec(p.scopeCounter)
   eat(p, pxCurlyRi)
 
+proc skipInheritKeyw(p: var TParser) =
+  if p.tok.xkind == pxSymbol and (p.tok.s == "private" or 
+                                  p.tok.s == "protected" or
+                                  p.tok.s == "public"):
+    getTok(p)
+
+proc parseConstructor(p: var TParser, pragmas: PNode, 
+                      isDestructor=false): PNode =
+  var origName = p.tok.s
+  getTok(p)
+  
+  result = newNodeP(nkProcDef, p)
+  var rettyp = if isDestructor: newNodeP(nkNilLit, p)
+               else: mangledIdent(origName, p)
+  
+  let oname = if isDestructor: "destroy" & origName
+              else: "construct" & origName
+  var name = mangledIdent(oname, p)
+  var params = newNodeP(nkFormalParams, p)
+  addReturnType(params, rettyp)
+  if p.tok.xkind == pxParLe:
+    parseFormalParams(p, params, pragmas)
+  if p.tok.xkind == pxSymbol and p.tok.s == "const":
+    addSon(pragmas, newIdentNodeP("noSideEffect", p))
+  if pfCDecl in p.options.flags:
+    addSon(pragmas, newIdentNodeP("cdecl", p))
+  elif pfStdcall in p.options.flags:
+    addSon(pragmas, newIdentNodeP("stdcall", p))
+  if p.tok.xkind == pxColon:
+    # skip initializer list:
+    while true:
+      getTok(p)
+      discard expression(p)
+      if p.tok.xkind != pxComma: break
+  # no pattern, no exceptions:
+  addSon(result, exportSym(p, name, origName), ast.emptyNode, ast.emptyNode)
+  addSon(result, params, pragmas, ast.emptyNode) # no exceptions
+  addSon(result, ast.emptyNode) # no body
+  case p.tok.xkind 
+  of pxSemicolon: getTok(p)
+  of pxCurlyLe:
+    let body = compoundStatement(p)
+    if pfKeepBodies in p.options.flags:
+      result.sons[bodyPos] = body
+  else:
+    parMessage(p, errTokenExpected, ";")
+  if result.sons[bodyPos].kind == nkEmpty:
+    DoImport((if isDestructor: "~" else: "") & origName, pragmas, p)
+  elif isDestructor:
+    addSon(pragmas, newIdentNodeP("destructor", p))
+  if sonsLen(result.sons[pragmasPos]) == 0:
+    result.sons[pragmasPos] = ast.emptyNode
+
+proc parseMethod(p: var TParser, origName: string, rettyp, pragmas: PNode,
+                 isStatic: bool): PNode =
+  result = newNodeP(nkProcDef, p)
+  var params = newNodeP(nkFormalParams, p)
+  addReturnType(params, rettyp)
+  var thisDef = newNodeP(nkIdentDefs, p)
+  if not isStatic:
+    # declare 'this':
+    var t = newNodeP(nkVarTy, p)
+    t.add(p.currentClass)
+    addSon(thisDef, newIdentNodeP("this", p), t, ast.emptyNode)
+    params.add(thisDef)
+  parseFormalParams(p, params, pragmas)
+  if p.tok.xkind == pxSymbol and p.tok.s == "const":
+    addSon(pragmas, newIdentNodeP("noSideEffect", p))
+    getTok(p, result)
+    if not isStatic:
+      # fix the type of the 'this' parameter:
+      thisDef.sons[1] = thisDef.sons[1].sons[0]
+  if pfCDecl in p.options.flags:
+    addSon(pragmas, newIdentNodeP("cdecl", p))
+  elif pfStdcall in p.options.flags:
+    addSon(pragmas, newIdentNodeP("stdcall", p))
+  # no pattern, no exceptions:
+  let methodName = newIdentNodeP(origName, p)
+  addSon(result, exportSym(p, methodName, origName),
+         ast.emptyNode, ast.emptyNode)
+  addSon(result, params, pragmas, ast.emptyNode) # no exceptions
+  addSon(result, ast.emptyNode) # no body
+  case p.tok.xkind
+  of pxSemicolon: getTok(p)
+  of pxCurlyLe:
+    let body = compoundStatement(p)
+    if pfKeepBodies in p.options.flags:
+      result.sons[bodyPos] = body
+  else:
+    parMessage(p, errTokenExpected, ";")
+  if result.sons[bodyPos].kind == nkEmpty:
+    if isStatic: DoImport(origName, pragmas, p)
+    else: DoImportCpp(origName, pragmas, p)
+  if sonsLen(result.sons[pragmasPos]) == 0:
+    result.sons[pragmasPos] = ast.emptyNode
+
+proc parseStandaloneClass(p: var TParser, isStruct: bool): PNode
+
+proc followedByParLe(p: var TParser): bool =
+  saveContext(p)
+  getTok(p) # skip Identifier
+  result = p.tok.xkind == pxParLe
+  backtrackContext(p)
+
+proc parseOperator(p: var TParser, origName: var string): bool =
+  getTok(p) # skip 'operator' keyword
+  case p.tok.xkind
+  of pxAmp..pxArrow:
+    # ordinary operator symbol:
+    origName.add(TokKindToStr(p.tok.xkind))
+    getTok(p)
+  of pxSymbol:
+    if p.tok.s == "new" or p.tok.s == "delete":
+      origName.add(p.tok.s)
+      getTok(p)
+      if p.tok.xkind == pxBracketLe:
+        getTok(p)
+        eat(p, pxBracketRi)
+        origName.add("[]")
+    else:
+      # type converter
+      let x = typeAtom(p)
+      if x.kind == nkIdent:
+        origName.add(x.ident.s)
+      else:
+        parMessage(p, errGenerated, "operator symbol expected")
+      result = true
+  of pxParLe:
+    getTok(p)
+    eat(p, pxParRi)
+    origName.add("()")
+  of pxBracketLe:
+    getTok(p)
+    eat(p, pxBracketRi)
+    origName.add("[]")
+  else:
+    parMessage(p, errGenerated, "operator symbol expected")
+
+proc parseClass(p: var TParser; isStruct: bool; stmtList: PNode): PNode =
+  result = newNodeP(nkObjectTy, p)
+  addSon(result, ast.emptyNode, ast.emptyNode) # no pragmas, no inheritance 
+  
+  var recList = newNodeP(nkRecList, p)
+  addSon(result, recList)
+  if p.tok.xkind == pxColon:
+    getTok(p, result)
+    skipInheritKeyw(p)
+    var baseTyp = typeAtom(p)
+    var inh = newNodeP(nkOfInherit, p)
+    inh.add(baseTyp)
+    if p.tok.xkind == pxComma:
+      parMessage(p, errGenerated, "multiple inheritance is not supported")
+      while p.tok.xkind == pxComma:
+        getTok(p)
+        skipInheritKeyw(p)
+        discard typeAtom(p)
+    result.sons[0] = inh
+    
+  eat(p, pxCurlyLe, result)
+  var private = not isStruct
+  var pragmas = newNodeP(nkPragma, p)
+  while p.tok.xkind notin {pxEof, pxCurlyRi}:
+    skipCom(p, stmtList)
+    if p.tok.xkind == pxSymbol and (p.tok.s == "private" or 
+                                    p.tok.s == "protected"):
+      getTok(p, result)
+      eat(p, pxColon, result)
+      private = true
+    elif p.tok.xkind == pxSymbol and p.tok.s == "public":
+      getTok(p, result)
+      eat(p, pxColon, result)
+      private = false
+    if p.tok.xkind == pxSymbol and p.tok.s == "friend":
+      # we skip friend declarations:
+      while p.tok.xkind notin {pxEof, pxSemicolon}: getTok(p)
+      eat(p, pxSemicolon)
+    elif p.tok.xkind == pxSymbol and p.tok.s == "enum":
+      let x = enumSpecifier(p)
+      if not private or pfKeepBodies in p.options.flags: stmtList.add(x)
+    elif p.tok.xkind == pxSymbol and p.tok.s == "typedef":
+      let x = parseTypeDef(p)
+      if not private or pfKeepBodies in p.options.flags: stmtList.add(x)
+    elif p.tok.xkind == pxSymbol and(p.tok.s == "struct" or p.tok.s == "class"):
+      let x = parseStandaloneClass(p, isStruct=p.tok.s == "struct")
+      if not private or pfKeepBodies in p.options.flags: stmtList.add(x)
+    elif p.tok.xkind == pxSymbol and p.tok.s == "union":
+      let x = parseStandaloneStruct(p, isUnion=true)
+      if not private or pfKeepBodies in p.options.flags: stmtList.add(x)
+    else:
+      if pragmas.len != 0: pragmas = newNodeP(nkPragma, p)
+      parseCallConv(p, pragmas)
+      var isStatic = false
+      if p.tok.xkind == pxSymbol and p.tok.s == "virtual":
+        getTok(p, stmtList)
+      if p.tok.xkind == pxSymbol and p.tok.s == "explicit":
+        getTok(p, stmtList)
+      if p.tok.xkind == pxSymbol and p.tok.s == "static":
+        getTok(p, stmtList)
+        isStatic = true
+      parseCallConv(p, pragmas)
+      if p.tok.xkind == pxSymbol and p.tok.s == p.currentClass.ident.s and 
+          followedByParLe(p):
+        # constructor
+        let cons = parseConstructor(p, pragmas)
+        if not private or pfKeepBodies in p.options.flags: stmtList.add(cons)
+      elif p.tok.xkind == pxTilde:
+        # destructor
+        getTok(p, stmtList)
+        if p.tok.xkind == pxSymbol and p.tok.s == p.currentClass.ident.s:
+          let des = parseConstructor(p, pragmas, isDestructor=true)
+          if not private or pfKeepBodies in p.options.flags: stmtList.add(des)
+        else:
+          parMessage(p, errGenerated, "invalid destructor")
+      else:
+        # field declaration or method:
+        var baseTyp = typeAtom(p)
+        while true:
+          var def = newNodeP(nkIdentDefs, p)
+          var t = pointer(p, baseTyp)
+          let canBeMethod = p.tok.xkind != pxParLe
+          var origName: string
+          if p.tok.xkind == pxSymbol:
+            origName = p.tok.s
+            if p.tok.s == "operator":
+              var isConverter = parseOperator(p, origName)
+              let meth = parseMethod(p, origName, t, pragmas, isStatic)
+              if not private or pfKeepBodies in p.options.flags:
+                if isConverter: meth.kind = nkConverterDef
+                stmtList.add(meth)
+              break
+          var i = parseField(p, nkRecList)
+          if canBeMethod and p.tok.xkind == pxParLe:
+            let meth = parseMethod(p, origName, t, pragmas, isStatic)
+            if not private or pfKeepBodies in p.options.flags:
+              stmtList.add(meth)
+          else:
+            t = parseTypeSuffix(p, t)
+            addSon(def, i, t, ast.emptyNode)
+            if not isStatic: addSon(recList, def)
+          if p.tok.xkind != pxComma: break
+          getTok(p, def)
+        if p.tok.xkind == pxSemicolon:
+          getTok(p, lastSon(recList))
+  eat(p, pxCurlyRi, result)
+
+proc parseStandaloneClass(p: var TParser, isStruct: bool): PNode =
+  result = newNodeP(nkStmtList, p)
+  saveContext(p)
+  getTok(p, result) # skip "class" or "struct"
+  var origName = ""
+  let oldClass = p.currentClass
+  if p.tok.xkind == pxSymbol: 
+    markTypeIdent(p, nil)
+    origName = p.tok.s
+    getTok(p, result)
+    p.currentClass = mangledIdent(origName, p)
+  else:
+    p.currentClass = nil
+  if p.tok.xkind in {pxCurlyLe, pxSemiColon, pxColon}:
+    if origName.len > 0: 
+      var typeSection = newNodeP(nkTypeSection, p)
+      addSon(result, typeSection)
+      
+      var name = mangledIdent(origName, p)
+      var t = parseClass(p, isStruct, result)
+      addTypeDef(typeSection, structPragmas(p, name, origName), t)
+      parseTrailingDefinedIdents(p, result, name)
+    else:
+      var t = parseTuple(p, isUnion=false)
+      parseTrailingDefinedIdents(p, result, t)
+  else:
+    backtrackContext(p)
+    result = declaration(p)
+  p.currentClass = oldClass
+
+
 include cpp
 
 proc statement(p: var TParser): PNode = 
@@ -1681,14 +2075,29 @@ proc statement(p: var TParser): PNode =
       eat(p, pxSemicolon)
     of "enum": result = enumSpecifier(p)
     of "typedef": result = parseTypeDef(p)
-    of "struct": result = parseStandaloneStruct(p, isUnion=false)
-    of "union": result = parseStandaloneStruct(p, isUnion=true)    
+    of "union": result = parseStandaloneStruct(p, isUnion=true)
+    of "struct":
+      if pfCpp in p.options.flags:
+        result = parseStandaloneClass(p, isStruct=true)
+      else:
+        result = parseStandaloneStruct(p, isUnion=false)
+    of "class":
+      if pfCpp in p.options.flags:
+        result = parseStandaloneClass(p, isStruct=false)
+      else:
+        result = declarationOrStatement(p)
+    of "namespace":
+      if pfCpp in p.options.flags:
+        while p.tok.xkind notin {pxEof, pxCurlyLe}: getTok(p)
+        result = compoundStatement(p)
+      else:
+        result = declarationOrStatement(p)
     else: result = declarationOrStatement(p)
   of pxCurlyLe:
     result = compoundStatement(p)
   of pxDirective, pxDirectiveParLe:
     result = parseDir(p)
-  of pxLineComment, pxStarComment: 
+  of pxLineComment, pxStarComment:
     result = newNodeP(nkCommentStmt, p)
     skipCom(p, result)
   of pxSemicolon:
@@ -1703,10 +2112,10 @@ proc statement(p: var TParser): PNode =
     result = expressionStatement(p)
   assert result != nil
 
-proc parseUnit(p: var TParser): PNode = 
+proc parseUnit(p: var TParser): PNode =
   result = newNodeP(nkStmtList, p)
   getTok(p) # read first token
-  while p.tok.xkind != pxEof: 
+  while p.tok.xkind != pxEof:
     var s = statement(p)
     if s.kind != nkEmpty: embedStmts(result, s)
 
diff --git a/compiler/c2nim/cpp.nim b/compiler/c2nim/cpp.nim
index 439b000e7..6b2ee905c 100644
--- a/compiler/c2nim/cpp.nim
+++ b/compiler/c2nim/cpp.nim
@@ -26,7 +26,7 @@ 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))):
+        declKeyword(p, p.tok.s) or stmtKeyword(p.tok.s))):
     addSon(tmplDef, statement(p))
     result = "stmt"
   elif p.tok.xkind in {pxLineComment, pxNewLine}:
diff --git a/compiler/c2nim/tests/matrix.h b/compiler/c2nim/tests/matrix.h
new file mode 100644
index 000000000..248b3d5e8
--- /dev/null
+++ b/compiler/c2nim/tests/matrix.h
@@ -0,0 +1,241 @@
+/////////////////////////////////////////////////////////////////////////////

+// Name:         wx/matrix.h

+// Purpose:      wxTransformMatrix class. NOT YET USED

+// Author:       Chris Breeze, Julian Smart

+// Modified by:  Klaas Holwerda

+// Created:      01/02/97

+// RCS-ID:       $Id$

+// Copyright:    (c) Julian Smart, Chris Breeze

+// Licence:      wxWindows licence

+/////////////////////////////////////////////////////////////////////////////

+

+#ifndef _WX_MATRIXH__

+#define _WX_MATRIXH__

+

+//! headerfiles="matrix.h wx/object.h"

+#include "wx/object.h"

+#include "wx/math.h"

+

+//! codefiles="matrix.cpp"

+

+// A simple 3x3 matrix. This may be replaced by a more general matrix

+// class some day.

+//

+// Note: this is intended to be used in wxDC at some point to replace

+// the current system of scaling/translation. It is not yet used.

+
+#def WXDLLIMPEXP_CORE
+#header "wxmatrix.h"
+

+//:definition

+//  A 3x3 matrix to do 2D transformations.

+//  It can be used to map data to window coordinates,

+//  and also for manipulating your own data.

+//  For example drawing a picture (composed of several primitives)

+//  at a certain coordinate and angle within another parent picture.

+//  At all times m_isIdentity is set if the matrix itself is an Identity matrix.

+//  It is used where possible to optimize calculations.

+class WXDLLIMPEXP_CORE wxTransformMatrix: public wxObject<string, string<ubyte>>

+{

+public:

+    wxTransformMatrix(void);

+    wxTransformMatrix(const wxTransformMatrix& mat);
+    
+    ~wxTransformMatrix(void);

+

+    //get the value in the matrix at col,row

+    //rows are horizontal (second index of m_matrix member)

+    //columns are vertical (first index of m_matrix member)

+    double GetValue(int col, int row) const;

+

+    //set the value in the matrix at col,row

+    //rows are horizontal (second index of m_matrix member)

+    //columns are vertical (first index of m_matrix member)

+    void SetValue(int col, int row, double value);

+

+    void operator = (const wxTransformMatrix& mat);

+    bool operator == (const wxTransformMatrix& mat) const;

+    bool operator != (const wxTransformMatrix& mat) const;

+

+    //multiply every element by t

+    wxTransformMatrix&          operator*=(const double& t);

+    //divide every element by t

+    wxTransformMatrix&          operator/=(const double& t);

+    //add matrix m to this t

+    wxTransformMatrix&          operator+=(const wxTransformMatrix& m);

+    //subtract matrix m from this

+    wxTransformMatrix&          operator-=(const wxTransformMatrix& m);

+    //multiply matrix m with this

+    wxTransformMatrix&          operator*=(const wxTransformMatrix& m);

+

+    // constant operators

+

+    //multiply every element by t  and return result

+    wxTransformMatrix           operator*(const double& t) const;

+    //divide this matrix by t and return result

+    wxTransformMatrix           operator/(const double& t) const;

+    //add matrix m to this and return result

+    wxTransformMatrix           operator+(const wxTransformMatrix& m) const;

+    //subtract matrix m from this and return result

+    wxTransformMatrix           operator-(const wxTransformMatrix& m) const;

+    //multiply this by matrix m and return result

+    wxTransformMatrix           operator*(const wxTransformMatrix& m) const;

+    wxTransformMatrix           operator-() const;

+

+    //rows are horizontal (second index of m_matrix member)

+    //columns are vertical (first index of m_matrix member)

+    double& operator()(int col, int row);

+

+    //rows are horizontal (second index of m_matrix member)

+    //columns are vertical (first index of m_matrix member)

+    double operator()(int col, int row) const;

+

+    // Invert matrix

+    bool Invert(void);

+

+    // Make into identity matrix

+    bool Identity(void);

+

+    // Is the matrix the identity matrix?

+    // Only returns a flag, which is set whenever an operation

+    // is done.

+    inline bool IsIdentity(void) const { return m_isIdentity; }

+

+    // This does an actual check.

+    inline bool IsIdentity1(void) const ;

+

+    //Scale by scale (isotropic scaling i.e. the same in x and y):

+    //!ex:

+    //!code:           | scale  0      0      |

+    //!code: matrix' = |  0     scale  0      | x matrix

+    //!code:           |  0     0      scale  |

+    bool Scale(double scale);

+

+    //Scale with center point and x/y scale

+    //

+    //!ex:

+    //!code:           |  xs    0      xc(1-xs) |

+    //!code: matrix' = |  0    ys      yc(1-ys) | x matrix

+    //!code:           |  0     0      1        |

+    wxTransformMatrix&  Scale(const double &xs, const double &ys,const double &xc, const double &yc);

+

+    // mirror a matrix in x, y

+    //!ex:

+    //!code:           | -1     0      0 |

+    //!code: matrix' = |  0    -1      0 | x matrix

+    //!code:           |  0     0      1 |

+    wxTransformMatrix<float>&  Mirror(bool x=true, bool y=false);

+    // Translate by dx, dy:

+    //!ex:

+    //!code:           | 1  0 dx |

+    //!code: matrix' = | 0  1 dy | x matrix

+    //!code:           | 0  0  1 |

+    bool Translate(double x, double y);

+

+    // Rotate clockwise by the given number of degrees:

+    //!ex:

+    //!code:           |  cos sin 0 |

+    //!code: matrix' = | -sin cos 0 | x matrix

+    //!code:           |   0   0  1 |

+    bool Rotate(double angle);

+

+    //Rotate counter clockwise with point of rotation

+    //

+    //!ex:

+    //!code:           |  cos(r) -sin(r)    x(1-cos(r))+y(sin(r)|

+    //!code: matrix' = |  sin(r)  cos(r)    y(1-cos(r))-x(sin(r)| x matrix

+    //!code:           |   0          0                       1 |

+    wxTransformMatrix&  Rotate(const double &r, const double &x, const double &y);

+

+    // Transform X value from logical to device

+    inline double TransformX(double x) const;

+

+    // Transform Y value from logical to device

+    inline double TransformY(double y) const;

+

+    // Transform a point from logical to device coordinates

+    bool TransformPoint(double x, double y, double& tx, double& ty) const;

+

+    // Transform a point from device to logical coordinates.

+    // Example of use:

+    //   wxTransformMatrix mat = dc.GetTransformation();

+    //   mat.Invert();

+    //   mat.InverseTransformPoint(x, y, x1, y1);

+    // OR (shorthand:)

+    //   dc.LogicalToDevice(x, y, x1, y1);

+    // The latter is slightly less efficient if we're doing several

+    // conversions, since the matrix is inverted several times.

+    // N.B. 'this' matrix is the inverse at this point

+    bool InverseTransformPoint(double x, double y, double& tx, double& ty) const;

+

+    double Get_scaleX();

+    double Get_scaleY();

+    double GetRotation();

+    void   SetRotation(double rotation);

+

+

+public:

+    double  m_matrix[3][3];

+    bool    m_isIdentity;

+};

+

+

+/*

+Chris Breeze reported, that

+some functions of wxTransformMatrix cannot work because it is not

+known if he matrix has been inverted. Be careful when using it.

+

+

+// Transform X value from logical to device

+// warning: this function can only be used for this purpose

+// because no rotation is involved when mapping logical to device coordinates

+// mirror and scaling for x and y will be part of the matrix

+// if you have a matrix that is rotated, eg a shape containing a matrix to place

+// it in the logical coordinate system, use TransformPoint

+inline double wxTransformMatrix::TransformX(double x) const

+{

+    //normally like this, but since no rotation is involved (only mirror and scale)

+    //we can do without Y -> m_matrix[1]{0] is -sin(rotation angle) and therefore zero

+    //(x * m_matrix[0][0] + y * m_matrix[1][0] + m_matrix[2][0]))

+    return (m_isIdentity ? x : (x * m_matrix[0][0] +  m_matrix[2][0]));

+}

+

+// Transform Y value from logical to device

+// warning: this function can only be used for this purpose

+// because no rotation is involved when mapping logical to device coordinates

+// mirror and scaling for x and y will be part of the matrix

+// if you have a matrix that is rotated, eg a shape containing a matrix to place

+// it in the logical coordinate system, use TransformPoint

+inline double wxTransformMatrix::TransformY(double y) const

+{

+    //normally like this, but since no rotation is involved (only mirror and scale)

+    //we can do without X -> m_matrix[0]{1] is sin(rotation angle) and therefore zero

+    //(x * m_matrix[0][1] + y * m_matrix[1][1] + m_matrix[2][1]))

+    return (m_isIdentity ? y : (y * m_matrix[1][1] + m_matrix[2][1]));

+}

+

+

+// Is the matrix the identity matrix?

+// Each operation checks whether the result is still the identity matrix and sets a flag.

+inline bool wxTransformMatrix::IsIdentity1(void) const

+{

+    return

+    ( wxIsSameDouble(m_matrix[0][0], 1.0) &&

+      wxIsSameDouble(m_matrix[1][1], 1.0) &&

+      wxIsSameDouble(m_matrix[2][2], 1.0) &&

+      wxIsSameDouble(m_matrix[1][0], 0.0) &&

+      wxIsSameDouble(m_matrix[2][0], 0.0) &&

+      wxIsSameDouble(m_matrix[0][1], 0.0) &&

+      wxIsSameDouble(m_matrix[2][1], 0.0) &&

+      wxIsSameDouble(m_matrix[0][2], 0.0) &&

+      wxIsSameDouble(m_matrix[1][2], 0.0) );

+}

+

+// Calculates the determinant of a 2 x 2 matrix

+inline double wxCalculateDet(double a11, double a21, double a12, double a22)

+{

+    return a11 * a22 - a12 * a21;

+}

+*/
+

+#endif // _WX_MATRIXH__

diff --git a/doc/apis.txt b/doc/apis.txt
index 67534dec8..d76bd721e 100644
--- a/doc/apis.txt
+++ b/doc/apis.txt
@@ -74,6 +74,7 @@ coordinate              coord
 rectangle               rect
 point                   point
 symbol                  sym
+literal                 lit
 string                  str
 identifier              ident
 indentation             indent
diff --git a/doc/manual.txt b/doc/manual.txt
index 914e6eaf5..095f25b30 100644
--- a/doc/manual.txt
+++ b/doc/manual.txt
@@ -4471,7 +4471,7 @@ proc with no side effects:
 destructor pragma
 -----------------
 
-The `destructor` pragma is used to mark a proc to act as a type destructor.
+The `destructor`:idx: pragma is used to mark a proc to act as a type destructor.
 The proc must have a single parameter with a concrete type (the name of a
 generic type is allowed too).
 
diff --git a/web/news.txt b/web/news.txt
index d863b2ed8..32eab8c59 100644
--- a/web/news.txt
+++ b/web/news.txt
@@ -15,6 +15,7 @@ Library Additions
 -----------------
 
 - Added ``macros.genSym`` builtin for AST generation.
+- Added ``macros.newLit`` procs for easier AST generation.
 
 
 Changes affecting backwards compatibility
@@ -56,6 +57,13 @@ Language Additions
 - Support for user-defined type classes have been added.
 
 
+Tools improvements
+------------------
+
+- c2nim can deal with a subset of C++. Use the ``--cpp`` command line option
+  to activate.
+
+
 2013-05-20 New website design!
 ==============================