mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-29 09:24:36 +00:00
1999 lines
52 KiB
ObjectPascal
Executable File
1999 lines
52 KiB
ObjectPascal
Executable File
//
|
|
//
|
|
// 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.
|