diff options
Diffstat (limited to 'nim/pnimsyn.pas')
-rwxr-xr-x | nim/pnimsyn.pas | 1728 |
1 files changed, 1728 insertions, 0 deletions
diff --git a/nim/pnimsyn.pas b/nim/pnimsyn.pas new file mode 100755 index 000000000..8407804e7 --- /dev/null +++ b/nim/pnimsyn.pas @@ -0,0 +1,1728 @@ +// +// +// The Nimrod Compiler +// (c) Copyright 2008 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 is 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, 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; // we need a stack of lexers because + // of support for the `include` command + tok: PToken; // the current token + end; + +function ParseModule(var p: TParser): PNode; +// Note: The module's tree must always be valid. +function parseExpr(var p: TParser): PNode; +function parseStmt(var p: TParser): PNode; + +function openParser(var p: TParser; const filename: string): TResult; +procedure bufferParser(var p: TParser; const buffer: string); + // the same as `openParser`, but does use a buffer and does not read from + // a file +procedure closeParser(var p: TParser); + +implementation + +function ParseFile(const filename: string): PNode; +var + p: TParser; +begin + if OpenParser(p, filename) = failure then begin + rawMessage(errCannotOpenFile, filename); + exit + end; + result := ParseModule(p); + CloseParser(p); +end; + +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 bufferParser(var p: TParser; const buffer: string); +begin + initParser(p); + bufferLexer(p.lex^, buffer); +end; + +function OpenParser(var p: TParser; const filename: string): TResult; +begin + initParser(p); + result := OpenLexer(p.lex^, filename); +end; + +procedure CloseParser(var p: TParser); +begin + CloseLexer(p.lex^); +{@ignore} + dispose(p.lex); +{@emit} +end; + +// ---------------- parser helpers -------------------------------------------- + +procedure getTok(var p: TParser); +begin + rawGetTok(p.lex^, p.tok^); + //printTok(p.tok); // DEBUG +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 := ''; + node.comment := node.comment +{&} p.tok.literal; + end + else + assert(false); + getTok(p); + end +end; + +procedure skipInd(var p: TParser); +begin + if p.tok.tokType = tkInd 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; + +procedure parMessage(const p: TParser; const msg: TMsgKind; + const arg: string = ''); +begin + lexMessage(p.lex^, msg, arg); +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 := newNode(kind); + result.info := 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 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; +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 + addSon(result, newIdentNodeP(getIdent('[]'), p)); + getTok(p); + eat(p, tkBracketRi); + end; + tkParLe: begin + addSon(result, newIdentNodeP(getIdent('()'), p)); + getTok(p); + eat(p, tkParRi); + end; + tokKeywordLow..tokKeywordHigh, tkSymbol, tkOpr: begin + addSon(result, newIdentNodeP(p.tok.ident, p)); + getTok(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; + info: TLineInfo; +begin + info := parLineInfo(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; + if (p.tok.tokType = tkParLe) or (p.tok.tokType = tkColon) then begin + result := newNodeP(nkHeaderQuoted, p); + addSon(result, x); + addSon(result, parseParamList(p)); + end + else begin + result := newNodeP(nkAccQuoted, p); + addSon(result, x); + end; + result.info := info; + 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 parseTypeDescK(var p: TParser): PNode; forward; + +function namedTypeOrExpr(var p: TParser): PNode; +var + a, b: PNode; +begin + case p.tok.tokType of + tkDotDot: result := dotdotExpr(p); + tkVar, tkRef, tkPtr, tkProc, tkType: result := parseTypeDescK(p); + else begin + a := parseExpr(p); + case p.tok.tokType of + tkEquals: begin + result := newNodeP(nkExprEqExpr, p); + addSon(result, a); + getTok(p); + optInd(p, result); + case p.tok.tokType of + tkVar, tkRef, tkPtr, tkProc: + addSon(result, parseTypeDescK(p)); + tkDotDot: addSon(result, dotdotExpr(p)); + else begin + b := parseExpr(p); + if p.tok.tokType = tkDotDot then + b := dotdotExpr(p, b); + addSon(result, b); + end + end + end; + tkDotDot: result := dotdotExpr(p, a); + else result := a + end + end + end +end; + +function namedTypeOrExprList(var p: TParser; first: PNode): PNode; +var + a: PNode; +begin + result := newNodeP(nkBracketExpr, p); + addSon(result, first); + getTok(p); + optInd(p, result); + while true do begin + if p.tok.tokType = tkBracketRi then begin + getTok(p); break + end; + if p.tok.tokType = tkEof then begin + parMessage(p, errTokenExpected, TokTypeToStr[tkBracketRi]); break + end; + a := namedTypeOrExpr(p); + optInd(p, a); + if p.tok.tokType = tkComma then begin + getTok(p); + optInd(p, a) + end; + addSon(result, a); + end; +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 true do begin + if p.tok.tokType = endTok then begin + getTok(p); break + end; + if p.tok.tokType = tkEof then begin + parMessage(p, errTokenExpected, TokTypeToStr[endtok]); break + end; + a := exprColonEqExpr(p, elemKind, sepTok); + optInd(p, a); + if p.tok.tokType = tkComma then begin + getTok(p); + optInd(p, a) + end; + addSon(result, a); + end; +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 := newNode(nkQualified); + result.info := 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 true do begin + if p.tok.tokType = endTok then begin + getTok(p); break + end; + if p.tok.tokType = tkEof then begin + parMessage(p, errTokenExpected, TokTypeToStr[endtok]); break + end; + a := qualifiedIdent(p); + optInd(p, a); + if p.tok.tokType = tkComma then begin + getTok(p); + optInd(p, a) + end; + addSon(result, a); + end; +end; + +function exprColonEqExprList(var p: TParser; kind, elemKind: TNodeKind; + endTok, sepTok: TTokType): PNode; +begin + result := newNodeP(kind, p); + exprListAux(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)); + optInd(p, result); + eat(p, tkBracketRi); + eat(p, tkParLe); + optInd(p, result); + addSon(result, parseExpr(p)); + optInd(p, result); + 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)); + optInd(p, result); + eat(p, tkParRi); +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); + result.base := p.tok.base; + getTok(p); + end; + tkInt8Lit: begin + result := newIntNodeP(nkInt8Lit, p.tok.iNumber, p); + result.base := p.tok.base; + getTok(p); + end; + tkInt16Lit: begin + result := newIntNodeP(nkInt16Lit, p.tok.iNumber, p); + result.base := p.tok.base; + getTok(p); + end; + tkInt32Lit: begin + result := newIntNodeP(nkInt32Lit, p.tok.iNumber, p); + result.base := p.tok.base; + getTok(p); + end; + tkInt64Lit: begin + result := newIntNodeP(nkInt64Lit, p.tok.iNumber, p); + result.base := p.tok.base; + getTok(p); + end; + tkFloatLit: begin + result := newFloatNodeP(nkFloatLit, p.tok.fNumber, p); + result.base := p.tok.base; + getTok(p); + end; + tkFloat32Lit: begin + result := newFloatNodeP(nkFloat32Lit, p.tok.fNumber, p); + result.base := p.tok.base; + getTok(p); + end; + tkFloat64Lit: begin + result := newFloatNodeP(nkFloat64Lit, p.tok.fNumber, p); + result.base := 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; + tkCharLit: begin + result := newIntNodeP(nkCharLit, ord(p.tok.literal[strStart]), p); + getTok(p); + end; + tkRCharLit: begin + result := newIntNodeP(nkRCharLit, 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; + result := identOrLiteral(p); + while true do begin + case p.tok.tokType of + tkParLe: begin + a := result; + result := newNodeP(nkCall, p); + addSon(result, a); + exprListAux(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 := namedTypeOrExprList(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' + (*if p.tok.tokType = tkInd then begin // BUGFIX: parser allowed too much + getTok(p); + if getPrecedence(p.tok) < 0 then + parMessage(p, errOperatorExpected, tokToStr(p.tok)); + end; *) + 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); + skipComment(p, opNode); + skipInd(p); + + // 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 parseLambda(var p: TParser): PNode; forward; + +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); + optInd(p, branch); + addSon(branch, parseExpr(p)); + eat(p, tkColon); + optInd(p, branch); + addSon(branch, parseExpr(p)); + optInd(p, branch); + 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 parseExpr(var p: TParser): PNode; +begin + case p.tok.toktype of + tkLambda: result := parseLambda(p); + tkIf: result := parseIfExpr(p); + else result := lowestExpr(p); + end +end; + +// ------------------------- pragma parser ----------------------------------- +function parsePragma(var p: TParser): PNode; +var + a: PNode; +begin + result := newNodeP(nkPragma, p); + getTok(p); + while true do begin + skipComment(p, result); + skipInd(p); + case p.tok.TokType of + tkCurlyDotRi, tkCurlyRi: begin + getTok(p); // skip } or .} + break + end; + tkEof: begin + parMessage(p, errTokenExpected, '.}'); + break + end + else begin + a := exprColonEqExpr(p, nkExprColonExpr, tkColon); + addSon(result, a); + if p.tok.tokType = tkComma then begin + getTok(p); + skipComment(p, a) + end + end + end + end +end; + +// ---------------------- statement parser ------------------------------------ +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 := newNode(nkAsgn); + result.info := 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; + e := parseExpr(p); + if p.tok.tokType = tkComma then begin + getTok(p); + skipComment(p, e) + end; + addSon(result, e); + 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 parseImportStmt(var p: TParser): PNode; +var + a, b: PNode; +begin + result := newNodeP(nkImportStmt, p); + getTok(p); // skip `import` + 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; + optInd(p, a); + if p.tok.tokType = tkAs then begin + getTok(p); + optInd(p, a); + b := a; + a := newNodeP(nkImportAs, p); + addSon(a, b); + addSon(a, parseSymbol(p)); + end; + if p.tok.tokType = tkComma then begin + getTok(p); + optInd(p, a) + end; + addSon(result, a); + end; +end; + +function parseIncludeStmt(var p: TParser): PNode; +var + a: PNode; +begin + result := newNodeP(nkIncludeStmt, p); + getTok(p); // skip `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; + optInd(p, a); + if p.tok.tokType = tkComma then begin + getTok(p); + optInd(p, a) + end; + addSon(result, 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); + if p.tok.tokType = tkComma then begin + getTok(p); + optInd(p, a) + end; + addSon(result, 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; + //PrintTok(p.tok); + 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)); + 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); + while true do begin + if p.tok.tokType = tkIn then begin + getTok(p); break + end; + if p.tok.tokType = tkEof then begin + parMessage(p, errTokenExpected, TokTypeToStr[tkIn]); break + end; + + a := parseSymbol(p); + if a = nil then break; + optInd(p, a); + if p.tok.tokType = tkComma then begin + getTok(p); optInd(p, a) + end; + addSon(result, a); + end; + 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 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; + +function parseIdentColonEquals(var p: TParser; withPragma: bool): PNode; +var + a: PNode; +begin + result := newNodeP(nkIdentDefs, p); + while true do begin + case p.tok.tokType of + tkColon, tkEof, tkSad, tkDed, tkParRi, tkEquals: break; + else begin + if withPragma then + a := identWithPragma(p) + else + a := parseSymbol(p); + if a = nil then exit; + end + end; + optInd(p, a); + if p.tok.tokType = tkComma then begin + getTok(p); + optInd(p, a) + end; + addSon(result, 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 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 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, false); + tkParRi: begin getTok(p); break end; + else begin parMessage(p, errTokenExpected, ')'+''); break; end; + end; + optInd(p, a); + if p.tok.tokType = tkComma then begin + getTok(p); optInd(p, a) + end; + addSon(result, a); + end; + end; + if p.tok.tokType = tkColon then begin + getTok(p); + optInd(p, result); + result.sons[0] := parseTypeDesc(p) + 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 parseTypeDescK(var p: TParser): PNode; +begin + case p.tok.tokType of + tkVar: result := parseTypeDescKAux(p, nkVarTy); + tkRef: result := parseTypeDescKAux(p, nkRefTy); + tkPtr: result := parseTypeDescKAux(p, nkPtrTy); + tkType: begin + result := newNodeP(nkTypeOfExpr, p); + getTok(p); + optInd(p, result); + addSon(result, parseExpr(p)) + end; + tkProc: begin + result := newNodeP(nkProcTy, p); + getTok(p); + optInd(p, result); + addSon(result, parseParamList(p)); + if p.tok.tokType = tkCurlyDotLe then + addSon(result, parsePragma(p)) + else + addSon(result, nil); + end + else begin + InternalError(parLineInfo(p), 'pnimsyn.parseTypeDescK'); + result := nil + end + end +end; + +function parseTypeDesc(var p: TParser): PNode; +begin + case p.tok.tokType of + tkVar, tkRef, tkPtr, tkProc, tkType: + result := parseTypeDescK(p); + else result := primary(p) + end +end; + +function parseGenericParam(var p: TParser): PNode; +var + a: PNode; +begin + a := parseSymbol(p); + optInd(p, a); + if p.tok.tokType = tkEquals then begin + result := newNodeP(nkDefaultTypeParam, p); + getTok(p); + optInd(p, a); + addSon(result, a); + addSon(result, parseTypeDesc(p)); + end + else + result := a +end; + +function parseGenericParamList(var p: TParser): PNode; +var + a: PNode; +begin + result := newNodeP(nkGenericParams, p); + getTok(p); + optInd(p, result); + while true do begin + case p.tok.tokType of + tkSymbol, tkAccent: a := parseGenericParam(p); + tkBracketRi: begin getTok(p); break end; + else begin parMessage(p, errTokenExpected, ']'+''); break; end; + end; + optInd(p, a); + if p.tok.tokType = tkComma then begin + getTok(p); optInd(p, a) + end; + addSon(result, a); + end; +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 parseLambda(var p: TParser): PNode; +begin + result := newNodeP(nkLambda, p); + getTok(p); + optInd(p, result); + addSon(result, nil); // no name part + addSon(result, nil); // no generic parameters + 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 begin + addSon(result, nil); + parMessage(p, errTokenExpected, tokTypeToStr[tkEquals]); + end +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 := newNodeP(nkCommentStmt, p); + skipComment(p, a); + addSon(result, a); + end; + else begin + parMessage(p, errIdentifierExpected, tokToStr(p.tok)); + break + end + end + end + end; + tkSymbol, tkAccent: addSon(result, defparser(p)); + 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); + optInd(p, result); + 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); + + 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 parseRecordPart(var p: TParser): PNode; forward; + +function parseRecordWhen(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, parseRecordPart(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, parseRecordPart(p)); + addSon(result, branch); + end +end; + +function parseRecordCase(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, parseRecordPart(p)); + addSon(result, b); + if b.kind = nkElse then break; + end +end; + +function parseRecordPart(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: begin + addSon(result, parseRecordPart(p)); + end; + tkDed: begin getTok(p); break end; + tkEof: break; + else begin + parMessage(p, errIdentifierExpected, tokToStr(p.tok)); + break + end + end + end + end; + tkWhen: result := parseRecordWhen(p); + tkCase: result := parseRecordCase(p); + tkSymbol, tkAccent: begin + result := parseIdentColonEquals(p, true); + skipComment(p, result); + end; + else result := nil + end +end; + +function parseRecordOrObject(var p: TParser; kind: TNodeKind): PNode; +var + a: PNode; +begin + result := newNodeP(kind, 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)); + if kind = nkObjectTy then + addSon(result, a) + else + parMessage(p, errInheritanceOnlyWithObjects); + end + else addSon(result, nil); + skipComment(p, result); + addSon(result, parseRecordPart(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 + tkRecord: a := parseRecordOrObject(p, nkRecordTy); + tkObject: a := parseRecordOrObject(p, nkObjectTy); + tkEnum: a := parseEnum(p); + else a := parseTypeDesc(p); + end; + addSon(result, a); + end + else + addSon(result, nil); + indAndComment(p, result); // XXX: special extension! +end; + +function parseVariable(var p: TParser): PNode; +begin + result := parseIdentColonEquals(p, true); + indAndComment(p, result); // XXX: 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 := parseImportStmt(p); + tkFrom: result := parseFromStmt(p); + tkInclude: result := parseIncludeStmt(p); + tkComment: begin + result := newNodeP(nkCommentStmt, p); + end; + //tkSad, tkInd, tkDed: assert(false); + else result := parseExprStmt(p) + end; + 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); + tkIterator: result := parseRoutine(p, nkIteratorDef); + tkMacro: result := parseRoutine(p, nkMacroDef); + tkTemplate: result := parseRoutine(p, nkTemplateDef); + 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; +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 addSon(result, complexOrSimpleStmt(p)); + end; + end + 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); + skipComment(p, result); + if p.tok.tokType = tkSad then getTok(p); + end + end + end +end; + +function parseModule(var p: TParser): PNode; +begin + result := newNodeP(nkStmtList, p); + getTok(p); // read first token + while true do begin + case p.tok.tokType of + tkSad: getTok(p); + tkDed, tkInd: parMessage(p, errInvalidIndentation); + tkEof: break; + else addSon(result, complexOrSimpleStmt(p)); + end + end +end; + +end. |