summary refs log blame commit diff stats
path: root/nim/pasparse.pas
blob: dbfbf043759d49ff20654741feffe7cbdaa2e73d (plain) (tree)



















                                                                                
                                                                                




















                                                            
                                                     


                                
                             















                                
                                                     

                              


                              





















                           
                                  





                                  

                             

                        
      












                                             

                                                                  







                                             

                                                                  





                            
                                          

































































                                                                               
                                               










































                                                                              

                                                              
 
                                                                


















                                                    
                                                          



                       
                                                                       
























                                                         
                                                              



                                                         
                                       








                                                            
                                               


                    
                                                                             











                                                        



                                                          



                                                            
            
                                                            




















































































































                                                                               
                                            





































                                                                          









                                                       











                                                        





                                                          



                                                            
                                       
































































                                                                               
                                                        
              






                                           




















































































































                                                                              


                                             































                                                                               
                                     

































































































                                                                           
                     








                                                
                                                       























































































































                                                                                 
                                     






























































































































































































































































































                                                                               







                                                                     






                                                                          













                                                              






                                                             


                                                                





































































































































































                                                                          

                                                 




































                                                                  
                                       
                                                        




















                                                                       
                     




                                                           
                        




                                                                           



















                                                                        







                                                   



















                                                                             
                      
                     

    

                                                                













                                     
                                         

    
                                                                        


                          
         




                                           
                                                     
                                                           



                                           
                                        
                                                      
                                               






                                                                   
                                          

                            





                                                                          

          
                                                                       





























                                                                 

                                      
          
                                       
























                                                            
   
           





                                        
                                  
                      






























































































































































































































































                                                                     
                                                          










                                                                
//
//
//           The Nimrod Compiler
//        (c) Copyright 2008 Andreas Rumpf
//
//    See the file "copying.txt", included in this
//    distribution, for details about the copyright.
//

unit pasparse;

// This module implements the parser of the Pascal variant Nimrod is written in.
// It transfers a Pascal module into a Nimrod AST. Then the renderer can be
// used to generate the Nimrod version of the compiler.

{$include config.inc}

interface

uses
  nsystem, nos, llstream, charsets, scanner, paslex, idents, wordrecg, strutils,
  ast, astalgo, msgs, options;

type
  TPasSection = (seImplementation, seInterface);
  TPasContext = (conExpr, conStmt, conTypeDesc);
  TPasParser = record
    section: TPasSection;
    inParamList: boolean;
    context: TPasContext;    // needed for the @emit command
    lastVarSection: PNode;
    lex: TPasLex;
    tok: TPasTok;
    repl: TIdTable;       // replacements
  end;

  TReplaceTuple = array [0..1] of string;

const
  ImportBlackList: array [1..3] of string = (
    'nsystem', 'sysutils', 'charsets'
  );
  stdReplacements: array [1..19] of TReplaceTuple = (
    ('include',      'incl'),
    ('exclude',      'excl'),
    ('pchar',        'cstring'),
    ('assignfile',   'open'),
    ('integer',      'int'),
    ('longword',     'int32'),
    ('cardinal',     'int'),
    ('boolean',      'bool'),
    ('shortint',     'int8'),
    ('smallint',     'int16'),
    ('longint',      'int32'),
    ('byte',         'int8'),
    ('word',         'int16'),
    ('single',       'float32'),
    ('double',       'float64'),
    ('real',         'float'),
    ('length',       'len'),
    ('len',          'length'),
    ('setlength',    'setlen')
  );
  nimReplacements: array [1..35] of TReplaceTuple = (
    ('nimread',      'read'),
    ('nimwrite',     'write'),
    ('nimclosefile', 'close'),
    ('closefile',    'close'),
    ('openfile',     'open'),
    ('nsystem', 'system'),
    ('ntime', 'times'),
    ('nos', 'os'),
    ('nmath', 'math'),

    ('ncopy', 'copy'),
    ('addChar', 'add'),
    ('halt', 'quit'),
    ('nobject', 'TObject'),
    ('eof', 'EndOfFile'),

    ('input', 'stdin'),
    ('output', 'stdout'),
    ('addu', '`+%`'),
    ('subu', '`-%`'),
    ('mulu', '`*%`'),
    ('divu', '`/%`'),
    ('modu', '`%%`'),
    ('ltu', '`<%`'),
    ('leu', '`<=%`'),
    ('shlu', '`shl`'),
    ('shru', '`shr`'),
    ('assigned',     'not isNil'),

    ('eintoverflow', 'EOverflow'),
    ('format', '`%`'),
    ('snil', 'nil'),
    ('tostringf', '$'+''),
    ('ttextfile', 'tfile'),
    ('tbinaryfile', 'tfile'),
    ('strstart', '0'+''),
    ('nl', '"\n"'),
    ('tostring', '$'+'')
    {,
    ('NL', '"\n"'),
    ('tabulator', '''\t'''),
    ('esc', '''\e'''),
    ('cr', '''\r'''),
    ('lf', '''\l'''),
    ('ff', '''\f'''),
    ('bel', '''\a'''),
    ('backspace', '''\b'''),
    ('vt', '''\v''') }
  );

function ParseUnit(var p: TPasParser): PNode;

procedure openPasParser(var p: TPasParser; const filename: string;
                        inputStream: PLLStream);
procedure closePasParser(var p: TPasParser);

procedure exSymbol(var n: PNode);
procedure fixRecordDef(var n: PNode);
// XXX: move these two to an auxiliary module

implementation

procedure OpenPasParser(var p: TPasParser; const filename: string;
                        inputStream: PLLStream);
var
  i: int;
begin
{@ignore}
  FillChar(p, sizeof(p), 0);
{@emit}
  OpenLexer(p.lex, filename, inputStream);
  initIdTable(p.repl);
  for i := low(stdReplacements) to high(stdReplacements) do
    IdTablePut(p.repl, getIdent(stdReplacements[i][0]),
                       getIdent(stdReplacements[i][1]));
  if gCmd = cmdBoot then
    for i := low(nimReplacements) to high(nimReplacements) do
      IdTablePut(p.repl, getIdent(nimReplacements[i][0]),
                         getIdent(nimReplacements[i][1]));
end;

procedure ClosePasParser(var p: TPasParser);
begin
  CloseLexer(p.lex);
end;

// ---------------- parser helpers --------------------------------------------

procedure getTok(var p: TPasParser);
begin
  getPasTok(p.lex, p.tok)
end;

procedure parMessage(const p: TPasParser; const msg: TMsgKind;
                     const arg: string = '');
begin
  lexMessage(p.lex, msg, arg);
end;

function parLineInfo(const p: TPasParser): TLineInfo;
begin
  result := getLineInfo(p.lex)
end;

procedure skipCom(var p: TPasParser; n: PNode);
begin
  while p.tok.xkind = pxComment do begin
    if (n <> nil) then begin
      if n.comment = snil then n.comment := p.tok.literal
      else n.comment := n.comment +{&} nl +{&} p.tok.literal;
    end
    else
      parMessage(p, warnCommentXIgnored, p.tok.literal);
    getTok(p);
  end
end;

procedure ExpectIdent(const p: TPasParser);
begin
  if p.tok.xkind <> pxSymbol then
    lexMessage(p.lex, errIdentifierExpected, pasTokToStr(p.tok));
end;

procedure Eat(var p: TPasParser; xkind: TPasTokKind);
begin
  if p.tok.xkind = xkind then getTok(p)
  else lexMessage(p.lex, errTokenExpected, PasTokKindToStr[xkind])
end;

procedure Opt(var p: TPasParser; xkind: TPasTokKind);
begin
  if p.tok.xkind = xkind then getTok(p)
end;
// ----------------------------------------------------------------------------

function newNodeP(kind: TNodeKind; const p: TPasParser): PNode;
begin
  result := newNodeI(kind, getLineInfo(p.lex));
end;

function newIntNodeP(kind: TNodeKind; const intVal: BiggestInt;
                     const p: TPasParser): PNode;
begin
  result := newNodeP(kind, p);
  result.intVal := intVal;
end;

