#
#
# 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
TSection = enum
seImplementation, seInterface
TContext = enum
conExpr, conStmt, conTypeDesc
TParserFlag* = enum
pfRefs, ## use "ref" instead of "ptr" for Pascal's ^typ
pfMoreReplacements, ## use more than the default replacements
pfImportBlackList ## use import blacklist
TParser*{.final.} = object
section: TSection
inParamList: bool
context: TContext # needed for the @emit command
lastVarSection: PNode
lex: TLexer
tok: TToken
repl: TIdTable # replacements
flags: set[TParserFlag]
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", "$"]]
proc ParseUnit*(p: var TParser): PNode
proc openParser*(p: var TParser, filename: string, inputStream: PLLStream,
flags: set[TParserFlag] = {})
proc closeParser*(p: var TParser)
proc exSymbol*(n: var PNode)
proc fixRecordDef*(n: var PNode)
# XXX: move these two to an auxiliary module
# implementation
proc OpenParser(p: var TParser, filename: string,
inputStream: PLLStream, flags: set[TParserFlag] = {}) =
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 pfMoreReplacements in flags:
for i in countup(low(nimReplacements), high(nimReplacements)):
IdTablePut(p.repl, getIdent(nimReplacements[i][0]),
getIdent(nimReplacements[i][1]))
p.flags = flags
proc CloseParser(p: var TParser) = CloseLexer(p.lex)
proc getTok(p: var TParser) = getTok(p.lex, p.tok)
proc parMessage(p: TParser, msg: TMsgKind, arg = "") =
lexMessage(p.lex, msg, arg)
proc parLineInfo(p: TParser): TLineInfo =
result = getLineInfo(p.lex)
proc skipCom(p: var TParser, 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: TParser) =
if p.tok.xkind != pxSymbol:
lexMessage(p.lex, errIdentifierExpected, $(p.tok))
proc Eat(p: var TParser, xkind: TTokKind) =
if p.tok.xkind == xkind: getTok(p)
else: lexMessage(p.lex, errTokenExpected, TokKindToStr(xkind))
proc Opt(p: var TParser, xkind: TTokKind) =
if p.tok.xkind == xkind: getTok(p)
proc newNodeP(kind: TNodeKind, p: TParser): PNode =
result = newNodeI(kind, getLineInfo(p.lex))
proc newIntNodeP(kind: TNodeKind, intVal: BiggestInt, p: TParser): PNode =
result = newNodeP(kind, p)
result.intVal = intVal
proc newFloatNodeP(kind: TNodeKind, floatVal: BiggestFloat,
p: TParser): PNode =
result = newNodeP(kind, p)
result.floatVal = floatVal
proc newStrNodeP(kind: TNodeKind, strVal: string, p: TParser): PNode =
result = newNodeP(kind, p)
result.strVal = strVal
proc newIdentNodeP(ident: PIdent, p: TParser): PNode =
result = newNodeP(nkIdent, p)
result.ident = ident
proc createIdentNodeP(ident: PIdent, p: TParser): 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 TParser): PNode
proc parseStmt(p: var TParser): PNode
proc parseTypeDesc(p: var TParser, definition: PNode = nil): PNode
proc parseEmit(p: var TParser, definition: PNode): PNode =
getTok(p) # skip 'emit'
result = ast.emptyNode
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 TParser, definition: PNode = nil): PNode =
result = ast.emptyNode
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)
if result.kind == nkEmpty: InternalError("emptyNode modified")
result.kind = nkCurly
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, $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, $p.tok)
while true:
getTok(p)
if p.tok.xkind == pxCurlyDirRi or p.tok.xkind == pxEof: break
eat(p, pxCurlyDirRi)
result = ast.emptyNode
proc getPrecedence(kind: TTokKind): 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 TParser): 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 TParser, 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, TokKindToStr(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 TParser, kind: TNodeKind,
tok: TTokKind): 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 TParser, elemKind: TNodeKind,
endTok, sepTok: TTokKind, 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, TokKindToStr(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 TParser): PNode =
if p.tok.xkind == pxSymbol:
result = createIdentNodeP(p.tok.ident, p)
else:
parMessage(p, errIdentifierExpected, $p.tok)
return ast.emptyNode
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, $p.tok)
proc qualifiedIdentListAux(p: var TParser, endTok: TTokKind,
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, TokKindToStr(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 TParser, kind, elemKind: TNodeKind,
endTok, sepTok: TTokKind): 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 TParser): 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, $(p.tok))
getTok(p) # we must consume a token here to prevend endless loops!
result = ast.emptyNode
if result.kind != nkEmpty: skipCom(p, result)
proc primary(p: var TParser): 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($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($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, $p.tok)
of pxHat:
var a = result
result = newNodeP(nkBracketExpr, p)
addSon(result, a)
getTok(p)
of pxBracketLe:
result = bracketExprList(p, result)
else: break
proc lowestExprAux(p: var TParser, v: var PNode, limit: int): TTokKind =
var
nextop: TTokKind
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($(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
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 TParser): 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 TParser): 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 TParser): 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, $(p.tok))
break
getTok(p) # skip identifier, string
skipCom(p, a)
if pfImportBlackList notin p.flags 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 = ast.emptyNode
proc parseIncludeDir(p: var TParser): PNode =
result = newNodeP(nkIncludeStmt, p)
getTok(p) # skip `include`
var filename = ""
while true:
case p.tok.xkind
of pxSymbol, pxDot, pxDotDot, pxSlash:
add(filename, $p.tok)
getTok(p)
of pxStrLit:
filename = p.tok.literal
getTok(p)
break
of pxCurlyDirRi:
break
else:
parMessage(p, errIdentifierExpected, $p.tok)
break
addSon(result, newStrNodeP(nkStrLit, changeFileExt(filename, "nim"), p))
if filename == "config.inc": result = ast.emptyNode
proc definedExprAux(p: var TParser): 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: TParser): bool =
if p.tok.xkind in {pxCurlyDirLe, pxStarDirLe}:
case toLower(p.tok.ident.s)
of "else", "endif": result = false
else: result = true
proc parseStmtList(p: var TParser): 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 TParser, result: PNode) =
addSon(result.sons[0], parseStmtList(p))
if p.tok.xkind in {pxCurlyDirLe, pxStarDirLe}:
var endMarker = succ(p.tok.xkind)
if toLower(p.tok.ident.s) == "else":
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 toLower(p.tok.ident.s) == "endif":
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 TParser, endMarker: TTokKind): 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 TParser, endMarker: TTokKind): 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 TParser, endMarker: TTokKind): 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 TParser): PNode =
result = ast.emptyNode
if not (p.tok.xkind in {pxCurlyDirLe, pxStarDirLe}): return
var endMarker = succ(p.tok.xkind)
if p.tok.ident != nil:
case toLower(p.tok.ident.s)
of "include":
result = parseIncludeDir(p)
eat(p, endMarker)
of "if": result = parseIfDir(p, endMarker)
of "ifdef": result = parseIfdefDir(p, endMarker)
of "ifndef": 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 TParser): PNode =
result = newNodeP(nkRaiseStmt, p)
getTok(p)
skipCom(p, result)
if p.tok.xkind != pxSemicolon: addSon(result, parseExpr(p))
else: addSon(result, ast.emptyNode)
proc parseIf(p: var TParser): 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 TParser): 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 TParser): 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, ast.emptyNode)
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 TParser): 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 TParser): 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 TParser): PNode =
result = newNodeP(nkForStmt, p)
getTok(p)
skipCom(p, result)
expectIdent(p)
addSon(result, createIdentNodeP(p.tok.ident, p))
getTok(p)
eat(p, pxAsgn)
var a = parseExpr(p)
var b = ast.emptyNode
var 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, TokKindToStr(pxTo))
addSon(c, a)
addSon(c, b)
eat(p, pxDo)
skipCom(p, result)
addSon(result, c)
addSon(result, parseStmt(p))
proc parseParam(p: var TParser): PNode =
var a: PNode
result = newNodeP(nkIdentDefs, p)
var v = ast.emptyNode
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, $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.kind != nkEmpty: addSon(v, parseTypeDesc(p))
else: v = parseTypeDesc(p)
addSon(result, v)
else:
addSon(result, ast.emptyNode)
if p.tok.xkind != pxEquals:
parMessage(p, errColonOrEqualsExpected, $p.tok)
if p.tok.xkind == pxEquals:
getTok(p)
skipCom(p, result)
addSon(result, parseExpr(p))
else:
addSon(result, ast.emptyNode)
proc parseParamList(p: var TParser): PNode =
var a: PNode
result = newNodeP(nkFormalParams, p)
addSon(result, ast.emptyNode) # 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 TParser): PNode =
result = ast.emptyNode
if p.tok.xkind == pxSymbol:
case toLower(p.tok.ident.s)
of "stdcall", "cdecl", "safecall", "syscall", "inline", "fastcall":
result = newNodeP(nkPragma, p)
addSon(result, newIdentNodeP(p.tok.ident, p))
getTok(p)
opt(p, pxSemicolon)
of "register":
result = newNodeP(nkPragma, p)
addSon(result, newIdentNodeP(getIdent("fastcall"), p))
getTok(p)
opt(p, pxSemicolon)
else:
nil
proc parseRoutineSpecifiers(p: var TParser, noBody: var bool): PNode =
var e: PNode
result = parseCallingConvention(p)
noBody = false
while p.tok.xkind == pxSymbol:
case toLower(p.tok.ident.s)
of "assembler", "overload", "far":
getTok(p)
opt(p, pxSemicolon)
of "forward":
noBody = true
getTok(p)
opt(p, pxSemicolon)
of "importc":
# This is a fake for platform module. There is no ``importc``
# directive in Pascal.
if result.kind == nkEmpty: result = newNodeP(nkPragma, p)
addSon(result, newIdentNodeP(getIdent("importc"), p))
noBody = true
getTok(p)
opt(p, pxSemicolon)
of "noconv":
# This is a fake for platform module. There is no ``noconv``
# directive in Pascal.
if result.kind == nkEmpty: result = newNodeP(nkPragma, p)
addSon(result, newIdentNodeP(getIdent("noconv"), p))
noBody = true
getTok(p)
opt(p, pxSemicolon)
of "procvar":
# This is a fake for the Nimrod compiler. There is no ``procvar``
# directive in Pascal.
if result.kind == nkEmpty: result = newNodeP(nkPragma, p)
addSon(result, newIdentNodeP(getIdent("procvar"), p))
getTok(p)
opt(p, pxSemicolon)
of "varargs":
if result.kind == nkEmpty: result = newNodeP(nkPragma, p)
addSon(result, newIdentNodeP(getIdent("varargs"), p))
getTok(p)
opt(p, pxSemicolon)
of "external":
if result.kind == nkEmpty: 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.kind == nkEmpty: break
if result.kind == nkEmpty: result = newNodeP(nkPragma, p)
addSon(result, e.sons[0])
proc parseRoutineType(p: var TParser): 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 TParser): PNode =
var a: PNode
result = newNodeP(nkEnumTy, p)
getTok(p)
skipCom(p, result)
addSon(result, ast.emptyNode) # 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, $(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)
var 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 TParser): 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 TParser): PNode
proc rawIdent(p: var TParser): PNode =
result = createIdentNodeP(p.tok.ident, p)
getTok(p)
proc parseIdentColonEquals(p: var TParser,
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, $(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, ast.emptyNode)
if p.tok.xkind != pxEquals:
parMessage(p, errColonOrEqualsExpected, $(p.tok))
if p.tok.xkind == pxEquals:
getTok(p)
skipCom(p, result)
addSon(result, parseExpr(p))
else:
addSon(result, ast.emptyNode)
if p.tok.xkind == pxSemicolon:
getTok(p)
skipCom(p, result)
proc parseRecordCase(p: var TParser): PNode =
var b, c: PNode
result = newNodeP(nkRecCase, p)
getTok(p)
var a = newNodeP(nkIdentDefs, p)
addSon(a, rawIdent(p))
eat(p, pxColon)
addSon(a, parseTypeDesc(p))
addSon(a, ast.emptyNode)
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 TParser): PNode =
result = ast.emptyNode
while (p.tok.xkind != pxEof) and (p.tok.xkind != pxEnd):
if result.kind == nkEmpty: 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, $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) =
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, nkEmpty: 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 TParser, 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 TParser, kind: TNodeKind,
definition: PNode): PNode =
result = newNodeP(kind, p)
getTok(p)
addSon(result, ast.emptyNode)
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, ast.emptyNode)
parseRecordBody(p, result, definition)
proc parseTypeDesc(p: var TParser, 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, ast.emptyNode)
addSon(result, ast.emptyNode)
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 pfRefs in p.flags: 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 TParser): PNode =
result = newNodeP(nkTypeDef, p)
addSon(result, identVis(p))
addSon(result, ast.emptyNode) # generic params
if p.tok.xkind == pxEquals:
getTok(p)
skipCom(p, result)
addSon(result, parseTypeDesc(p, result))
else:
addSon(result, ast.emptyNode)
if p.tok.xkind == pxSemicolon:
getTok(p)
skipCom(p, result)
proc parseTypeSection(p: var TParser): PNode =
result = newNodeP(nkTypeSection, p)
getTok(p)
skipCom(p, result)
while p.tok.xkind == pxSymbol:
addSon(result, parseTypeDef(p))
proc parseConstant(p: var TParser): 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, ast.emptyNode)
if p.tok.xkind != pxEquals:
parMessage(p, errColonOrEqualsExpected, $(p.tok))
if p.tok.xkind == pxEquals:
getTok(p)
skipCom(p, result)
addSon(result, parseExpr(p))
else:
addSon(result, ast.emptyNode)
if p.tok.xkind == pxSemicolon:
getTok(p)
skipCom(p, result)
proc parseConstSection(p: var TParser): PNode =
result = newNodeP(nkConstSection, p)
getTok(p)
skipCom(p, result)
while p.tok.xkind == pxSymbol:
addSon(result, parseConstant(p))
proc parseVar(p: var TParser): 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 TParser): PNode =
var noBody: bool
result = newNodeP(nkProcDef, p)
getTok(p)
skipCom(p, result)
expectIdent(p)
addSon(result, identVis(p))
addSon(result, ast.emptyNode) # generic parameters
addSon(result, parseParamList(p))
opt(p, pxSemicolon)
addSon(result, parseRoutineSpecifiers(p, noBody))
if (p.section == seInterface) or noBody:
addSon(result, ast.emptyNode)
else:
var 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 TParser, n: PNode): bool =
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 TParser, 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 exSymbols(n: PNode) =
case n.kind
of nkEmpty..nkNilLit: nil
of nkProcDef..nkIteratorDef: exSymbol(n.sons[namePos])
of nkWhenStmt, nkStmtList:
for i in countup(0, sonsLen(n) - 1): exSymbols(n.sons[i])
of nkVarSection, nkConstSection:
for i in countup(0, sonsLen(n) - 1): exSymbol(n.sons[i].sons[0])
of nkTypeSection:
for i in countup(0, sonsLen(n) - 1):
exSymbol(n.sons[i].sons[0])
if n.sons[i].sons[2].kind == nkObjectTy:
fixRecordDef(n.sons[i].sons[2])
else: nil
proc parseBegin(p: var TParser, 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:
var a = parseStmt(p)
if a.kind != nkEmpty: addSon(result, a)
if sonsLen(result) == 0: addSon(result, newNodeP(nkNilLit, p))
proc parseStmt(p: var TParser): PNode =
var oldcontext = p.context
p.context = conStmt
result = ast.emptyNode
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, ast.emptyNode)
elif p.tok.ident.id == getIdent("continue").id:
result = newNodeP(nkContinueStmt, p)
getTok(p)
skipCom(p, result)
addSon(result, ast.emptyNode)
elif p.tok.ident.id == getIdent("exit").id:
result = newNodeP(nkReturnStmt, p)
getTok(p)
skipCom(p, result)
addSon(result, ast.emptyNode)
else:
result = parseExprStmt(p)
of pxDot: getTok(p) # BUGFIX for ``end.`` in main program
else: result = parseExprStmt(p)
opt(p, pxSemicolon)
if result.kind != nkEmpty: skipCom(p, result)
p.context = oldcontext
proc parseUnit(p: var TParser): 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.'