Files
Nim/nim/pnimsyn.pas
2010-02-14 00:29:35 +01:00

1803 lines
46 KiB
ObjectPascal

//
//
// The Nimrod Compiler
// (c) Copyright 2009 Andreas Rumpf
//
// See the file "copying.txt", included in this
// distribution, for details about the copyright.
//
unit pnimsyn;
// This module implements the parser of the standard Nimrod representation.
// The parser strictly reflects the grammar ("doc/grammar.txt"); however
// it uses several helper routines to keep the parser small. A special
// efficient algorithm is used for the precedence levels. The parser here can
// be seen as a refinement of the grammar, as it specifies how the AST is build
// from the grammar and how comments belong to the AST.
{$include config.inc}
interface
uses
nsystem, llstream, scanner, idents, strutils, ast, msgs;
// function ParseFile(const filename: string): PNode;
type
TParser = record // a TParser object represents a module that
// is being parsed
lex: PLexer; // the lexer that is used for parsing
tok: PToken; // the current token
end;
function ParseAll(var p: TParser): PNode;
procedure openParser(var p: TParser; const filename: string;
inputstream: PLLStream);
procedure closeParser(var p: TParser);
function parseTopLevelStmt(var p: TParser): PNode;
// implements an iterator. Returns the next top-level statement or nil if end
// of stream.
// helpers for the other parsers
function getPrecedence(tok: PToken): int;
function isOperator(tok: PToken): bool;
procedure getTok(var p: TParser);
procedure parMessage(const p: TParser; const msg: TMsgKind;
const arg: string = '');
procedure skipComment(var p: TParser; node: PNode);
function newNodeP(kind: TNodeKind; const p: TParser): PNode;
function newIntNodeP(kind: TNodeKind; const intVal: BiggestInt;
const p: TParser): PNode;
function newFloatNodeP(kind: TNodeKind; const floatVal: BiggestFloat;
const p: TParser): PNode;
function newStrNodeP(kind: TNodeKind; const strVal: string;
const p: TParser): PNode;
function newIdentNodeP(ident: PIdent; const p: TParser): PNode;
procedure expectIdentOrKeyw(const p: TParser);
procedure ExpectIdent(const p: TParser);
procedure expectIdentOrOpr(const p: TParser);
function parLineInfo(const p: TParser): TLineInfo;
procedure Eat(var p: TParser; TokType: TTokType);
procedure skipInd(var p: TParser);
procedure optSad(var p: TParser);
procedure optInd(var p: TParser; n: PNode);
procedure indAndComment(var p: TParser; n: PNode);
procedure setBaseFlags(n: PNode; base: TNumericalBase);
function parseSymbol(var p: TParser): PNode;
function accExpr(var p: TParser): PNode;
implementation
procedure initParser(var p: TParser);
begin
{@ignore}
FillChar(p, sizeof(p), 0);
{@emit}
new(p.lex);
{@ignore}
fillChar(p.lex^, sizeof(p.lex^), 0);
{@emit}
new(p.tok);
{@ignore}
fillChar(p.tok^, sizeof(p.tok^), 0);
{@emit}
end;
procedure getTok(var p: TParser);
begin
rawGetTok(p.lex^, p.tok^);
end;
procedure OpenParser(var p: TParser; const filename: string;
inputStream: PLLStream);
begin
initParser(p);
OpenLexer(p.lex^, filename, inputstream);
getTok(p); // read the first token
end;
procedure CloseParser(var p: TParser);
begin
CloseLexer(p.lex^);
{@ignore}
dispose(p.lex);
{@emit}
end;
// ---------------- parser helpers --------------------------------------------
procedure parMessage(const p: TParser; const msg: TMsgKind;
const arg: string = '');
begin
lexMessage(p.lex^, msg, arg);
end;
procedure skipComment(var p: TParser; node: PNode);
begin
if p.tok.tokType = tkComment then begin
if node <> nil then begin
if node.comment = snil then node.comment := '';
add(node.comment, p.tok.literal);
end
else
parMessage(p, errInternal, 'skipComment');
getTok(p);
end
end;
procedure skipInd(var p: TParser);
begin
if p.tok.tokType = tkInd then getTok(p)
end;
procedure optSad(var p: TParser);
begin
if p.tok.tokType = tkSad then getTok(p)
end;
procedure optInd(var p: TParser; n: PNode);
begin
skipComment(p, n);
skipInd(p);
end;
procedure expectIdentOrKeyw(const p: TParser);
begin
if (p.tok.tokType <> tkSymbol) and not isKeyword(p.tok.tokType) then
lexMessage(p.lex^, errIdentifierExpected, tokToStr(p.tok));
end;
procedure ExpectIdent(const p: TParser);
begin
if p.tok.tokType <> tkSymbol then
lexMessage(p.lex^, errIdentifierExpected, tokToStr(p.tok));
end;
procedure expectIdentOrOpr(const p: TParser);
begin
if not (p.tok.tokType in tokOperators) then
lexMessage(p.lex^, errOperatorExpected, tokToStr(p.tok));
end;
procedure Eat(var p: TParser; TokType: TTokType);
begin
if p.tok.TokType = TokType then getTok(p)
else lexMessage(p.lex^, errTokenExpected, TokTypeToStr[tokType])
end;
function parLineInfo(const p: TParser): TLineInfo;
begin
result := getLineInfo(p.lex^)
end;
procedure indAndComment(var p: TParser; n: PNode);
var
info: TLineInfo;
begin
if p.tok.tokType = tkInd then begin
info := parLineInfo(p);
getTok(p);
if p.tok.tokType = tkComment then skipComment(p, n)
else liMessage(info, errInvalidIndentation);
end
else skipComment(p, n);
end;
// ----------------------------------------------------------------------------
function newNodeP(kind: TNodeKind; const p: TParser): PNode;
begin
result := newNodeI(kind, getLineInfo(p.lex^));
end;
function newIntNodeP(kind: TNodeKind; const intVal: BiggestInt;
const p: TParser): PNode;
begin
result := newNodeP(kind, p);
result.intVal := intVal;
end;
function newFloatNodeP(kind: TNodeKind; const floatVal: BiggestFloat;
const p: TParser): PNode;
begin
result := newNodeP(kind, p);
result.floatVal := floatVal;
end;
function newStrNodeP(kind: TNodeKind; const strVal: string;
const p: TParser): PNode;
begin
result := newNodeP(kind, p);
result.strVal := strVal;
end;
function newIdentNodeP(ident: PIdent; const p: TParser): PNode;
begin
result := newNodeP(nkIdent, p);
result.ident := ident;
end;
// ------------------- Expression parsing ------------------------------------
function parseExpr(var p: TParser): PNode; forward;
function parseStmt(var p: TParser): PNode; forward;
function parseTypeDesc(var p: TParser): PNode; forward;
function parseParamList(var p: TParser): PNode; forward;
function getPrecedence(tok: PToken): int;
begin
case tok.tokType of
tkOpr: begin
case tok.ident.s[strStart] of
'$': result := 7;
'*', '%', '/', '\': result := 6;
'+', '-', '~', '|': result := 5;
'&': result := 4;
'=', '<', '>', '!': result := 3;
else result := 0
end
end;
tkDiv, tkMod, tkShl, tkShr: result := 6;
tkIn, tkNotIn, tkIs, tkIsNot: result := 3;
tkAnd: result := 2;
tkOr, tkXor: result := 1;
else result := -1;
end;
end;
function isOperator(tok: PToken): bool;
begin
result := getPrecedence(tok) >= 0
end;
function parseSymbol(var p: TParser): PNode;
var
s: string;
id: PIdent;
begin
case p.tok.tokType of
tkSymbol: begin
result := newIdentNodeP(p.tok.ident, p);
getTok(p);
end;
tkAccent: begin
result := newNodeP(nkAccQuoted, p);
getTok(p);
case p.tok.tokType of
tkBracketLe: begin
s := '['+'';
getTok(p);
if (p.tok.tokType = tkOpr) and (p.tok.ident.s = '$'+'') then begin
s := s + '$..';
getTok(p);
eat(p, tkDotDot);
if (p.tok.tokType = tkOpr) and (p.tok.ident.s = '$'+'') then begin
addChar(s, '$');
getTok(p);
end;
end
else if p.tok.tokType = tkDotDot then begin
s := s + '..';
getTok(p);
if (p.tok.tokType = tkOpr) and (p.tok.ident.s = '$'+'') then begin
addChar(s, '$');
getTok(p);
end;
end;
eat(p, tkBracketRi);
addChar(s, ']');
if p.tok.tokType = tkEquals then begin
addChar(s, '='); getTok(p);
end;
addSon(result, newIdentNodeP(getIdent(s), p));
end;
tkParLe: begin
addSon(result, newIdentNodeP(getIdent('()'), p));
getTok(p);
eat(p, tkParRi);
end;
tokKeywordLow..tokKeywordHigh, tkSymbol, tkOpr: begin
id := p.tok.ident;
getTok(p);
if p.tok.tokType = tkEquals then begin
addSon(result, newIdentNodeP(getIdent(id.s + '='), p));
getTok(p);
end
else
addSon(result, newIdentNodeP(id, p));
end;
else begin
parMessage(p, errIdentifierExpected, tokToStr(p.tok));
result := nil
end
end;
eat(p, tkAccent);
end
else begin
parMessage(p, errIdentifierExpected, tokToStr(p.tok));
result := nil
end
end
end;
function accExpr(var p: TParser): PNode;
var
x, y: PNode;
begin
result := newNodeP(nkAccQuoted, p);
getTok(p); // skip `
x := nil;
y := nil;
case p.tok.tokType of
tkSymbol, tkOpr, tokKeywordLow..tokKeywordHigh: begin
x := newIdentNodeP(p.tok.ident, p);
getTok(p);
end
else begin
parMessage(p, errIdentifierExpected, tokToStr(p.tok));
end
end;
if p.tok.tokType = tkDot then begin
getTok(p);
case p.tok.tokType of
tkSymbol, tkOpr, tokKeywordLow..tokKeywordHigh: begin
y := newNodeP(nkDotExpr, p);
addSon(y, x);
addSon(y, newIdentNodeP(p.tok.ident, p));
getTok(p);
x := y;
end
else begin
parMessage(p, errIdentifierExpected, tokToStr(p.tok));
end
end;
end;
addSon(result, x);
eat(p, tkAccent);
end;
function optExpr(var p: TParser): PNode; // [expr]
begin
if (p.tok.tokType <> tkComma) and (p.tok.tokType <> tkBracketRi)
and (p.tok.tokType <> tkDotDot) then
result := parseExpr(p)
else
result := nil;
end;
function dotdotExpr(var p: TParser; first: PNode = nil): PNode;
begin
result := newNodeP(nkRange, p);
addSon(result, first);
getTok(p);
optInd(p, result);
addSon(result, optExpr(p));
end;
function indexExpr(var p: TParser): PNode;
// indexExpr ::= '..' [expr] | expr ['=' expr | '..' expr]
var
a, b: PNode;
begin
if p.tok.tokType = tkDotDot then
result := dotdotExpr(p)
else begin
a := parseExpr(p);
case p.tok.tokType of
tkEquals: begin
result := newNodeP(nkExprEqExpr, p);
addSon(result, a);
getTok(p);
if p.tok.tokType = tkDotDot then
addSon(result, dotdotExpr(p))
else begin
b := parseExpr(p);
if p.tok.tokType = tkDotDot then b := dotdotExpr(p, b);
addSon(result, b);
end
end;
tkDotDot: result := dotdotExpr(p, a);
else result := a
end
end
end;
function indexExprList(var p: TParser; first: PNode): PNode;
var
a: PNode;
begin
result := newNodeP(nkBracketExpr, p);
addSon(result, first);
getTok(p);
optInd(p, result);
while (p.tok.tokType <> tkBracketRi) and (p.tok.tokType <> tkEof)
and (p.tok.tokType <> tkSad) do begin
a := indexExpr(p);
addSon(result, a);
if p.tok.tokType <> tkComma then break;
getTok(p);
optInd(p, a)
end;
optSad(p);
eat(p, tkBracketRi);
end;
function exprColonEqExpr(var p: TParser; kind: TNodeKind;
tok: TTokType): PNode;
var
a: PNode;
begin
a := parseExpr(p);
if p.tok.tokType = tok then begin
result := newNodeP(kind, p);
getTok(p);
//optInd(p, result);
addSon(result, a);
addSon(result, parseExpr(p));
end
else
result := a
end;
procedure exprListAux(var p: TParser; elemKind: TNodeKind;
endTok, sepTok: TTokType; result: PNode);
var
a: PNode;
begin
getTok(p);
optInd(p, result);
while (p.tok.tokType <> endTok) and (p.tok.tokType <> tkEof) do begin
a := exprColonEqExpr(p, elemKind, sepTok);
addSon(result, a);
if p.tok.tokType <> tkComma then break;
getTok(p);
optInd(p, a)
end;
eat(p, endTok);
end;
function qualifiedIdent(var p: TParser): PNode;
var
a: PNode;
begin
result := parseSymbol(p);
//optInd(p, result);
if p.tok.tokType = tkDot then begin
getTok(p);
optInd(p, result);
a := result;
result := newNodeI(nkDotExpr, a.info);
addSon(result, a);
addSon(result, parseSymbol(p));
end;
end;
procedure qualifiedIdentListAux(var p: TParser; endTok: TTokType;
result: PNode);
var
a: PNode;
begin
getTok(p);
optInd(p, result);
while (p.tok.tokType <> endTok) and (p.tok.tokType <> tkEof) do begin
a := qualifiedIdent(p);
addSon(result, a);
//optInd(p, a);
if p.tok.tokType <> tkComma then break;
getTok(p);
optInd(p, a)
end;
eat(p, endTok);
end;
procedure exprColonEqExprListAux(var p: TParser; elemKind: TNodeKind;
endTok, sepTok: TTokType; result: PNode);
var
a: PNode;
begin
getTok(p);
optInd(p, result);
while (p.tok.tokType <> endTok) and (p.tok.tokType <> tkEof)
and (p.tok.tokType <> tkSad) do begin
a := exprColonEqExpr(p, elemKind, sepTok);
addSon(result, a);
if p.tok.tokType <> tkComma then break;
getTok(p);
optInd(p, a)
end;
optSad(p);
eat(p, endTok);
end;
function exprColonEqExprList(var p: TParser; kind, elemKind: TNodeKind;
endTok, sepTok: TTokType): PNode;
begin
result := newNodeP(kind, p);
exprColonEqExprListAux(p, elemKind, endTok, sepTok, result);
end;
function parseCast(var p: TParser): PNode;
begin
result := newNodeP(nkCast, p);
getTok(p);
eat(p, tkBracketLe);
optInd(p, result);
addSon(result, parseTypeDesc(p));
optSad(p);
eat(p, tkBracketRi);
eat(p, tkParLe);
optInd(p, result);
addSon(result, parseExpr(p));
optSad(p);
eat(p, tkParRi);
end;
function parseAddr(var p: TParser): PNode;
begin
result := newNodeP(nkAddr, p);
getTok(p);
eat(p, tkParLe);
optInd(p, result);
addSon(result, parseExpr(p));
optSad(p);
eat(p, tkParRi);
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: TParser): PNode;
begin
case p.tok.tokType of
tkSymbol: begin
result := newIdentNodeP(p.tok.ident, p);
getTok(p)
end;
tkAccent: result := accExpr(p);
// literals
tkIntLit: begin
result := newIntNodeP(nkIntLit, p.tok.iNumber, p);
setBaseFlags(result, p.tok.base);
getTok(p);
end;
tkInt8Lit: begin
result := newIntNodeP(nkInt8Lit, p.tok.iNumber, p);
setBaseFlags(result, p.tok.base);
getTok(p);
end;
tkInt16Lit: begin
result := newIntNodeP(nkInt16Lit, p.tok.iNumber, p);
setBaseFlags(result, p.tok.base);
getTok(p);
end;
tkInt32Lit: begin
result := newIntNodeP(nkInt32Lit, p.tok.iNumber, p);
setBaseFlags(result, p.tok.base);
getTok(p);
end;
tkInt64Lit: begin
result := newIntNodeP(nkInt64Lit, p.tok.iNumber, p);
setBaseFlags(result, p.tok.base);
getTok(p);
end;
tkFloatLit: begin
result := newFloatNodeP(nkFloatLit, p.tok.fNumber, p);
setBaseFlags(result, p.tok.base);
getTok(p);
end;
tkFloat32Lit: begin
result := newFloatNodeP(nkFloat32Lit, p.tok.fNumber, p);
setBaseFlags(result, p.tok.base);
getTok(p);
end;
tkFloat64Lit: begin
result := newFloatNodeP(nkFloat64Lit, p.tok.fNumber, p);
setBaseFlags(result, p.tok.base);
getTok(p);
end;
tkStrLit: begin
result := newStrNodeP(nkStrLit, p.tok.literal, p);
getTok(p);
end;
tkRStrLit: begin
result := newStrNodeP(nkRStrLit, p.tok.literal, p);
getTok(p);
end;
tkTripleStrLit: begin
result := newStrNodeP(nkTripleStrLit, p.tok.literal, p);
getTok(p);
end;
tkCallRStrLit: begin
result := newNodeP(nkCallStrLit, p);
addSon(result, newIdentNodeP(p.tok.ident, p));
addSon(result, newStrNodeP(nkRStrLit, p.tok.literal, p));
getTok(p);
end;
tkCallTripleStrLit: begin
result := newNodeP(nkCallStrLit, p);
addSon(result, newIdentNodeP(p.tok.ident, p));
addSon(result, newStrNodeP(nkTripleStrLit, p.tok.literal, p));
getTok(p);
end;
tkCharLit: begin
result := newIntNodeP(nkCharLit, ord(p.tok.literal[strStart]), p);
getTok(p);
end;
tkNil: begin
result := newNodeP(nkNilLit, p);
getTok(p);
end;
tkParLe: begin // () constructor
result := exprColonEqExprList(p, nkPar, nkExprColonExpr, tkParRi,
tkColon);
end;
tkCurlyLe: begin // {} constructor
result := exprColonEqExprList(p, nkCurly, nkRange, tkCurlyRi, tkDotDot);
end;
tkBracketLe: begin // [] constructor
result := exprColonEqExprList(p, nkBracket, nkExprColonExpr, tkBracketRi,
tkColon);
end;
tkCast: result := parseCast(p);
tkAddr: result := parseAddr(p);
else begin
parMessage(p, errExprExpected, tokToStr(p.tok));
getTok(p); // we must consume a token here to prevend endless loops!
result := nil
end
end
end;
function primary(var p: TParser): PNode;
var
a: PNode;
begin
// prefix operator?
if (p.tok.tokType = tkNot) or (p.tok.tokType = tkOpr) then begin
result := newNodeP(nkPrefix, p);
a := newIdentNodeP(p.tok.ident, p);
addSon(result, a);
getTok(p);
optInd(p, a);
addSon(result, primary(p));
exit
end
else if p.tok.tokType = tkBind then begin
result := newNodeP(nkBind, p);
getTok(p);
optInd(p, result);
addSon(result, primary(p));
exit
end;
result := identOrLiteral(p);
while true do begin
case p.tok.tokType of
tkParLe: begin
a := result;
result := newNodeP(nkCall, p);
addSon(result, a);
exprColonEqExprListAux(p, nkExprEqExpr, tkParRi, tkEquals, result);
end;
tkDot: begin
a := result;
result := newNodeP(nkDotExpr, p);
addSon(result, a);
getTok(p); // skip '.'
optInd(p, result);
addSon(result, parseSymbol(p));
end;
tkHat: begin
a := result;
result := newNodeP(nkDerefExpr, p);
addSon(result, a);
getTok(p);
end;
tkBracketLe: result := indexExprList(p, result);
else break
end
end
end;
function lowestExprAux(var p: TParser; out v: PNode; limit: int): PToken;
var
op, nextop: PToken;
opPred: int;
v2, node, opNode: PNode;
begin
v := primary(p);
// expand while operators have priorities higher than 'limit'
op := p.tok;
opPred := getPrecedence(p.tok);
while (opPred > limit) do begin
node := newNodeP(nkInfix, p);
opNode := newIdentNodeP(op.ident, p);
// skip operator:
getTok(p);
optInd(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 lowestExpr(var p: TParser): PNode;
begin
{@discard} lowestExprAux(p, result, -1);
end;
function parseIfExpr(var p: TParser): PNode;
var
branch: PNode;
begin
result := newNodeP(nkIfExpr, p);
while true do begin
getTok(p); // skip `if`, `elif`
branch := newNodeP(nkElifExpr, p);
addSon(branch, parseExpr(p));
eat(p, tkColon);
addSon(branch, parseExpr(p));
addSon(result, branch);
if p.tok.tokType <> tkElif then break
end;
branch := newNodeP(nkElseExpr, p);
eat(p, tkElse); eat(p, tkColon);
addSon(branch, parseExpr(p));
addSon(result, branch);
end;
function parsePragma(var p: TParser): PNode;
var
a: PNode;
begin
result := newNodeP(nkPragma, p);
getTok(p);
optInd(p, result);
while (p.tok.tokType <> tkCurlyDotRi) and (p.tok.tokType <> tkCurlyRi)
and (p.tok.tokType <> tkEof) and (p.tok.tokType <> tkSad) do begin
a := exprColonEqExpr(p, nkExprColonExpr, tkColon);
addSon(result, a);
if p.tok.tokType = tkComma then begin
getTok(p);
optInd(p, a)
end
end;
optSad(p);
if (p.tok.tokType = tkCurlyDotRi) or (p.tok.tokType = tkCurlyRi) then
getTok(p)
else
parMessage(p, errTokenExpected, '.}');
end;
function identVis(var p: TParser): PNode; // identifier with visability
var
a: PNode;
begin
a := parseSymbol(p);
if p.tok.tokType = tkOpr then begin
result := newNodeP(nkPostfix, p);
addSon(result, newIdentNodeP(p.tok.ident, p));
addSon(result, a);
getTok(p);
end
else
result := a;
end;
function identWithPragma(var p: TParser): PNode;
var
a: PNode;
begin
a := identVis(p);
if p.tok.tokType = tkCurlyDotLe then begin
result := newNodeP(nkPragmaExpr, p);
addSon(result, a);
addSon(result, parsePragma(p));
end
else
result := a
end;
type
TDeclaredIdentFlag = (
withPragma, // identifier may have pragma
withBothOptional // both ':' and '=' parts are optional
);
TDeclaredIdentFlags = set of TDeclaredIdentFlag;
function parseIdentColonEquals(var p: TParser;
flags: TDeclaredIdentFlags): PNode;
var
a: PNode;
begin
result := newNodeP(nkIdentDefs, p);
while true do begin
case p.tok.tokType of
tkSymbol, tkAccent: begin
if withPragma in flags then
a := identWithPragma(p)
else
a := parseSymbol(p);
if a = nil then exit;
end;
else break;
end;
addSon(result, a);
if p.tok.tokType <> tkComma then break;
getTok(p);
optInd(p, a)
end;
if p.tok.tokType = tkColon then begin
getTok(p); optInd(p, result);
addSon(result, parseTypeDesc(p));
end
else begin
addSon(result, nil);
if (p.tok.tokType <> tkEquals) and not (withBothOptional in flags) then
parMessage(p, errColonOrEqualsExpected, tokToStr(p.tok))
end;
if p.tok.tokType = tkEquals then begin
getTok(p); optInd(p, result);
addSon(result, parseExpr(p));
end
else
addSon(result, nil);
end;
function parseTuple(var p: TParser): PNode;
var
a: PNode;
begin
result := newNodeP(nkTupleTy, p);
getTok(p);
eat(p, tkBracketLe);
optInd(p, result);
while (p.tok.tokType = tkSymbol) or (p.tok.tokType = tkAccent) do begin
a := parseIdentColonEquals(p, {@set}[]);
addSon(result, a);
if p.tok.tokType <> tkComma then break;
getTok(p);
optInd(p, a)
end;
optSad(p);
eat(p, tkBracketRi);
end;
function parseParamList(var p: TParser): PNode;
var
a: PNode;
begin
result := newNodeP(nkFormalParams, p);
addSon(result, nil); // return type
if p.tok.tokType = tkParLe then begin
getTok(p);
optInd(p, result);
while true do begin
case p.tok.tokType of
tkSymbol, tkAccent: a := parseIdentColonEquals(p, {@set}[]);
tkParRi: break;
else begin parMessage(p, errTokenExpected, ')'+''); break; end;
end;
//optInd(p, a);
addSon(result, a);
if p.tok.tokType <> tkComma then break;
getTok(p);
optInd(p, a)
end;
optSad(p);
eat(p, tkParRi);
end;
if p.tok.tokType = tkColon then begin
getTok(p);
optInd(p, result);
result.sons[0] := parseTypeDesc(p)
end
end;
function parseProcExpr(var p: TParser; isExpr: bool): PNode;
// either a proc type or a anonymous proc
var
pragmas, params: PNode;
info: TLineInfo;
begin
info := parLineInfo(p);
getTok(p);
params := parseParamList(p);
if p.tok.tokType = tkCurlyDotLe then pragmas := parsePragma(p)
else pragmas := nil;
if (p.tok.tokType = tkEquals) and isExpr then begin
result := newNodeI(nkLambda, info);
addSon(result, nil); // no name part
addSon(result, nil); // no generic parameters
addSon(result, params);
addSon(result, pragmas);
getTok(p); skipComment(p, result);
addSon(result, parseStmt(p));
end
else begin
result := newNodeI(nkProcTy, info);
addSon(result, params);
addSon(result, pragmas);
end
end;
function parseTypeDescKAux(var p: TParser; kind: TNodeKind): PNode;
begin
result := newNodeP(kind, p);
getTok(p);
optInd(p, result);
addSon(result, parseTypeDesc(p));
end;
function parseExpr(var p: TParser): PNode;
(*
expr ::= lowestExpr
| 'if' expr ':' expr ('elif' expr ':' expr)* 'else' ':' expr
| 'var' expr
| 'ref' expr
| 'ptr' expr
| 'type' expr
| 'tuple' tupleDesc
| 'proc' paramList [pragma] ['=' stmt]
*)
begin
case p.tok.toktype of
tkVar: result := parseTypeDescKAux(p, nkVarTy);
tkRef: result := parseTypeDescKAux(p, nkRefTy);
tkPtr: result := parseTypeDescKAux(p, nkPtrTy);
tkType: result := parseTypeDescKAux(p, nkTypeOfExpr);
tkTuple: result := parseTuple(p);
tkProc: result := parseProcExpr(p, true);
tkIf: result := parseIfExpr(p);
else result := lowestExpr(p);
end
end;
function parseTypeDesc(var p: TParser): PNode;
begin
if p.tok.toktype = tkProc then result := parseProcExpr(p, false)
else result := parseExpr(p);
end;
// ---------------------- statement parser ------------------------------------
function isExprStart(const p: TParser): bool;
begin
case p.tok.tokType of
tkSymbol, tkAccent, tkOpr, tkNot, tkNil, tkCast, tkIf, tkProc, tkBind,
tkParLe, tkBracketLe, tkCurlyLe, tkIntLit..tkCharLit,
tkVar, tkRef, tkPtr, tkTuple, tkType: result := true;
else result := false;
end;
end;
function parseExprStmt(var p: TParser): PNode;
var
a, b, e: PNode;
begin
a := lowestExpr(p);
if p.tok.tokType = tkEquals then begin
getTok(p);
optInd(p, result);
b := parseExpr(p);
result := newNodeI(nkAsgn, a.info);
addSon(result, a);
addSon(result, b);
end
else begin
result := newNodeP(nkCommand, p);
result.info := a.info;
addSon(result, a);
while true do begin
(*case p.tok.tokType of
tkColon, tkInd, tkSad, tkDed, tkEof, tkComment: break;
else begin end
end;*)
if not isExprStart(p) then break;
e := parseExpr(p);
addSon(result, e);
if p.tok.tokType <> tkComma then break;
getTok(p);
optInd(p, a);
end;
if sonsLen(result) <= 1 then result := a
else a := result;
if p.tok.tokType = tkColon then begin // macro statement
result := newNodeP(nkMacroStmt, p);
result.info := a.info;
addSon(result, a);
getTok(p);
skipComment(p, result);
if (p.tok.tokType = tkInd)
or not (p.tok.TokType in [tkOf, tkElif, tkElse, tkExcept]) then
addSon(result, parseStmt(p));
while true do begin
if p.tok.tokType = tkSad then getTok(p);
case p.tok.tokType of
tkOf: begin
b := newNodeP(nkOfBranch, p);
exprListAux(p, nkRange, tkColon, tkDotDot, b);
end;
tkElif: begin
b := newNodeP(nkElifBranch, p);
getTok(p);
optInd(p, b);
addSon(b, parseExpr(p));
eat(p, tkColon);
end;
tkExcept: begin
b := newNodeP(nkExceptBranch, p);
qualifiedIdentListAux(p, tkColon, b);
skipComment(p, b);
end;
tkElse: begin
b := newNodeP(nkElse, p);
getTok(p);
eat(p, tkColon);
end;
else break;
end;
addSon(b, parseStmt(p));
addSon(result, b);
if b.kind = nkElse then break;
end
end
end
end;
function parseImportOrIncludeStmt(var p: TParser; kind: TNodeKind): PNode;
var
a: PNode;
begin
result := newNodeP(kind, p);
getTok(p); // skip `import` or `include`
optInd(p, result);
while true do begin
case p.tok.tokType of
tkEof, tkSad, tkDed: break;
tkSymbol, tkAccent: a := parseSymbol(p);
tkRStrLit: begin
a := newStrNodeP(nkRStrLit, p.tok.literal, p);
getTok(p)
end;
tkStrLit: begin
a := newStrNodeP(nkStrLit, p.tok.literal, p);
getTok(p);
end;
tkTripleStrLit: begin
a := newStrNodeP(nkTripleStrLit, p.tok.literal, p);
getTok(p)
end;
else begin
parMessage(p, errIdentifierExpected, tokToStr(p.tok));
break
end
end;
addSon(result, a);
if p.tok.tokType <> tkComma then break;
getTok(p);
optInd(p, a)
end;
end;
function parseFromStmt(var p: TParser): PNode;
var
a: PNode;
begin
result := newNodeP(nkFromStmt, p);
getTok(p); // skip `from`
optInd(p, result);
case p.tok.tokType of
tkSymbol, tkAccent: a := parseSymbol(p);
tkRStrLit: begin
a := newStrNodeP(nkRStrLit, p.tok.literal, p);
getTok(p)
end;
tkStrLit: begin
a := newStrNodeP(nkStrLit, p.tok.literal, p);
getTok(p);
end;
tkTripleStrLit: begin
a := newStrNodeP(nkTripleStrLit, p.tok.literal, p);
getTok(p)
end;
else begin
parMessage(p, errIdentifierExpected, tokToStr(p.tok)); exit
end
end;
addSon(result, a);
//optInd(p, a);
eat(p, tkImport);
optInd(p, result);
while true do begin
case p.tok.tokType of
tkEof, tkSad, tkDed: break;
tkSymbol, tkAccent: a := parseSymbol(p);
else begin
parMessage(p, errIdentifierExpected, tokToStr(p.tok));
break
end;
end;
//optInd(p, a);
addSon(result, a);
if p.tok.tokType <> tkComma then break;
getTok(p);
optInd(p, a)
end;
end;
function parseReturnOrRaise(var p: TParser; kind: TNodeKind): PNode;
begin
result := newNodeP(kind, p);
getTok(p);
optInd(p, result);
case p.tok.tokType of
tkEof, tkSad, tkDed: addSon(result, nil);
else addSon(result, parseExpr(p));
end;
end;
function parseYieldOrDiscard(var p: TParser; kind: TNodeKind): PNode;
begin
result := newNodeP(kind, p);
getTok(p);
optInd(p, result);
addSon(result, parseExpr(p));
end;
function parseBreakOrContinue(var p: TParser; kind: TNodeKind): PNode;
begin
result := newNodeP(kind, p);
getTok(p);
optInd(p, result);
case p.tok.tokType of
tkEof, tkSad, tkDed: addSon(result, nil);
else addSon(result, parseSymbol(p));
end;
end;
function parseIfOrWhen(var p: TParser; kind: TNodeKind): PNode;
var
branch: PNode;
begin
result := newNodeP(kind, p);
while true do begin
getTok(p); // skip `if`, `when`, `elif`
branch := newNodeP(nkElifBranch, p);
optInd(p, branch);
addSon(branch, parseExpr(p));
eat(p, tkColon);
skipComment(p, branch);
addSon(branch, parseStmt(p));
skipComment(p, branch);
addSon(result, branch);
if p.tok.tokType <> tkElif then break
end;
if p.tok.tokType = tkElse then begin
branch := newNodeP(nkElse, p);
eat(p, tkElse); eat(p, tkColon);
skipComment(p, branch);
addSon(branch, parseStmt(p));
addSon(result, branch);
end
end;
function parseWhile(var p: TParser): PNode;
begin
result := newNodeP(nkWhileStmt, p);
getTok(p);
optInd(p, result);
addSon(result, parseExpr(p));
eat(p, tkColon);
skipComment(p, result);
addSon(result, parseStmt(p));
end;
function parseCase(var p: TParser): PNode;
var
b: PNode;
inElif: bool;
begin
result := newNodeP(nkCaseStmt, p);
getTok(p);
addSon(result, parseExpr(p));
if p.tok.tokType = tkColon then getTok(p);
skipComment(p, result);
inElif := false;
while true do begin
if p.tok.tokType = tkSad then getTok(p);
case p.tok.tokType of
tkOf: begin
if inElif then break;
b := newNodeP(nkOfBranch, p);
exprListAux(p, nkRange, tkColon, tkDotDot, b);
end;
tkElif: begin
inElif := true;
b := newNodeP(nkElifBranch, p);
getTok(p);
optInd(p, b);
addSon(b, parseExpr(p));
eat(p, tkColon);
end;
tkElse: begin
b := newNodeP(nkElse, p);
getTok(p);
eat(p, tkColon);
end;
else break;
end;
skipComment(p, b);
addSon(b, parseStmt(p));
addSon(result, b);
if b.kind = nkElse then break;
end
end;
function parseTry(var p: TParser): PNode;
var
b: PNode;
begin
result := newNodeP(nkTryStmt, p);
getTok(p);
eat(p, tkColon);
skipComment(p, result);
addSon(result, parseStmt(p));
b := nil;
while true do begin
if p.tok.tokType = tkSad then getTok(p);
case p.tok.tokType of
tkExcept: begin
b := newNodeP(nkExceptBranch, p);
qualifiedIdentListAux(p, tkColon, b);
end;
tkFinally: begin
b := newNodeP(nkFinally, p);
getTok(p);
eat(p, tkColon);
end;
else break;
end;
skipComment(p, b);
addSon(b, parseStmt(p));
addSon(result, b);
if b.kind = nkFinally then break;
end;
if b = nil then parMessage(p, errTokenExpected, 'except');
end;
function parseFor(var p: TParser): PNode;
var
a: PNode;
begin
result := newNodeP(nkForStmt, p);
getTok(p);
optInd(p, result);
a := parseSymbol(p);
addSon(result, a);
while p.tok.tokType = tkComma do begin
getTok(p);
optInd(p, a);
a := parseSymbol(p);
addSon(result, a);
end;
eat(p, tkIn);
addSon(result, exprColonEqExpr(p, nkRange, tkDotDot));
eat(p, tkColon);
skipComment(p, result);
addSon(result, parseStmt(p))
end;
function parseBlock(var p: TParser): PNode;
begin
result := newNodeP(nkBlockStmt, p);
getTok(p);
optInd(p, result);
case p.tok.tokType of
tkEof, tkSad, tkDed, tkColon: addSon(result, nil);
else addSon(result, parseSymbol(p));
end;
eat(p, tkColon);
skipComment(p, result);
addSon(result, parseStmt(p));
end;
function parseAsm(var p: TParser): PNode;
begin
result := newNodeP(nkAsmStmt, p);
getTok(p);
optInd(p, result);
if p.tok.tokType = tkCurlyDotLe then addSon(result, parsePragma(p))
else addSon(result, nil);
case p.tok.tokType of
tkStrLit: addSon(result, newStrNodeP(nkStrLit, p.tok.literal, p));
tkRStrLit: addSon(result, newStrNodeP(nkRStrLit, p.tok.literal, p));
tkTripleStrLit:
addSon(result, newStrNodeP(nkTripleStrLit, p.tok.literal, p));
else begin
parMessage(p, errStringLiteralExpected);
addSon(result, nil); exit
end;
end;
getTok(p);
end;
function parseGenericParamList(var p: TParser): PNode;
var
a: PNode;
begin
result := newNodeP(nkGenericParams, p);
getTok(p);
optInd(p, result);
while (p.tok.tokType = tkSymbol) or (p.tok.tokType = tkAccent) do begin
a := parseIdentColonEquals(p, {@set}[withBothOptional]);
addSon(result, a);
if p.tok.tokType <> tkComma then break;
getTok(p);
optInd(p, a)
end;
optSad(p);
eat(p, tkBracketRi);
end;
function parseRoutine(var p: TParser; kind: TNodeKind): PNode;
begin
result := newNodeP(kind, p);
getTok(p);
optInd(p, result);
addSon(result, identVis(p));
if p.tok.tokType = tkBracketLe then addSon(result, parseGenericParamList(p))
else addSon(result, nil);
addSon(result, parseParamList(p));
if p.tok.tokType = tkCurlyDotLe then addSon(result, parsePragma(p))
else addSon(result, nil);
if p.tok.tokType = tkEquals then begin
getTok(p); skipComment(p, result);
addSon(result, parseStmt(p));
end
else
addSon(result, nil);
indAndComment(p, result); // XXX: document this in the grammar!
end;
function newCommentStmt(var p: TParser): PNode;
begin
result := newNodeP(nkCommentStmt, p);
result.info.line := result.info.line - int16(1);
end;
type
TDefParser = function (var p: TParser): PNode;
function parseSection(var p: TParser; kind: TNodeKind;
defparser: TDefParser): PNode;
var
a: PNode;
begin
result := newNodeP(kind, p);
getTok(p);
skipComment(p, result);
case p.tok.tokType of
tkInd: begin
pushInd(p.lex^, p.tok.indent);
getTok(p); skipComment(p, result);
while true do begin
case p.tok.tokType of
tkSad: getTok(p);
tkSymbol, tkAccent: begin
a := defparser(p);
skipComment(p, a);
addSon(result, a);
end;
tkDed: begin getTok(p); break end;
tkEof: break; // BUGFIX
tkComment: begin
a := newCommentStmt(p);
skipComment(p, a);
addSon(result, a);
end;
else begin
parMessage(p, errIdentifierExpected, tokToStr(p.tok));
break
end
end
end;
popInd(p.lex^);
end;
tkSymbol, tkAccent, tkParLe: begin
// tkParLe is allowed for ``var (x, y) = ...`` tuple parsing
addSon(result, defparser(p));
end
else parMessage(p, errIdentifierExpected, tokToStr(p.tok));
end
end;
function parseConstant(var p: TParser): PNode;
begin
result := newNodeP(nkConstDef, p);
addSon(result, identWithPragma(p));
if p.tok.tokType = tkColon then begin
getTok(p); optInd(p, result);
addSon(result, parseTypeDesc(p));
end
else
addSon(result, nil);
eat(p, tkEquals);
optInd(p, result);
addSon(result, parseExpr(p));
indAndComment(p, result); // XXX: special extension!
end;
function parseEnum(var p: TParser): PNode;
var
a, b: PNode;
begin
result := newNodeP(nkEnumTy, p);
a := nil;
getTok(p);
if p.tok.tokType = tkOf then begin
a := newNodeP(nkOfInherit, p);
getTok(p); optInd(p, a);
addSon(a, parseTypeDesc(p));
addSon(result, a)
end
else addSon(result, nil);
optInd(p, result);
while true do begin
case p.tok.tokType of
tkEof, tkSad, tkDed: break;
else a := parseSymbol(p);
end;
optInd(p, a);
if p.tok.tokType = tkEquals then begin
getTok(p);
optInd(p, a);
b := a;
a := newNodeP(nkEnumFieldDef, p);
addSon(a, b);
addSon(a, parseExpr(p));
skipComment(p, a);
end;
if p.tok.tokType = tkComma then begin
getTok(p);
optInd(p, a)
end;
addSon(result, a);
end
end;
function parseObjectPart(var p: TParser): PNode; forward;
function parseObjectWhen(var p: TParser): PNode;
var
branch: PNode;
begin
result := newNodeP(nkRecWhen, p);
while true do begin
getTok(p); // skip `when`, `elif`
branch := newNodeP(nkElifBranch, p);
optInd(p, branch);
addSon(branch, parseExpr(p));
eat(p, tkColon);
skipComment(p, branch);
addSon(branch, parseObjectPart(p));
skipComment(p, branch);
addSon(result, branch);
if p.tok.tokType <> tkElif then break
end;
if p.tok.tokType = tkElse then begin
branch := newNodeP(nkElse, p);
eat(p, tkElse); eat(p, tkColon);
skipComment(p, branch);
addSon(branch, parseObjectPart(p));
addSon(result, branch);
end
end;
function parseObjectCase(var p: TParser): PNode;
var
a, b: PNode;
begin
result := newNodeP(nkRecCase, p);
getTok(p);
a := newNodeP(nkIdentDefs, p);
addSon(a, identWithPragma(p));
eat(p, tkColon);
addSon(a, parseTypeDesc(p));
addSon(a, nil);
addSon(result, a);
skipComment(p, result);
while true do begin
if p.tok.tokType = tkSad then getTok(p);
case p.tok.tokType of
tkOf: begin
b := newNodeP(nkOfBranch, p);
exprListAux(p, nkRange, tkColon, tkDotDot, b);
end;
tkElse: begin
b := newNodeP(nkElse, p);
getTok(p);
eat(p, tkColon);
end;
else break;
end;
skipComment(p, b);
addSon(b, parseObjectPart(p));
addSon(result, b);
if b.kind = nkElse then break;
end
end;
function parseObjectPart(var p: TParser): PNode;
begin
case p.tok.tokType of
tkInd: begin
result := newNodeP(nkRecList, p);
pushInd(p.lex^, p.tok.indent);
getTok(p); skipComment(p, result);
while true do begin
case p.tok.tokType of
tkSad: getTok(p);
tkCase, tkWhen, tkSymbol, tkAccent, tkNil: begin
addSon(result, parseObjectPart(p));
end;
tkDed: begin getTok(p); break end;
tkEof: break;
else begin
parMessage(p, errIdentifierExpected, tokToStr(p.tok));
break
end
end
end;
popInd(p.lex^);
end;
tkWhen: result := parseObjectWhen(p);
tkCase: result := parseObjectCase(p);
tkSymbol, tkAccent: begin
result := parseIdentColonEquals(p, {@set}[withPragma]);
skipComment(p, result);
end;
tkNil: begin
result := newNodeP(nkNilLit, p);
getTok(p);
end;
else result := nil
end
end;
function parseObject(var p: TParser): PNode;
var
a: PNode;
begin
result := newNodeP(nkObjectTy, p);
getTok(p);
if p.tok.tokType = tkCurlyDotLe then addSon(result, parsePragma(p))
else addSon(result, nil);
if p.tok.tokType = tkOf then begin
a := newNodeP(nkOfInherit, p);
getTok(p);
addSon(a, parseTypeDesc(p));
addSon(result, a);
end
else addSon(result, nil);
skipComment(p, result);
addSon(result, parseObjectPart(p));
end;
function parseDistinct(var p: TParser): PNode;
begin
result := newNodeP(nkDistinctTy, p);
getTok(p);
optInd(p, result);
addSon(result, parseTypeDesc(p));
end;
function parseTypeDef(var p: TParser): PNode;
var
a: PNode;
begin
result := newNodeP(nkTypeDef, p);
addSon(result, identWithPragma(p));
if p.tok.tokType = tkBracketLe then addSon(result, parseGenericParamList(p))
else addSon(result, nil);
if p.tok.tokType = tkEquals then begin
getTok(p); optInd(p, result);
case p.tok.tokType of
tkObject: a := parseObject(p);
tkEnum: a := parseEnum(p);
tkDistinct: a := parseDistinct(p);
else a := parseTypeDesc(p);
end;
addSon(result, a);
end
else
addSon(result, nil);
indAndComment(p, result); // special extension!
end;
function parseVarTuple(var p: TParser): PNode;
var
a: PNode;
begin
result := newNodeP(nkVarTuple, p);
getTok(p); // skip '('
optInd(p, result);
while (p.tok.tokType = tkSymbol) or (p.tok.tokType = tkAccent) do begin
a := identWithPragma(p);
addSon(result, a);
if p.tok.tokType <> tkComma then break;
getTok(p);
optInd(p, a)
end;
addSon(result, nil); // no type desc
optSad(p);
eat(p, tkParRi);
eat(p, tkEquals);
optInd(p, result);
addSon(result, parseExpr(p));
end;
function parseVariable(var p: TParser): PNode;
begin
if p.tok.tokType = tkParLe then
result := parseVarTuple(p)
else
result := parseIdentColonEquals(p, {@set}[withPragma]);
indAndComment(p, result); // special extension!
end;
function simpleStmt(var p: TParser): PNode;
begin
case p.tok.tokType of
tkReturn: result := parseReturnOrRaise(p, nkReturnStmt);
tkRaise: result := parseReturnOrRaise(p, nkRaiseStmt);
tkYield: result := parseYieldOrDiscard(p, nkYieldStmt);
tkDiscard: result := parseYieldOrDiscard(p, nkDiscardStmt);
tkBreak: result := parseBreakOrContinue(p, nkBreakStmt);
tkContinue: result := parseBreakOrContinue(p, nkContinueStmt);
tkCurlyDotLe: result := parsePragma(p);
tkImport: result := parseImportOrIncludeStmt(p, nkImportStmt);
tkFrom: result := parseFromStmt(p);
tkInclude: result := parseImportOrIncludeStmt(p, nkIncludeStmt);
tkComment: result := newCommentStmt(p);
else begin
if isExprStart(p) then
result := parseExprStmt(p)
else
result := nil;
end
end;
if result <> nil then
skipComment(p, result);
end;
function complexOrSimpleStmt(var p: TParser): PNode;
begin
case p.tok.tokType of
tkIf: result := parseIfOrWhen(p, nkIfStmt);
tkWhile: result := parseWhile(p);
tkCase: result := parseCase(p);
tkTry: result := parseTry(p);
tkFor: result := parseFor(p);
tkBlock: result := parseBlock(p);
tkAsm: result := parseAsm(p);
tkProc: result := parseRoutine(p, nkProcDef);
tkMethod: result := parseRoutine(p, nkMethodDef);
tkIterator: result := parseRoutine(p, nkIteratorDef);
tkMacro: result := parseRoutine(p, nkMacroDef);
tkTemplate: result := parseRoutine(p, nkTemplateDef);
tkConverter: result := parseRoutine(p, nkConverterDef);
tkType: result := parseSection(p, nkTypeSection, parseTypeDef);
tkConst: result := parseSection(p, nkConstSection, parseConstant);
tkWhen: result := parseIfOrWhen(p, nkWhenStmt);
tkVar: result := parseSection(p, nkVarSection, parseVariable);
else result := simpleStmt(p);
end
end;
function parseStmt(var p: TParser): PNode;
var
a: PNode;
begin
if p.tok.tokType = tkInd then begin
result := newNodeP(nkStmtList, p);
pushInd(p.lex^, p.tok.indent);
getTok(p);
while true do begin
case p.tok.tokType of
tkSad: getTok(p);
tkEof: break;
tkDed: begin getTok(p); break end;
else begin
a := complexOrSimpleStmt(p);
if a = nil then break;
addSon(result, a);
end
end
end;
popInd(p.lex^);
end
else begin
// the case statement is only needed for better error messages:
case p.tok.tokType of
tkIf, tkWhile, tkCase, tkTry, tkFor, tkBlock, tkAsm,
tkProc, tkIterator, tkMacro, tkType, tkConst, tkWhen, tkVar: begin
parMessage(p, errComplexStmtRequiresInd);
result := nil
end
else begin
result := simpleStmt(p);
if result = nil then parMessage(p, errExprExpected, tokToStr(p.tok));
if p.tok.tokType = tkSad then getTok(p);
end
end
end
end;
function parseAll(var p: TParser): PNode;
var
a: PNode;
begin
result := newNodeP(nkStmtList, p);
while true do begin
case p.tok.tokType of
tkSad: getTok(p);
tkDed, tkInd: parMessage(p, errInvalidIndentation);
tkEof: break;
else begin
a := complexOrSimpleStmt(p);
if a = nil then parMessage(p, errExprExpected, tokToStr(p.tok));
addSon(result, a);
end
end
end
end;
function parseTopLevelStmt(var p: TParser): PNode;
begin
result := nil;
while true do begin
case p.tok.tokType of
tkSad: getTok(p);
tkDed, tkInd: begin
parMessage(p, errInvalidIndentation);
break;
end;
tkEof: break;
else begin
result := complexOrSimpleStmt(p);
if result = nil then parMessage(p, errExprExpected, tokToStr(p.tok));
break
end
end
end
end;
end.