function newFloatNodeP(kind: TNodeKind; const floatVal: BiggestFloat;
                       const p: TPasParser): PNode;
begin
  result := newNodeP(kind, p);
  result.floatVal := floatVal;
end;

function newStrNodeP(kind: TNodeKind; const strVal: string;
                     const p: TPasParser): PNode;
begin
  result := newNodeP(kind, p);
  result.strVal := strVal;
end;

function newIdentNodeP(ident: PIdent; const p: TPasParser): PNode;
begin
  result := newNodeP(nkIdent, p);
  result.ident := ident;
end;

function createIdentNodeP(ident: PIdent; const p: TPasParser): PNode;
var
  x: PIdent;
begin
  result := newNodeP(nkIdent, p);
  x := PIdent(IdTableGet(p.repl, ident));
  if x <> nil then result.ident := x
  else result.ident := ident;
end;

// ------------------- Expression parsing ------------------------------------

function parseExpr(var p: TPasParser): PNode; forward;
function parseStmt(var p: TPasParser): PNode; forward;
function parseTypeDesc(var p: TPasParser;
                       definition: PNode=nil): PNode; forward;

function parseEmit(var p: TPasParser; definition: PNode): PNode;
var
  a: PNode;
begin
  getTok(p); // skip 'emit'
  result := nil;
  if p.tok.xkind <> pxCurlyDirRi then
    case p.context of
      conExpr: result := parseExpr(p);
      conStmt: begin
        result := parseStmt(p);
        if p.tok.xkind <> pxCurlyDirRi then begin
          a := result;
          result := newNodeP(nkStmtList, p);
          addSon(result, a);
          while p.tok.xkind <> pxCurlyDirRi do begin
            addSon(result, parseStmt(p));
          end
        end
      end;
      conTypeDesc: result := parseTypeDesc(p, definition);
    end;
  eat(p, pxCurlyDirRi);
end;

function parseCommand(var p: TPasParser; definition: PNode=nil): PNode;
var
  a: PNode;
begin
  result := nil;
  getTok(p);
  if p.tok.ident.id = getIdent('discard').id then begin
    result := newNodeP(nkDiscardStmt, p);
    getTok(p); eat(p, pxCurlyDirRi);
    addSon(result, parseExpr(p));
  end
  else if p.tok.ident.id = getIdent('set').id then begin
    getTok(p); eat(p, pxCurlyDirRi);
    result := parseExpr(p);
    result.kind := nkCurly;
    assert(sonsNotNil(result));
  end
  else if p.tok.ident.id = getIdent('cast').id then begin
    getTok(p); eat(p, pxCurlyDirRi);
    a := parseExpr(p);
    if (a.kind = nkCall) and (sonsLen(a) = 2) then begin
      result := newNodeP(nkCast, p);
      addSon(result, a.sons[0]);
      addSon(result, a.sons[1]);
    end
    else begin
      parMessage(p, errInvalidDirectiveX, pasTokToStr(p.tok));
      result := a
    end
  end
  else if p.tok.ident.id = getIdent('emit').id then begin
    result := parseEmit(p, definition);
  end
  else if p.tok.ident.id = getIdent('ignore').id then begin
    getTok(p); eat(p, pxCurlyDirRi);
    while true do begin
      case p.tok.xkind of
        pxEof: parMessage(p, errTokenExpected, '{@emit}');
        pxCommand: begin
          getTok(p);
          if p.tok.ident.id = getIdent('emit').id then begin
            result := parseEmit(p, definition);
            break
          end
          else begin
            while (p.tok.xkind <> pxCurlyDirRi) and (p.tok.xkind <> pxEof) do
              getTok(p);
            eat(p, pxCurlyDirRi);
          end;
        end;
        else getTok(p) // skip token
      end
    end
  end
  else if p.tok.ident.id = getIdent('ptr').id then begin
    result := newNodeP(nkPtrTy, p);
    getTok(p); eat(p, pxCurlyDirRi);
  end
  else if p.tok.ident.id = getIdent('tuple').id then begin
    result := newNodeP(nkTupleTy, p);
    getTok(p); eat(p, pxCurlyDirRi);
  end
  else if p.tok.ident.id = getIdent('acyclic').id then begin
    result := newIdentNodeP(p.tok.ident, p);
    getTok(p); eat(p, pxCurlyDirRi);
  end
  else begin
    parMessage(p, errInvalidDirectiveX, pasTokToStr(p.tok));
    while true do begin
      getTok(p);
      if (p.tok.xkind = pxCurlyDirRi) or (p.tok.xkind = pxEof) then break;
    end;
    eat(p, pxCurlyDirRi);
    result := nil
  end;
end;

function getPrecedence(const kind: TPasTokKind): int;
begin
  case kind of
    pxDiv, pxMod, pxStar, pxSlash, pxShl, pxShr, pxAnd: result := 5; // highest
    pxPlus, pxMinus, pxOr, pxXor: result := 4;
    pxIn, pxEquals, pxLe, pxLt, pxGe, pxGt, pxNeq, pxIs: result := 3;
    else result := -1;
  end;
end;

function rangeExpr(var p: TPasParser): PNode;
var
  a: PNode;
begin
  a := parseExpr(p);
  if p.tok.xkind = pxDotDot then begin
    result := newNodeP(nkRange, p);
    addSon(result, a);
    getTok(p); skipCom(p, result);
    addSon(result, parseExpr(p))
  end
  else result := a
end;

function bracketExprList(var p: TPasParser; first: PNode): PNode;
var
  a: PNode;
begin
  result := newNodeP(nkBracketExpr, p);
  addSon(result, first);
  getTok(p);
  skipCom(p, result);
  while true do begin
    if p.tok.xkind = pxBracketRi then begin
      getTok(p); break
    end;
    if p.tok.xkind = pxEof then begin
      parMessage(p, errTokenExpected, PasTokKindToStr[pxBracketRi]); break
    end;
    a := rangeExpr(p);
    skipCom(p, a);
    if p.tok.xkind = pxComma then begin
      getTok(p);
      skipCom(p, a)
    end;
    addSon(result, a);
  end;
end;

function exprColonEqExpr(var p: TPasParser; kind: TNodeKind;
                         tok: TPasTokKind): PNode;
var
  a: PNode;
begin
  a := parseExpr(p);
  if p.tok.xkind = tok then begin
    result := newNodeP(kind, p);
    getTok(p);
    skipCom(p, result);
    addSon(result, a);
    addSon(result, parseExpr(p));
  end
  else
    result := a
end;

procedure exprListAux(var p: TPasParser; elemKind: TNodeKind;
                      endTok, sepTok: TPasTokKind; result: PNode);
var
  a: PNode;
begin
  getTok(p);
  skipCom(p, result);
  while true do begin
    if p.tok.xkind = endTok then begin
      getTok(p); break
    end;
    if p.tok.xkind = pxEof then begin
      parMessage(p, errTokenExpected, PasTokKindToStr[endtok]); break
    end;
    a := exprColonEqExpr(p, elemKind, sepTok);
    skipCom(p, a);
    if (p.tok.xkind = pxComma) or (p.tok.xkind = pxSemicolon) then begin
      getTok(p);
      skipCom(p, a)
    end;
    addSon(result, a);
  end;
end;

function qualifiedIdent(var p: TPasParser): PNode;
var
  a: PNode;
begin
  if p.tok.xkind = pxSymbol then
    result := createIdentNodeP(p.tok.ident, p)
  else begin
    parMessage(p, errIdentifierExpected, pasTokToStr(p.tok));
    result := nil;
    exit
  end;
  getTok(p);
  skipCom(p, result);
  if p.tok.xkind = pxDot then begin
    getTok(p);
    skipCom(p, result);
    if p.tok.xkind = pxSymbol then begin
      a := result;
      result := newNodeI(nkDotExpr, a.info);
      addSon(result, a);
      addSon(result, createIdentNodeP(p.tok.ident, p));
      getTok(p);
    end
    else parMessage(p, errIdentifierExpected, pasTokToStr(p.tok))
  end;
end;

