summary refs log blame commit diff stats
path: root/compiler/c2nim/cparse.nim
blob: df263ee4e8527940a04bf36c56ae25544458a3bf (plain) (tree)
1
2
3
4
5
6
7
8
9


                                           
                                         




                                                   


                                                                            
 
      

                                                     
                                          
                            
 
       
                                                                           


                  
                    

                                                                  
                                                      

                                                                    



                                                        
  
                 



                                                           
                         
                           
                                   
                                                        
                           
                             
                       
                          
                         

                                      
                   





                                         
                               
                                                                           






                                           
                     
                          
                           


                       
                                                     
                                                    


                                                                            
                    






                                                    
                                                            

                                                              


                                                                      
                                                 

                      
                                      


                                                                          


                
                                                  
                                                                        
                                         


                     

                                                       
               

                             
                                                    
                                                         







                                                                 
                          




                                                                          
                      


                  






                                               






                                                    
                                                            





                                                                 
                
             







                                        

                                                                             







                                                                       






                                                      
                

















                                                                          
                         


                          
                           



                          
 





                                           



                                          
                                           
                                     


                                              



                                               
                                         
                                                                       

                                             
                                                      




                                       
                               
                                                                              
  
                                                      
                                       
                                                            
  
                                            
                                    
                                                            
  
                                                  


                                            
                                                      

































                                                                           



                                                      
                                          



                                               
                                       







                                                 
                                                 




                                                                 
                                    





















                                                                      
                                                                
                          
                                                        





                                                          
                                                      








                                                              
                                                    
                                                      








                                                            
                                                           


                                                              
                                                              






                                                                        




                                                             
 






                                              
                                                          
                   












                                                              
                                          





                                                                
                                         

                                                                  
  




                                                                              


                                                                    


                                             
                                                






                                                                               

                                            








                                                                               









                                                                              




















































                                                                          











                                                
                                       
              
                







                                                









                                                                                
                    



                                                  
                                     
                     
                                
                                






                                                  
 



                                                



















                                                              








                                                       








                                                  
                                     

                                           

                                                      
                                     


                                                              

                                                          















                                                                        
           


















                                              

                                       
                                  
 















                                                                      
 
                                                   
                                                          
                            





                                             
                                 
                               
                                      

                                                                    




                                        

                                                                       
                                    

                                              
                                                                      
                              

                                                                      



                                                  

                                     
                                      

                                                                       


                         
                                                         
                                  
                                                                            
                              
                                               















                                                            
                      




                                            
                            



























                                                                        
                                 

                                          
                               


                                             
                                           
                               


                                       
                               



                                      
                                              


                                   

                                                 


                                           
                                     


                                                         

                 
                                              

                             
                                              
                                          

               

                       
                       
                             






                                                                      
                            
                                    




                                         
                                                                     






                                     
                     




                                    

                                      
 
                                                                        

                              
                                   
                          
                         




                                                               
                         



                                 
                                     


                                                    
                             
                              
                               
                                                               
                                                  
           



















                                                                     

                          
                           
                                                                       


                                                             

                          


                                 
                       




                                                    

                              
                               
                                                                           
                                                  
           

                                                                         



















                                                         

                                                             
                                          





                                                    


                                

























                                                                    
                                




                                                       
                                    








                                      
                                     



                                      

                     









                                                            










                                          







                                                                               
                       
          


                                   


                                            
                                 
                                         


                                                               
               



                                                  


                                                                              


                     
                                            
                                                            



                                          

                                              
       
                                                       
                      





                                                            
                


                              
                   



                                        
                          
               
                                   



                                   




                                  
           


                                                                         

                                         
                                                      


                                      

                                        


                             

                          
                         


                     





                                                 
                                                               
                       








                                        
       
                   
                                        
                          








                                                    
 


                                                             
              


                                    

                                                






                                          
                                   
                       
                                                                            
                     
                                  







                                           

                               
                                          
         










































































































































                                                                                                                                
                                









































































































                                                                    
       
                 
 

                                                          
 

                   
 
                                  
 

                                         
                     
                                           



















                                                                      
                                             










                                                                              
                          



                                              
                      






                                                                  




                                      
                      


















                                          

                             
                                            



                                   

                         
                         


                                          































                                          


                                                     
                                


                           
                  
                                             

                                               














                                                                       
                      
 



















                                                                         
                                                                  




                                              
                         

                      
                                             













                                                                  





                                                                       
                              
                                

                                        
                                                                    
                      
                                                                         

                                   
                          
                                      
                              






























































                                                                               
                             
                                    






                                                   

                                
                  
       
                              
                                                






                                                
                               

                          


                                                        


                     














































                                                                            
                                                                    










































                                                                          

                                               















                                                                
                                           





























































                                                                            
                                                                               





















































































                                                                                


                                          















                                                                  























                                                                              
                                   



                                       
                                   











                                                                       
                                     
                         

                                          
















                                                               






                                                               
                                            



                                   
                                  











                                                     
                      
 
                                       







                                                                           
 
#
#
#      c2nim - C to Nimrod source converter
#        (c) Copyright 2013 Andreas Rumpf
#
#    See the file "copying.txt", included in this
#    distribution, for details about the copyright.
#

## This module implements an Ansi C parser.
## It translates a C source file into a Nimrod AST. Then the renderer can be
## used to convert the AST to its text representation.

# TODO
# - document 'cpp' mode
# - implement handling of '::': function declarations
# - C++'s "operator" still needs some love
# - support '#if' in classes

import 
  os, llstream, renderer, clex, idents, strutils, pegs, ast, astalgo, msgs,
  options, strtabs

type 
  TParserFlag = enum
    pfRefs,             ## use "ref" instead of "ptr" for C's typ*
    pfCDecl,            ## annotate procs with cdecl
    pfStdCall,          ## annotate procs with stdcall
    pfSkipInclude,      ## skip all ``#include``
    pfTypePrefixes,     ## all generated types start with 'T' or 'P'
    pfSkipComments,     ## do not generate comments
    pfCpp,              ## process C++
    pfIgnoreRValueRefs, ## transform C++'s 'T&&' to 'T'
    pfKeepBodies        ## do not skip C++ method bodies
  
  TMacro = object
    name: string
    params: int           # number of parameters
    body: seq[ref TToken] # can contain pxMacroParam tokens
  
  TParserOptions = object
    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
    classes: PStringTable
  PParserOptions* = ref TParserOptions
  
  TParser* = object
    lex: TLexer
    tok: ref TToken       # current token
    options: PParserOptions
    backtrack: seq[ref TToken]
    inTypeDef: int
    scopeCounter: int
    hasDeadCodeElimPragma: bool
    currentClass: PNode   # type that needs to be added as 'this' parameter
  
  TReplaceTuple* = array[0..1, string]

