//
//
// 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.