procedure qualifiedIdentListAux(var p: TPasParser; endTok: TPasTokKind;
                                result: PNode);
var
  a: PNode;
begin
  getTok(p);
  skipCom(p, result);
  while true do begin
    if p.tok.xkind = endTok then begin
      getTok(p); break
    end;
    if p.tok.xkind = pxEof then begin
      parMessage(p, errTokenExpected, PasTokKindToStr[endtok]); break
    end;
    a := qualifiedIdent(p);
    skipCom(p, a);
    if p.tok.xkind = pxComma then begin
      getTok(p); skipCom(p, a)
    end;
    addSon(result, a);
  end
end;

function exprColonEqExprList(var p: TPasParser; kind, elemKind: TNodeKind;
                             endTok, sepTok: TPasTokKind): PNode;
begin
  result := newNodeP(kind, p);
  exprListAux(p, elemKind, endTok, sepTok, result);
end;

procedure setBaseFlags(n: PNode; base: TNumericalBase);
begin
  case base of
    base10: begin end;
    base2: include(n.flags, nfBase2);
    base8: include(n.flags, nfBase8);
    base16: include(n.flags, nfBase16);
  end
end;

function identOrLiteral(var p: TPasParser): PNode;
var
  a: PNode;
begin
  case p.tok.xkind of
    pxSymbol: begin
      result := createIdentNodeP(p.tok.ident, p);
      getTok(p)
    end;
    // literals
    pxIntLit: begin
      result := newIntNodeP(nkIntLit, p.tok.iNumber, p);
      setBaseFlags(result, p.tok.base);
      getTok(p);
    end;
    pxInt64Lit: begin
      result := newIntNodeP(nkInt64Lit, p.tok.iNumber, p);
      setBaseFlags(result, p.tok.base);
      getTok(p);
    end;
    pxFloatLit: begin
      result := newFloatNodeP(nkFloatLit, p.tok.fNumber, p);
      setBaseFlags(result, p.tok.base);
      getTok(p);
    end;
    pxStrLit: begin
      if length(p.tok.literal) <> 1 then
        result := newStrNodeP(nkStrLit, p.tok.literal, p)
      else
        result := newIntNodeP(nkCharLit, ord(p.tok.literal[strStart]), p);
      getTok(p);
    end;
    pxNil: begin
      result := newNodeP(nkNilLit, p);
      getTok(p);
    end;

    pxParLe: begin // () constructor
      result := exprColonEqExprList(p, nkPar, nkExprColonExpr, pxParRi,
                                    pxColon);
      //if hasSonWith(result, nkExprColonExpr) then
      //  replaceSons(result, nkExprColonExpr, nkExprEqExpr)
      if (sonsLen(result) > 1) and not hasSonWith(result, nkExprColonExpr) then
        result.kind := nkBracket; // is an array constructor
    end;
    pxBracketLe: begin // [] constructor
      result := newNodeP(nkBracket, p);
      getTok(p);
      skipCom(p, result);
      while (p.tok.xkind <> pxBracketRi) and (p.tok.xkind <> pxEof) do begin
        a := rangeExpr(p);
        if a.kind = nkRange then
          result.kind := nkCurly; // it is definitely a set literal
        opt(p, pxComma);
        skipCom(p, a);
        assert(a <> nil);
        addSon(result, a);
      end;
      eat(p, pxBracketRi);
    end;
    pxCommand: result := parseCommand(p);
    else begin
      parMessage(p, errExprExpected, pasTokToStr(p.tok));
      getTok(p); // we must consume a token here to prevend endless loops!
      result := nil
    end
  end;
  if result <> nil then
    skipCom(p, result);
end;

function primary(var p: TPasParser): PNode;
var
  a: PNode;
begin
  // prefix operator?
  if (p.tok.xkind = pxNot) or (p.tok.xkind = pxMinus)
  or (p.tok.xkind = pxPlus) then begin
    result := newNodeP(nkPrefix, p);
    a := newIdentNodeP(getIdent(pasTokToStr(p.tok)), p);
    addSon(result, a);
    getTok(p);
    skipCom(p, a);
    addSon(result, primary(p));
    exit
  end
  else if p.tok.xkind = pxAt then begin
    result := newNodeP(nkAddr, p);
    a := newIdentNodeP(getIdent(pasTokToStr(p.tok)), p);
    getTok(p);
    if p.tok.xkind = pxBracketLe then begin
      result := newNodeP(nkPrefix, p);
      addSon(result, a);
      addSon(result, identOrLiteral(p));
    end
    else
      addSon(result, primary(p));
    exit
  end;
  result := identOrLiteral(p);
  while true do begin
    case p.tok.xkind of
      pxParLe: begin
        a := result;
        result := newNodeP(nkCall, p);
        addSon(result, a);
        exprListAux(p, nkExprEqExpr, pxParRi, pxEquals, result);
      end;
      pxDot: begin
        a := result;
        result := newNodeP(nkDotExpr, p);
        addSon(result, a);
        getTok(p); // skip '.'
        skipCom(p, result);
        if p.tok.xkind = pxSymbol then begin
          addSon(result, createIdentNodeP(p.tok.ident, p));
          getTok(p);
        end
        else
          parMessage(p, errIdentifierExpected, pasTokToStr(p.tok));
      end;
      pxHat: begin
        a := result;
        result := newNodeP(nkDerefExpr, p);
        addSon(result, a);
        getTok(p);
      end;
      pxBracketLe: result := bracketExprList(p, result);
      else break
    end
  end
end;

function lowestExprAux(var p: TPasParser; out v: PNode;
                       limit: int): TPasTokKind;
var
  op, nextop: TPasTokKind;
  opPred: int;
  v2, node, opNode: PNode;
begin
  v := primary(p);
  // expand while operators have priorities higher than 'limit'
  op := p.tok.xkind;
  opPred := getPrecedence(op);
  while (opPred > limit) do begin
    node := newNodeP(nkInfix, p);
    opNode := newIdentNodeP(getIdent(pasTokToStr(p.tok)), p);
    // skip operator:
    getTok(p);
    case op of
      pxPlus: begin
        case p.tok.xkind of
          pxPer: begin getTok(p); eat(p, pxCurlyDirRi);
                       opNode.ident := getIdent('+%') end;
          pxAmp: begin getTok(p); eat(p, pxCurlyDirRi);
                       opNode.ident := getIdent('&'+'') end;
          else begin end
        end
      end;
      pxMinus: begin
        if p.tok.xkind = pxPer then begin
          getTok(p); eat(p, pxCurlyDirRi);
          opNode.ident := getIdent('-%')
        end;
      end;
      pxEquals: opNode.ident := getIdent('==');
      pxNeq:    opNode.ident := getIdent('!=');
      else begin end
    end;

    skipCom(p, opNode);

    // read sub-expression with higher priority
    nextop := lowestExprAux(p, v2, opPred);
    addSon(node, opNode);
    addSon(node, v);
    addSon(node, v2);
    v := node;
    op := nextop;
    opPred := getPrecedence(nextop);
  end;
  result := op;  // return first untreated operator
end;

function fixExpr(n: PNode): PNode;
var
  i: int;
begin
  result := n;
  if n = nil then exit;
  case n.kind of
    nkInfix: begin
      if n.sons[1].kind = nkBracket then // binary expression with [] is a set
        n.sons[1].kind := nkCurly;
      if n.sons[2].kind = nkBracket then // binary expression with [] is a set
        n.sons[2].kind := nkCurly;
      if (n.sons[0].kind = nkIdent) then begin
        if (n.sons[0].ident.id = getIdent('+'+'').id) then begin
          if (n.sons[1].kind = nkCharLit)
              and (n.sons[2].kind = nkStrLit) and (n.sons[2].strVal = '') then
          begin
              result := newStrNode(nkStrLit, chr(int(n.sons[1].intVal))+'');
              result.info := n.info;
              exit; // do not process sons as they don't exist anymore
          end
          else if (n.sons[1].kind in [nkCharLit, nkStrLit])
               or (n.sons[2].kind in [nkCharLit, nkStrLit]) then begin
            n.sons[0].ident := getIdent('&'+''); // fix operator
          end
        end
      end
    end
    else begin end
  end;
  if not (n.kind in [nkEmpty..nkNilLit]) then
    for i := 0 to sonsLen(n)-1 do
      result.sons[i] := fixExpr(n.sons[i])