proc newParserOptions*(): PParserOptions = 
  new(result)
  result.prefixes = @[]
  result.suffixes = @[]
  result.macros = @[]
  result.mangleRules = @[]
  result.privateRules = @[]
  result.flags = {}
  result.dynlibSym = ""
  result.header = ""
  result.toMangle = newStringTable(modeCaseSensitive)
  result.classes = newStringTable(modeCaseSensitive)

proc setOption*(parserOptions: PParserOptions, key: string, val=""): bool = 
  result = true
  case key.normalize
  of "ref": incl(parserOptions.flags, pfRefs)
  of "dynlib": parserOptions.dynlibSym = val
  of "header": parserOptions.header = val
  of "cdecl": incl(parserOptions.flags, pfCdecl)
  of "stdcall": incl(parserOptions.flags, pfStdCall)
  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)
  of "cpp": incl(parserOptions.flags, pfCpp)
  of "keepbodies": incl(parserOptions.flags, pfKeepBodies)
  of "ignorervaluerefs": incl(parserOptions.flags, pfIgnoreRValueRefs)
  of "class": parserOptions.classes[val] = "true"
  else: result = false

proc parseUnit*(p: var TParser): PNode
proc openParser*(p: var TParser, filename: string, inputStream: PLLStream,
                 options = newParserOptions())
proc closeParser*(p: var TParser)

# implementation

proc openParser(p: var TParser, filename: string, 
                inputStream: PLLStream, options = newParserOptions()) = 
  openLexer(p.lex, filename, inputStream)
  p.options = options
  p.backtrack = @[]
  new(p.tok)

proc parMessage(p: TParser, msg: TMsgKind, arg = "") = 
  #assert false
  lexMessage(p.lex, msg, arg)

proc closeParser(p: var TParser) = closeLexer(p.lex)
proc 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()

proc rawGetTok(p: var TParser) = 
  if p.tok.next != nil:
    p.tok = p.tok.next
  elif p.backtrack.len == 0: 
    p.tok.next = nil
    getTok(p.lex, p.tok[])
  else: 
    # We need the next token and must be able to backtrack. So we need to 
    # allocate a new token.
    var t: ref TToken
    new(t)
    getTok(p.lex, t[])
    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
  return -1

proc rawEat(p: var TParser, xkind: TTokKind) = 
  if p.tok.xkind == xkind: rawGetTok(p)
  else: parMessage(p, errTokenExpected, tokKindToStr(xkind))

proc parseMacroArguments(p: var TParser): seq[seq[ref TToken]] = 
  result = @[]
  result.add(@[])
  var i: array[pxParLe..pxCurlyLe, int]
  var L = 0
  saveContext(p)
  while true:
    var kind = p.tok.xkind
    case kind
    of pxEof: rawEat(p, pxParRi)
    of pxParLe, pxBracketLe, pxCurlyLe: 
      inc(i[kind])
      result[L].add(p.tok)
    of pxParRi:
      # end of arguments?
      if i[pxParLe] == 0 and i[pxBracketLe] == 0 and i[pxCurlyLe] == 0: break
      if i[pxParLe] > 0: dec(i[pxParLe])
      result[L].add(p.tok)
    of pxBracketRi, pxCurlyRi:
      kind = pred(kind, 3)
      if i[kind] > 0: dec(i[kind])
      result[L].add(p.tok)
    of pxComma: 
      if i[pxParLe] == 0 and i[pxBracketLe] == 0 and i[pxCurlyLe] == 0:
        # next argument: comma is not part of the argument
        result.add(@[])
        inc(L)
      else: 
        # comma does not separate different arguments:
        result[L].add(p.tok)
    else:
      result[L].add(p.tok)
    rawGetTok(p)
  closeContext(p)

proc expandMacro(p: var TParser, m: TMacro) = 
  rawGetTok(p) # skip macro name
  var arguments: seq[seq[ref TToken]]
  if m.params > 0:
    rawEat(p, pxParLe)
    arguments = parseMacroArguments(p)
    if arguments.len != m.params: parMessage(p, errWrongNumberOfArguments)
    rawEat(p, pxParRi)
  # insert into the token list:
  if m.body.len > 0:
    var newList: ref TToken
    new(newList)
    var lastTok = newList
    for tok in items(m.body): 
      if tok.xkind == pxMacroParam: 
        for t in items(arguments[int(tok.iNumber)]):
          #echo "t: ", t^
          lastTok.next = t
          lastTok = t
      else:
        #echo "tok: ", tok^
        lastTok.next = tok
        lastTok = tok
    lastTok.next = p.tok
    p.tok = newList.next

proc getTok(p: var TParser) = 
  rawGetTok(p)
  if p.tok.xkind == pxSymbol:
    var idx = findMacro(p)
    if idx >= 0: 
      expandMacro(p, p.options.macros[idx])

proc parLineInfo(p: TParser): TLineInfo = 
  result = getLineInfo(p.lex)

proc skipComAux(p: var TParser, n: PNode) =
  if n != nil and n.kind != nkEmpty: 
    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)

proc skipCom(p: var TParser, n: PNode) = 
  while p.tok.xkind in {pxLineComment, pxStarComment}: skipComAux(p, n)

proc skipStarCom(p: var TParser, n: PNode) = 
  while p.tok.xkind == pxStarComment: skipComAux(p, n)

proc getTok(p: var TParser, n: PNode) =
  getTok(p)
  skipCom(p, n)

proc expectIdent(p: TParser) = 
  if p.tok.xkind != pxSymbol: parMessage(p, errIdentifierExpected, $(p.tok[]))
  
proc eat(p: var TParser, xkind: TTokKind, n: PNode) = 
  if p.tok.xkind == xkind: getTok(p, n)
  else: parMessage(p, errTokenExpected, tokKindToStr(xkind))
  
proc eat(p: var TParser, xkind: TTokKind) = 
  if p.tok.xkind == xkind: getTok(p)
  else: parMessage(p, errTokenExpected, tokKindToStr(xkind))
  
proc eat(p: var TParser, tok: string, n: PNode) = 
  if p.tok.s == tok: getTok(p, n)
  else: parMessage(p, errTokenExpected, tok)
  
proc opt(p: var TParser, xkind: TTokKind, n: PNode) = 
  if p.tok.xkind == xkind: getTok(p, n)
  
proc addSon(father, a, b: PNode) = 
  addSon(father, a)
  addSon(father, b)

proc addSon(father, a, b, c: PNode) = 
  addSon(father, a)
  addSon(father, b)
  addSon(father, c)
  
proc newNodeP(kind: TNodeKind, p: TParser): PNode = 
  result = newNodeI(kind, getLineInfo(p.lex))

