diff options
Diffstat (limited to 'rod/pas2nim/pasparse.nim')
-rwxr-xr-x | rod/pas2nim/pasparse.nim | 1507 |
1 files changed, 1507 insertions, 0 deletions
diff --git a/rod/pas2nim/pasparse.nim b/rod/pas2nim/pasparse.nim new file mode 100755 index 000000000..79d5620e7 --- /dev/null +++ b/rod/pas2nim/pasparse.nim @@ -0,0 +1,1507 @@ +# +# +# Pas2nim - Pascal to Nimrod source converter +# (c) Copyright 2010 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +# 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 convert the AST to its text representation. + +import + os, llstream, paslex, idents, strutils, ast, astalgo, msgs, options + +type + TPasSection* = enum + seImplementation, seInterface + TPasContext* = enum + conExpr, conStmt, conTypeDesc + TPasParser*{.final.} = object + section*: TPasSection + inParamList*: bool + context*: TPasContext # needed for the @emit command + lastVarSection*: PNode + lex*: TPasLex + tok*: TPasTok + repl*: TIdTable # replacements + + TReplaceTuple* = array[0..1, string] + +const + ImportBlackList*: array[1..3, string] = ["nsystem", "sysutils", "charsets"] + stdReplacements*: array[1..19, 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, 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''') + +proc ParseUnit*(p: var TPasParser): PNode +proc openPasParser*(p: var TPasParser, filename: string, inputStream: PLLStream) +proc closePasParser*(p: var TPasParser) +proc exSymbol*(n: var PNode) +proc fixRecordDef*(n: var PNode) + # XXX: move these two to an auxiliary module + +# implementation + +proc OpenPasParser(p: var TPasParser, filename: string, + inputStream: PLLStream) = + OpenLexer(p.lex, filename, inputStream) + initIdTable(p.repl) + for i in countup(low(stdReplacements), high(stdReplacements)): + IdTablePut(p.repl, getIdent(stdReplacements[i][0]), + getIdent(stdReplacements[i][1])) + if gCmd == cmdBoot: + for i in countup(low(nimReplacements), high(nimReplacements)): + IdTablePut(p.repl, getIdent(nimReplacements[i][0]), + getIdent(nimReplacements[i][1])) + +proc ClosePasParser(p: var TPasParser) = CloseLexer(p.lex) +proc getTok(p: var TPasParser) = getPasTok(p.lex, p.tok) + +proc parMessage(p: TPasParser, msg: TMsgKind, arg = "") = + lexMessage(p.lex, msg, arg) + +proc parLineInfo(p: TPasParser): TLineInfo = + result = getLineInfo(p.lex) + +proc skipCom(p: var TPasParser, n: PNode) = + while p.tok.xkind == pxComment: + if (n != nil): + if n.comment == nil: n.comment = p.tok.literal + else: add(n.comment, "\n" & p.tok.literal) + else: + parMessage(p, warnCommentXIgnored, p.tok.literal) + getTok(p) + +proc ExpectIdent(p: TPasParser) = + if p.tok.xkind != pxSymbol: + lexMessage(p.lex, errIdentifierExpected, pasTokToStr(p.tok)) + +proc Eat(p: var TPasParser, xkind: TPasTokKind) = + if p.tok.xkind == xkind: getTok(p) + else: lexMessage(p.lex, errTokenExpected, PasTokKindToStr[xkind]) + +proc Opt(p: var TPasParser, xkind: TPasTokKind) = + if p.tok.xkind == xkind: getTok(p) + +proc newNodeP(kind: TNodeKind, p: TPasParser): PNode = + result = newNodeI(kind, getLineInfo(p.lex)) + +proc newIntNodeP(kind: TNodeKind, intVal: BiggestInt, p: TPasParser): PNode = + result = newNodeP(kind, p) + result.intVal = intVal + +proc newFloatNodeP(kind: TNodeKind, floatVal: BiggestFloat, + p: TPasParser): PNode = + result = newNodeP(kind, p) + result.floatVal = floatVal + +proc newStrNodeP(kind: TNodeKind, strVal: string, p: TPasParser): PNode = + result = newNodeP(kind, p) + result.strVal = strVal + +proc newIdentNodeP(ident: PIdent, p: TPasParser): PNode = + result = newNodeP(nkIdent, p) + result.ident = ident + +proc createIdentNodeP(ident: PIdent, p: TPasParser): PNode = + result = newNodeP(nkIdent, p) + var x = PIdent(IdTableGet(p.repl, ident)) + if x != nil: result.ident = x + else: result.ident = ident + +proc parseExpr(p: var TPasParser): PNode +proc parseStmt(p: var TPasParser): PNode +proc parseTypeDesc(p: var TPasParser, definition: PNode = nil): PNode + +proc parseEmit(p: var TPasParser, definition: PNode): PNode = + getTok(p) # skip 'emit' + result = nil + if p.tok.xkind != pxCurlyDirRi: + case p.context + of conExpr: + result = parseExpr(p) + of conStmt: + result = parseStmt(p) + if p.tok.xkind != pxCurlyDirRi: + var a = result + result = newNodeP(nkStmtList, p) + addSon(result, a) + while p.tok.xkind != pxCurlyDirRi: + addSon(result, parseStmt(p)) + of conTypeDesc: + result = parseTypeDesc(p, definition) + eat(p, pxCurlyDirRi) + +proc parseCommand(p: var TPasParser, definition: PNode = nil): PNode = + result = nil + getTok(p) + if p.tok.ident.id == getIdent("discard").id: + result = newNodeP(nkDiscardStmt, p) + getTok(p) + eat(p, pxCurlyDirRi) + addSon(result, parseExpr(p)) + elif p.tok.ident.id == getIdent("set").id: + getTok(p) + eat(p, pxCurlyDirRi) + result = parseExpr(p) + result.kind = nkCurly + assert(sonsNotNil(result)) + elif p.tok.ident.id == getIdent("cast").id: + getTok(p) + eat(p, pxCurlyDirRi) + var a = parseExpr(p) + if (a.kind == nkCall) and (sonsLen(a) == 2): + result = newNodeP(nkCast, p) + addSon(result, a.sons[0]) + addSon(result, a.sons[1]) + else: + parMessage(p, errInvalidDirectiveX, pasTokToStr(p.tok)) + result = a + elif p.tok.ident.id == getIdent("emit").id: + result = parseEmit(p, definition) + elif p.tok.ident.id == getIdent("ignore").id: + getTok(p) + eat(p, pxCurlyDirRi) + while true: + case p.tok.xkind + of pxEof: + parMessage(p, errTokenExpected, "{@emit}") + of pxCommand: + getTok(p) + if p.tok.ident.id == getIdent("emit").id: + result = parseEmit(p, definition) + break + else: + while (p.tok.xkind != pxCurlyDirRi) and (p.tok.xkind != pxEof): + getTok(p) + eat(p, pxCurlyDirRi) + else: + getTok(p) # skip token + elif p.tok.ident.id == getIdent("ptr").id: + result = newNodeP(nkPtrTy, p) + getTok(p) + eat(p, pxCurlyDirRi) + elif p.tok.ident.id == getIdent("tuple").id: + result = newNodeP(nkTupleTy, p) + getTok(p) + eat(p, pxCurlyDirRi) + elif p.tok.ident.id == getIdent("acyclic").id: + result = newIdentNodeP(p.tok.ident, p) + getTok(p) + eat(p, pxCurlyDirRi) + else: + parMessage(p, errInvalidDirectiveX, pasTokToStr(p.tok)) + while true: + getTok(p) + if (p.tok.xkind == pxCurlyDirRi) or (p.tok.xkind == pxEof): break + eat(p, pxCurlyDirRi) + result = nil + +proc getPrecedence(kind: TPasTokKind): int = + case kind + of pxDiv, pxMod, pxStar, pxSlash, pxShl, pxShr, pxAnd: + result = 5 + of pxPlus, pxMinus, pxOr, pxXor: + result = 4 + of pxIn, pxEquals, pxLe, pxLt, pxGe, pxGt, pxNeq, pxIs: + result = 3 + else: result = -1 + +proc rangeExpr(p: var TPasParser): PNode = + var a = parseExpr(p) + if p.tok.xkind == pxDotDot: + result = newNodeP(nkRange, p) + addSon(result, a) + getTok(p) + skipCom(p, result) + addSon(result, parseExpr(p)) + else: + result = a + +proc bracketExprList(p: var TPasParser, first: PNode): PNode = + result = newNodeP(nkBracketExpr, p) + addSon(result, first) + getTok(p) + skipCom(p, result) + while true: + if p.tok.xkind == pxBracketRi: + getTok(p) + break + if p.tok.xkind == pxEof: + parMessage(p, errTokenExpected, PasTokKindToStr[pxBracketRi]) + break + var a = rangeExpr(p) + skipCom(p, a) + if p.tok.xkind == pxComma: + getTok(p) + skipCom(p, a) + addSon(result, a) + +proc exprColonEqExpr(p: var TPasParser, kind: TNodeKind, + tok: TPasTokKind): PNode = + var a = parseExpr(p) + if p.tok.xkind == tok: + result = newNodeP(kind, p) + getTok(p) + skipCom(p, result) + addSon(result, a) + addSon(result, parseExpr(p)) + else: + result = a + +proc exprListAux(p: var TPasParser, elemKind: TNodeKind, + endTok, sepTok: TPasTokKind, result: PNode) = + getTok(p) + skipCom(p, result) + while true: + if p.tok.xkind == endTok: + getTok(p) + break + if p.tok.xkind == pxEof: + parMessage(p, errTokenExpected, PasTokKindToStr[endtok]) + break + var a = exprColonEqExpr(p, elemKind, sepTok) + skipCom(p, a) + if (p.tok.xkind == pxComma) or (p.tok.xkind == pxSemicolon): + getTok(p) + skipCom(p, a) + addSon(result, a) + +proc qualifiedIdent(p: var TPasParser): PNode = + if p.tok.xkind == pxSymbol: + result = createIdentNodeP(p.tok.ident, p) + else: + parMessage(p, errIdentifierExpected, pasTokToStr(p.tok)) + return nil + getTok(p) + skipCom(p, result) + if p.tok.xkind == pxDot: + getTok(p) + skipCom(p, result) + if p.tok.xkind == pxSymbol: + var a = result + result = newNodeI(nkDotExpr, a.info) + addSon(result, a) + addSon(result, createIdentNodeP(p.tok.ident, p)) + getTok(p) + else: + parMessage(p, errIdentifierExpected, pasTokToStr(p.tok)) + +proc qualifiedIdentListAux(p: var TPasParser, endTok: TPasTokKind, + result: PNode) = + getTok(p) + skipCom(p, result) + while true: + if p.tok.xkind == endTok: + getTok(p) + break + if p.tok.xkind == pxEof: + parMessage(p, errTokenExpected, PasTokKindToStr[endtok]) + break + var a = qualifiedIdent(p) + skipCom(p, a) + if p.tok.xkind == pxComma: + getTok(p) + skipCom(p, a) + addSon(result, a) + +proc exprColonEqExprList(p: var TPasParser, kind, elemKind: TNodeKind, + endTok, sepTok: TPasTokKind): PNode = + result = newNodeP(kind, p) + exprListAux(p, elemKind, endTok, sepTok, result) + +proc setBaseFlags(n: PNode, base: TNumericalBase) = + case base + of base10: nil + of base2: incl(n.flags, nfBase2) + of base8: incl(n.flags, nfBase8) + of base16: incl(n.flags, nfBase16) + +proc identOrLiteral(p: var TPasParser): PNode = + case p.tok.xkind + of pxSymbol: + result = createIdentNodeP(p.tok.ident, p) + getTok(p) + of pxIntLit: + result = newIntNodeP(nkIntLit, p.tok.iNumber, p) + setBaseFlags(result, p.tok.base) + getTok(p) + of pxInt64Lit: + result = newIntNodeP(nkInt64Lit, p.tok.iNumber, p) + setBaseFlags(result, p.tok.base) + getTok(p) + of pxFloatLit: + result = newFloatNodeP(nkFloatLit, p.tok.fNumber, p) + setBaseFlags(result, p.tok.base) + getTok(p) + of pxStrLit: + if len(p.tok.literal) != 1: result = newStrNodeP(nkStrLit, p.tok.literal, p) + else: result = newIntNodeP(nkCharLit, ord(p.tok.literal[0]), p) + getTok(p) + of pxNil: + result = newNodeP(nkNilLit, p) + getTok(p) + of pxParLe: + # () 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): + result.kind = nkBracket # is an array constructor + of pxBracketLe: + # [] constructor + result = newNodeP(nkBracket, p) + getTok(p) + skipCom(p, result) + while (p.tok.xkind != pxBracketRi) and (p.tok.xkind != pxEof): + var a = rangeExpr(p) + if a.kind == nkRange: + result.kind = nkCurly # it is definitely a set literal + opt(p, pxComma) + skipCom(p, a) + assert(a != nil) + addSon(result, a) + eat(p, pxBracketRi) + of pxCommand: + result = parseCommand(p) + else: + parMessage(p, errExprExpected, pasTokToStr(p.tok)) + getTok(p) # we must consume a token here to prevend endless loops! + result = nil + if result != nil: skipCom(p, result) + +proc primary(p: var TPasParser): PNode = + # prefix operator? + if (p.tok.xkind == pxNot) or (p.tok.xkind == pxMinus) or + (p.tok.xkind == pxPlus): + result = newNodeP(nkPrefix, p) + var a = newIdentNodeP(getIdent(pasTokToStr(p.tok)), p) + addSon(result, a) + getTok(p) + skipCom(p, a) + addSon(result, primary(p)) + return + elif p.tok.xkind == pxAt: + result = newNodeP(nkAddr, p) + var a = newIdentNodeP(getIdent(pasTokToStr(p.tok)), p) + getTok(p) + if p.tok.xkind == pxBracketLe: + result = newNodeP(nkPrefix, p) + addSon(result, a) + addSon(result, identOrLiteral(p)) + else: + addSon(result, primary(p)) + return + result = identOrLiteral(p) + while true: + case p.tok.xkind + of pxParLe: + var a = result + result = newNodeP(nkCall, p) + addSon(result, a) + exprListAux(p, nkExprEqExpr, pxParRi, pxEquals, result) + of pxDot: + var a = result + result = newNodeP(nkDotExpr, p) + addSon(result, a) + getTok(p) # skip '.' + skipCom(p, result) + if p.tok.xkind == pxSymbol: + addSon(result, createIdentNodeP(p.tok.ident, p)) + getTok(p) + else: + parMessage(p, errIdentifierExpected, pasTokToStr(p.tok)) + of pxHat: + var a = result + result = newNodeP(nkDerefExpr, p) + addSon(result, a) + getTok(p) + of pxBracketLe: + result = bracketExprList(p, result) + else: break + +proc lowestExprAux(p: var TPasParser, v: var PNode, limit: int): TPasTokKind = + var + nextop: TPasTokKind + v2, node, opNode: PNode + v = primary(p) # expand while operators have priorities higher than 'limit' + var op = p.tok.xkind + var opPred = getPrecedence(op) + while (opPred > limit): + node = newNodeP(nkInfix, p) + opNode = newIdentNodeP(getIdent(pasTokToStr(p.tok)), p) # skip operator: + getTok(p) + case op + of pxPlus: + case p.tok.xkind + of pxPer: + getTok(p) + eat(p, pxCurlyDirRi) + opNode.ident = getIdent("+%") + of pxAmp: + getTok(p) + eat(p, pxCurlyDirRi) + opNode.ident = getIdent("&") + else: + nil + of pxMinus: + if p.tok.xkind == pxPer: + getTok(p) + eat(p, pxCurlyDirRi) + opNode.ident = getIdent("-%") + of pxEquals: + opNode.ident = getIdent("==") + of pxNeq: + opNode.ident = getIdent("!=") + else: + nil + 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) + result = op # return first untreated operator + +proc fixExpr(n: PNode): PNode = + result = n + if n == nil: return + case n.kind + of nkInfix: + if n.sons[1].kind == nkBracket: + n.sons[1].kind = nkCurly + if n.sons[2].kind == nkBracket: + n.sons[2].kind = nkCurly + if (n.sons[0].kind == nkIdent): + if (n.sons[0].ident.id == getIdent("+").id): + if (n.sons[1].kind == nkCharLit) and (n.sons[2].kind == nkStrLit) and + (n.sons[2].strVal == ""): + result = newStrNode(nkStrLit, chr(int(n.sons[1].intVal)) & "") + result.info = n.info + return # do not process sons as they don't exist anymore + elif (n.sons[1].kind in {nkCharLit, nkStrLit}) or + (n.sons[2].kind in {nkCharLit, nkStrLit}): + n.sons[0].ident = getIdent("&") # fix operator + else: + nil + if not (n.kind in {nkEmpty..nkNilLit}): + for i in countup(0, sonsLen(n) - 1): result.sons[i] = fixExpr(n.sons[i]) + +proc parseExpr(p: var TPasParser): PNode = + var oldcontext = p.context + p.context = conExpr + if p.tok.xkind == pxCommand: + result = parseCommand(p) + else: + discard lowestExprAux(p, result, - 1) + result = fixExpr(result) + p.context = oldcontext + +proc parseExprStmt(p: var TPasParser): PNode = + var info = parLineInfo(p) + var a = parseExpr(p) + if p.tok.xkind == pxAsgn: + getTok(p) + skipCom(p, a) + var b = parseExpr(p) + result = newNodeI(nkAsgn, info) + addSon(result, a) + addSon(result, b) + else: + result = a + +proc inImportBlackList(ident: PIdent): bool = + for i in countup(low(ImportBlackList), high(ImportBlackList)): + if ident.id == getIdent(ImportBlackList[i]).id: + return true + +proc parseUsesStmt(p: var TPasParser): PNode = + var a: PNode + result = newNodeP(nkImportStmt, p) + getTok(p) # skip `import` + skipCom(p, result) + while true: + case p.tok.xkind + of pxEof: break + of pxSymbol: a = newIdentNodeP(p.tok.ident, p) + else: + parMessage(p, errIdentifierExpected, pasTokToStr(p.tok)) + break + getTok(p) # skip identifier, string + skipCom(p, a) + if (gCmd != cmdBoot) or not inImportBlackList(a.ident): + addSon(result, createIdentNodeP(a.ident, p)) + if p.tok.xkind == pxComma: + getTok(p) + skipCom(p, a) + else: + break + if sonsLen(result) == 0: result = nil + +proc parseIncludeDir(p: var TPasParser): PNode = + result = newNodeP(nkIncludeStmt, p) + getTok(p) # skip `include` + var filename = "" + while true: + case p.tok.xkind + of pxSymbol, pxDot, pxDotDot, pxSlash: + filename = filename & pasTokToStr(p.tok) + getTok(p) + of pxStrLit: + filename = p.tok.literal + getTok(p) + break + of pxCurlyDirRi: + break + else: + parMessage(p, errIdentifierExpected, pasTokToStr(p.tok)) + break + addSon(result, newStrNodeP(nkStrLit, changeFileExt(filename, "nim"), p)) + if filename == "config.inc": result = nil + +proc definedExprAux(p: var TPasParser): PNode = + result = newNodeP(nkCall, p) + addSon(result, newIdentNodeP(getIdent("defined"), p)) + ExpectIdent(p) + addSon(result, createIdentNodeP(p.tok.ident, p)) + getTok(p) + +proc isHandledDirective(p: TPasParser): bool = + result = false + if p.tok.xkind in {pxCurlyDirLe, pxStarDirLe}: + case whichKeyword(p.tok.ident) + of wElse, wEndif: result = false + else: result = true + +proc parseStmtList(p: var TPasParser): PNode = + result = newNodeP(nkStmtList, p) + while true: + case p.tok.xkind + of pxEof: + break + of pxCurlyDirLe, pxStarDirLe: + if not isHandledDirective(p): break + else: + nil + addSon(result, parseStmt(p)) + if sonsLen(result) == 1: result = result.sons[0] + +proc parseIfDirAux(p: var TPasParser, result: PNode) = + addSon(result.sons[0], parseStmtList(p)) + if p.tok.xkind in {pxCurlyDirLe, pxStarDirLe}: + var endMarker = succ(p.tok.xkind) + if whichKeyword(p.tok.ident) == wElse: + var s = newNodeP(nkElse, p) + while (p.tok.xkind != pxEof) and (p.tok.xkind != endMarker): getTok(p) + eat(p, endMarker) + addSon(s, parseStmtList(p)) + addSon(result, s) + if p.tok.xkind in {pxCurlyDirLe, pxStarDirLe}: + endMarker = succ(p.tok.xkind) + if whichKeyword(p.tok.ident) == wEndif: + while (p.tok.xkind != pxEof) and (p.tok.xkind != endMarker): getTok(p) + eat(p, endMarker) + else: + parMessage(p, errXExpected, "{$endif}") + else: + parMessage(p, errXExpected, "{$endif}") + +proc parseIfdefDir(p: var TPasParser, endMarker: TPasTokKind): PNode = + result = newNodeP(nkWhenStmt, p) + addSon(result, newNodeP(nkElifBranch, p)) + getTok(p) + addSon(result.sons[0], definedExprAux(p)) + eat(p, endMarker) + parseIfDirAux(p, result) + +proc parseIfndefDir(p: var TPasParser, endMarker: TPasTokKind): PNode = + result = newNodeP(nkWhenStmt, p) + addSon(result, newNodeP(nkElifBranch, p)) + getTok(p) + var 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) + +proc parseIfDir(p: var TPasParser, endMarker: TPasTokKind): PNode = + result = newNodeP(nkWhenStmt, p) + addSon(result, newNodeP(nkElifBranch, p)) + getTok(p) + addSon(result.sons[0], parseExpr(p)) + eat(p, endMarker) + parseIfDirAux(p, result) + +proc parseDirective(p: var TPasParser): PNode = + result = nil + if not (p.tok.xkind in {pxCurlyDirLe, pxStarDirLe}): return + var endMarker = succ(p.tok.xkind) + if p.tok.ident != nil: + case whichKeyword(p.tok.ident) + of wInclude: + result = parseIncludeDir(p) + eat(p, endMarker) + of wIf: result = parseIfDir(p, endMarker) + of wIfdef: result = parseIfdefDir(p, endMarker) + of wIfndef: result = parseIfndefDir(p, endMarker) + else: + # skip unknown compiler directive + while (p.tok.xkind != pxEof) and (p.tok.xkind != endMarker): getTok(p) + eat(p, endMarker) + else: + eat(p, endMarker) + +proc parseRaise(p: var TPasParser): PNode = + result = newNodeP(nkRaiseStmt, p) + getTok(p) + skipCom(p, result) + if p.tok.xkind != pxSemicolon: addSon(result, parseExpr(p)) + else: addSon(result, nil) + +proc parseIf(p: var TPasParser): PNode = + result = newNodeP(nkIfStmt, p) + while true: + getTok(p) # skip ``if`` + var 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: + getTok(p) + if p.tok.xkind != pxIf: + # ordinary else part: + branch = newNodeP(nkElse, p) + skipCom(p, result) # BUGFIX + addSon(branch, parseStmt(p)) + addSon(result, branch) + break + else: + break + +proc parseWhile(p: var TPasParser): PNode = + result = newNodeP(nkWhileStmt, p) + getTok(p) + skipCom(p, result) + addSon(result, parseExpr(p)) + eat(p, pxDo) + skipCom(p, result) + addSon(result, parseStmt(p)) + +proc parseRepeat(p: var TPasParser): PNode = + result = newNodeP(nkWhileStmt, p) + getTok(p) + skipCom(p, result) + addSon(result, newIdentNodeP(getIdent("true"), p)) + var s = newNodeP(nkStmtList, p) + while (p.tok.xkind != pxEof) and (p.tok.xkind != pxUntil): + addSon(s, parseStmt(p)) + eat(p, pxUntil) + var a = newNodeP(nkIfStmt, p) + skipCom(p, a) + var b = newNodeP(nkElifBranch, p) + var 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: + nil + else: + addSon(s, a) + addSon(result, s) + +proc parseCase(p: var TPasParser): PNode = + var b: PNode + 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): + if p.tok.xkind == pxElse: + b = newNodeP(nkElse, p) + getTok(p) + else: + b = newNodeP(nkOfBranch, p) + while (p.tok.xkind != pxEof) and (p.tok.xkind != pxColon): + addSon(b, rangeExpr(p)) + opt(p, pxComma) + skipcom(p, b) + eat(p, pxColon) + skipCom(p, b) + addSon(b, parseStmt(p)) + addSon(result, b) + if b.kind == nkElse: break + eat(p, pxEnd) + +proc parseTry(p: var TPasParser): PNode = + result = newNodeP(nkTryStmt, p) + getTok(p) + skipCom(p, result) + var b = newNodeP(nkStmtList, p) + while not (p.tok.xkind in {pxFinally, pxExcept, pxEof, pxEnd}): + addSon(b, parseStmt(p)) + addSon(result, b) + if p.tok.xkind == pxExcept: + getTok(p) + while p.tok.ident.id == getIdent("on").id: + b = newNodeP(nkExceptBranch, p) + getTok(p) + var e = qualifiedIdent(p) + if p.tok.xkind == pxColon: + getTok(p) + e = qualifiedIdent(p) + addSon(b, e) + eat(p, pxDo) + addSon(b, parseStmt(p)) + addSon(result, b) + if p.tok.xkind == pxCommand: discard parseCommand(p) + if p.tok.xkind == pxElse: + b = newNodeP(nkExceptBranch, p) + getTok(p) + addSon(b, parseStmt(p)) + addSon(result, b) + if p.tok.xkind == pxFinally: + b = newNodeP(nkFinally, p) + getTok(p) + var e = newNodeP(nkStmtList, p) + while (p.tok.xkind != pxEof) and (p.tok.xkind != pxEnd): + addSon(e, parseStmt(p)) + if sonsLen(e) == 0: addSon(e, newNodeP(nkNilLit, p)) + addSon(result, e) + eat(p, pxEnd) + +proc parseFor(p: var TPasParser): PNode = + var a, b, c: PNode + 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: + addSon(c, newIdentNodeP(getIdent("countup"), p)) + getTok(p) + b = parseExpr(p) + elif p.tok.xkind == pxDownto: + addSon(c, newIdentNodeP(getIdent("countdown"), p)) + getTok(p) + b = parseExpr(p) + 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)) + +proc parseParam(p: var TPasParser): PNode = + var a, v: PNode + result = newNodeP(nkIdentDefs, p) + v = nil + case p.tok.xkind + of pxConst: + getTok(p) + of pxVar: + getTok(p) + v = newNodeP(nkVarTy, p) + of pxOut: + getTok(p) + v = newNodeP(nkVarTy, p) + else: + nil + while true: + case p.tok.xkind + of pxSymbol: a = createIdentNodeP(p.tok.ident, p) + of pxColon, pxEof, pxParRi, pxEquals: break + else: + parMessage(p, errIdentifierExpected, pasTokToStr(p.tok)) + return + getTok(p) # skip identifier + skipCom(p, a) + if p.tok.xkind == pxComma: + getTok(p) + skipCom(p, a) + addSon(result, a) + if p.tok.xkind == pxColon: + getTok(p) + skipCom(p, result) + if v != nil: addSon(v, parseTypeDesc(p)) + else: v = parseTypeDesc(p) + addSon(result, v) + else: + addSon(result, nil) + if p.tok.xkind != pxEquals: + parMessage(p, errColonOrEqualsExpected, pasTokToStr(p.tok)) + if p.tok.xkind == pxEquals: + getTok(p) + skipCom(p, result) + addSon(result, parseExpr(p)) + else: + addSon(result, nil) + +proc parseParamList(p: var TPasParser): PNode = + var a: PNode + result = newNodeP(nkFormalParams, p) + addSon(result, nil) # return type + if p.tok.xkind == pxParLe: + p.inParamList = true + getTok(p) + skipCom(p, result) + while true: + case p.tok.xkind + of pxSymbol, pxConst, pxVar, pxOut: + a = parseParam(p) + of pxParRi: + getTok(p) + break + else: + parMessage(p, errTokenExpected, ")") + break + skipCom(p, a) + if p.tok.xkind == pxSemicolon: + getTok(p) + skipCom(p, a) + addSon(result, a) + p.inParamList = false + if p.tok.xkind == pxColon: + getTok(p) + skipCom(p, result) + result.sons[0] = parseTypeDesc(p) + +proc parseCallingConvention(p: var TPasParser): PNode = + result = nil + if p.tok.xkind == pxSymbol: + case whichKeyword(p.tok.ident) + of wStdcall, wCDecl, wSafeCall, wSysCall, wInline, wFastCall: + result = newNodeP(nkPragma, p) + addSon(result, newIdentNodeP(p.tok.ident, p)) + getTok(p) + opt(p, pxSemicolon) + of wRegister: + result = newNodeP(nkPragma, p) + addSon(result, newIdentNodeP(getIdent("fastcall"), p)) + getTok(p) + opt(p, pxSemicolon) + else: + nil + +proc parseRoutineSpecifiers(p: var TPasParser, noBody: var bool): PNode = + var e: PNode + result = parseCallingConvention(p) + noBody = false + while p.tok.xkind == pxSymbol: + case whichKeyword(p.tok.ident) + of wAssembler, wOverload, wFar: + getTok(p) + opt(p, pxSemicolon) + of wForward: + noBody = true + getTok(p) + opt(p, pxSemicolon) + of wImportc: + # This is a fake for platform module. There is no ``importc`` + # directive in Pascal. + if result == nil: result = newNodeP(nkPragma, p) + addSon(result, newIdentNodeP(getIdent("importc"), p)) + noBody = true + getTok(p) + opt(p, pxSemicolon) + of wNoConv: + # This is a fake for platform module. There is no ``noconv`` + # directive in Pascal. + if result == nil: result = newNodeP(nkPragma, p) + addSon(result, newIdentNodeP(getIdent("noconv"), p)) + noBody = true + getTok(p) + opt(p, pxSemicolon) + of wProcVar: + # This is a fake for the Nimrod compiler. There is no ``procvar`` + # directive in Pascal. + if result == nil: result = newNodeP(nkPragma, p) + addSon(result, newIdentNodeP(getIdent("procvar"), p)) + getTok(p) + opt(p, pxSemicolon) + of wVarargs: + if result == nil: result = newNodeP(nkPragma, p) + addSon(result, newIdentNodeP(getIdent("varargs"), p)) + getTok(p) + opt(p, pxSemicolon) + of wExternal: + if result == nil: 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): + e = newNodeP(nkExprColonExpr, p) + getTok(p) + addSon(e, newIdentNodeP(getIdent("importc"), p)) + addSon(e, parseExpr(p)) + addSon(result, e) + else: + addSon(result, newIdentNodeP(getIdent("importc"), p)) + opt(p, pxSemicolon) + else: + e = parseCallingConvention(p) + if e == nil: break + if result == nil: result = newNodeP(nkPragma, p) + addSon(result, e.sons[0]) + +proc parseRoutineType(p: var TPasParser): PNode = + result = newNodeP(nkProcTy, p) + getTok(p) + skipCom(p, result) + addSon(result, parseParamList(p)) + opt(p, pxSemicolon) + addSon(result, parseCallingConvention(p)) + skipCom(p, result) + +proc parseEnum(p: var TPasParser): PNode = + var a, b: PNode + result = newNodeP(nkEnumTy, p) + getTok(p) + skipCom(p, result) + addSon(result, nil) # it does not inherit from any enumeration + while true: + case p.tok.xkind + of pxEof, pxParRi: break + of pxSymbol: a = newIdentNodeP(p.tok.ident, p) + else: + parMessage(p, errIdentifierExpected, pasTokToStr(p.tok)) + break + getTok(p) # skip identifier + skipCom(p, a) + if (p.tok.xkind == pxEquals) or (p.tok.xkind == pxAsgn): + getTok(p) + skipCom(p, a) + b = a + a = newNodeP(nkEnumFieldDef, p) + addSon(a, b) + addSon(a, parseExpr(p)) + if p.tok.xkind == pxComma: + getTok(p) + skipCom(p, a) + addSon(result, a) + eat(p, pxParRi) + +proc identVis(p: var TPasParser): PNode = + # identifier with visability + var a = createIdentNodeP(p.tok.ident, p) + if p.section == seInterface: + result = newNodeP(nkPostfix, p) + addSon(result, newIdentNodeP(getIdent("*"), p)) + addSon(result, a) + else: + result = a + getTok(p) + +type + TSymbolParser = proc (p: var TPasParser): PNode + +proc rawIdent(p: var TPasParser): PNode = + result = createIdentNodeP(p.tok.ident, p) + getTok(p) + +proc parseIdentColonEquals(p: var TPasParser, + identParser: TSymbolParser): PNode = + var a: PNode + result = newNodeP(nkIdentDefs, p) + while true: + case p.tok.xkind + of pxSymbol: a = identParser(p) + of pxColon, pxEof, pxParRi, pxEquals: break + else: + parMessage(p, errIdentifierExpected, pasTokToStr(p.tok)) + return + skipCom(p, a) + if p.tok.xkind == pxComma: + getTok(p) + skipCom(p, a) + addSon(result, a) + if p.tok.xkind == pxColon: + getTok(p) + skipCom(p, result) + addSon(result, parseTypeDesc(p)) + else: + addSon(result, nil) + if p.tok.xkind != pxEquals: + parMessage(p, errColonOrEqualsExpected, pasTokToStr(p.tok)) + if p.tok.xkind == pxEquals: + getTok(p) + skipCom(p, result) + addSon(result, parseExpr(p)) + else: + addSon(result, nil) + if p.tok.xkind == pxSemicolon: + getTok(p) + skipCom(p, result) + +proc parseRecordCase(p: var TPasParser): PNode = + var a, b, c: PNode + 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: + case p.tok.xkind + of pxEof, pxEnd: + break + of pxElse: + b = newNodeP(nkElse, p) + getTok(p) + else: + b = newNodeP(nkOfBranch, p) + while (p.tok.xkind != pxEof) and (p.tok.xkind != pxColon): + addSon(b, rangeExpr(p)) + opt(p, pxComma) + skipcom(p, b) + eat(p, pxColon) + skipCom(p, b) + c = newNodeP(nkRecList, p) + eat(p, pxParLe) + while (p.tok.xkind != pxParRi) and (p.tok.xkind != pxEof): + addSon(c, parseIdentColonEquals(p, rawIdent)) + opt(p, pxSemicolon) + skipCom(p, lastSon(c)) + eat(p, pxParRi) + opt(p, pxSemicolon) + if sonsLen(c) > 0: skipCom(p, lastSon(c)) + else: addSon(c, newNodeP(nkNilLit, p)) + addSon(b, c) + addSon(result, b) + if b.kind == nkElse: break + +proc parseRecordPart(p: var TPasParser): PNode = + result = nil + while (p.tok.xkind != pxEof) and (p.tok.xkind != pxEnd): + if result == nil: result = newNodeP(nkRecList, p) + case p.tok.xkind + of pxSymbol: + addSon(result, parseIdentColonEquals(p, rawIdent)) + opt(p, pxSemicolon) + skipCom(p, lastSon(result)) + of pxCase: + addSon(result, parseRecordCase(p)) + of pxComment: + skipCom(p, lastSon(result)) + else: + parMessage(p, errIdentifierExpected, pasTokToStr(p.tok)) + break + +proc exSymbol(n: var PNode) = + case n.kind + of nkPostfix: + nil + of nkPragmaExpr: + exSymbol(n.sons[0]) + of nkIdent, nkAccQuoted: + var a = newNodeI(nkPostFix, n.info) + addSon(a, newIdentNode(getIdent("*"), n.info)) + addSon(a, n) + n = a + else: internalError(n.info, "exSymbol(): " & $n.kind) + +proc fixRecordDef(n: var PNode) = + if n == nil: return + case n.kind + of nkRecCase: + fixRecordDef(n.sons[0]) + for i in countup(1, sonsLen(n) - 1): + var length = sonsLen(n.sons[i]) + fixRecordDef(n.sons[i].sons[length - 1]) + of nkRecList, nkRecWhen, nkElse, nkOfBranch, nkElifBranch, nkObjectTy: + for i in countup(0, sonsLen(n) - 1): fixRecordDef(n.sons[i]) + of nkIdentDefs: + for i in countup(0, sonsLen(n) - 3): exSymbol(n.sons[i]) + of nkNilLit: nil + else: internalError(n.info, "fixRecordDef(): " & $n.kind) + +proc addPragmaToIdent(ident: var PNode, pragma: PNode) = + var pragmasNode: PNode + if ident.kind != nkPragmaExpr: + pragmasNode = newNodeI(nkPragma, ident.info) + var e = newNodeI(nkPragmaExpr, ident.info) + addSon(e, ident) + addSon(e, pragmasNode) + ident = e + else: + pragmasNode = ident.sons[1] + if pragmasNode.kind != nkPragma: + InternalError(ident.info, "addPragmaToIdent") + addSon(pragmasNode, pragma) + +proc parseRecordBody(p: var TPasParser, result, definition: PNode) = + skipCom(p, result) + var a = parseRecordPart(p) + if result.kind != nkTupleTy: fixRecordDef(a) + addSon(result, a) + eat(p, pxEnd) + case p.tok.xkind + of pxSymbol: + if p.tok.ident.id == getIdent("acyclic").id: + if definition != nil: + addPragmaToIdent(definition.sons[0], newIdentNodeP(p.tok.ident, p)) + else: + InternalError(result.info, "anonymous record is not supported") + getTok(p) + else: + InternalError(result.info, "parseRecordBody") + of pxCommand: + if definition != nil: addPragmaToIdent(definition.sons[0], parseCommand(p)) + else: InternalError(result.info, "anonymous record is not supported") + else: + nil + opt(p, pxSemicolon) + skipCom(p, result) + +proc parseRecordOrObject(p: var TPasParser, kind: TNodeKind, + definition: PNode): PNode = + result = newNodeP(kind, p) + getTok(p) + addSon(result, nil) + if p.tok.xkind == pxParLe: + var a = newNodeP(nkOfInherit, p) + getTok(p) + addSon(a, parseTypeDesc(p)) + addSon(result, a) + eat(p, pxParRi) + else: + addSon(result, nil) + parseRecordBody(p, result, definition) + +proc parseTypeDesc(p: var TPasParser, definition: PNode = nil): PNode = + var oldcontext = p.context + p.context = conTypeDesc + if p.tok.xkind == pxPacked: getTok(p) + case p.tok.xkind + of pxCommand: + result = parseCommand(p, definition) + of pxProcedure, pxFunction: + result = parseRoutineType(p) + of pxRecord: + getTok(p) + if p.tok.xkind == pxCommand: + result = parseCommand(p) + if result.kind != nkTupleTy: InternalError(result.info, "parseTypeDesc") + parseRecordBody(p, result, definition) + var a = lastSon(result) # embed nkRecList directly into nkTupleTy + for i in countup(0, sonsLen(a) - 1): + if i == 0: result.sons[sonsLen(result) - 1] = a.sons[0] + else: addSon(result, a.sons[i]) + else: + result = newNodeP(nkObjectTy, p) + addSon(result, nil) + addSon(result, nil) + parseRecordBody(p, result, definition) + if definition != nil: + addPragmaToIdent(definition.sons[0], newIdentNodeP(getIdent("final"), p)) + else: + InternalError(result.info, "anonymous record is not supported") + of pxObject: result = parseRecordOrObject(p, nkObjectTy, definition) + of pxParLe: result = parseEnum(p) + of pxArray: + result = newNodeP(nkBracketExpr, p) + getTok(p) + if p.tok.xkind == pxBracketLe: + addSon(result, newIdentNodeP(getIdent("array"), p)) + getTok(p) + addSon(result, rangeExpr(p)) + eat(p, pxBracketRi) + else: + if p.inParamList: addSon(result, newIdentNodeP(getIdent("openarray"), p)) + else: addSon(result, newIdentNodeP(getIdent("seq"), p)) + eat(p, pxOf) + addSon(result, parseTypeDesc(p)) + of pxSet: + result = newNodeP(nkBracketExpr, p) + getTok(p) + eat(p, pxOf) + addSon(result, newIdentNodeP(getIdent("set"), p)) + addSon(result, parseTypeDesc(p)) + of pxHat: + getTok(p) + if p.tok.xkind == pxCommand: result = parseCommand(p) + elif gCmd == cmdBoot: result = newNodeP(nkRefTy, p) + else: result = newNodeP(nkPtrTy, p) + addSon(result, parseTypeDesc(p)) + of pxType: + getTok(p) + result = parseTypeDesc(p) + else: + var a = primary(p) + if p.tok.xkind == pxDotDot: + result = newNodeP(nkBracketExpr, p) + var r = newNodeP(nkRange, p) + addSon(result, newIdentNodeP(getIdent("range"), p)) + getTok(p) + addSon(r, a) + addSon(r, parseExpr(p)) + addSon(result, r) + else: + result = a + p.context = oldcontext + +proc parseTypeDef(p: var TPasParser): PNode = + result = newNodeP(nkTypeDef, p) + addSon(result, identVis(p)) + addSon(result, nil) # generic params + if p.tok.xkind == pxEquals: + getTok(p) + skipCom(p, result) + addSon(result, parseTypeDesc(p, result)) + else: + addSon(result, nil) + if p.tok.xkind == pxSemicolon: + getTok(p) + skipCom(p, result) + +proc parseTypeSection(p: var TPasParser): PNode = + result = newNodeP(nkTypeSection, p) + getTok(p) + skipCom(p, result) + while p.tok.xkind == pxSymbol: + addSon(result, parseTypeDef(p)) + +proc parseConstant(p: var TPasParser): PNode = + result = newNodeP(nkConstDef, p) + addSon(result, identVis(p)) + if p.tok.xkind == pxColon: + getTok(p) + skipCom(p, result) + addSon(result, parseTypeDesc(p)) + else: + addSon(result, nil) + if p.tok.xkind != pxEquals: + parMessage(p, errColonOrEqualsExpected, pasTokToStr(p.tok)) + if p.tok.xkind == pxEquals: + getTok(p) + skipCom(p, result) + addSon(result, parseExpr(p)) + else: + addSon(result, nil) + if p.tok.xkind == pxSemicolon: + getTok(p) + skipCom(p, result) + +proc parseConstSection(p: var TPasParser): PNode = + result = newNodeP(nkConstSection, p) + getTok(p) + skipCom(p, result) + while p.tok.xkind == pxSymbol: + addSon(result, parseConstant(p)) + +proc parseVar(p: var TPasParser): PNode = + result = newNodeP(nkVarSection, p) + getTok(p) + skipCom(p, result) + while p.tok.xkind == pxSymbol: + addSon(result, parseIdentColonEquals(p, identVis)) + p.lastVarSection = result + +proc parseRoutine(p: var TPasParser): PNode = + var + stmts: PNode + noBody: bool + 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: + addSon(result, nil) + else: + stmts = newNodeP(nkStmtList, p) + while true: + case p.tok.xkind + of pxVar: addSon(stmts, parseVar(p)) + of pxConst: addSon(stmts, parseConstSection(p)) + of pxType: addSon(stmts, parseTypeSection(p)) + of pxComment: skipCom(p, result) + of pxBegin: break + else: + parMessage(p, errTokenExpected, "begin") + break + var a = parseStmt(p) + for i in countup(0, sonsLen(a) - 1): addSon(stmts, a.sons[i]) + addSon(result, stmts) + +proc fixExit(p: var TPasParser, n: PNode): bool = + result = false + if (p.tok.ident.id == getIdent("exit").id): + var length = sonsLen(n) + if (length <= 0): return + var a = n.sons[length-1] + if (a.kind == nkAsgn) and (a.sons[0].kind == nkIdent) and + (a.sons[0].ident.id == getIdent("result").id): + delSon(a, 0) + a.kind = nkReturnStmt + result = true + getTok(p) + opt(p, pxSemicolon) + skipCom(p, a) + +proc fixVarSection(p: var TPasParser, counter: PNode) = + if p.lastVarSection == nil: return + assert(counter.kind == nkIdent) + for i in countup(0, sonsLen(p.lastVarSection) - 1): + var v = p.lastVarSection.sons[i] + for j in countup(0, sonsLen(v) - 3): + if v.sons[j].ident.id == counter.ident.id: + delSon(v, j) + if sonsLen(v) <= 2: + delSon(p.lastVarSection, i) + return + +proc parseBegin(p: var TPasParser, result: PNode) = + getTok(p) + while true: + case p.tok.xkind + of pxComment: addSon(result, parseStmt(p)) + of pxSymbol: + if not fixExit(p, result): addSon(result, parseStmt(p)) + of pxEnd: + getTok(p) + break + of pxSemicolon: getTok(p) + of pxEof: parMessage(p, errExprExpected) + else: addSonIfNotNil(result, parseStmt(p)) + if sonsLen(result) == 0: addSon(result, newNodeP(nkNilLit, p)) + +proc parseStmt(p: var TPasParser): PNode = + var oldcontext = p.context + p.context = conStmt + result = nil + case p.tok.xkind + of pxBegin: + result = newNodeP(nkStmtList, p) + parseBegin(p, result) + of pxCommand: result = parseCommand(p) + of pxCurlyDirLe, pxStarDirLe: + if isHandledDirective(p): result = parseDirective(p) + of pxIf: result = parseIf(p) + of pxWhile: result = parseWhile(p) + of pxRepeat: result = parseRepeat(p) + of pxCase: result = parseCase(p) + of pxTry: result = parseTry(p) + of pxProcedure, pxFunction: result = parseRoutine(p) + of pxType: result = parseTypeSection(p) + of pxConst: result = parseConstSection(p) + of pxVar: result = parseVar(p) + of pxFor: + result = parseFor(p) + fixVarSection(p, result.sons[0]) + of pxRaise: result = parseRaise(p) + of pxUses: result = parseUsesStmt(p) + of pxProgram, pxUnit, pxLibrary: + # skip the pointless header + while not (p.tok.xkind in {pxSemicolon, pxEof}): getTok(p) + getTok(p) + of pxInitialization: getTok(p) # just skip the token + of pxImplementation: + p.section = seImplementation + result = newNodeP(nkCommentStmt, p) + result.comment = "# implementation" + getTok(p) + of pxInterface: + p.section = seInterface + getTok(p) + of pxComment: + result = newNodeP(nkCommentStmt, p) + skipCom(p, result) + of pxSemicolon: getTok(p) + of pxSymbol: + if p.tok.ident.id == getIdent("break").id: + result = newNodeP(nkBreakStmt, p) + getTok(p) + skipCom(p, result) + addSon(result, nil) + elif p.tok.ident.id == getIdent("continue").id: + result = newNodeP(nkContinueStmt, p) + getTok(p) + skipCom(p, result) + addSon(result, nil) + elif p.tok.ident.id == getIdent("exit").id: + result = newNodeP(nkReturnStmt, p) + getTok(p) + skipCom(p, result) + addSon(result, nil) + else: + result = parseExprStmt(p) + of pxDot: getTok(p) # BUGFIX for ``end.`` in main program + else: result = parseExprStmt(p) + opt(p, pxSemicolon) + if result != nil: skipCom(p, result) + p.context = oldcontext + +proc parseUnit(p: var TPasParser): PNode = + result = newNodeP(nkStmtList, p) + getTok(p) # read first token + while true: + case p.tok.xkind + of pxEof, pxEnd: break + of pxBegin: parseBegin(p, result) + of pxCurlyDirLe, pxStarDirLe: + if isHandledDirective(p): addSon(result, parseDirective(p)) + else: parMessage(p, errXNotAllowedHere, p.tok.ident.s) + else: addSon(result, parseStmt(p)) + opt(p, pxEnd) + opt(p, pxDot) + if p.tok.xkind != pxEof: + addSon(result, parseStmt(p)) # comments after final 'end.' + |