end;

function parseExpr(var p: TPasParser): PNode;
var
  oldcontext: TPasContext;
begin
  oldcontext := p.context;
  p.context := conExpr;
  if p.tok.xkind = pxCommand then begin
    result := parseCommand(p)
  end
  else begin
  {@discard} lowestExprAux(p, result, -1);
    result := fixExpr(result)
  end;
  //if result = nil then
  //  internalError(parLineInfo(p), 'parseExpr() returned nil');
  p.context := oldcontext;
end;

// ---------------------- statement parser ------------------------------------
function parseExprStmt(var p: TPasParser): PNode;
var
  a, b: PNode;
  info: TLineInfo;
begin
  info := parLineInfo(p);
  a := parseExpr(p);
  if p.tok.xkind = pxAsgn then begin
    getTok(p);
    skipCom(p, a);
    b := parseExpr(p);
    result := newNodeI(nkAsgn, info);
    addSon(result, a);
    addSon(result, b);
  end
  else
    result := a
end;

function inImportBlackList(ident: PIdent): bool;
var
  i: int;
begin
  for i := low(ImportBlackList) to high(ImportBlackList) do
    if ident.id = getIdent(ImportBlackList[i]).id then begin
      result := true; exit
    end;
  result := false
end;

function parseUsesStmt(var p: TPasParser): PNode;
var
  a: PNode;
begin
  result := newNodeP(nkImportStmt, p);
  getTok(p); // skip `import`
  skipCom(p, result);
  while true do begin
    case p.tok.xkind of
      pxEof: break;
      pxSymbol:   a := newIdentNodeP(p.tok.ident, p);
      else begin
        parMessage(p, errIdentifierExpected, pasTokToStr(p.tok));
        break
      end;
    end;
    getTok(p); // skip identifier, string
    skipCom(p, a);
    if (gCmd <> cmdBoot) or not inImportBlackList(a.ident) then
      addSon(result, createIdentNodeP(a.ident, p));
    if p.tok.xkind = pxComma then begin
      getTok(p);
      skipCom(p, a)
    end
    else break
  end;
  if sonsLen(result) = 0 then result := nil;
end;

function parseIncludeDir(var p: TPasParser): PNode;
var
  filename: string;
begin
  result := newNodeP(nkIncludeStmt, p);
  getTok(p); // skip `include`
  filename := '';
  while true do begin
    case p.tok.xkind of
      pxSymbol, pxDot, pxDotDot, pxSlash: begin
        filename := filename +{&} pasTokToStr(p.tok);
        getTok(p);
      end;
      pxStrLit: begin
        filename := p.tok.literal;
        getTok(p);
        break
      end;
      pxCurlyDirRi: break;
      else begin
        parMessage(p, errIdentifierExpected, pasTokToStr(p.tok));
        break
      end;
    end;
  end;
  addSon(result, newStrNodeP(nkStrLit, changeFileExt(filename, 'nim'), p));
  if filename = 'config.inc' then result := nil;
end;

function definedExprAux(var p: TPasParser): PNode;
begin
  result := newNodeP(nkCall, p);
  addSon(result, newIdentNodeP(getIdent('defined'), p));
  ExpectIdent(p);
  addSon(result, createIdentNodeP(p.tok.ident, p));
  getTok(p);
end;

function isHandledDirective(const p: TPasParser): bool;
begin
  result := false;
  if p.tok.xkind in [pxCurlyDirLe, pxStarDirLe] then
    case whichKeyword(p.tok.ident) of
      wElse, wEndif: result := false
      else result := true
    end
end;

function parseStmtList(var p: TPasParser): PNode;
begin
  result := newNodeP(nkStmtList, p);
  while true do begin
    case p.tok.xkind of
      pxEof: break;
      pxCurlyDirLe, pxStarDirLe: begin
        if not isHandledDirective(p) then break;
      end
      else begin end
    end;
    addSon(result, parseStmt(p))
  end;
  if sonsLen(result) = 1 then result := result.sons[0];
end;

procedure parseIfDirAux(var p: TPasParser; result: PNode);
var
  s: PNode;
  endMarker: TPasTokKind;
begin
  addSon(result.sons[0], parseStmtList(p));
  if p.tok.xkind in [pxCurlyDirLe, pxStarDirLe] then begin
    endMarker := succ(p.tok.xkind);
    if whichKeyword(p.tok.ident) = wElse then begin
      s := newNodeP(nkElse, p);
      while (p.tok.xkind <> pxEof) and (p.tok.xkind <> endMarker) do getTok(p);
      eat(p, endMarker);
      addSon(s, parseStmtList(p));
      addSon(result, s);
    end;
    if p.tok.xkind in [pxCurlyDirLe, pxStarDirLe] then begin
      endMarker := succ(p.tok.xkind);
      if whichKeyword(p.tok.ident) = wEndif then begin
        while (p.tok.xkind <> pxEof) and (p.tok.xkind <> endMarker) do getTok(p);
        eat(p, endMarker);
      end
      else parMessage(p, errXExpected, '{$endif}');
    end
  end
  else
    parMessage(p, errXExpected, '{$endif}');
end;

function parseIfdefDir(var p: TPasParser; endMarker: TPasTokKind): PNode;
begin
  result := newNodeP(nkWhenStmt, p);
  addSon(result, newNodeP(nkElifBranch, p));
  getTok(p);
  addSon(result.sons[0], definedExprAux(p));
  eat(p, endMarker);
  parseIfDirAux(p, result);
end;

function parseIfndefDir(var p: TPasParser; endMarker: TPasTokKind): PNode;
var
  e: PNode;
begin
  result := newNodeP(nkWhenStmt, p);
  addSon(result, newNodeP(nkElifBranch, p));
  getTok(p);
  e := newNodeP(nkCall, p);
  addSon(e, newIdentNodeP(getIdent('not'), p));
  addSon(e, definedExprAux(p));
  eat(p, endMarker);
  addSon(result.sons[0], e);
  parseIfDirAux(p, result);
end;

function parseIfDir(var p: TPasParser; endMarker: TPasTokKind): PNode;
begin
  result := newNodeP(nkWhenStmt, p);
  addSon(result, newNodeP(nkElifBranch, p));
  getTok(p);
  addSon(result.sons[0], parseExpr(p));
  eat(p, endMarker);
  parseIfDirAux(p, result);
end;

function parseDirective(var p: TPasParser): PNode;
var
  endMarker: TPasTokKind;
begin
  result := nil;
  if not (p.tok.xkind in [pxCurlyDirLe, pxStarDirLe]) then exit;
  endMarker := succ(p.tok.xkind);
  if p.tok.ident <> nil then
    case whichKeyword(p.tok.ident) of
      wInclude: begin
        result := parseIncludeDir(p);
        eat(p, endMarker);
      end;
      wIf: result := parseIfDir(p, endMarker);
      wIfdef: result := parseIfdefDir(p, endMarker);
      wIfndef: result := parseIfndefDir(p, endMarker);
      else begin
        // skip unknown compiler directive
        while (p.tok.xkind <> pxEof) and (p.tok.xkind <> endMarker) do
          getTok(p);
        eat(p, endMarker);
      end
    end
  else eat(p, endMarker);
end;

function parseRaise(var p: TPasParser): PNode;
begin
  result := newNodeP(nkRaiseStmt, p);
  getTok(p);
  skipCom(p, result);
  if p.tok.xkind <> pxSemicolon then addSon(result, parseExpr(p))
  else addSon(result, nil);
end;

function parseIf(var p: TPasParser): PNode;
var
  branch: PNode;