proc newIntNodeP(kind: TNodeKind, intVal: BiggestInt, p: TParser): PNode = 
  result = newNodeP(kind, p)
  result.intVal = intVal

proc newFloatNodeP(kind: TNodeKind, floatVal: BiggestFloat, 
                   p: TParser): PNode = 
  result = newNodeP(kind, p)
  result.floatVal = floatVal

proc newStrNodeP(kind: TNodeKind, strVal: string, p: TParser): PNode = 
  result = newNodeP(kind, p)
  result.strVal = strVal

proc newIdentNodeP(ident: PIdent, p: TParser): PNode = 
  result = newNodeP(nkIdent, p)
  result.ident = ident

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.replacef(pattern, frmt)
        break mangle
    block prefixes:
      for prefix in items(p.options.prefixes): 
        if s.startsWith(prefix): 
          result = s.substr(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 = 
  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)
  result.ident = getIdent(mangleName(ident, p))

proc newIdentPair(a, b: string, p: TParser): PNode = 
  result = newNodeP(nkExprColonExpr, p)
  addSon(result, newIdentNodeP(a, p))
  addSon(result, newIdentNodeP(b, p))

proc newIdentStrLitPair(a, b: string, p: TParser): PNode =
  result = newNodeP(nkExprColonExpr, p)
  addSon(result, newIdentNodeP(a, p))
  addSon(result, newStrNodeP(nkStrLit, b, p))

proc addImportToPragma(pragmas: PNode, ident: string, p: TParser) =
  addSon(pragmas, newIdentStrLitPair("importc", 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 exportSym(p: TParser, i: PNode, origName: string): PNode = 
  assert i.kind == nkIdent
  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), ident)
  if p.scopeCounter > 0: return
  if p.options.dynlibSym.len > 0 or p.options.header.len > 0: 
    var a = result
    result = newNodeP(nkPragmaExpr, p)
    var pragmas = newNodeP(nkPragma, p)
    addSon(result, a)
    addSon(result, pragmas)
    addImportToPragma(pragmas, ident, p)

proc fieldIdent(ident: string, p: TParser): PNode = 
  result = exportSym(p, mangledIdent(ident, p), ident)
  if p.scopeCounter > 0: return
  if p.options.header.len > 0: 
    var a = result
    result = newNodeP(nkPragmaExpr, p)
    var pragmas = newNodeP(nkPragma, p)
    addSon(result, a)
    addSon(result, pragmas)
    addSon(pragmas, newIdentStrLitPair("importc", ident, p))

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))
  addSon(result, a)
  addSon(result, b)

proc skipIdent(p: var TParser): PNode = 
  expectIdent(p)
  result = mangledIdent(p.tok.s, p)
  getTok(p, result)

proc skipIdentExport(p: var TParser): PNode = 
  expectIdent(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 or typ.kind == nkEmpty: 
      prefix = "T"
    else: 
      var t = typ
      while t != nil and t.kind in {nkVarTy, nkPtrTy, nkRefTy}: 
        prefix.add('P')
        t = t.sons[0]
      if prefix.len == 0: prefix.add('T')
    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
# avoids to build a symbol table, which can't be done reliably anyway for our
# purposes.

proc expression(p: var TParser, rbp: int = 0): PNode
proc constantExpression(p: var TParser): PNode = expression(p, 40)
proc assignmentExpression(p: var TParser): PNode = expression(p, 30)
proc compoundStatement(p: var TParser): PNode
proc statement(p: var TParser): PNode

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",
      "inline", "__inline", "__cdecl", "__stdcall", "__syscall", "__fastcall",
      "__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
  of  "if", "for", "while", "do", "switch", "break", "continue", "return",
      "goto":
    result = true

# ------------------- type desc -----------------------------------------------

proc isIntType(s: string): bool =
  case s
  of "short", "int", "long", "float", "double", "signed", "unsigned":
    result = true

proc skipConst(p: var TParser) = 
  while p.tok.xkind == pxSymbol and
      (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 optScope(p: var TParser, n: PNode): PNode =
  result = n
  if pfCpp in p.options.flags:
    while p.tok.xkind == pxScope:
      let a = result
      result = newNodeP(nkDotExpr, p)
      result.add(a)
      getTok(p, result)
      expectIdent(p)
      result.add(mangledIdent(p.tok.s, p))
      getTok(p, result)

proc typeAtom(p: var TParser): PNode = 
  skipConst(p)
  expectIdent(p)
  case p.tok.s
  of "void": 
    result = newNodeP(nkNilLit, p) # little hack
    getTok(p, nil)
  of "struct", "union", "enum": 
    getTok(p, nil)
    result = skipIdent(p)
  elif isIntType(p.tok.s):
    var x = ""
    #getTok(p, nil)
    var isUnsigned = false
    while p.tok.xkind == pxSymbol and (isIntType(p.tok.s) or p.tok.s == "char"):
      if p.tok.s == "unsigned":
        isUnsigned = true
      elif p.tok.s == "signed" or p.tok.s == "int":
        nil
      else:
        add(x, p.tok.s)
      getTok(p, nil)
    if x.len == 0: x = "int"
    let xx = if isUnsigned: "cu" & x else: "c" & x
    result = mangledIdent(xx, p)
  else:
    result = mangledIdent(p.tok.s, p)
    getTok(p, result)
    result = optScope(p, result)
    result = optAngle(p, result)
    
proc newPointerTy(p: TParser, typ: PNode): PNode =
  if pfRefs in p.options.flags: 
    result = newNodeP(nkRefTy, p)
  else:
    result = newNodeP(nkPtrTy, p)
  result.addSon(typ)

proc pointer(p: var TParser, a: PNode): PNode = 
  result = a
  var i = 0
  skipConst(p)
  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)
      for j in 1..i-2: result = newPointerTy(p, result)
    elif i == 1: result = newIdentNodeP("cstring", p)
  elif a.kind == nkNilLit and i > 0:
    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, ast.emptyNode)

proc addReturnType(params, rettyp: PNode) =
  if rettyp == nil: addSon(params, ast.emptyNode)
  elif rettyp.kind != nkNilLit: addSon(params, rettyp)
  else: addSon(params, ast.emptyNode)

proc parseFormalParams(p: var TParser, params, pragmas: PNode)

proc parseTypeSuffix(p: var TParser, typ: PNode): PNode = 
  result = typ
  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:
        # 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 = pointer(p, typeAtom(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)
      var i = parseField(p, kind)
      t = parseTypeSuffix(p, t)
      addSon(def, i, t, ast.emptyNode)
      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))
  eat(p, pxCurlyRi, result)

proc structPragmas(p: TParser, name: PNode, origName: string): PNode = 
  assert name.kind == nkIdent
  result = newNodeP(nkPragmaExpr, p)
  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:
    addSon(pragmas, newIdentStrLitPair("importc", origName, p),
                    newIdentStrLitPair("header", p.options.header, p))
  addSon(result, pragmas)

