mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-29 09:24:36 +00:00
1599 lines
46 KiB
Nim
Executable File
1599 lines
46 KiB
Nim
Executable File
#
|
|
#
|
|
# The Nimrod Compiler
|
|
# (c) Copyright 2012 Andreas Rumpf
|
|
#
|
|
# See the file "copying.txt", included in this
|
|
# distribution, for details about the copyright.
|
|
#
|
|
|
|
# This module implements the parser of the standard Nimrod syntax.
|
|
# The parser strictly reflects the grammar ("doc/grammar.txt"); however
|
|
# it uses several helper routines to keep the parser small. A special
|
|
# efficient algorithm is used for the precedence levels. The parser here can
|
|
# be seen as a refinement of the grammar, as it specifies how the AST is build
|
|
# from the grammar and how comments belong to the AST.
|
|
|
|
import
|
|
llstream, lexer, idents, strutils, ast, msgs
|
|
|
|
type
|
|
TParser*{.final.} = object # a TParser object represents a module that
|
|
# is being parsed
|
|
lex*: TLexer # the lexer that is used for parsing
|
|
tok*: TToken # the current token
|
|
|
|
|
|
proc ParseAll*(p: var TParser): PNode
|
|
proc openParser*(p: var TParser, filename: string, inputstream: PLLStream)
|
|
proc closeParser*(p: var TParser)
|
|
proc parseTopLevelStmt*(p: var TParser): PNode
|
|
# implements an iterator. Returns the next top-level statement or
|
|
# emtyNode if end of stream.
|
|
|
|
proc parseString*(s: string, filename: string = "", line: int = 0): PNode
|
|
# filename and line could be set optionally, when the string originates
|
|
# from a certain source file. This way, the compiler could generate
|
|
# correct error messages referring to the original source.
|
|
|
|
# helpers for the other parsers
|
|
proc getPrecedence*(tok: TToken): int
|
|
proc isOperator*(tok: TToken): bool
|
|
proc getTok*(p: var TParser)
|
|
proc parMessage*(p: TParser, msg: TMsgKind, arg: string = "")
|
|
proc skipComment*(p: var TParser, node: PNode)
|
|
proc newNodeP*(kind: TNodeKind, p: TParser): PNode
|
|
proc newIntNodeP*(kind: TNodeKind, intVal: BiggestInt, p: TParser): PNode
|
|
proc newFloatNodeP*(kind: TNodeKind, floatVal: BiggestFloat, p: TParser): PNode
|
|
proc newStrNodeP*(kind: TNodeKind, strVal: string, p: TParser): PNode
|
|
proc newIdentNodeP*(ident: PIdent, p: TParser): PNode
|
|
proc expectIdentOrKeyw*(p: TParser)
|
|
proc ExpectIdent*(p: TParser)
|
|
proc parLineInfo*(p: TParser): TLineInfo
|
|
proc Eat*(p: var TParser, TokType: TTokType)
|
|
proc skipInd*(p: var TParser)
|
|
proc optPar*(p: var TParser)
|
|
proc optInd*(p: var TParser, n: PNode)
|
|
proc indAndComment*(p: var TParser, n: PNode)
|
|
proc setBaseFlags*(n: PNode, base: TNumericalBase)
|
|
proc parseSymbol*(p: var TParser): PNode
|
|
proc parseTry(p: var TParser): PNode
|
|
proc parseCase(p: var TParser): PNode
|
|
# implementation
|
|
|
|
proc getTok(p: var TParser) =
|
|
rawGetTok(p.lex, p.tok)
|
|
|
|
proc OpenParser(p: var TParser, filename: string, inputStream: PLLStream) =
|
|
initToken(p.tok)
|
|
OpenLexer(p.lex, filename, inputstream)
|
|
getTok(p) # read the first token
|
|
|
|
proc CloseParser(p: var TParser) =
|
|
CloseLexer(p.lex)
|
|
|
|
proc parMessage(p: TParser, msg: TMsgKind, arg: string = "") =
|
|
lexMessage(p.lex, msg, arg)
|
|
|
|
proc parMessage(p: TParser, msg: TMsgKind, tok: TToken) =
|
|
lexMessage(p.lex, msg, prettyTok(tok))
|
|
|
|
proc skipComment(p: var TParser, node: PNode) =
|
|
if p.tok.tokType == tkComment:
|
|
if node != nil:
|
|
if node.comment == nil: node.comment = ""
|
|
add(node.comment, p.tok.literal)
|
|
else:
|
|
parMessage(p, errInternal, "skipComment")
|
|
getTok(p)
|
|
|
|
proc skipInd(p: var TParser) =
|
|
if p.tok.tokType == tkInd: getTok(p)
|
|
|
|
proc optPar(p: var TParser) =
|
|
if p.tok.tokType == tkSad or p.tok.tokType == tkInd: getTok(p)
|
|
|
|
proc optInd(p: var TParser, n: PNode) =
|
|
skipComment(p, n)
|
|
skipInd(p)
|
|
|
|
proc ExpectNl(p: TParser) =
|
|
if p.tok.tokType notin {tkEof, tkSad, tkInd, tkDed, tkComment}:
|
|
lexMessage(p.lex, errNewlineExpected, prettyTok(p.tok))
|
|
|
|
proc expectIdentOrKeyw(p: TParser) =
|
|
if p.tok.tokType != tkSymbol and not isKeyword(p.tok.tokType):
|
|
lexMessage(p.lex, errIdentifierExpected, prettyTok(p.tok))
|
|
|
|
proc ExpectIdent(p: TParser) =
|
|
if p.tok.tokType != tkSymbol:
|
|
lexMessage(p.lex, errIdentifierExpected, prettyTok(p.tok))
|
|
|
|
proc Eat(p: var TParser, TokType: TTokType) =
|
|
if p.tok.TokType == TokType: getTok(p)
|
|
else: lexMessage(p.lex, errTokenExpected, TokTypeToStr[tokType])
|
|
|
|
proc parLineInfo(p: TParser): TLineInfo =
|
|
result = getLineInfo(p.lex)
|
|
|
|
proc indAndComment(p: var TParser, n: PNode) =
|
|
if p.tok.tokType == tkInd:
|
|
var info = parLineInfo(p)
|
|
getTok(p)
|
|
if p.tok.tokType == tkComment: skipComment(p, n)
|
|
else: LocalError(info, errInvalidIndentation)
|
|
else:
|
|
skipComment(p, n)
|
|
|
|
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 parseExpr(p: var TParser): PNode
|
|
proc parseStmt(p: var TParser): PNode
|
|
proc parseTypeDesc(p: var TParser): PNode
|
|
proc parseDoBlocks(p: var TParser, call: PNode)
|
|
proc parseParamList(p: var TParser, retColon = true): PNode
|
|
|
|
proc relevantOprChar(ident: PIdent): char {.inline.} =
|
|
result = ident.s[0]
|
|
var L = ident.s.len
|
|
if result == '\\' and L > 1:
|
|
result = ident.s[1]
|
|
|
|
proc IsSigilLike(tok: TToken): bool {.inline.} =
|
|
result = tok.tokType == tkOpr and relevantOprChar(tok.ident) == '@'
|
|
|
|
proc IsLeftAssociative(tok: TToken): bool {.inline.} =
|
|
result = tok.tokType != tkOpr or relevantOprChar(tok.ident) != '^'
|
|
|
|
proc getPrecedence(tok: TToken): int =
|
|
case tok.tokType
|
|
of tkOpr:
|
|
let L = tok.ident.s.len
|
|
let relevantChar = relevantOprChar(tok.ident)
|
|
|
|
template considerAsgn(value: expr) =
|
|
result = if tok.ident.s[L-1] == '=': 1 else: value
|
|
|
|
case relevantChar
|
|
of '$', '^': considerAsgn(10)
|
|
of '*', '%', '/', '\\': considerAsgn(9)
|
|
of '~': result = 8
|
|
of '+', '-', '|': considerAsgn(8)
|
|
of '&': considerAsgn(7)
|
|
of '=', '<', '>', '!': result = 5
|
|
of '.': considerAsgn(6)
|
|
of '?': result = 2
|
|
else: considerAsgn(2)
|
|
of tkDiv, tkMod, tkShl, tkShr: result = 9
|
|
of tkIn, tkNotIn, tkIs, tkIsNot, tkNot, tkOf, tkAs: result = 5
|
|
of tkDotDot: result = 6
|
|
of tkAnd: result = 4
|
|
of tkOr, tkXor: result = 3
|
|
else: result = - 10
|
|
|
|
proc isOperator(tok: TToken): bool =
|
|
result = getPrecedence(tok) >= 0
|
|
|
|
proc parseSymbol(p: var TParser): PNode =
|
|
case p.tok.tokType
|
|
of tkSymbol:
|
|
result = newIdentNodeP(p.tok.ident, p)
|
|
getTok(p)
|
|
of tkAccent:
|
|
result = newNodeP(nkAccQuoted, p)
|
|
getTok(p)
|
|
while true:
|
|
case p.tok.tokType
|
|
of tkBracketLe:
|
|
add(result, newIdentNodeP(getIdent"[]", p))
|
|
getTok(p)
|
|
eat(p, tkBracketRi)
|
|
of tkEquals:
|
|
add(result, newIdentNodeP(getIdent"=", p))
|
|
getTok(p)
|
|
of tkParLe:
|
|
add(result, newIdentNodeP(getIdent"()", p))
|
|
getTok(p)
|
|
eat(p, tkParRi)
|
|
of tkCurlyLe:
|
|
add(result, newIdentNodeP(getIdent"{}", p))
|
|
getTok(p)
|
|
eat(p, tkCurlyRi)
|
|
of tokKeywordLow..tokKeywordHigh, tkSymbol, tkOpr, tkDotDot:
|
|
add(result, newIdentNodeP(p.tok.ident, p))
|
|
getTok(p)
|
|
of tkIntLit..tkCharLit:
|
|
add(result, newIdentNodeP(getIdent(tokToStr(p.tok)), p))
|
|
getTok(p)
|
|
else:
|
|
if result.len == 0:
|
|
parMessage(p, errIdentifierExpected, p.tok)
|
|
break
|
|
eat(p, tkAccent)
|
|
else:
|
|
parMessage(p, errIdentifierExpected, p.tok)
|
|
getTok(p) # BUGFIX: We must consume a token here to prevent endless loops!
|
|
result = ast.emptyNode
|
|
|
|
proc indexExpr(p: var TParser): PNode =
|
|
result = parseExpr(p)
|
|
|
|
proc indexExprList(p: var TParser, first: PNode, k: TNodeKind,
|
|
endToken: TTokType): PNode =
|
|
result = newNodeP(k, p)
|
|
addSon(result, first)
|
|
getTok(p)
|
|
optInd(p, result)
|
|
while p.tok.tokType notin {endToken, tkEof, tkSad}:
|
|
var a = indexExpr(p)
|
|
addSon(result, a)
|
|
if p.tok.tokType != tkComma: break
|
|
getTok(p)
|
|
optInd(p, a)
|
|
optPar(p)
|
|
eat(p, endToken)
|
|
|
|
proc exprColonEqExpr(p: var TParser, kind: TNodeKind, tok: TTokType): PNode =
|
|
var a = parseExpr(p)
|
|
if p.tok.tokType == tok:
|
|
result = newNodeP(kind, p)
|
|
getTok(p)
|
|
#optInd(p, result)
|
|
addSon(result, a)
|
|
addSon(result, parseExpr(p))
|
|
else:
|
|
result = a
|
|
|
|
proc exprList(p: var TParser, endTok: TTokType, result: PNode) =
|
|
getTok(p)
|
|
optInd(p, result)
|
|
while (p.tok.tokType != endTok) and (p.tok.tokType != tkEof):
|
|
var a = parseExpr(p)
|
|
addSon(result, a)
|
|
if p.tok.tokType != tkComma: break
|
|
getTok(p)
|
|
optInd(p, a)
|
|
eat(p, endTok)
|
|
|
|
proc dotExpr(p: var TParser, a: PNode): PNode =
|
|
var info = p.lex.getlineInfo
|
|
getTok(p)
|
|
optInd(p, a)
|
|
case p.tok.tokType
|
|
of tkType:
|
|
result = newNodeP(nkTypeOfExpr, p)
|
|
getTok(p)
|
|
addSon(result, a)
|
|
of tkAddr:
|
|
result = newNodeP(nkAddr, p)
|
|
getTok(p)
|
|
addSon(result, a)
|
|
else:
|
|
result = newNodeI(nkDotExpr, info)
|
|
addSon(result, a)
|
|
addSon(result, parseSymbol(p))
|
|
|
|
proc qualifiedIdent(p: var TParser): PNode =
|
|
result = parseSymbol(p) #optInd(p, result);
|
|
if p.tok.tokType == tkDot: result = dotExpr(p, result)
|
|
|
|
proc qualifiedIdentListAux(p: var TParser, endTok: TTokType, result: PNode) =
|
|
getTok(p)
|
|
optInd(p, result)
|
|
while (p.tok.tokType != endTok) and (p.tok.tokType != tkEof):
|
|
var a = qualifiedIdent(p)
|
|
addSon(result, a) #optInd(p, a);
|
|
if p.tok.tokType != tkComma: break
|
|
getTok(p)
|
|
optInd(p, a)
|
|
eat(p, endTok)
|
|
|
|
proc exprColonEqExprListAux(p: var TParser, elemKind: TNodeKind,
|
|
endTok, sepTok: TTokType, result: PNode) =
|
|
assert(endTok in {tkCurlyRi, tkCurlyDotRi, tkBracketRi, tkParRi})
|
|
getTok(p)
|
|
optInd(p, result)
|
|
while (p.tok.tokType != endTok) and (p.tok.tokType != tkEof) and
|
|
(p.tok.tokType != tkSad) and (p.tok.tokType != tkInd):
|
|
var a = exprColonEqExpr(p, elemKind, sepTok)
|
|
addSon(result, a)
|
|
if p.tok.tokType != tkComma: break
|
|
getTok(p)
|
|
optInd(p, a)
|
|
optPar(p)
|
|
eat(p, endTok)
|
|
|
|
proc exprColonEqExprList(p: var TParser, kind, elemKind: TNodeKind,
|
|
endTok, sepTok: TTokType): PNode =
|
|
result = newNodeP(kind, p)
|
|
exprColonEqExprListAux(p, elemKind, endTok, sepTok, result)
|
|
|
|
proc setOrTableConstr(p: var TParser): PNode =
|
|
result = newNodeP(nkCurly, p)
|
|
getTok(p) # skip '{'
|
|
optInd(p, result)
|
|
if p.tok.tokType == tkColon:
|
|
getTok(p) # skip ':'
|
|
result.kind = nkTableConstr
|
|
else:
|
|
while p.tok.tokType notin {tkCurlyRi, tkEof, tkSad, tkInd}:
|
|
var a = exprColonEqExpr(p, nkExprColonExpr, tkColon)
|
|
if a.kind == nkExprColonExpr: result.kind = nkTableConstr
|
|
addSon(result, a)
|
|
if p.tok.tokType != tkComma: break
|
|
getTok(p)
|
|
optInd(p, a)
|
|
optPar(p)
|
|
eat(p, tkCurlyRi) # skip '}'
|
|
|
|
proc parseCast(p: var TParser): PNode =
|
|
result = newNodeP(nkCast, p)
|
|
getTok(p)
|
|
eat(p, tkBracketLe)
|
|
optInd(p, result)
|
|
addSon(result, parseTypeDesc(p))
|
|
optPar(p)
|
|
eat(p, tkBracketRi)
|
|
eat(p, tkParLe)
|
|
optInd(p, result)
|
|
addSon(result, parseExpr(p))
|
|
optPar(p)
|
|
eat(p, tkParRi)
|
|
|
|
proc parseAddr(p: var TParser): PNode =
|
|
result = newNodeP(nkAddr, p)
|
|
getTok(p)
|
|
eat(p, tkParLe)
|
|
optInd(p, result)
|
|
addSon(result, parseExpr(p))
|
|
optPar(p)
|
|
eat(p, tkParRi)
|
|
|
|
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 parseGStrLit(p: var TParser, a: PNode): PNode =
|
|
case p.tok.tokType
|
|
of tkGStrLit:
|
|
result = newNodeP(nkCallStrLit, p)
|
|
addSon(result, a)
|
|
addSon(result, newStrNodeP(nkRStrLit, p.tok.literal, p))
|
|
getTok(p)
|
|
of tkGTripleStrLit:
|
|
result = newNodeP(nkCallStrLit, p)
|
|
addSon(result, a)
|
|
addSon(result, newStrNodeP(nkTripleStrLit, p.tok.literal, p))
|
|
getTok(p)
|
|
else:
|
|
result = a
|
|
|
|
proc identOrLiteral(p: var TParser): PNode =
|
|
case p.tok.tokType
|
|
of tkSymbol:
|
|
result = newIdentNodeP(p.tok.ident, p)
|
|
getTok(p)
|
|
result = parseGStrLit(p, result)
|
|
of tkAccent:
|
|
result = parseSymbol(p) # literals
|
|
of tkIntLit:
|
|
result = newIntNodeP(nkIntLit, p.tok.iNumber, p)
|
|
setBaseFlags(result, p.tok.base)
|
|
getTok(p)
|
|
of tkInt8Lit:
|
|
result = newIntNodeP(nkInt8Lit, p.tok.iNumber, p)
|
|
setBaseFlags(result, p.tok.base)
|
|
getTok(p)
|
|
of tkInt16Lit:
|
|
result = newIntNodeP(nkInt16Lit, p.tok.iNumber, p)
|
|
setBaseFlags(result, p.tok.base)
|
|
getTok(p)
|
|
of tkInt32Lit:
|
|
result = newIntNodeP(nkInt32Lit, p.tok.iNumber, p)
|
|
setBaseFlags(result, p.tok.base)
|
|
getTok(p)
|
|
of tkInt64Lit:
|
|
result = newIntNodeP(nkInt64Lit, p.tok.iNumber, p)
|
|
setBaseFlags(result, p.tok.base)
|
|
getTok(p)
|
|
of tkUIntLit:
|
|
result = newIntNodeP(nkUIntLit, p.tok.iNumber, p)
|
|
setBaseFlags(result, p.tok.base)
|
|
getTok(p)
|
|
of tkUInt8Lit:
|
|
result = newIntNodeP(nkUInt8Lit, p.tok.iNumber, p)
|
|
setBaseFlags(result, p.tok.base)
|
|
getTok(p)
|
|
of tkUInt16Lit:
|
|
result = newIntNodeP(nkUInt16Lit, p.tok.iNumber, p)
|
|
setBaseFlags(result, p.tok.base)
|
|
getTok(p)
|
|
of tkUInt32Lit:
|
|
result = newIntNodeP(nkUInt32Lit, p.tok.iNumber, p)
|
|
setBaseFlags(result, p.tok.base)
|
|
getTok(p)
|
|
of tkUInt64Lit:
|
|
result = newIntNodeP(nkUInt64Lit, p.tok.iNumber, p)
|
|
setBaseFlags(result, p.tok.base)
|
|
getTok(p)
|
|
of tkFloatLit:
|
|
result = newFloatNodeP(nkFloatLit, p.tok.fNumber, p)
|
|
setBaseFlags(result, p.tok.base)
|
|
getTok(p)
|
|
of tkFloat32Lit:
|
|
result = newFloatNodeP(nkFloat32Lit, p.tok.fNumber, p)
|
|
setBaseFlags(result, p.tok.base)
|
|
getTok(p)
|
|
of tkFloat64Lit:
|
|
result = newFloatNodeP(nkFloat64Lit, p.tok.fNumber, p)
|
|
setBaseFlags(result, p.tok.base)
|
|
getTok(p)
|
|
of tkFloat128Lit:
|
|
result = newFloatNodeP(nkFloat128Lit, p.tok.fNumber, p)
|
|
setBaseFlags(result, p.tok.base)
|
|
getTok(p)
|
|
of tkStrLit:
|
|
result = newStrNodeP(nkStrLit, p.tok.literal, p)
|
|
getTok(p)
|
|
of tkRStrLit:
|
|
result = newStrNodeP(nkRStrLit, p.tok.literal, p)
|
|
getTok(p)
|
|
of tkTripleStrLit:
|
|
result = newStrNodeP(nkTripleStrLit, p.tok.literal, p)
|
|
getTok(p)
|
|
of tkCharLit:
|
|
result = newIntNodeP(nkCharLit, ord(p.tok.literal[0]), p)
|
|
getTok(p)
|
|
of tkNil:
|
|
result = newNodeP(nkNilLit, p)
|
|
getTok(p)
|
|
of tkParLe:
|
|
# () constructor
|
|
result = exprColonEqExprList(p, nkPar, nkExprColonExpr, tkParRi, tkColon)
|
|
of tkCurlyLe:
|
|
# {} constructor
|
|
result = setOrTableConstr(p)
|
|
of tkBracketLe:
|
|
# [] constructor
|
|
result = exprColonEqExprList(p, nkBracket, nkExprColonExpr, tkBracketRi,
|
|
tkColon)
|
|
of tkCast:
|
|
result = parseCast(p)
|
|
else:
|
|
parMessage(p, errExprExpected, p.tok)
|
|
getTok(p) # we must consume a token here to prevend endless loops!
|
|
result = ast.emptyNode
|
|
|
|
proc primarySuffix(p: var TParser, r: PNode): PNode =
|
|
result = r
|
|
while true:
|
|
case p.tok.tokType
|
|
of tkParLe:
|
|
var a = result
|
|
result = newNodeP(nkCall, p)
|
|
addSon(result, a)
|
|
exprColonEqExprListAux(p, nkExprEqExpr, tkParRi, tkEquals, result)
|
|
parseDoBlocks(p, result)
|
|
of tkDo:
|
|
var a = result
|
|
result = newNodeP(nkCall, p)
|
|
addSon(result, a)
|
|
parseDoBlocks(p, result)
|
|
of tkDot:
|
|
result = dotExpr(p, result)
|
|
result = parseGStrLit(p, result)
|
|
of tkBracketLe:
|
|
result = indexExprList(p, result, nkBracketExpr, tkBracketRi)
|
|
of tkCurlyLe:
|
|
result = indexExprList(p, result, nkCurlyExpr, tkCurlyRi)
|
|
else: break
|
|
|
|
proc primary(p: var TParser, skipSuffix = false): PNode
|
|
|
|
proc lowestExprAux(p: var TParser, limit: int): PNode =
|
|
result = primary(p)
|
|
# expand while operators have priorities higher than 'limit'
|
|
var opPrec = getPrecedence(p.tok)
|
|
while opPrec >= limit:
|
|
var leftAssoc = ord(IsLeftAssociative(p.tok))
|
|
var a = newNodeP(nkInfix, p)
|
|
var opNode = newIdentNodeP(p.tok.ident, p) # skip operator:
|
|
getTok(p)
|
|
optInd(p, opNode)
|
|
# read sub-expression with higher priority:
|
|
var b = lowestExprAux(p, opPrec + leftAssoc)
|
|
addSon(a, opNode)
|
|
addSon(a, result)
|
|
addSon(a, b)
|
|
result = a
|
|
opPrec = getPrecedence(p.tok)
|
|
|
|
proc lowestExpr(p: var TParser): PNode =
|
|
result = lowestExprAux(p, -1)
|
|
|
|
proc parseIfExpr(p: var TParser, kind: TNodeKind): PNode =
|
|
result = newNodeP(kind, p)
|
|
while true:
|
|
getTok(p) # skip `if`, `elif`
|
|
var branch = newNodeP(nkElifExpr, p)
|
|
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: break
|
|
var branch = newNodeP(nkElseExpr, p)
|
|
eat(p, tkElse)
|
|
eat(p, tkColon)
|
|
optInd(p, branch)
|
|
addSon(branch, parseExpr(p))
|
|
addSon(result, branch)
|
|
|
|
proc parsePragma(p: var TParser): PNode =
|
|
result = newNodeP(nkPragma, p)
|
|
getTok(p)
|
|
optInd(p, result)
|
|
while (p.tok.tokType != tkCurlyDotRi) and (p.tok.tokType != tkCurlyRi) and
|
|
(p.tok.tokType != tkEof) and (p.tok.tokType != tkSad):
|
|
var a = exprColonEqExpr(p, nkExprColonExpr, tkColon)
|
|
addSon(result, a)
|
|
if p.tok.tokType == tkComma:
|
|
getTok(p)
|
|
optInd(p, a)
|
|
optPar(p)
|
|
if p.tok.tokType in {tkCurlyDotRi, tkCurlyRi}: getTok(p)
|
|
else: parMessage(p, errTokenExpected, ".}")
|
|
|
|
proc identVis(p: var TParser): PNode =
|
|
# identifier with visability
|
|
var a = parseSymbol(p)
|
|
if p.tok.tokType == tkOpr:
|
|
result = newNodeP(nkPostfix, p)
|
|
addSon(result, newIdentNodeP(p.tok.ident, p))
|
|
addSon(result, a)
|
|
getTok(p)
|
|
else:
|
|
result = a
|
|
|
|
proc identWithPragma(p: var TParser): PNode =
|
|
var a = identVis(p)
|
|
if p.tok.tokType == tkCurlyDotLe:
|
|
result = newNodeP(nkPragmaExpr, p)
|
|
addSon(result, a)
|
|
addSon(result, parsePragma(p))
|
|
else:
|
|
result = a
|
|
|
|
type
|
|
TDeclaredIdentFlag = enum
|
|
withPragma, # identifier may have pragma
|
|
withBothOptional # both ':' and '=' parts are optional
|
|
TDeclaredIdentFlags = set[TDeclaredIdentFlag]
|
|
|
|
proc parseIdentColonEquals(p: var TParser, flags: TDeclaredIdentFlags): PNode =
|
|
var a: PNode
|
|
result = newNodeP(nkIdentDefs, p)
|
|
while true:
|
|
case p.tok.tokType
|
|
of tkSymbol, tkAccent:
|
|
if withPragma in flags: a = identWithPragma(p)
|
|
else: a = parseSymbol(p)
|
|
if a.kind == nkEmpty: return
|
|
else: break
|
|
addSon(result, a)
|
|
if p.tok.tokType != tkComma: break
|
|
getTok(p)
|
|
optInd(p, a)
|
|
if p.tok.tokType == tkColon:
|
|
getTok(p)
|
|
optInd(p, result)
|
|
addSon(result, parseTypeDesc(p))
|
|
else:
|
|
addSon(result, ast.emptyNode)
|
|
if (p.tok.tokType != tkEquals) and not (withBothOptional in flags):
|
|
parMessage(p, errColonOrEqualsExpected, p.tok)
|
|
if p.tok.tokType == tkEquals:
|
|
getTok(p)
|
|
optInd(p, result)
|
|
addSon(result, parseExpr(p))
|
|
else:
|
|
addSon(result, ast.emptyNode)
|
|
|
|
proc parseTuple(p: var TParser, indentAllowed = false): PNode =
|
|
result = newNodeP(nkTupleTy, p)
|
|
getTok(p)
|
|
if p.tok.tokType == tkBracketLe:
|
|
getTok(p)
|
|
optInd(p, result)
|
|
while (p.tok.tokType == tkSymbol) or (p.tok.tokType == tkAccent):
|
|
var a = parseIdentColonEquals(p, {})
|
|
addSon(result, a)
|
|
if p.tok.tokType notin {tkComma, tkSemicolon}: break
|
|
getTok(p)
|
|
optInd(p, a)
|
|
optPar(p)
|
|
eat(p, tkBracketRi)
|
|
elif indentAllowed:
|
|
skipComment(p, result)
|
|
if p.tok.tokType == tkInd:
|
|
pushInd(p.lex, p.tok.indent)
|
|
getTok(p)
|
|
skipComment(p, result)
|
|
while true:
|
|
case p.tok.tokType
|
|
of tkSad:
|
|
getTok(p)
|
|
of tkSymbol, tkAccent:
|
|
var a = parseIdentColonEquals(p, {})
|
|
skipComment(p, a)
|
|
addSon(result, a)
|
|
of tkDed:
|
|
getTok(p)
|
|
break
|
|
of tkEof:
|
|
break
|
|
else:
|
|
parMessage(p, errIdentifierExpected, p.tok)
|
|
break
|
|
popInd(p.lex)
|
|
|
|
proc parseParamList(p: var TParser, retColon = true): PNode =
|
|
var a: PNode
|
|
result = newNodeP(nkFormalParams, p)
|
|
addSon(result, ast.emptyNode) # return type
|
|
if p.tok.tokType == tkParLe:
|
|
getTok(p)
|
|
optInd(p, result)
|
|
while true:
|
|
case p.tok.tokType #optInd(p, a);
|
|
of tkSymbol, tkAccent:
|
|
a = parseIdentColonEquals(p, {withBothOptional})
|
|
of tkParRi:
|
|
break
|
|
else:
|
|
parMessage(p, errTokenExpected, ")")
|
|
break
|
|
addSon(result, a)
|
|
if p.tok.tokType notin {tkComma, tkSemicolon}: break
|
|
getTok(p)
|
|
optInd(p, a)
|
|
optPar(p)
|
|
eat(p, tkParRi)
|
|
let hasRet = if retColon: p.tok.tokType == tkColon
|
|
else: p.tok.tokType == tkOpr and IdentEq(p.tok.ident, "->")
|
|
if hasRet:
|
|
getTok(p)
|
|
optInd(p, result)
|
|
result.sons[0] = parseTypeDesc(p)
|
|
|
|
proc optPragmas(p: var TParser): PNode =
|
|
if p.tok.tokType == tkCurlyDotLe: result = parsePragma(p)
|
|
else: result = ast.emptyNode
|
|
|
|
proc parseDoBlock(p: var TParser): PNode =
|
|
var info = parLineInfo(p)
|
|
getTok(p)
|
|
var params = parseParamList(p, retColon=false)
|
|
var pragmas = optPragmas(p)
|
|
eat(p, tkColon)
|
|
result = newNodeI(nkDo, info)
|
|
addSon(result, ast.emptyNode) # no name part
|
|
addSon(result, ast.emptyNode) # no pattern part
|
|
addSon(result, ast.emptyNode) # no generic parameters
|
|
addSon(result, params)
|
|
addSon(result, pragmas)
|
|
skipComment(p, result)
|
|
addSon(result, ast.emptyNode) # no exception list
|
|
addSon(result, parseStmt(p))
|
|
|
|
proc parseDoBlocks(p: var TParser, call: PNode) =
|
|
while p.tok.tokType == tkDo:
|
|
addSon(call, parseDoBlock(p))
|
|
|
|
proc parseProcExpr(p: var TParser, isExpr: bool): PNode =
|
|
# either a proc type or a anonymous proc
|
|
var
|
|
pragmas, params: PNode
|
|
info: TLineInfo
|
|
info = parLineInfo(p)
|
|
getTok(p)
|
|
let hasSignature = p.tok.tokType in {tkParLe, tkColon}
|
|
params = parseParamList(p)
|
|
pragmas = optPragmas(p)
|
|
if p.tok.tokType == tkEquals and isExpr:
|
|
result = newNodeI(nkLambda, info)
|
|
addSon(result, ast.emptyNode) # no name part
|
|
addSon(result, ast.emptyNode) # no pattern
|
|
addSon(result, ast.emptyNode) # no generic parameters
|
|
addSon(result, params)
|
|
addSon(result, pragmas)
|
|
getTok(p)
|
|
skipComment(p, result)
|
|
addSon(result, ast.emptyNode) # no exception list
|
|
addSon(result, parseStmt(p))
|
|
else:
|
|
result = newNodeI(nkProcTy, info)
|
|
if hasSignature:
|
|
addSon(result, params)
|
|
addSon(result, pragmas)
|
|
|
|
proc isExprStart(p: TParser): bool =
|
|
case p.tok.tokType
|
|
of tkSymbol, tkAccent, tkOpr, tkNot, tkNil, tkCast, tkIf, tkProc, tkBind,
|
|
tkParLe, tkBracketLe, tkCurlyLe, tkIntLit..tkCharLit, tkVar, tkRef, tkPtr,
|
|
tkTuple, tkType, tkWhen:
|
|
result = true
|
|
else: result = false
|
|
|
|
proc parseTypeDescKAux(p: var TParser, kind: TNodeKind): PNode =
|
|
result = newNodeP(kind, p)
|
|
getTok(p)
|
|
optInd(p, result)
|
|
if not isOperator(p.tok) and isExprStart(p):
|
|
addSon(result, parseTypeDesc(p))
|
|
|
|
proc parseExpr(p: var TParser): PNode =
|
|
#
|
|
#expr ::= lowestExpr
|
|
# | 'if' expr ':' expr ('elif' expr ':' expr)* 'else' ':' expr
|
|
# | 'when' expr ':' expr ('elif' expr ':' expr)* 'else' ':' expr
|
|
#
|
|
case p.tok.tokType:
|
|
of tkIf: result = parseIfExpr(p, nkIfExpr)
|
|
of tkWhen: result = parseIfExpr(p, nkWhenExpr)
|
|
else: result = lowestExpr(p)
|
|
# XXX needs proper support:
|
|
#of tkCase: result = parseCase(p)
|
|
#of tkTry: result = parseTry(p)
|
|
|
|
proc primary(p: var TParser, skipSuffix = false): PNode =
|
|
# prefix operator?
|
|
if isOperator(p.tok):
|
|
let isSigil = IsSigilLike(p.tok)
|
|
result = newNodeP(nkPrefix, p)
|
|
var a = newIdentNodeP(p.tok.ident, p)
|
|
addSon(result, a)
|
|
getTok(p)
|
|
optInd(p, a)
|
|
if isSigil:
|
|
#XXX prefix operators
|
|
addSon(result, primary(p, true))
|
|
result = primarySuffix(p, result)
|
|
else:
|
|
addSon(result, primary(p))
|
|
return
|
|
|
|
case p.tok.tokType:
|
|
of tkVar: result = parseTypeDescKAux(p, nkVarTy)
|
|
of tkRef: result = parseTypeDescKAux(p, nkRefTy)
|
|
of tkPtr: result = parseTypeDescKAux(p, nkPtrTy)
|
|
of tkType: result = parseTypeDescKAux(p, nkTypeOfExpr)
|
|
of tkTuple: result = parseTuple(p)
|
|
of tkProc: result = parseProcExpr(p, true)
|
|
of tkEnum:
|
|
result = newNodeP(nkEnumTy, p)
|
|
getTok(p)
|
|
of tkObject:
|
|
result = newNodeP(nkObjectTy, p)
|
|
getTok(p)
|
|
of tkDistinct:
|
|
result = newNodeP(nkDistinctTy, p)
|
|
getTok(p)
|
|
of tkAddr:
|
|
result = newNodeP(nkAddr, p)
|
|
getTok(p)
|
|
addSon(result, primary(p))
|
|
of tkStatic:
|
|
result = newNodeP(nkStaticExpr, p)
|
|
getTok(p)
|
|
addSon(result, primary(p))
|
|
of tkBind:
|
|
result = newNodeP(nkBind, p)
|
|
getTok(p)
|
|
optInd(p, result)
|
|
addSon(result, primary(p))
|
|
else:
|
|
result = identOrLiteral(p)
|
|
if not skipSuffix:
|
|
result = primarySuffix(p, result)
|
|
|
|
proc parseTypeDesc(p: var TParser): PNode =
|
|
if p.tok.toktype == tkProc: result = parseProcExpr(p, false)
|
|
else: result = parseExpr(p)
|
|
|
|
proc parseExprStmt(p: var TParser): PNode =
|
|
var a = lowestExpr(p)
|
|
if p.tok.tokType == tkEquals:
|
|
getTok(p)
|
|
optInd(p, result)
|
|
var b = parseExpr(p)
|
|
result = newNodeI(nkAsgn, a.info)
|
|
addSon(result, a)
|
|
addSon(result, b)
|
|
else:
|
|
result = newNodeP(nkCommand, p)
|
|
result.info = a.info
|
|
addSon(result, a)
|
|
while true:
|
|
if not isExprStart(p): break
|
|
var e = parseExpr(p)
|
|
addSon(result, e)
|
|
if p.tok.tokType != tkComma: break
|
|
getTok(p)
|
|
optInd(p, a)
|
|
if p.tok.tokType == tkDo:
|
|
parseDoBlocks(p, result)
|
|
return
|
|
if sonsLen(result) <= 1: result = a
|
|
else: a = result
|
|
if p.tok.tokType == tkColon:
|
|
# macro statement
|
|
result = newNodeP(nkMacroStmt, p)
|
|
result.info = a.info
|
|
addSon(result, a)
|
|
getTok(p)
|
|
skipComment(p, result)
|
|
if p.tok.tokType == tkSad: getTok(p)
|
|
if not (p.tok.TokType in {tkOf, tkElif, tkElse, tkExcept}):
|
|
addSon(result, parseStmt(p))
|
|
while true:
|
|
if p.tok.tokType == tkSad: getTok(p)
|
|
var b: PNode
|
|
case p.tok.tokType
|
|
of tkOf:
|
|
b = newNodeP(nkOfBranch, p)
|
|
exprList(p, tkColon, b)
|
|
of tkElif:
|
|
b = newNodeP(nkElifBranch, p)
|
|
getTok(p)
|
|
optInd(p, b)
|
|
addSon(b, parseExpr(p))
|
|
eat(p, tkColon)
|
|
of tkExcept:
|
|
b = newNodeP(nkExceptBranch, p)
|
|
qualifiedIdentListAux(p, tkColon, b)
|
|
skipComment(p, b)
|
|
of tkElse:
|
|
b = newNodeP(nkElse, p)
|
|
getTok(p)
|
|
eat(p, tkColon)
|
|
else: break
|
|
addSon(b, parseStmt(p))
|
|
addSon(result, b)
|
|
if b.kind == nkElse: break
|
|
|
|
proc parseImportOrIncludeStmt(p: var TParser, kind: TNodeKind): PNode =
|
|
var a: PNode
|
|
result = newNodeP(kind, p)
|
|
getTok(p) # skip `import` or `include`
|
|
optInd(p, result)
|
|
while true:
|
|
case p.tok.tokType
|
|
of tkEof, tkSad, tkDed:
|
|
break
|
|
of tkSymbol, tkAccent:
|
|
a = parseSymbol(p)
|
|
of tkRStrLit:
|
|
a = newStrNodeP(nkRStrLit, p.tok.literal, p)
|
|
getTok(p)
|
|
of tkStrLit:
|
|
a = newStrNodeP(nkStrLit, p.tok.literal, p)
|
|
getTok(p)
|
|
of tkTripleStrLit:
|
|
a = newStrNodeP(nkTripleStrLit, p.tok.literal, p)
|
|
getTok(p)
|
|
else:
|
|
parMessage(p, errIdentifierExpected, p.tok)
|
|
break
|
|
addSon(result, a)
|
|
if p.tok.tokType != tkComma: break
|
|
getTok(p)
|
|
optInd(p, a)
|
|
expectNl(p)
|
|
|
|
proc parseFromStmt(p: var TParser): PNode =
|
|
var a: PNode
|
|
result = newNodeP(nkFromStmt, p)
|
|
getTok(p) # skip `from`
|
|
optInd(p, result)
|
|
case p.tok.tokType
|
|
of tkSymbol, tkAccent:
|
|
a = parseSymbol(p)
|
|
of tkRStrLit:
|
|
a = newStrNodeP(nkRStrLit, p.tok.literal, p)
|
|
getTok(p)
|
|
of tkStrLit:
|
|
a = newStrNodeP(nkStrLit, p.tok.literal, p)
|
|
getTok(p)
|
|
of tkTripleStrLit:
|
|
a = newStrNodeP(nkTripleStrLit, p.tok.literal, p)
|
|
getTok(p)
|
|
else:
|
|
parMessage(p, errIdentifierExpected, p.tok)
|
|
return
|
|
addSon(result, a) #optInd(p, a);
|
|
eat(p, tkImport)
|
|
optInd(p, result)
|
|
while true:
|
|
case p.tok.tokType #optInd(p, a);
|
|
of tkEof, tkSad, tkDed:
|
|
break
|
|
of tkSymbol, tkAccent:
|
|
a = parseSymbol(p)
|
|
else:
|
|
parMessage(p, errIdentifierExpected, p.tok)
|
|
break
|
|
addSon(result, a)
|
|
if p.tok.tokType != tkComma: break
|
|
getTok(p)
|
|
optInd(p, a)
|
|
expectNl(p)
|
|
|
|
proc parseReturnOrRaise(p: var TParser, kind: TNodeKind): PNode =
|
|
result = newNodeP(kind, p)
|
|
getTok(p)
|
|
optInd(p, result)
|
|
case p.tok.tokType
|
|
of tkEof, tkSad, tkDed: addSon(result, ast.emptyNode)
|
|
else: addSon(result, parseExpr(p))
|
|
|
|
proc parseYieldOrDiscard(p: var TParser, kind: TNodeKind): PNode =
|
|
result = newNodeP(kind, p)
|
|
getTok(p)
|
|
optInd(p, result)
|
|
addSon(result, parseExpr(p))
|
|
|
|
proc parseBreakOrContinue(p: var TParser, kind: TNodeKind): PNode =
|
|
result = newNodeP(kind, p)
|
|
getTok(p)
|
|
optInd(p, result)
|
|
case p.tok.tokType
|
|
of tkEof, tkSad, tkDed: addSon(result, ast.emptyNode)
|
|
else: addSon(result, parseSymbol(p))
|
|
|
|
proc parseIfOrWhen(p: var TParser, kind: TNodeKind): PNode =
|
|
result = newNodeP(kind, p)
|
|
while true:
|
|
getTok(p) # skip `if`, `when`, `elif`
|
|
var 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: break
|
|
if p.tok.tokType == tkElse:
|
|
var branch = newNodeP(nkElse, p)
|
|
eat(p, tkElse)
|
|
eat(p, tkColon)
|
|
skipComment(p, branch)
|
|
addSon(branch, parseStmt(p))
|
|
addSon(result, branch)
|
|
|
|
proc parseWhile(p: var TParser): PNode =
|
|
result = newNodeP(nkWhileStmt, p)
|
|
getTok(p)
|
|
optInd(p, result)
|
|
addSon(result, parseExpr(p))
|
|
eat(p, tkColon)
|
|
skipComment(p, result)
|
|
addSon(result, parseStmt(p))
|
|
|
|
proc parseCase(p: var TParser): PNode =
|
|
var
|
|
b: PNode
|
|
inElif= false
|
|
wasIndented = false
|
|
result = newNodeP(nkCaseStmt, p)
|
|
getTok(p)
|
|
addSon(result, parseExpr(p))
|
|
if p.tok.tokType == tkColon: getTok(p)
|
|
skipComment(p, result)
|
|
|
|
if p.tok.tokType == tkInd:
|
|
pushInd(p.lex, p.tok.indent)
|
|
getTok(p)
|
|
wasIndented = true
|
|
|
|
while true:
|
|
if p.tok.tokType == tkSad: getTok(p)
|
|
case p.tok.tokType
|
|
of tkOf:
|
|
if inElif: break
|
|
b = newNodeP(nkOfBranch, p)
|
|
exprList(p, tkColon, b)
|
|
of tkElif:
|
|
inElif = true
|
|
b = newNodeP(nkElifBranch, p)
|
|
getTok(p)
|
|
optInd(p, b)
|
|
addSon(b, parseExpr(p))
|
|
eat(p, tkColon)
|
|
of tkElse:
|
|
b = newNodeP(nkElse, p)
|
|
getTok(p)
|
|
eat(p, tkColon)
|
|
else: break
|
|
skipComment(p, b)
|
|
addSon(b, parseStmt(p))
|
|
addSon(result, b)
|
|
if b.kind == nkElse: break
|
|
|
|
if wasIndented:
|
|
eat(p, tkDed)
|
|
popInd(p.lex)
|
|
|
|
proc parseTry(p: var TParser): PNode =
|
|
result = newNodeP(nkTryStmt, p)
|
|
getTok(p)
|
|
eat(p, tkColon)
|
|
skipComment(p, result)
|
|
addSon(result, parseStmt(p))
|
|
var b: PNode = nil
|
|
while true:
|
|
if p.tok.tokType == tkSad: getTok(p)
|
|
case p.tok.tokType
|
|
of tkExcept:
|
|
b = newNodeP(nkExceptBranch, p)
|
|
qualifiedIdentListAux(p, tkColon, b)
|
|
of tkFinally:
|
|
b = newNodeP(nkFinally, p)
|
|
getTok(p)
|
|
eat(p, tkColon)
|
|
else: break
|
|
skipComment(p, b)
|
|
addSon(b, parseStmt(p))
|
|
addSon(result, b)
|
|
if b.kind == nkFinally: break
|
|
if b == nil: parMessage(p, errTokenExpected, "except")
|
|
|
|
proc parseExceptBlock(p: var TParser, kind: TNodeKind): PNode =
|
|
result = newNodeP(kind, p)
|
|
getTok(p)
|
|
eat(p, tkColon)
|
|
skipComment(p, result)
|
|
addSon(result, parseStmt(p))
|
|
|
|
proc parseFor(p: var TParser): PNode =
|
|
result = newNodeP(nkForStmt, p)
|
|
getTok(p)
|
|
optInd(p, result)
|
|
var a = parseSymbol(p)
|
|
addSon(result, a)
|
|
while p.tok.tokType == tkComma:
|
|
getTok(p)
|
|
optInd(p, a)
|
|
a = parseSymbol(p)
|
|
addSon(result, a)
|
|
eat(p, tkIn)
|
|
addSon(result, parseExpr(p))
|
|
eat(p, tkColon)
|
|
skipComment(p, result)
|
|
addSon(result, parseStmt(p))
|
|
|
|
proc parseBlock(p: var TParser): PNode =
|
|
result = newNodeP(nkBlockStmt, p)
|
|
getTok(p)
|
|
optInd(p, result)
|
|
case p.tok.tokType
|
|
of tkEof, tkSad, tkDed, tkColon: addSon(result, ast.emptyNode)
|
|
else: addSon(result, parseSymbol(p))
|
|
eat(p, tkColon)
|
|
skipComment(p, result)
|
|
addSon(result, parseStmt(p))
|
|
|
|
proc parseStatic(p: var TParser): PNode =
|
|
result = newNodeP(nkStaticStmt, p)
|
|
getTok(p)
|
|
optInd(p, result)
|
|
eat(p, tkColon)
|
|
skipComment(p, result)
|
|
addSon(result, parseStmt(p))
|
|
|
|
proc parseAsm(p: var TParser): PNode =
|
|
result = newNodeP(nkAsmStmt, p)
|
|
getTok(p)
|
|
optInd(p, result)
|
|
if p.tok.tokType == tkCurlyDotLe: addSon(result, parsePragma(p))
|
|
else: addSon(result, ast.emptyNode)
|
|
case p.tok.tokType
|
|
of tkStrLit: addSon(result, newStrNodeP(nkStrLit, p.tok.literal, p))
|
|
of tkRStrLit: addSon(result, newStrNodeP(nkRStrLit, p.tok.literal, p))
|
|
of tkTripleStrLit: addSon(result,
|
|
newStrNodeP(nkTripleStrLit, p.tok.literal, p))
|
|
else:
|
|
parMessage(p, errStringLiteralExpected)
|
|
addSon(result, ast.emptyNode)
|
|
return
|
|
getTok(p)
|
|
|
|
proc parseGenericParam(p: var TParser): PNode =
|
|
var a: PNode
|
|
result = newNodeP(nkIdentDefs, p)
|
|
while true:
|
|
case p.tok.tokType
|
|
of tkSymbol, tkAccent:
|
|
a = parseSymbol(p)
|
|
if a.kind == nkEmpty: return
|
|
else: break
|
|
addSon(result, a)
|
|
if p.tok.tokType != tkComma: break
|
|
getTok(p)
|
|
optInd(p, a)
|
|
if p.tok.tokType == tkColon:
|
|
getTok(p)
|
|
optInd(p, result)
|
|
addSon(result, parseExpr(p))
|
|
else:
|
|
addSon(result, ast.emptyNode)
|
|
if p.tok.tokType == tkEquals:
|
|
getTok(p)
|
|
optInd(p, result)
|
|
addSon(result, parseExpr(p))
|
|
else:
|
|
addSon(result, ast.emptyNode)
|
|
|
|
proc parseGenericParamList(p: var TParser): PNode =
|
|
result = newNodeP(nkGenericParams, p)
|
|
getTok(p)
|
|
optInd(p, result)
|
|
while (p.tok.tokType == tkSymbol) or (p.tok.tokType == tkAccent):
|
|
var a = parseGenericParam(p)
|
|
addSon(result, a)
|
|
if p.tok.tokType notin {tkComma, tkSemicolon}: break
|
|
getTok(p)
|
|
optInd(p, a)
|
|
optPar(p)
|
|
eat(p, tkBracketRi)
|
|
|
|
proc parsePattern(p: var TParser): PNode =
|
|
eat(p, tkCurlyLe)
|
|
result = parseStmt(p)
|
|
eat(p, tkCurlyRi)
|
|
|
|
proc parseRoutine(p: var TParser, kind: TNodeKind): PNode =
|
|
result = newNodeP(kind, p)
|
|
getTok(p)
|
|
optInd(p, result)
|
|
addSon(result, identVis(p))
|
|
if p.tok.tokType == tkCurlyLe: addSon(result, parsePattern(p))
|
|
else: addSon(result, ast.emptyNode)
|
|
if p.tok.tokType == tkBracketLe: addSon(result, parseGenericParamList(p))
|
|
else: addSon(result, ast.emptyNode)
|
|
addSon(result, parseParamList(p))
|
|
if p.tok.tokType == tkCurlyDotLe: addSon(result, parsePragma(p))
|
|
else: addSon(result, ast.emptyNode)
|
|
# empty exception tracking:
|
|
addSon(result, ast.emptyNode)
|
|
if p.tok.tokType == tkEquals:
|
|
getTok(p)
|
|
skipComment(p, result)
|
|
addSon(result, parseStmt(p))
|
|
else:
|
|
addSon(result, ast.emptyNode)
|
|
indAndComment(p, result) # XXX: document this in the grammar!
|
|
|
|
proc newCommentStmt(p: var TParser): PNode =
|
|
result = newNodeP(nkCommentStmt, p)
|
|
result.info.line = result.info.line - int16(1)
|
|
|
|
type
|
|
TDefParser = proc (p: var TParser): PNode {.nimcall.}
|
|
|
|
proc parseSection(p: var TParser, kind: TNodeKind,
|
|
defparser: TDefParser): PNode =
|
|
result = newNodeP(kind, p)
|
|
getTok(p)
|
|
skipComment(p, result)
|
|
case p.tok.tokType
|
|
of tkInd:
|
|
pushInd(p.lex, p.tok.indent)
|
|
getTok(p)
|
|
skipComment(p, result)
|
|
while true:
|
|
case p.tok.tokType
|
|
of tkSad:
|
|
getTok(p)
|
|
of tkSymbol, tkAccent:
|
|
var a = defparser(p)
|
|
skipComment(p, a)
|
|
addSon(result, a)
|
|
of tkDed:
|
|
getTok(p)
|
|
break
|
|
of tkEof:
|
|
break # BUGFIX
|
|
of tkComment:
|
|
var a = newCommentStmt(p)
|
|
skipComment(p, a)
|
|
addSon(result, a)
|
|
else:
|
|
parMessage(p, errIdentifierExpected, p.tok)
|
|
break
|
|
popInd(p.lex)
|
|
of tkSymbol, tkAccent, tkParLe:
|
|
# tkParLe is allowed for ``var (x, y) = ...`` tuple parsing
|
|
addSon(result, defparser(p))
|
|
else: parMessage(p, errIdentifierExpected, p.tok)
|
|
|
|
proc parseConstant(p: var TParser): PNode =
|
|
result = newNodeP(nkConstDef, p)
|
|
addSon(result, identWithPragma(p))
|
|
if p.tok.tokType == tkColon:
|
|
getTok(p)
|
|
optInd(p, result)
|
|
addSon(result, parseTypeDesc(p))
|
|
else:
|
|
addSon(result, ast.emptyNode)
|
|
eat(p, tkEquals)
|
|
optInd(p, result)
|
|
addSon(result, parseExpr(p))
|
|
indAndComment(p, result) # XXX: special extension!
|
|
|
|
proc parseEnum(p: var TParser): PNode =
|
|
var a, b: PNode
|
|
result = newNodeP(nkEnumTy, p)
|
|
a = nil
|
|
getTok(p)
|
|
if false and p.tok.tokType == tkOf:
|
|
a = newNodeP(nkOfInherit, p)
|
|
getTok(p)
|
|
optInd(p, a)
|
|
addSon(a, parseTypeDesc(p))
|
|
addSon(result, a)
|
|
else:
|
|
addSon(result, ast.emptyNode)
|
|
optInd(p, result)
|
|
while true:
|
|
case p.tok.tokType
|
|
of tkEof, tkSad, tkDed: break
|
|
else: a = parseSymbol(p)
|
|
optInd(p, a)
|
|
if p.tok.tokType == tkEquals:
|
|
getTok(p)
|
|
optInd(p, a)
|
|
b = a
|
|
a = newNodeP(nkEnumFieldDef, p)
|
|
addSon(a, b)
|
|
addSon(a, parseExpr(p))
|
|
skipComment(p, a)
|
|
if p.tok.tokType == tkComma:
|
|
getTok(p)
|
|
optInd(p, a)
|
|
addSon(result, a)
|
|
if result.len <= 1:
|
|
lexMessage(p.lex, errIdentifierExpected, prettyTok(p.tok))
|
|
|
|
proc parseObjectPart(p: var TParser): PNode
|
|
proc parseObjectWhen(p: var TParser): PNode =
|
|
result = newNodeP(nkRecWhen, p)
|
|
while true:
|
|
getTok(p) # skip `when`, `elif`
|
|
var branch = newNodeP(nkElifBranch, p)
|
|
optInd(p, branch)
|
|
addSon(branch, parseExpr(p))
|
|
eat(p, tkColon)
|
|
skipComment(p, branch)
|
|
addSon(branch, parseObjectPart(p))
|
|
skipComment(p, branch)
|
|
addSon(result, branch)
|
|
if p.tok.tokType != tkElif: break
|
|
if p.tok.tokType == tkElse:
|
|
var branch = newNodeP(nkElse, p)
|
|
eat(p, tkElse)
|
|
eat(p, tkColon)
|
|
skipComment(p, branch)
|
|
addSon(branch, parseObjectPart(p))
|
|
addSon(result, branch)
|
|
|
|
proc parseObjectCase(p: var TParser): PNode =
|
|
result = newNodeP(nkRecCase, p)
|
|
getTok(p)
|
|
var a = newNodeP(nkIdentDefs, p)
|
|
addSon(a, identWithPragma(p))
|
|
eat(p, tkColon)
|
|
addSon(a, parseTypeDesc(p))
|
|
addSon(a, ast.emptyNode)
|
|
addSon(result, a)
|
|
if p.tok.tokType == tkColon: getTok(p)
|
|
skipComment(p, result)
|
|
var wasIndented = false
|
|
if p.tok.tokType == tkInd:
|
|
pushInd(p.lex, p.tok.indent)
|
|
getTok(p)
|
|
wasIndented = true
|
|
while true:
|
|
if p.tok.tokType == tkSad: getTok(p)
|
|
var b: PNode
|
|
case p.tok.tokType
|
|
of tkOf:
|
|
b = newNodeP(nkOfBranch, p)
|
|
exprList(p, tkColon, b)
|
|
of tkElse:
|
|
b = newNodeP(nkElse, p)
|
|
getTok(p)
|
|
eat(p, tkColon)
|
|
else: break
|
|
skipComment(p, b)
|
|
var fields = parseObjectPart(p)
|
|
if fields.kind == nkEmpty:
|
|
parMessage(p, errIdentifierExpected, p.tok)
|
|
fields = newNodeP(nkNilLit, p) # don't break further semantic checking
|
|
addSon(b, fields)
|
|
addSon(result, b)
|
|
if b.kind == nkElse: break
|
|
if wasIndented:
|
|
eat(p, tkDed)
|
|
popInd(p.lex)
|
|
|
|
proc parseObjectPart(p: var TParser): PNode =
|
|
case p.tok.tokType
|
|
of tkInd:
|
|
result = newNodeP(nkRecList, p)
|
|
pushInd(p.lex, p.tok.indent)
|
|
getTok(p)
|
|
skipComment(p, result)
|
|
while true:
|
|
case p.tok.tokType
|
|
of tkSad:
|
|
getTok(p)
|
|
of tkCase, tkWhen, tkSymbol, tkAccent, tkNil:
|
|
addSon(result, parseObjectPart(p))
|
|
of tkDed:
|
|
getTok(p)
|
|
break
|
|
of tkEof:
|
|
break
|
|
else:
|
|
parMessage(p, errIdentifierExpected, p.tok)
|
|
break
|
|
popInd(p.lex)
|
|
of tkWhen:
|
|
result = parseObjectWhen(p)
|
|
of tkCase:
|
|
result = parseObjectCase(p)
|
|
of tkSymbol, tkAccent:
|
|
result = parseIdentColonEquals(p, {withPragma})
|
|
skipComment(p, result)
|
|
of tkNil:
|
|
result = newNodeP(nkNilLit, p)
|
|
getTok(p)
|
|
else: result = ast.emptyNode
|
|
|
|
proc parseObject(p: var TParser): PNode =
|
|
result = newNodeP(nkObjectTy, p)
|
|
getTok(p)
|
|
if p.tok.tokType == tkCurlyDotLe: addSon(result, parsePragma(p))
|
|
else: addSon(result, ast.emptyNode)
|
|
if p.tok.tokType == tkOf:
|
|
var a = newNodeP(nkOfInherit, p)
|
|
getTok(p)
|
|
addSon(a, parseTypeDesc(p))
|
|
addSon(result, a)
|
|
else:
|
|
addSon(result, ast.emptyNode)
|
|
skipComment(p, result)
|
|
addSon(result, parseObjectPart(p))
|
|
|
|
proc parseDistinct(p: var TParser): PNode =
|
|
result = newNodeP(nkDistinctTy, p)
|
|
getTok(p)
|
|
optInd(p, result)
|
|
addSon(result, parseTypeDesc(p))
|
|
|
|
proc parsePointerInTypeSection(p: var TParser, kind: TNodeKind): PNode =
|
|
result = newNodeP(kind, p)
|
|
getTok(p)
|
|
optInd(p, result)
|
|
if not isOperator(p.tok):
|
|
case p.tok.tokType
|
|
of tkObject: addSon(result, parseObject(p))
|
|
of tkTuple: addSon(result, parseTuple(p, true))
|
|
else:
|
|
if isExprStart(p):
|
|
addSon(result, parseTypeDesc(p))
|
|
|
|
proc parseTypeDef(p: var TParser): PNode =
|
|
result = newNodeP(nkTypeDef, p)
|
|
addSon(result, identWithPragma(p))
|
|
if p.tok.tokType == tkBracketLe: addSon(result, parseGenericParamList(p))
|
|
else: addSon(result, ast.emptyNode)
|
|
if p.tok.tokType == tkEquals:
|
|
getTok(p)
|
|
optInd(p, result)
|
|
var a: PNode
|
|
case p.tok.tokType
|
|
of tkObject: a = parseObject(p)
|
|
of tkEnum: a = parseEnum(p)
|
|
of tkDistinct: a = parseDistinct(p)
|
|
of tkTuple: a = parseTuple(p, true)
|
|
of tkRef: a = parsePointerInTypeSection(p, nkRefTy)
|
|
of tkPtr: a = parsePointerInTypeSection(p, nkPtrTy)
|
|
else: a = parseTypeDesc(p)
|
|
addSon(result, a)
|
|
else:
|
|
addSon(result, ast.emptyNode)
|
|
indAndComment(p, result) # special extension!
|
|
|
|
proc parseVarTuple(p: var TParser): PNode =
|
|
result = newNodeP(nkVarTuple, p)
|
|
getTok(p) # skip '('
|
|
optInd(p, result)
|
|
while (p.tok.tokType == tkSymbol) or (p.tok.tokType == tkAccent):
|
|
var a = identWithPragma(p)
|
|
addSon(result, a)
|
|
if p.tok.tokType != tkComma: break
|
|
getTok(p)
|
|
optInd(p, a)
|
|
addSon(result, ast.emptyNode) # no type desc
|
|
optPar(p)
|
|
eat(p, tkParRi)
|
|
eat(p, tkEquals)
|
|
optInd(p, result)
|
|
addSon(result, parseExpr(p))
|
|
|
|
proc parseVariable(p: var TParser): PNode =
|
|
if p.tok.tokType == tkParLe: result = parseVarTuple(p)
|
|
else: result = parseIdentColonEquals(p, {withPragma})
|
|
indAndComment(p, result) # special extension!
|
|
|
|
proc parseBind(p: var TParser, k: TNodeKind): PNode =
|
|
result = newNodeP(k, p)
|
|
getTok(p)
|
|
optInd(p, result)
|
|
while true:
|
|
var a = qualifiedIdent(p)
|
|
addSon(result, a)
|
|
if p.tok.tokType != tkComma: break
|
|
getTok(p)
|
|
optInd(p, a)
|
|
expectNl(p)
|
|
|
|
proc parseStmtPragma(p: var TParser): PNode =
|
|
result = parsePragma(p)
|
|
if p.tok.tokType == tkColon:
|
|
let a = result
|
|
result = newNodeI(nkPragmaBlock, a.info)
|
|
getTok(p)
|
|
result.add a
|
|
result.add parseStmt(p)
|
|
|
|
proc simpleStmt(p: var TParser): PNode =
|
|
case p.tok.tokType
|
|
of tkReturn: result = parseReturnOrRaise(p, nkReturnStmt)
|
|
of tkRaise: result = parseReturnOrRaise(p, nkRaiseStmt)
|
|
of tkYield: result = parseYieldOrDiscard(p, nkYieldStmt)
|
|
of tkDiscard: result = parseReturnOrRaise(p, nkDiscardStmt)
|
|
of tkBreak: result = parseBreakOrContinue(p, nkBreakStmt)
|
|
of tkContinue: result = parseBreakOrContinue(p, nkContinueStmt)
|
|
of tkCurlyDotLe: result = parseStmtPragma(p)
|
|
of tkImport: result = parseImportOrIncludeStmt(p, nkImportStmt)
|
|
of tkFrom: result = parseFromStmt(p)
|
|
of tkInclude: result = parseImportOrIncludeStmt(p, nkIncludeStmt)
|
|
of tkComment: result = newCommentStmt(p)
|
|
else:
|
|
if isExprStart(p): result = parseExprStmt(p)
|
|
else: result = ast.emptyNode
|
|
if result.kind != nkEmpty: skipComment(p, result)
|
|
|
|
proc complexOrSimpleStmt(p: var TParser): PNode =
|
|
case p.tok.tokType
|
|
of tkIf: result = parseIfOrWhen(p, nkIfStmt)
|
|
of tkWhile: result = parseWhile(p)
|
|
of tkCase: result = parseCase(p)
|
|
of tkTry: result = parseTry(p)
|
|
of tkFinally: result = parseExceptBlock(p, nkFinally)
|
|
of tkExcept: result = parseExceptBlock(p, nkExceptBranch)
|
|
of tkFor: result = parseFor(p)
|
|
of tkBlock: result = parseBlock(p)
|
|
of tkStatic: result = parseStatic(p)
|
|
of tkAsm: result = parseAsm(p)
|
|
of tkProc: result = parseRoutine(p, nkProcDef)
|
|
of tkMethod: result = parseRoutine(p, nkMethodDef)
|
|
of tkIterator: result = parseRoutine(p, nkIteratorDef)
|
|
of tkMacro: result = parseRoutine(p, nkMacroDef)
|
|
of tkTemplate: result = parseRoutine(p, nkTemplateDef)
|
|
of tkConverter: result = parseRoutine(p, nkConverterDef)
|
|
of tkType: result = parseSection(p, nkTypeSection, parseTypeDef)
|
|
of tkConst: result = parseSection(p, nkConstSection, parseConstant)
|
|
of tkLet: result = parseSection(p, nkLetSection, parseVariable)
|
|
of tkWhen: result = parseIfOrWhen(p, nkWhenStmt)
|
|
of tkVar: result = parseSection(p, nkVarSection, parseVariable)
|
|
of tkBind: result = parseBind(p, nkBindStmt)
|
|
of tkMixin: result = parseBind(p, nkMixinStmt)
|
|
else: result = simpleStmt(p)
|
|
|
|
proc parseStmt(p: var TParser): PNode =
|
|
if p.tok.tokType == tkInd:
|
|
result = newNodeP(nkStmtList, p)
|
|
pushInd(p.lex, p.tok.indent)
|
|
getTok(p)
|
|
while true:
|
|
case p.tok.tokType
|
|
of tkSad, tkSemicolon: getTok(p)
|
|
of tkEof: break
|
|
of tkDed:
|
|
getTok(p)
|
|
break
|
|
else:
|
|
var a = complexOrSimpleStmt(p)
|
|
if a.kind == nkEmpty:
|
|
# XXX this needs a proper analysis;
|
|
if isKeyword(p.tok.tokType): parMessage(p, errInvalidIndentation)
|
|
break
|
|
addSon(result, a)
|
|
popInd(p.lex)
|
|
else:
|
|
# 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:
|
|
parMessage(p, errComplexStmtRequiresInd)
|
|
result = ast.emptyNode
|
|
else:
|
|
result = simpleStmt(p)
|
|
if result.kind == nkEmpty: parMessage(p, errExprExpected, p.tok)
|
|
if p.tok.tokType == tkSemicolon: getTok(p)
|
|
if p.tok.tokType == tkSad: getTok(p)
|
|
|
|
proc parseAll(p: var TParser): PNode =
|
|
result = newNodeP(nkStmtList, p)
|
|
while true:
|
|
case p.tok.tokType
|
|
of tkSad: getTok(p)
|
|
of tkDed, tkInd: parMessage(p, errInvalidIndentation)
|
|
of tkEof: break
|
|
else:
|
|
var a = complexOrSimpleStmt(p)
|
|
if a.kind == nkEmpty: parMessage(p, errExprExpected, p.tok)
|
|
addSon(result, a)
|
|
|
|
proc parseTopLevelStmt(p: var TParser): PNode =
|
|
result = ast.emptyNode
|
|
while true:
|
|
case p.tok.tokType
|
|
of tkSad, tkSemicolon: getTok(p)
|
|
of tkDed, tkInd:
|
|
parMessage(p, errInvalidIndentation)
|
|
getTok(p)
|
|
of tkEof: break
|
|
else:
|
|
result = complexOrSimpleStmt(p)
|
|
if result.kind == nkEmpty: parMessage(p, errExprExpected, p.tok)
|
|
break
|
|
|
|
proc parseString(s: string, filename: string = "", line: int = 0): PNode =
|
|
var stream = LLStreamOpen(s)
|
|
stream.lineOffset = line
|
|
|
|
var parser: TParser
|
|
OpenParser(parser, filename, stream)
|
|
|
|
result = parser.parseAll
|
|
CloseParser(parser)
|
|
|