begin
  result := newNodeP(nkIfStmt, p);
  while true do begin
    getTok(p); // skip ``if``
    branch := newNodeP(nkElifBranch, p);
    skipCom(p, branch);
    addSon(branch, parseExpr(p));
    eat(p, pxThen);
    skipCom(p, branch);
    addSon(branch, parseStmt(p));
    skipCom(p, branch);
    addSon(result, branch);
    if p.tok.xkind = pxElse then begin
      getTok(p);
      if p.tok.xkind <> pxIf then begin
        // ordinary else part:
        branch := newNodeP(nkElse, p);
        skipCom(p, result); // BUGFIX
        addSon(branch, parseStmt(p));
        addSon(result, branch);
        break
      end
      // else: next iteration
    end
    else break
  end
end;

function parseWhile(var p: TPasParser): PNode;
begin
  result := newNodeP(nkWhileStmt, p);
  getTok(p);
  skipCom(p, result);
  addSon(result, parseExpr(p));
  eat(p, pxDo);
  skipCom(p, result);
  addSon(result, parseStmt(p));
end;

function parseRepeat(var p: TPasParser): PNode;
var
  a, b, c, s: PNode;
begin
  result := newNodeP(nkWhileStmt, p);
  getTok(p);
  skipCom(p, result);
  addSon(result, newIdentNodeP(getIdent('true'), p));
  s := newNodeP(nkStmtList, p);
  while (p.tok.xkind <> pxEof) and (p.tok.xkind <> pxUntil) do begin
    addSon(s, parseStmt(p))
  end;
  eat(p, pxUntil);
  a := newNodeP(nkIfStmt, p);
  skipCom(p, a);
  b := newNodeP(nkElifBranch, p);
  c := newNodeP(nkBreakStmt, p);
  addSon(c, nil);
  addSon(b, parseExpr(p));
  skipCom(p, a);
  addSon(b, c);
  addSon(a, b);

  if (b.sons[0].kind = nkIdent) and (b.sons[0].ident.id = getIdent('false').id)
  then begin end // do not add an ``if false: break`` statement
  else addSon(s, a);
  addSon(result, s);
end;

function parseCase(var p: TPasParser): PNode;
var
  b: PNode;
begin
  result := newNodeP(nkCaseStmt, p);
  getTok(p);
  addSon(result, parseExpr(p));
  eat(p, pxOf);
  skipCom(p, result);
  while (p.tok.xkind <> pxEnd) and (p.tok.xkind <> pxEof) do begin
    if p.tok.xkind = pxElse then begin
      b := newNodeP(nkElse, p);
      getTok(p);
    end
    else begin
      b := newNodeP(nkOfBranch, p);
      while (p.tok.xkind <> pxEof) and (p.tok.xkind <> pxColon) do begin
        addSon(b, rangeExpr(p));
        opt(p, pxComma);
        skipcom(p, b);
      end;
      eat(p, pxColon);
    end;
    skipCom(p, b);
    addSon(b, parseStmt(p));
    addSon(result, b);
    if b.kind = nkElse then break;
  end;
  eat(p, pxEnd);
end;

function parseTry(var p: TPasParser): PNode;
var
  b, e: PNode;
begin
  result := newNodeP(nkTryStmt, p);
  getTok(p);
  skipCom(p, result);
  b := newNodeP(nkStmtList, p);
  while not (p.tok.xkind in [pxFinally, pxExcept, pxEof, pxEnd]) do
    addSon(b, parseStmt(p));
  addSon(result, b);
  if p.tok.xkind = pxExcept then begin
    getTok(p);
    while p.tok.ident.id = getIdent('on').id do begin
      b := newNodeP(nkExceptBranch, p);
      getTok(p);
      e := qualifiedIdent(p);
      if p.tok.xkind = pxColon then begin
        getTok(p);
        e := qualifiedIdent(p);
      end;
      addSon(b, e);
      eat(p, pxDo);
      addSon(b, parseStmt(p));
      addSon(result, b);
      if p.tok.xkind = pxCommand then {@discard} parseCommand(p);
    end;
    if p.tok.xkind = pxElse then begin
      b := newNodeP(nkExceptBranch, p);
      getTok(p);
      addSon(b, parseStmt(p));
      addSon(result, b);
    end
  end;
  if p.tok.xkind = pxFinally then begin
    b := newNodeP(nkFinally, p);
    getTok(p);
    e := newNodeP(nkStmtList, p);
    while (p.tok.xkind <> pxEof) and (p.tok.xkind <> pxEnd) do begin
      addSon(e, parseStmt(p))
    end;
    if sonsLen(e) = 0 then
      addSon(e, newNodeP(nkNilLit, p));
    addSon(result, e);
  end;
  eat(p, pxEnd);
end;

function parseFor(var p: TPasParser): PNode;
var
  a, b, c: PNode;
begin
  result := newNodeP(nkForStmt, p);
  getTok(p);
  skipCom(p, result);
  expectIdent(p);
  addSon(result, createIdentNodeP(p.tok.ident, p));
  getTok(p);
  eat(p, pxAsgn);
  a := parseExpr(p);
  b := nil;
  c := newNodeP(nkCall, p);
  if p.tok.xkind = pxTo then begin
    addSon(c, newIdentNodeP(getIdent('countup'), p));
    getTok(p);
    b := parseExpr(p);
  end
  else if p.tok.xkind = pxDownto then begin
    addSon(c, newIdentNodeP(getIdent('countdown'), p));
    getTok(p);
    b := parseExpr(p);
  end
  else
    parMessage(p, errTokenExpected, PasTokKindToStr[pxTo]);
  addSon(c, a);
  addSon(c, b);

  eat(p, pxDo);
  skipCom(p, result);
  addSon(result, c);
  addSon(result, parseStmt(p))
end;

function parseParam(var p: TPasParser): PNode;
var
  a, v: PNode;
begin
  result := newNodeP(nkIdentDefs, p);
  v := nil;
  case p.tok.xkind of
    pxConst: getTok(p);
    pxVar:   begin getTok(p); v := newNodeP(nkVarTy, p); end;
    pxOut:   begin getTok(p); v := newNodeP(nkVarTy, p); end;
    else begin end
  end;
  while true do begin
    case p.tok.xkind of
      pxSymbol: a := createIdentNodeP(p.tok.ident, p);
      pxColon, pxEof, pxParRi, pxEquals: break;
      else begin
        parMessage(p, errIdentifierExpected, pasTokToStr(p.tok));
        exit;
      end;
    end;
    getTok(p); // skip identifier
    skipCom(p, a);
    if p.tok.xkind = pxComma then begin
      getTok(p); skipCom(p, a)
    end;
    addSon(result, a);
  end;
  if p.tok.xkind = pxColon then begin
    getTok(p); skipCom(p, result);
    if v <> nil then addSon(v, parseTypeDesc(p))
    else v := parseTypeDesc(p);
    addSon(result, v);
  end
  else begin
    addSon(result, nil);
    if p.tok.xkind <> pxEquals then
      parMessage(p, errColonOrEqualsExpected, pasTokToStr(p.tok))
  end;
  if p.tok.xkind = pxEquals then begin
    getTok(p); skipCom(p, result);
    addSon(result, parseExpr(p));
  end
  else
    addSon(result, nil);
end;

function parseParamList(var p: TPasParser): PNode;
var
  a: PNode;
begin
  result := newNodeP(nkFormalParams, p);
  addSon(result, nil); // return type
  if p.tok.xkind = pxParLe then begin
    p.inParamList := true;
    getTok(p);
    skipCom(p, result);
    while true do begin
      case p.tok.xkind of
        pxSymbol, pxConst, pxVar, pxOut: a := parseParam(p);
        pxParRi: begin getTok(p); break end;
        else begin parMessage(p, errTokenExpected, ')'+''); break; end;
      end;
      skipCom(p, a);
      if p.tok.xkind = pxSemicolon then begin
        getTok(p); skipCom(p, a)
      end;
      addSon(result, a)
    end;
    p.inParamList := false
  end;
  if p.tok.xkind = pxColon then begin
    getTok(p);
    skipCom(p, result);
    result.sons[0] := parseTypeDesc(p)
  end
end;