proc enumPragmas(p: TParser, name: PNode): PNode =
  result = newNodeP(nkPragmaExpr, p)
  addSon(result, name)
  var pragmas = newNodeP(nkPragma, p)
  var e = newNodeP(nkExprColonExpr, p)
  # HACK: sizeof(cint) should be constructed as AST
  addSon(e, newIdentNodeP("size", p), newIdentNodeP("sizeof(cint)", p))
  addSon(pragmas, e)
  addSon(result, pragmas)

proc parseStruct(p: var TParser, isUnion: bool): PNode = 
  result = newNodeP(nkObjectTy, p)
  addSon(result, ast.emptyNode, ast.emptyNode) # no pragmas, no inheritance 
  if p.tok.xkind == pxCurlyLe:
    addSon(result, parseStructBody(p, isUnion))
  else: 
    addSon(result, newNodeP(nkRecList, p))

proc parseParam(p: var TParser, params: PNode) = 
  var typ = typeDesc(p)
  # support for ``(void)`` parameter list: 
  if typ.kind == nkNilLit and p.tok.xkind == pxParRi: return
  var name: PNode
  if p.tok.xkind == pxSymbol: 
    name = skipIdent(p)
  else:
    # generate a name for the formal parameter:
    var idx = sonsLen(params)+1
    name = newIdentNodeP("a" & $idx, p)
  typ = parseTypeSuffix(p, typ)
  var x = newNodeP(nkIdentDefs, p)
  addSon(x, name, typ)
  if p.tok.xkind == pxAsgn: 
    # we support default parameters for C++:
    getTok(p, x)
    addSon(x, assignmentExpression(p))
  else:
    addSon(x, ast.emptyNode)
  addSon(params, x)

proc parseFormalParams(p: var TParser, params, pragmas: PNode) = 
  eat(p, pxParLe, params)
  while p.tok.xkind notin {pxEof, pxParRi}:
    if p.tok.xkind == pxDotDotDot:  
      addSon(pragmas, newIdentNodeP("varargs", p))
      getTok(p, pragmas)
      break
    parseParam(p, params)
    if p.tok.xkind != pxComma: break
    getTok(p, params)
  eat(p, pxParRi, params)

proc parseCallConv(p: var TParser, pragmas: PNode) = 
  while p.tok.xkind == pxSymbol:
    case p.tok.s
    of "inline", "__inline": addSon(pragmas, newIdentNodeP("inline", p))
    of "__cdecl": addSon(pragmas, newIdentNodeP("cdecl", p))
    of "__stdcall": addSon(pragmas, newIdentNodeP("stdcall", p))
    of "__syscall": addSon(pragmas, newIdentNodeP("syscall", p))
    of "__fastcall": addSon(pragmas, newIdentNodeP("fastcall", p))
    of "__safecall": addSon(pragmas, newIdentNodeP("safecall", p))
    else: break
    getTok(p, nil)

proc parseFunctionPointerDecl(p: var TParser, rettyp: PNode): PNode = 
  var procType = newNodeP(nkProcTy, p)
  var pragmas = newProcPragmas(p)
  var params = newNodeP(nkFormalParams, p)
  eat(p, pxParLe, params)
  addReturnType(params, rettyp)
  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)
  addSon(procType, params)
  addPragmas(procType, pragmas)
  
  if p.inTypeDef == 0:
    result = newNodeP(nkVarSection, p)
    var def = newNodeP(nkIdentDefs, p)
    addSon(def, name, procType, ast.emptyNode)
    addSon(result, def)    
  else:
    result = newNodeP(nkTypeDef, p)
    addSon(result, name, ast.emptyNode, procType)
  assert result != nil
  
proc addTypeDef(section, name, t: PNode) = 
  var def = newNodeI(nkTypeDef, name.info)
  addSon(def, name, ast.emptyNode, t)
  addSon(section, def)
  
proc otherTypeDef(p: var TParser, section, typ: PNode) = 
  var name: PNode
  var t = typ
  if p.tok.xkind in {pxStar, pxAmp, pxAmpAmp}:
    t = pointer(p, t)
  if p.tok.xkind == pxParLe: 
    # function pointer: typedef typ (*name)();
    var x = parseFunctionPointerDecl(p, t)
    name = x[0]
    t = x[2]
  else: 
    # typedef typ name;
    markTypeIdent(p, t)
    name = skipIdentExport(p)
  t = parseTypeSuffix(p, t)
  addTypeDef(section, name, t)

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)

proc enumFields(p: var TParser): PNode = 
  result = newNodeP(nkEnumTy, p)
  addSon(result, ast.emptyNode) # enum does not inherit from anything
  while true:
    var e = skipIdent(p)
    if p.tok.xkind == pxAsgn: 
      getTok(p, e)
      var c = constantExpression(p)
      var a = e
      e = newNodeP(nkEnumFieldDef, p)
      addSon(e, a, c)
      skipCom(p, e)
    
    addSon(result, e)
    if p.tok.xkind != pxComma: break
    getTok(p, e)
    # allow trailing comma:
    if p.tok.xkind == pxCurlyRi: break

proc parseTypedefStruct(p: var TParser, result: PNode, isUnion: bool) = 
  getTok(p, result)
  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 
    of pxCurlyLe:
      var t = parseStruct(p, isUnion)
      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)
        parseTrailingDefinedTypes(p, result, name)
      else:
        addTypeDef(result, structPragmas(p, nameOrType, origName), t)
    of pxSymbol: 
      # typedef struct a a?
      if mangleName(p.tok.s, p) == nameOrType.ident.s:
        # ignore the declaration:
        getTok(p, nil)
      else:
        # typedef struct a b; or typedef struct a b[45];
        otherTypeDef(p, result, nameOrType)
    else: 
      otherTypeDef(p, result, nameOrType)
  else:
    expectIdent(p)

proc parseTypedefEnum(p: var TParser, result: PNode) = 
  getTok(p, result)
  if p.tok.xkind == pxCurlyLe:
    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, 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:
      getTok(p, result)
      var t = enumFields(p)
      eat(p, pxCurlyRi, t)
      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, origName)), t)
        parseTrailingDefinedTypes(p, result, name)
      else:
        addTypeDef(result, 
                   enumPragmas(p, exportSym(p, nameOrType, origName)), t)
    of pxSymbol: 
      # typedef enum a a?
      if mangleName(p.tok.s, p) == nameOrType.ident.s:
        # ignore the declaration:
        getTok(p, nil)
      else:
        # typedef enum a b; or typedef enum a b[45];
        otherTypeDef(p, result, nameOrType)
    else: 
      otherTypeDef(p, result, nameOrType)
  else:
    expectIdent(p)