function parseCallingConvention(var p: TPasParser): PNode;
begin
  result := nil;
  if p.tok.xkind = pxSymbol then begin
    case whichKeyword(p.tok.ident) of
      wStdcall, wCDecl, wSafeCall, wSysCall, wInline, wFastCall: begin
        result := newNodeP(nkPragma, p);
        addSon(result, newIdentNodeP(p.tok.ident, p));
        getTok(p);
        opt(p, pxSemicolon);
      end;
      wRegister: begin
        result := newNodeP(nkPragma, p);
        addSon(result, newIdentNodeP(getIdent('fastcall'), p));
        getTok(p);
        opt(p, pxSemicolon);
      end
      else begin end
    end
  end
end;

function parseRoutineSpecifiers(var p: TPasParser; out noBody: boolean): PNode;
var
  e: PNode;
begin
  result := parseCallingConvention(p);
  noBody := false;
  while p.tok.xkind = pxSymbol do begin
    case whichKeyword(p.tok.ident) of
      wAssembler, wOverload, wFar: begin
        getTok(p); opt(p, pxSemicolon);
      end;
      wForward: begin
        noBody := true;
        getTok(p); opt(p, pxSemicolon);
      end;
      wImportc: begin
        // This is a fake for platform module. There is no ``importc``
        // directive in Pascal.
        if result = nil then result := newNodeP(nkPragma, p);
        addSon(result, newIdentNodeP(getIdent('importc'), p));
        noBody := true;
        getTok(p); opt(p, pxSemicolon);
      end;
      wNoConv: begin
        // This is a fake for platform module. There is no ``noconv``
        // directive in Pascal.
        if result = nil then result := newNodeP(nkPragma, p);
        addSon(result, newIdentNodeP(getIdent('noconv'), p));
        noBody := true;
        getTok(p); opt(p, pxSemicolon);
      end;
      wProcVar: begin
        // This is a fake for the Nimrod compiler. There is no ``procvar``
        // directive in Pascal.
        if result = nil then result := newNodeP(nkPragma, p);
        addSon(result, newIdentNodeP(getIdent('procvar'), p));
        getTok(p); opt(p, pxSemicolon);
      end;
      wVarargs: begin
        if result = nil then result := newNodeP(nkPragma, p);
        addSon(result, newIdentNodeP(getIdent('varargs'), p));
        getTok(p); opt(p, pxSemicolon);
      end;
      wExternal: begin
        if result = nil then result := newNodeP(nkPragma, p);
        getTok(p);
        noBody := true;
        e := newNodeP(nkExprColonExpr, p);
        addSon(e, newIdentNodeP(getIdent('dynlib'), p));
        addSon(e, parseExpr(p));
        addSon(result, e);
        opt(p, pxSemicolon);
        if (p.tok.xkind = pxSymbol)
        and (p.tok.ident.id = getIdent('name').id) then begin
          e := newNodeP(nkExprColonExpr, p);
          getTok(p);
          addSon(e, newIdentNodeP(getIdent('importc'), p));
          addSon(e, parseExpr(p));
          addSon(result, e);
        end
        else
          addSon(result, newIdentNodeP(getIdent('importc'), p));
        opt(p, pxSemicolon);
      end
      else begin
        e := parseCallingConvention(p);
        if e = nil then break;
        if result = nil then result := newNodeP(nkPragma, p);
        addSon(result, e.sons[0]);
      end;
    end
  end
end;

function parseRoutineType(var p: TPasParser): PNode;
begin
  result := newNodeP(nkProcTy, p);
  getTok(p); skipCom(p, result);
  addSon(result, parseParamList(p));
  opt(p, pxSemicolon);
  addSon(result, parseCallingConvention(p));
  skipCom(p, result);
end;

function parseEnum(var p: TPasParser): PNode;
var
  a, b: PNode;
begin
  result := newNodeP(nkEnumTy, p);
  getTok(p);
  skipCom(p, result);
  addSon(result, nil); // it does not inherit from any enumeration

  while true do begin
    case p.tok.xkind of
      pxEof, pxParRi: break;
      pxSymbol: a := newIdentNodeP(p.tok.ident, p);
      else begin
        parMessage(p, errIdentifierExpected, pasTokToStr(p.tok));
        break
      end;
    end;
    getTok(p); // skip identifier
    skipCom(p, a);
    if (p.tok.xkind = pxEquals) or (p.tok.xkind = pxAsgn) then begin
      getTok(p);
      skipCom(p, a);
      b := a;
      a := newNodeP(nkEnumFieldDef, p);
      addSon(a, b);
      addSon(a, parseExpr(p));
    end;
    if p.tok.xkind = pxComma then begin
      getTok(p); skipCom(p, a)
    end;
    addSon(result, a);
  end;
  eat(p, pxParRi)
end;

function identVis(var p: TPasParser): PNode; // identifier with visability
var
  a: PNode;
begin
  a := createIdentNodeP(p.tok.ident, p);
  if p.section = seInterface then begin
    result := newNodeP(nkPostfix, p);
    addSon(result, newIdentNodeP(getIdent('*'+''), p));
    addSon(result, a);
  end
  else
    result := a;
  getTok(p)
end;

type
  TSymbolParser = function (var p: TPasParser): PNode;

function rawIdent(var p: TPasParser): PNode;
begin
  result := createIdentNodeP(p.tok.ident, p);
  getTok(p);
end;

function parseIdentColonEquals(var p: TPasParser;
                               identParser: TSymbolParser): PNode;
var
  a: PNode;
begin
  result := newNodeP(nkIdentDefs, p);
  while true do begin
    case p.tok.xkind of
      pxSymbol: a := identParser(p);
      pxColon, pxEof, pxParRi, pxEquals: break;
      else begin
        parMessage(p, errIdentifierExpected, pasTokToStr(p.tok));
        exit;
      end;
    end;
    skipCom(p, a);
    if p.tok.xkind = pxComma then begin
      getTok(p);
      skipCom(p, a)
    end;
    addSon(result, a);
  end;
  if p.tok.xkind = pxColon then begin
    getTok(p); skipCom(p, result);
    addSon(result, parseTypeDesc(p));
  end
  else begin
    addSon(result, nil);
    if p.tok.xkind <> pxEquals then
      parMessage(p, errColonOrEqualsExpected, pasTokToStr(p.tok))
  end;
  if p.tok.xkind = pxEquals then begin
    getTok(p); skipCom(p, result);
    addSon(result, parseExpr(p));
  end
  else
    addSon(result, nil);
  if p.tok.xkind = pxSemicolon then begin
    getTok(p); skipCom(p, result);
  end
end;

function parseRecordCase(var p: TPasParser): PNode;
var
  a, b, c: PNode;
begin
  result := newNodeP(nkRecCase, p);
  getTok(p);
  a := newNodeP(nkIdentDefs, p);
  addSon(a, rawIdent(p));
  eat(p, pxColon);
  addSon(a, parseTypeDesc(p));
  addSon(a, nil);
  addSon(result, a);
  eat(p, pxOf);
  skipCom(p, result);

  while true do begin
    case p.tok.xkind of
      pxEof, pxEnd: break;
      pxElse: begin
        b := newNodeP(nkElse, p);
        getTok(p);
      end;
      else begin
        b := newNodeP(nkOfBranch, p);
        while (p.tok.xkind <> pxEof) and (p.tok.xkind <> pxColon) do begin
          addSon(b, rangeExpr(p));
          opt(p, pxComma);
          skipcom(p, b);
        end;
        eat(p, pxColon);
      end
    end;
    skipCom(p, b);
    c := newNodeP(nkRecList, p);
    eat(p, pxParLe);
    while (p.tok.xkind <> pxParRi) and (p.tok.xkind <> pxEof) do begin
      addSon(c, parseIdentColonEquals(p, rawIdent));
      opt(p, pxSemicolon);
      skipCom(p, lastSon(c));
    end;
    eat(p, pxParRi);
    opt(p, pxSemicolon);
    if sonsLen(c) > 0 then skipCom(p, lastSon(c))
    else addSon(c, newNodeP(nkNilLit, p));
    addSon(b, c);
    addSon(result, b);
    if b.kind = nkElse then break;
  end