proc parseTypeDef(p: var TParser): PNode =  
  result = newNodeP(nkTypeSection, p)
  while p.tok.xkind == pxSymbol and p.tok.s == "typedef":
    getTok(p, result)
    inc(p.inTypeDef)
    expectIdent(p)
    case p.tok.s
    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)
    eat(p, pxSemicolon)
    dec(p.inTypeDef)
    
proc skipDeclarationSpecifiers(p: var TParser) =
  while p.tok.xkind == pxSymbol:
    case p.tok.s
    of "extern", "static", "auto", "register", "const", "volatile": 
      getTok(p, nil)
    else: break

proc parseInitializer(p: var TParser): PNode = 
  if p.tok.xkind == pxCurlyLe: 
    result = newNodeP(nkBracket, p)
    getTok(p, result)
    while p.tok.xkind notin {pxEof, pxCurlyRi}: 
      addSon(result, parseInitializer(p))
      opt(p, pxComma, nil)
    eat(p, pxCurlyRi, result)
  else:
    result = assignmentExpression(p)

proc addInitializer(p: var TParser, def: PNode) = 
  if p.tok.xkind == pxAsgn:
    getTok(p, def)
    addSon(def, parseInitializer(p))
  else:
    addSon(def, ast.emptyNode)  

proc parseVarDecl(p: var TParser, baseTyp, typ: PNode, 
                  origName: string): PNode =  
  result = newNodeP(nkVarSection, p)
  var def = newNodeP(nkIdentDefs, p)
  addSon(def, varIdent(origName, p))
  addSon(def, parseTypeSuffix(p, typ))
  addInitializer(p, def)
  addSon(result, def)
    
  while p.tok.xkind == pxComma: 
    getTok(p, def)
    var t = pointer(p, baseTyp)
    expectIdent(p)
    def = newNodeP(nkIdentDefs, p)
    addSon(def, varIdent(p.tok.s, p))
    getTok(p, def)
    addSon(def, parseTypeSuffix(p, t))
    addInitializer(p, def)
    addSon(result, def)
  eat(p, pxSemicolon)

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)
  var pragmas = newNodeP(nkPragma, p)
  
  skipDeclarationSpecifiers(p)
  parseCallConv(p, pragmas)
  skipDeclarationSpecifiers(p)
  expectIdent(p)
  var baseTyp = typeAtom(p)
  var rettyp = pointer(p, baseTyp)
  skipDeclarationSpecifiers(p)
  parseCallConv(p, pragmas)
  skipDeclarationSpecifiers(p)
  
  if p.tok.xkind == pxParLe: 
    # Function pointer declaration: This is of course only a heuristic, but the
    # best we can do here.
    result = parseFunctionPointerDecl(p, rettyp)
    eat(p, pxSemicolon)
    return
  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))
      getTok(p)
    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:
    addSon(result, exportSym(p, name, origName), ast.emptyNode, ast.emptyNode)
    addSon(result, params, pragmas, ast.emptyNode) # no exceptions
    case p.tok.xkind 
    of pxSemicolon: 
      getTok(p)
      addSon(result, ast.emptyNode) # nobody
      if p.scopeCounter == 0: doImport(origName, pragmas, p)
    of pxCurlyLe:
      addSon(result, compoundStatement(p))
    else:
      parMessage(p, errTokenExpected, ";")
    if sonsLen(result.sons[pragmasPos]) == 0: 
      result.sons[pragmasPos] = ast.emptyNode
  else:
    result = parseVarDecl(p, baseTyp, rettyp, origName)
  assert result != nil

proc createConst(name, typ, val: PNode, p: TParser): PNode =
  result = newNodeP(nkConstDef, p)
  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)
    var i = 0
    var hasUnknown = false
    while true:
      var name = skipIdentExport(p)
      var val: PNode
      if p.tok.xkind == pxAsgn: 
        getTok(p, name)
        val = constantExpression(p)
        if val.kind == nkIntLit:  
          i = int(val.intVal)+1
          hasUnknown = false
        else:
          hasUnknown = true
      else:
        if hasUnknown:
          parMessage(p, warnUser, "computed const value may be wrong: " &
            name.renderTree)
        val = newIntNodeP(nkIntLit, i, p)
        inc(i)
      var c = createConst(name, ast.emptyNode, val, p)
      addSon(result, c)
      if p.tok.xkind != pxComma: break
      getTok(p, c)
      # allow trailing comma:
      if p.tok.xkind == pxCurlyRi: break
    eat(p, pxCurlyRi, result)
    eat(p, pxSemicolon)
  of pxSymbol: 
    var origName = p.tok.s
    markTypeIdent(p, nil)
    result = skipIdent(p)
    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, origName), ast.emptyNode, e)
      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, "{")
    result = ast.emptyNode
    
# Expressions

proc setBaseFlags(n: PNode, base: TNumericalBase) = 
  case base
  of base10: nil
  of base2: incl(n.flags, nfBase2)
  of base8: incl(n.flags, nfBase8)
  of base16: incl(n.flags, nfBase16)

proc startExpression(p : var TParser, tok : TToken) : PNode =
  #echo "nud ", $tok
  case tok.xkind:
  of pxSymbol:
    if tok.s == "NULL":
      result = newNodeP(nkNilLit, p)
    elif tok.s == "sizeof":
      result = newNodeP(nkCall, p)
      addSon(result, newIdentNodeP("sizeof", p))
      saveContext(p)
      try:
        addSon(result, expression(p, 139))
        closeContext(p)
      except:
        backtrackContext(p)
        eat(p, pxParLe)
        addSon(result, typeDesc(p))
        eat(p, pxParRi)
    elif (tok.s == "new" or tok.s == "delete") and pfCpp in p.options.flags:
      var opr = tok.s
      result = newNodeP(nkCall, p)
      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, expression(p, 139))
    else:
      result = mangledIdent(tok.s, p)
      result = optScope(p, result)
      result = optAngle(p, result)
  of pxIntLit: 
    result = newIntNodeP(nkIntLit, tok.iNumber, p)
    setBaseFlags(result, tok.base)
  of pxInt64Lit: 
    result = newIntNodeP(nkInt64Lit, tok.iNumber, p)
    setBaseFlags(result, tok.base)
  of pxFloatLit: 
    result = newFloatNodeP(nkFloatLit, tok.fNumber, p)
    setBaseFlags(result, tok.base)
  of pxStrLit: 
    result = newStrNodeP(nkStrLit, tok.s, p)
    while p.tok.xkind == pxStrLit:
      add(result.strVal, p.tok.s)
      getTok(p, result)
  of pxCharLit:
    result = newIntNodeP(nkCharLit, ord(tok.s[0]), p)
  of pxParLe:
    try:
      saveContext(p)
      result = newNodeP(nkPar, p)
      addSon(result, expression(p, 0))
      if p.tok.xkind != pxParRi:
        raise
      getTok(p, result)
      if p.tok.xkind in {pxSymbol, pxIntLit, pxFloatLit, pxStrLit, pxCharLit}:
        raise
      closeContext(p)
    except:
      backtrackContext(p)
      result = newNodeP(nkCast, p)
      addSon(result, typeDesc(p))
      eat(p, pxParRi, result)
      addSon(result, expression(p, 139))
  of pxPlusPlus:
    result = newNodeP(nkCall, p)
    addSon(result, newIdentNodeP("inc", p))
    addSon(result, expression(p, 139))
  of pxMinusMinus:
    result = newNodeP(nkCall, p)
    addSon(result, newIdentNodeP("dec", p))
    addSon(result, expression(p, 139))
  of pxAmp:
    result = newNodeP(nkAddr, p)
    addSon(result, expression(p, 139))
  of pxStar:
    result = newNodeP(nkBracketExpr, p)
    addSon(result, expression(p, 139))
  of pxPlus:
    result = newNodeP(nkPrefix, p)
    addSon(result, newIdentNodeP("+", p))
    addSon(result, expression(p, 139))
  of pxMinus:
    result = newNodeP(nkPrefix, p)
    addSon(result, newIdentNodeP("-", p))
    addSon(result, expression(p, 139))
  of pxTilde:
    result = newNodeP(nkPrefix, p)
    addSon(result, newIdentNodeP("not", p))
    addSon(result, expression(p, 139))
  of pxNot:
    result = newNodeP(nkPrefix, p)
    addSon(result, newIdentNodeP("not", p))
    addSon(result, expression(p, 139))
  else:
    # probably from a failed sub expression attempt, try a type cast
    raise newException(E_Base, "not " & $tok)

proc leftBindingPower(p : var TParser, tok : ref TToken) : int =
  #echo "lbp ", $tok[]
  case tok.xkind:
  of pxComma:
    return 10
    # throw == 20
  of pxAsgn, pxPlusAsgn, pxMinusAsgn, pxStarAsgn, pxSlashAsgn, pxModAsgn, pxShlAsgn, pxShrAsgn, pxAmpAsgn, pxHatAsgn, pxBarAsgn:
    return 30
  of pxConditional:
    return 40
  of pxBarBar:
    return 50
  of pxAmpAmp:
    return 60
  of pxBar:
    return 70
  of pxHat:
    return 80
  of pxAmp:
    return 90
  of pxEquals, pxNeq:
    return 100
  of pxLt, pxLe, pxGt, pxGe:
    return 110
  of pxShl, pxShr:
    return 120
  of pxPlus, pxMinus:
    return 130
  of pxStar, pxSlash, pxMod:
    return 140
    # .* ->* == 150
  of pxPlusPlus, pxMinusMinus, pxParLe, pxDot, pxArrow, pxBracketLe:
    return 160
    # :: == 170
  else:
    return 0

proc buildStmtList(a: PNode): PNode

proc leftExpression(p : var TParser, tok : TToken, left : PNode) : PNode =
  #echo "led ", $tok
  case tok.xkind:
  of pxComma: # 10
    # not supported as an expression, turns into a statement list
    result = buildStmtList(left)
    addSon(result, expression(p, 0))
    # throw == 20
  of pxAsgn: # 30
    result = newNodeP(nkAsgn, p)
    addSon(result, left, expression(p, 29))
  of pxPlusAsgn: # 30
    result = newNodeP(nkCall, p)
    addSon(result, newIdentNodeP(getIdent("inc"), p), left, expression(p, 29))
  of pxMinusAsgn: # 30
    result = newNodeP(nkCall, p)
    addSon(result, newIdentNodeP(getIdent("dec"), p), left, expression(p, 29))
  of pxStarAsgn: # 30
    result = newNodeP(nkAsgn, p)
    var right = expression(p, 29)
    addSon(result, left, newBinary("*", copyTree(left), right, p))
  of pxSlashAsgn: # 30
    result = newNodeP(nkAsgn, p)
    var right = expression(p, 29)
    addSon(result, left, newBinary("/", copyTree(left), right, p))
  of pxModAsgn: # 30
    result = newNodeP(nkAsgn, p)
    var right = expression(p, 29)
    addSon(result, left, newBinary("mod", copyTree(left), right, p))
  of pxShlAsgn: # 30
    result = newNodeP(nkAsgn, p)
    var right = expression(p, 29)
    addSon(result, left, newBinary("shl", copyTree(left), right, p))
  of pxShrAsgn: # 30
    result = newNodeP(nkAsgn, p)
    var right = expression(p, 29)
    addSon(result, left, newBinary("shr", copyTree(left), right, p))
  of pxAmpAsgn: # 30
    result = newNodeP(nkAsgn, p)
    var right = expression(p, 29)
    addSon(result, left, newBinary("and", copyTree(left), right, p))
  of pxHatAsgn: # 30
    result = newNodeP(nkAsgn, p)
    var right = expression(p, 29)
    addSon(result, left, newBinary("xor", copyTree(left), right, p))
  of pxBarAsgn: # 30
    result = newNodeP(nkAsgn, p)
    var right = expression(p, 29)
    addSon(result, left, newBinary("or", copyTree(left), right, p))
  of pxConditional: # 40
    var a = expression(p, 0)
    eat(p, pxColon, a)
    var b = expression(p, 39)
    result = newNodeP(nkIfExpr, p)
    var branch = newNodeP(nkElifExpr, p)
    addSon(branch, left, a)
    addSon(result, branch)
    branch = newNodeP(nkElseExpr, p)
    addSon(branch, b)
    addSon(result, branch)
  of pxBarBar: # 50
    result = newBinary("or", left, expression(p, 50), p)
  of pxAmpAmp: # 60
    result = newBinary("and", left, expression(p, 60), p)
  of pxBar: # 70
    result = newBinary("or", left, expression(p, 70), p)
  of pxHat: # 80
    result = newBinary("^", left, expression(p, 80), p)
  of pxAmp: # 90
    result = newBinary("and", left, expression(p, 90), p)
  of pxEquals: # 100
    result = newBinary("==", left, expression(p, 100), p)
  of pxNeq: # 100
    result = newBinary("!=", left, expression(p, 100), p)
  of pxLt: # 110
    result = newBinary("<", left, expression(p, 110), p)
  of pxLe: # 110
    result = newBinary("<=", left, expression(p, 110), p)
  of pxGt: # 110
    result = newBinary(">", left, expression(p, 110), p)
  of pxGe: # 110
    result = newBinary(">=", left, expression(p, 110), p)
  of pxShl: # 120
    result = newBinary("shl", left, expression(p, 120), p)
  of pxShr: # 120
    result = newBinary("shr", left, expression(p, 120), p)
  of pxPlus: # 130
    result = newNodeP(nkInfix, p)
    addSon(result, newIdentNodeP("+", p), left)
    addSon(result, expression(p, 130))
  of pxMinus: # 130
    result = newNodeP(nkInfix, p)
    addSon(result, newIdentNodeP("+", p), left)
    addSon(result, expression(p, 130))
  of pxStar: # 140
    result = newNodeP(nkInfix, p)
    addSon(result, newIdentNodeP("*", p), left)
    addSon(result, expression(p, 140))
  of pxSlash: # 140
    result = newNodeP(nkInfix, p)
    addSon(result, newIdentNodeP("div", p), left)
    addSon(result, expression(p, 140))
  of pxMod: # 140
    result = newNodeP(nkInfix, p)
    addSon(result, newIdentNodeP("mod", p), left)
    addSon(result, expression(p, 140))
    # .* ->* == 150
  of pxPlusPlus: # 160
    result = newNodeP(nkCall, p)
    addSon(result, newIdentNodeP("inc", p), left)
  of pxMinusMinus: # 160
    result = newNodeP(nkCall, p)
    addSon(result, newIdentNodeP("dec", p), left)
  of pxParLe: # 160
    result = newNodeP(nkCall, p)
    addSon(result, left)
    while p.tok.xkind != pxParRi:
      var a = expression(p, 29)
      addSon(result, a)
      while p.tok.xkind == pxComma:
        getTok(p, a)
        a = expression(p, 29)
        addSon(result, a)
    eat(p, pxParRi, result)
  of pxDot: # 160
    result = newNodeP(nkDotExpr, p)
    addSon(result, left)
    addSon(result, skipIdent(p))
  of pxArrow: # 160
    result = newNodeP(nkDotExpr, p)
    addSon(result, left)
    addSon(result, skipIdent(p))
  of pxBracketLe: # 160
    result = newNodeP(nkBracketExpr, p)
    addSon(result, left, expression(p))
    eat(p, pxBracketRi, result)
    # :: == 170
  else:
    result = left