end;

function parseRecordPart(var p: TPasParser): PNode;
begin
  result := nil;
  while (p.tok.xkind <> pxEof) and (p.tok.xkind <> pxEnd) do begin
    if result = nil then result := newNodeP(nkRecList, p);
    case p.tok.xkind of
      pxSymbol: begin
        addSon(result, parseIdentColonEquals(p, rawIdent));
        opt(p, pxSemicolon);
        skipCom(p, lastSon(result));
      end;
      pxCase: begin
        addSon(result, parseRecordCase(p));
      end;
      pxComment: skipCom(p, lastSon(result));
      else begin
        parMessage(p, errIdentifierExpected, pasTokToStr(p.tok));
        break
      end
    end
  end
end;

procedure exSymbol(var n: PNode);
var
  a: PNode;
begin
  case n.kind of
    nkPostfix: begin end; // already an export marker
    nkPragmaExpr: exSymbol(n.sons[0]);
    nkIdent, nkAccQuoted: begin
      a := newNodeI(nkPostFix, n.info);
      addSon(a, newIdentNode(getIdent('*'+''), n.info));
      addSon(a, n);
      n := a
    end;
    else internalError(n.info, 'exSymbol(): ' + nodekindtostr[n.kind]);
  end
end;

procedure fixRecordDef(var n: PNode);
var
  i, len: int;
begin
  if n = nil then exit;
  case n.kind of
    nkRecCase: begin
      fixRecordDef(n.sons[0]);
      for i := 1 to sonsLen(n)-1 do begin
        len := sonsLen(n.sons[i]);
        fixRecordDef(n.sons[i].sons[len-1])
      end
    end;
    nkRecList, nkRecWhen, nkElse, nkOfBranch, nkElifBranch,
    nkObjectTy: begin
      for i := 0 to sonsLen(n)-1 do fixRecordDef(n.sons[i])
    end;
    nkIdentDefs: begin
      for i := 0 to sonsLen(n)-3 do exSymbol(n.sons[i])
    end;
    nkNilLit: begin end;
    //nkIdent: exSymbol(n);
    else internalError(n.info, 'fixRecordDef(): ' + nodekindtostr[n.kind]);
  end
end;

procedure addPragmaToIdent(var ident: PNode; pragma: PNode);
var
  e, pragmasNode: PNode;
begin
  if ident.kind <> nkPragmaExpr then begin
    pragmasNode := newNodeI(nkPragma, ident.info);
    e := newNodeI(nkPragmaExpr, ident.info);
    addSon(e, ident);
    addSon(e, pragmasNode);
    ident := e;
  end
  else begin
    pragmasNode := ident.sons[1];
    if pragmasNode.kind <> nkPragma then
      InternalError(ident.info, 'addPragmaToIdent');
  end;
  addSon(pragmasNode, pragma);
end;

procedure parseRecordBody(var p: TPasParser; result, definition: PNode);
var
  a: PNode;
begin
  skipCom(p, result);
  a := parseRecordPart(p);
  if result.kind <> nkTupleTy then fixRecordDef(a);
  addSon(result, a);
  eat(p, pxEnd);
  case p.tok.xkind of
    pxSymbol: begin
      if (p.tok.ident.id = getIdent('acyclic').id) then begin
        if definition <> nil then
          addPragmaToIdent(definition.sons[0], newIdentNodeP(p.tok.ident, p))
        else
          InternalError(result.info, 'anonymous record is not supported');
        getTok(p);
      end
      else
        InternalError(result.info, 'parseRecordBody');
    end;
    pxCommand: begin
      if definition <> nil then
        addPragmaToIdent(definition.sons[0], parseCommand(p))
      else
        InternalError(result.info, 'anonymous record is not supported');
    end;
    else begin end
  end;
  opt(p, pxSemicolon);
  skipCom(p, result);
end;

function parseRecordOrObject(var p: TPasParser; kind: TNodeKind;
                             definition: PNode): PNode;
var
  a: PNode;
begin
  result := newNodeP(kind, p);
  getTok(p);
  addSon(result, nil);
  if p.tok.xkind = pxParLe then begin
    a := newNodeP(nkOfInherit, p);
    getTok(p);
    addSon(a, parseTypeDesc(p));
    addSon(result, a);
    eat(p, pxParRi);
  end
  else addSon(result, nil);
  parseRecordBody(p, result, definition);
end;

function parseTypeDesc(var p: TPasParser; definition: PNode=nil): PNode;
var
  oldcontext: TPasContext;
  a, r: PNode;
  i: int;
begin
  oldcontext := p.context;
  p.context := conTypeDesc;
  if p.tok.xkind = pxPacked then getTok(p);
  case p.tok.xkind of
    pxCommand: result := parseCommand(p, definition);
    pxProcedure, pxFunction: result := parseRoutineType(p);
    pxRecord: begin
      getTok(p);
      if p.tok.xkind = pxCommand then begin
        result := parseCommand(p);
        if result.kind <> nkTupleTy then
          InternalError(result.info, 'parseTypeDesc');
        parseRecordBody(p, result, definition);
        a := lastSon(result);
        // embed nkRecList directly into nkTupleTy
        for i := 0 to sonsLen(a)-1 do
          if i = 0 then result.sons[sonsLen(result)-1] := a.sons[0]
          else addSon(result, a.sons[i]);
      end
      else begin
        result := newNodeP(nkObjectTy, p);
        addSon(result, nil);
        addSon(result, nil);
        parseRecordBody(p, result, definition);
        if definition <> nil then
          addPragmaToIdent(definition.sons[0],
                           newIdentNodeP(getIdent('final'), p))
        else
          InternalError(result.info, 'anonymous record is not supported');
      end;
    end;
    pxObject: result := parseRecordOrObject(p, nkObjectTy, definition);
    pxParLe: result := parseEnum(p);
    pxArray: begin
      result := newNodeP(nkBracketExpr, p);
      getTok(p);
      if p.tok.xkind = pxBracketLe then begin
        addSon(result, newIdentNodeP(getIdent('array'), p));
        getTok(p);
        addSon(result, rangeExpr(p));
        eat(p, pxBracketRi);
      end
      else begin
        if p.inParamList then
          addSon(result, newIdentNodeP(getIdent('openarray'), p))
        else
          addSon(result, newIdentNodeP(getIdent('seq'), p));
      end;
      eat(p, pxOf);
      addSon(result, parseTypeDesc(p));
    end;
    pxSet: begin
      result := newNodeP(nkBracketExpr, p);
      getTok(p);
      eat(p, pxOf);
      addSon(result, newIdentNodeP(getIdent('set'), p));
      addSon(result, parseTypeDesc(p));
    end;
    pxHat: begin
      getTok(p);
      if p.tok.xkind = pxCommand then
        result := parseCommand(p)
      else if gCmd = cmdBoot then
        result := newNodeP(nkRefTy, p)
      else
        result := newNodeP(nkPtrTy, p);
      addSon(result, parseTypeDesc(p))
    end;
    pxType: begin
      getTok(p);
      result := parseTypeDesc(p);
    end;
    else begin
      a := primary(p);
      if p.tok.xkind = pxDotDot then begin
        result := newNodeP(nkBracketExpr, p);
        r := newNodeP(nkRange, p);
        addSon(result, newIdentNodeP(getIdent('range'), p));
        getTok(p);
        addSon(r, a);
        addSon(r, parseExpr(p));
        addSon(result, r);
      end
      else
        result := a
    end
  end;
  p.context := oldcontext;
end;

function parseTypeDef(var p: TPasParser): PNode;
var
  a: PNode;
begin
  result := newNodeP(nkTypeDef, p);
  addSon(result, identVis(p));
  addSon(result, nil); // generic params
  if p.tok.xkind = pxEquals then begin
    getTok(p); skipCom(p, result);
    a := parseTypeDesc(p, result);
    addSon(result, a);
  end
  else
    addSon(result, nil);
  if p.tok.xkind = pxSemicolon then begin
    getTok(p); skipCom(p, result);
  end;
end;