proc expression*(p : var TParser, rbp : int = 0) : PNode =
  var tok : TToken

  tok = p.tok[]
  getTok(p, result)

  result = startExpression(p, tok)

  while rbp < leftBindingPower(p, p.tok):
    tok = p.tok[]
    getTok(p, result)
    result = leftExpression(p, tok, result)
    
# Statements

proc buildStmtList(a: PNode): PNode = 
  if a.kind == nkStmtList: result = a
  else:
    result = newNodeI(nkStmtList, a.info)
    addSon(result, a)

proc nestedStatement(p: var TParser): PNode =
  # careful: We need to translate:
  # if (x) if (y) stmt;
  # into:
  # if x:
  #   if x:
  #     stmt
  # 
  # Nimrod requires complex statements to be nested in whitespace!
  const
    complexStmt = {nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef,
      nkTemplateDef, nkIteratorDef, nkIfStmt,
      nkWhenStmt, nkForStmt, nkWhileStmt, nkCaseStmt, nkVarSection, 
      nkConstSection, nkTypeSection, nkTryStmt, nkBlockStmt, nkStmtList,
      nkCommentStmt, nkStmtListExpr, nkBlockExpr, nkStmtListType, nkBlockType}
  result = statement(p)
  if result.kind in complexStmt:
    result = buildStmtList(result)

proc expressionStatement(p: var TParser): PNode = 
  # do not skip the comment after a semicolon to make a new nkCommentStmt
  if p.tok.xkind == pxSemicolon: 
    getTok(p)
    result = ast.emptyNode
  else:
    result = expression(p)
    if p.tok.xkind == pxSemicolon: getTok(p)
    else: parMessage(p, errTokenExpected, ";")
  assert result != nil

proc parseIf(p: var TParser): PNode = 
  # we parse additional "else if"s too here for better Nimrod code
  result = newNodeP(nkIfStmt, p)
  while true: 
    getTok(p) # skip ``if``
    var branch = newNodeP(nkElifBranch, p)
    eat(p, pxParLe, branch)
    addSon(branch, expression(p))
    eat(p, pxParRi, branch)
    addSon(branch, nestedStatement(p))
    addSon(result, branch)
    skipCom(p, branch)
    if p.tok.s == "else": 
      getTok(p, result)
      if p.tok.s != "if": 
        # ordinary else part:
        branch = newNodeP(nkElse, p)
        addSon(branch, nestedStatement(p))
        addSon(result, branch)
        break 
    else: 
      break 
  
proc parseWhile(p: var TParser): PNode = 
  result = newNodeP(nkWhileStmt, p)
  getTok(p, result)
  eat(p, pxParLe, result)
  addSon(result, expression(p))
  eat(p, pxParRi, result)
  addSon(result, nestedStatement(p))

proc embedStmts(sl, a: PNode)

proc parseDoWhile(p: var TParser): PNode =  
  # parsing
  result = newNodeP(nkWhileStmt, p)
  getTok(p, result)
  var stm = nestedStatement(p)
  eat(p, "while", result)
  eat(p, pxParLe, result)
  var exp = expression(p)
  eat(p, pxParRi, result)
  if p.tok.xkind == pxSemicolon: getTok(p)

  # while true:
  #   stmt
  #   if not expr:
  #     break
  addSon(result, newIdentNodeP("true", p))

  stm = buildStmtList(stm)

  # get the last exp if it is a stmtlist
  var cleanedExp = exp
  if exp.kind == nkStmtList:
    cleanedExp = exp.sons[exp.len-1]
    exp.sons = exp.sons[0..exp.len-2]
    embedStmts(stm, exp)

  var notExp = newNodeP(nkPrefix, p)
  addSon(notExp, newIdentNodeP("not", p))
  addSon(notExp, cleanedExp)

  var brkStm = newNodeP(nkBreakStmt, p)
  addSon(brkStm, ast.emptyNode)

  var ifStm = newNodeP(nkIfStmt, p)
  var ifBranch = newNodeP(nkElifBranch, p)
  addSon(ifBranch, notExp)
  addSon(ifBranch, brkStm)
  addSon(ifStm, ifBranch)

  embedStmts(stm, ifStm)

  addSon(result, stm)

proc declarationOrStatement(p: var TParser): PNode = 
  if p.tok.xkind != pxSymbol:
    result = expressionStatement(p)
  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, pxLt, pxAmp, pxAmpAmp:
      # we parse 
      # a b
      # a * b
      # always as declarations! This is of course not correct, but good
      # enough for most real world C code out there.
      backtrackContext(p)
      result = declaration(p)
    of pxColon: 
      # it is only a label:
      closeContext(p)
      getTok(p)
      result = statement(p)
    else: 
      backtrackContext(p)
      result = expressionStatement(p)
  assert result != nil

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: 
    markTypeIdent(p, nil)
    origName = p.tok.s
    getTok(p, result)
  if p.tok.xkind in {pxCurlyLe, pxSemiColon}:
    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
  getTok(p, result)
  eat(p, pxParLe, result)
  var initStmt = declarationOrStatement(p)
  if initStmt.kind != nkEmpty:
    embedStmts(result, initStmt)
  var w = newNodeP(nkWhileStmt, p)
  var condition = expressionStatement(p)
  if condition.kind == nkEmpty: condition = newIdentNodeP("true", p)
  addSon(w, condition)
  var step = if p.tok.xkind != pxParRi: expression(p) else: ast.emptyNode
  eat(p, pxParRi, step)
  var loopBody = nestedStatement(p)
  if step.kind != nkEmpty:
    loopBody = buildStmtList(loopBody)
    embedStmts(loopBody, step)
  addSon(w, loopBody)
  addSon(result, w)
  
proc switchStatement(p: var TParser): PNode = 
  result = newNodeP(nkStmtList, p)
  while true:
    if p.tok.xkind in {pxEof, pxCurlyRi}: break
    case p.tok.s 
    of "break":
      getTok(p, result)
      eat(p, pxSemicolon, result)
      break
    of "return", "continue", "goto": 
      addSon(result, statement(p))
      break
    of "case", "default":
      break
    else: nil
    addSon(result, statement(p))
  if sonsLen(result) == 0:
    # translate empty statement list to Nimrod's ``nil`` statement
    result = newNodeP(nkNilLit, p)

proc rangeExpression(p: var TParser): PNode =
  # We support GCC's extension: ``case expr...expr:`` 
  result = constantExpression(p)
  if p.tok.xkind == pxDotDotDot:
    getTok(p, result)
    var a = result
    var b = constantExpression(p)
    result = newNodeP(nkRange, p)
    addSon(result, a)
    addSon(result, b)

proc parseSwitch(p: var TParser): PNode = 
  # We cannot support Duff's device or C's crazy switch syntax. We just support
  # sane usages of switch. ;-)
  result = newNodeP(nkCaseStmt, p)
  getTok(p, result)
  eat(p, pxParLe, result)
  addSon(result, expression(p))
  eat(p, pxParRi, result)
  eat(p, pxCurlyLe, result)
  var b: PNode
  while (p.tok.xkind != pxCurlyRi) and (p.tok.xkind != pxEof): 
    case p.tok.s 
    of "default": 
      b = newNodeP(nkElse, p)
      getTok(p, b)
      eat(p, pxColon, b)
    of "case": 
      b = newNodeP(nkOfBranch, p)
      while p.tok.xkind == pxSymbol and p.tok.s == "case":
        getTok(p, b)
        addSon(b, rangeExpression(p))
        eat(p, pxColon, b)
    else:
      parMessage(p, errXExpected, "case")
    addSon(b, switchStatement(p))
    addSon(result, b)
    if b.kind == nkElse: break 
  eat(p, pxCurlyRi)

proc addStmt(sl, a: PNode) = 
  # merge type sections if 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:
    addStmt(sl, a)
  else:
    for i in 0..sonsLen(a)-1: 
      if a[i].kind != nkEmpty: addStmt(sl, a[i])

proc compoundStatement(p: var TParser): PNode = 
  result = newNodeP(nkStmtList, p)
  eat(p, pxCurlyLe)
  inc(p.scopeCounter)
  while p.tok.xkind notin {pxEof, pxCurlyRi}: 
    var a = statement(p)
    if a.kind == nkEmpty: break
    embedStmts(result, a)
  if sonsLen(result) == 0:
    # 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" or p.tok.s == "using"):
      # 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:
      p.options.classes[origName] = "true"

      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 = 
  case p.tok.xkind 
  of pxSymbol: 
    case p.tok.s
    of "if": result = parseIf(p)
    of "switch": result = parseSwitch(p)
    of "while": result = parseWhile(p)
    of "do": result = parseDoWhile(p)
    of "for": 
      result = newNodeP(nkStmtList, p)
      parseFor(p, result)
    of "goto": 
      # we cannot support "goto"; in hand-written C, "goto" is most often used
      # to break a block, so we convert it to a break statement with label.
      result = newNodeP(nkBreakStmt, p)
      getTok(p)
      addSon(result, skipIdent(p))
      eat(p, pxSemicolon)
    of "continue":
      result = newNodeP(nkContinueStmt, p)
      getTok(p)
      eat(p, pxSemicolon)
      addSon(result, ast.emptyNode)
    of "break":
      result = newNodeP(nkBreakStmt, p)
      getTok(p)
      eat(p, pxSemicolon)
      addSon(result, ast.emptyNode)
    of "return":
      result = newNodeP(nkReturnStmt, p)
      getTok(p)
      # special case for ``return (expr)`` because I hate the redundant
      # parenthesis ;-)
      if p.tok.xkind == pxParLe:
        getTok(p, result)
        addSon(result, expression(p))
        eat(p, pxParRi, result)
      elif p.tok.xkind != pxSemicolon:
        addSon(result, expression(p))
      else:
        addSon(result, ast.emptyNode)
      eat(p, pxSemicolon)
    of "enum": result = enumSpecifier(p)
    of "typedef": result = parseTypeDef(p)
    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)
    of "using":
      if pfCpp in p.options.flags:
        while p.tok.xkind notin {pxEof, pxSemicolon}: getTok(p)
        eat(p, pxSemicolon)
        result = newNodeP(nkNilLit, p)
      else:
        result = declarationOrStatement(p)
    else: result = declarationOrStatement(p)
  of pxCurlyLe:
    result = compoundStatement(p)
  of pxDirective, pxDirectiveParLe:
    result = parseDir(p)
  of pxLineComment, pxStarComment:
    result = newNodeP(nkCommentStmt, p)
    skipCom(p, result)
  of pxSemicolon:
    # empty statement:
    getTok(p)
    if p.tok.xkind in {pxLineComment, pxStarComment}:
      result = newNodeP(nkCommentStmt, p)
      skipCom(p, result)
    else:
      result = newNodeP(nkNilLit, p)
  else:
    result = expressionStatement(p)
  assert result != nil

proc parseUnit(p: var TParser): PNode =
  try:
    result = newNodeP(nkStmtList, p)
    getTok(p) # read first token
    while p.tok.xkind != pxEof:
      var s = statement(p)
      if s.kind != nkEmpty: embedStmts(result, s)
  except:
    parMessage(p, errGenerated, "Uncaught exception raised during parsing")