function parseTypeSection(var p: TPasParser): PNode;
begin
  result := newNodeP(nkTypeSection, p);
  getTok(p);
  skipCom(p, result);
  while p.tok.xkind = pxSymbol do begin
    addSon(result, parseTypeDef(p))
  end
end;

function parseConstant(var p: TPasParser): PNode;
begin
  result := newNodeP(nkConstDef, p);
  addSon(result, identVis(p));
  if p.tok.xkind = pxColon then begin
    getTok(p); skipCom(p, result);
    addSon(result, parseTypeDesc(p));
  end
  else begin
    addSon(result, nil);
    if p.tok.xkind <> pxEquals then
      parMessage(p, errColonOrEqualsExpected, pasTokToStr(p.tok));
  end;
  if p.tok.xkind = pxEquals then begin
    getTok(p); skipCom(p, result);
    addSon(result, parseExpr(p));
  end
  else
    addSon(result, nil);
  if p.tok.xkind = pxSemicolon then begin
    getTok(p); skipCom(p, result);
  end;
end;

function parseConstSection(var p: TPasParser): PNode;
begin
  result := newNodeP(nkConstSection, p);
  getTok(p);
  skipCom(p, result);
  while p.tok.xkind = pxSymbol do begin
    addSon(result, parseConstant(p))
  end
end;

function parseVar(var p: TPasParser): PNode;
begin
  result := newNodeP(nkVarSection, p);
  getTok(p);
  skipCom(p, result);
  while p.tok.xkind = pxSymbol do begin
    addSon(result, parseIdentColonEquals(p, identVis));
  end;
  p.lastVarSection := result
end;

function parseRoutine(var p: TPasParser): PNode;
var
  a, stmts: PNode;
  noBody: boolean;
  i: int;
begin
  result := newNodeP(nkProcDef, p);
  getTok(p);
  skipCom(p, result);
  expectIdent(p);
  addSon(result, identVis(p));
  addSon(result, nil); // generic parameters
  addSon(result, parseParamList(p));
  opt(p, pxSemicolon);
  addSon(result, parseRoutineSpecifiers(p, noBody));
  if (p.section = seInterface) or noBody then
    addSon(result, nil)
  else begin
    stmts := newNodeP(nkStmtList, p);
    while true do begin
      case p.tok.xkind of
        pxVar:   addSon(stmts, parseVar(p));
        pxConst: addSon(stmts, parseConstSection(p));
        pxType:  addSon(stmts, parseTypeSection(p));
        pxComment: skipCom(p, result);
        pxBegin: break;
        else begin
          parMessage(p, errTokenExpected, 'begin');
          break
        end
      end
    end;
    a := parseStmt(p);
    for i := 0 to sonsLen(a)-1 do addSon(stmts, a.sons[i]);
    addSon(result, stmts);
  end
end;

function fixExit(var p: TPasParser; n: PNode): boolean;
var
  len: int;
  a: PNode;
begin
  result := false;
  if (p.tok.ident.id = getIdent('exit').id) then begin
    len := sonsLen(n);
    if (len <= 0) then exit;
    a := n.sons[len-1];
    if (a.kind = nkAsgn)
    and (a.sons[0].kind = nkIdent)
    and (a.sons[0].ident.id = getIdent('result').id) then begin
      delSon(a, 0);
      a.kind := nkReturnStmt;
      result := true;
      getTok(p); opt(p, pxSemicolon);
      skipCom(p, a);
    end
  end
end;

procedure fixVarSection(var p: TPasParser; counter: PNode);
var
  i, j: int;
  v: PNode;
begin
  if p.lastVarSection = nil then exit;
  assert(counter.kind = nkIdent);
  for i := 0 to sonsLen(p.lastVarSection)-1 do begin
    v := p.lastVarSection.sons[i];
    for j := 0 to sonsLen(v)-3 do begin
      if v.sons[j].ident.id = counter.ident.id then begin
        delSon(v, j);
        if sonsLen(v) <= 2 then // : type = int remains --> delete it
          delSon(p.lastVarSection, i);
        exit
      end
    end
  end
end;

procedure parseBegin(var p: TPasParser; result: PNode);
begin
  getTok(p);
  while true do begin
    case p.tok.xkind of
      pxComment: addSon(result, parseStmt(p));
      pxSymbol: begin
        if not fixExit(p, result) then addSon(result, parseStmt(p))
      end;
      pxEnd: begin getTok(p); break end;
      pxSemicolon: begin getTok(p); end;
      pxEof: parMessage(p, errExprExpected);
      else addSonIfNotNil(result, parseStmt(p));
    end
  end;
  if sonsLen(result) = 0 then
    addSon(result, newNodeP(nkNilLit, p));
end;

function parseStmt(var p: TPasParser): PNode;
var
  oldcontext: TPasContext;
begin
  oldcontext := p.context;
  p.context := conStmt;
  result := nil;
  case p.tok.xkind of
    pxBegin:    begin
      result := newNodeP(nkStmtList, p);
      parseBegin(p, result);
    end;
    pxCommand:  result := parseCommand(p);
    pxCurlyDirLe, pxStarDirLe: begin
      if isHandledDirective(p) then
        result := parseDirective(p);
    end;
    pxIf:       result := parseIf(p);
    pxWhile:    result := parseWhile(p);
    pxRepeat:   result := parseRepeat(p);
    pxCase:     result := parseCase(p);
    pxTry:      result := parseTry(p);
    pxProcedure, pxFunction:  result := parseRoutine(p);
    pxType:     result := parseTypeSection(p);
    pxConst:    result := parseConstSection(p);
    pxVar:      result := parseVar(p);
    pxFor:      begin
      result := parseFor(p);
      fixVarSection(p, result.sons[0]);
    end;
    pxRaise:    result := parseRaise(p);
    pxUses:     result := parseUsesStmt(p);
    pxProgram, pxUnit, pxLibrary: begin
      // skip the pointless header
      while not (p.tok.xkind in [pxSemicolon, pxEof]) do getTok(p);
      getTok(p);
    end;
    pxInitialization: begin
      getTok(p); // just skip the token
    end;
    pxImplementation: begin
      p.section := seImplementation;
      result := newNodeP(nkCommentStmt, p);
      result.comment := '# implementation';
      getTok(p);
    end;
    pxInterface: begin
      p.section := seInterface;
      getTok(p);
    end;
    pxComment: begin
      result := newNodeP(nkCommentStmt, p);
      skipCom(p, result);
    end;
    pxSemicolon: getTok(p);
    pxSymbol: begin
      if p.tok.ident.id = getIdent('break').id then begin
        result := newNodeP(nkBreakStmt, p);
        getTok(p); skipCom(p, result);
        addSon(result, nil);
      end
      else if p.tok.ident.id = getIdent('continue').id then begin
        result := newNodeP(nkContinueStmt, p);
        getTok(p); skipCom(p, result);
        addSon(result, nil);
      end
      else if p.tok.ident.id = getIdent('exit').id then begin
        result := newNodeP(nkReturnStmt, p);
        getTok(p); skipCom(p, result);
        addSon(result, nil);
      end
      else result := parseExprStmt(p)
    end;
    pxDot: getTok(p); // BUGFIX for ``end.`` in main program
    else result := parseExprStmt(p)
  end;
  opt(p, pxSemicolon);
  if result <> nil then skipCom(p, result);
  p.context := oldcontext;
end;

function parseUnit(var p: TPasParser): PNode;
begin
  result := newNodeP(nkStmtList, p);
  getTok(p); // read first token
  while true do begin
    case p.tok.xkind of
      pxEof, pxEnd: break;
      pxBegin: parseBegin(p, result);
      pxCurlyDirLe, pxStarDirLe: begin
        if isHandledDirective(p) then
          addSon(result, parseDirective(p))
        else
          parMessage(p, errXNotAllowedHere, p.tok.ident.s)
      end
      else addSon(result, parseStmt(p))
    end;
  end;
  opt(p, pxEnd);
  opt(p, pxDot);
  if p.tok.xkind <> pxEof then
    addSon(result, parseStmt(p)); // comments after final 'end.'
end;

end.