12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507 |
- #
- #
- # The Nim Compiler
- # (c) Copyright 2015 Andreas Rumpf
- #
- # See the file "copying.txt", included in this
- # distribution, for details about the copyright.
- #
- # This module implements the parser of the standard Nim 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 built
- # from the grammar and how comments belong to the AST.
- # In fact the grammar is generated from this file:
- when isMainModule or defined(nimTestGrammar):
- # Leave a note in grammar.txt that it is generated:
- #| # This file is generated by compiler/parser.nim.
- import std/pegs
- when defined(nimPreviewSlimSystem):
- import std/syncio
- proc writeGrammarFile(x: string) =
- var outp = open(x, fmWrite)
- for line in lines("compiler/parser.nim"):
- if line =~ peg" \s* '#| ' {.*}":
- outp.write matches[0], "\L"
- outp.close
- when defined(nimTestGrammar):
- import std/os
- from ../testament/lib/stdtest/specialpaths import buildDir
- const newGrammarText = buildDir / "grammar.txt"
- if not dirExists(buildDir):
- createDir(buildDir)
- writeGrammarFile(newGrammarText)
- proc checkSameGrammar*() =
- doAssert sameFileContent(newGrammarText, "doc/grammar.txt"),
- "execute 'nim r compiler/parser.nim' to keep grammar.txt up-to-date"
- else:
- writeGrammarFile("doc/grammar.txt")
- import ".." / tools / grammar_nanny
- checkGrammarFile()
- import
- llstream, lexer, idents, strutils, ast, msgs, options, lineinfos,
- pathutils
- when defined(nimpretty):
- import layouter
- when defined(nimPreviewSlimSystem):
- import std/assertions
- type
- Parser* = object # A Parser object represents a file that
- # is being parsed
- currInd: int # current indentation level
- firstTok: bool # Has the first token been read?
- hasProgress: bool # some while loop requires progress ensurance
- lex*: Lexer # The lexer that is used for parsing
- tok*: Token # The current token
- lineStartPrevious*: int
- lineNumberPrevious*: int
- bufposPrevious*: int
- inPragma*: int # Pragma level
- inSemiStmtList*: int
- emptyNode: PNode
- when defined(nimpretty):
- em*: Emitter
- SymbolMode = enum
- smNormal, smAllowNil, smAfterDot
- PrimaryMode = enum
- pmNormal, pmTypeDesc, pmTypeDef, pmTrySimple
- proc parseAll*(p: var Parser): PNode
- proc closeParser*(p: var Parser)
- proc parseTopLevelStmt*(p: var Parser): PNode
- # helpers for the other parsers
- proc isOperator*(tok: Token): bool
- proc getTok*(p: var Parser)
- proc parMessage*(p: Parser, msg: TMsgKind, arg: string = "")
- proc skipComment*(p: var Parser, node: PNode)
- proc newNodeP*(kind: TNodeKind, p: Parser): PNode
- proc newIntNodeP*(kind: TNodeKind, intVal: BiggestInt, p: Parser): PNode
- proc newFloatNodeP*(kind: TNodeKind, floatVal: BiggestFloat, p: Parser): PNode
- proc newStrNodeP*(kind: TNodeKind, strVal: string, p: Parser): PNode
- proc newIdentNodeP*(ident: PIdent, p: Parser): PNode
- proc expectIdentOrKeyw*(p: Parser)
- proc expectIdent*(p: Parser)
- proc parLineInfo*(p: Parser): TLineInfo
- proc eat*(p: var Parser, tokType: TokType)
- proc skipInd*(p: var Parser)
- proc optPar*(p: var Parser)
- proc optInd*(p: var Parser, n: PNode)
- proc indAndComment*(p: var Parser, n: PNode, maybeMissEquals = false)
- proc setBaseFlags*(n: PNode, base: NumericalBase)
- proc parseSymbol*(p: var Parser, mode = smNormal): PNode
- proc parseTry(p: var Parser; isExpr: bool): PNode
- proc parseCase(p: var Parser): PNode
- proc parseStmtPragma(p: var Parser): PNode
- proc parsePragma(p: var Parser): PNode
- proc postExprBlocks(p: var Parser, x: PNode): PNode
- proc parseExprStmt(p: var Parser): PNode
- proc parseBlock(p: var Parser): PNode
- proc primary(p: var Parser, mode: PrimaryMode): PNode
- proc simpleExprAux(p: var Parser, limit: int, mode: PrimaryMode): PNode
- # implementation
- template prettySection(body) =
- when defined(nimpretty): beginSection(p.em)
- body
- when defined(nimpretty): endSection(p.em)
- proc getTok(p: var Parser) =
- ## Get the next token from the parser's lexer, and store it in the parser's
- ## `tok` member.
- p.lineNumberPrevious = p.lex.lineNumber
- p.lineStartPrevious = p.lex.lineStart
- p.bufposPrevious = p.lex.bufpos
- rawGetTok(p.lex, p.tok)
- p.hasProgress = true
- when defined(nimpretty):
- emitTok(p.em, p.lex, p.tok)
- # skip the additional tokens that nimpretty needs but the parser has no
- # interest in:
- while p.tok.tokType == tkComment:
- rawGetTok(p.lex, p.tok)
- emitTok(p.em, p.lex, p.tok)
- proc openParser*(p: var Parser, fileIdx: FileIndex, inputStream: PLLStream,
- cache: IdentCache; config: ConfigRef) =
- ## Open a parser, using the given arguments to set up its internal state.
- ##
- initToken(p.tok)
- openLexer(p.lex, fileIdx, inputStream, cache, config)
- when defined(nimpretty):
- openEmitter(p.em, cache, config, fileIdx)
- getTok(p) # read the first token
- p.firstTok = true
- p.emptyNode = newNode(nkEmpty)
- proc openParser*(p: var Parser, filename: AbsoluteFile, inputStream: PLLStream,
- cache: IdentCache; config: ConfigRef) =
- openParser(p, fileInfoIdx(config, filename), inputStream, cache, config)
- proc closeParser(p: var Parser) =
- ## Close a parser, freeing up its resources.
- closeLexer(p.lex)
- when defined(nimpretty):
- closeEmitter(p.em)
- proc parMessage(p: Parser, msg: TMsgKind, arg = "") =
- ## Produce and emit the parser message `arg` to output.
- lexMessageTok(p.lex, msg, p.tok, arg)
- proc parMessage(p: Parser, msg: string, tok: Token) =
- ## Produce and emit a parser message to output about the token `tok`
- parMessage(p, errGenerated, msg % prettyTok(tok))
- proc parMessage(p: Parser, arg: string) =
- ## Produce and emit the parser message `arg` to output.
- lexMessageTok(p.lex, errGenerated, p.tok, arg)
- template withInd(p, body: untyped) =
- let oldInd = p.currInd
- p.currInd = p.tok.indent
- body
- p.currInd = oldInd
- template newlineWasSplitting(p: var Parser) =
- when defined(nimpretty):
- layouter.newlineWasSplitting(p.em)
- template realInd(p): bool = p.tok.indent > p.currInd
- template sameInd(p): bool = p.tok.indent == p.currInd
- template sameOrNoInd(p): bool = p.tok.indent == p.currInd or p.tok.indent < 0
- proc validInd(p: var Parser): bool {.inline.} =
- result = p.tok.indent < 0 or p.tok.indent > p.currInd
- proc rawSkipComment(p: var Parser, node: PNode) =
- if p.tok.tokType == tkComment:
- if node != nil:
- var rhs = node.comment
- when defined(nimpretty):
- if p.tok.commentOffsetB > p.tok.commentOffsetA:
- rhs.add fileSection(p.lex.config, p.lex.fileIdx, p.tok.commentOffsetA, p.tok.commentOffsetB)
- else:
- rhs.add p.tok.literal
- else:
- rhs.add p.tok.literal
- node.comment = move rhs
- else:
- parMessage(p, errInternal, "skipComment")
- getTok(p)
- proc skipComment(p: var Parser, node: PNode) =
- if p.tok.indent < 0: rawSkipComment(p, node)
- proc flexComment(p: var Parser, node: PNode) =
- if p.tok.indent < 0 or realInd(p): rawSkipComment(p, node)
- const
- errInvalidIndentation = "invalid indentation"
- errIdentifierExpected = "identifier expected, but got '$1'"
- errExprExpected = "expression expected, but found '$1'"
- proc skipInd(p: var Parser) =
- if p.tok.indent >= 0:
- if not realInd(p): parMessage(p, errInvalidIndentation)
- proc optPar(p: var Parser) =
- if p.tok.indent >= 0:
- if p.tok.indent < p.currInd: parMessage(p, errInvalidIndentation)
- proc optInd(p: var Parser, n: PNode) =
- skipComment(p, n)
- skipInd(p)
- proc getTokNoInd(p: var Parser) =
- getTok(p)
- if p.tok.indent >= 0: parMessage(p, errInvalidIndentation)
- proc expectIdentOrKeyw(p: Parser) =
- if p.tok.tokType != tkSymbol and not isKeyword(p.tok.tokType):
- lexMessage(p.lex, errGenerated, errIdentifierExpected % prettyTok(p.tok))
- proc expectIdent(p: Parser) =
- if p.tok.tokType != tkSymbol:
- lexMessage(p.lex, errGenerated, errIdentifierExpected % prettyTok(p.tok))
- proc eat(p: var Parser, tokType: TokType) =
- ## Move the parser to the next token if the current token is of type
- ## `tokType`, otherwise error.
- if p.tok.tokType == tokType:
- getTok(p)
- else:
- lexMessage(p.lex, errGenerated,
- "expected: '" & $tokType & "', but got: '" & prettyTok(p.tok) & "'")
- proc parLineInfo(p: Parser): TLineInfo =
- ## Retrieve the line information associated with the parser's current state.
- result = getLineInfo(p.lex, p.tok)
- proc indAndComment(p: var Parser, n: PNode, maybeMissEquals = false) =
- if p.tok.indent > p.currInd:
- if p.tok.tokType == tkComment: rawSkipComment(p, n)
- elif maybeMissEquals:
- let col = p.bufposPrevious - p.lineStartPrevious
- var info = newLineInfo(p.lex.fileIdx, p.lineNumberPrevious, col)
- parMessage(p, "invalid indentation, maybe you forgot a '=' at $1 ?" % [p.lex.config$info])
- else: parMessage(p, errInvalidIndentation)
- else:
- skipComment(p, n)
- proc newNodeP(kind: TNodeKind, p: Parser): PNode =
- result = newNodeI(kind, parLineInfo(p))
- proc newIntNodeP(kind: TNodeKind, intVal: BiggestInt, p: Parser): PNode =
- result = newNodeP(kind, p)
- result.intVal = intVal
- proc newFloatNodeP(kind: TNodeKind, floatVal: BiggestFloat,
- p: Parser): PNode =
- result = newNodeP(kind, p)
- result.floatVal = floatVal
- proc newStrNodeP(kind: TNodeKind, strVal: string, p: Parser): PNode =
- result = newNodeP(kind, p)
- result.strVal = strVal
- proc newIdentNodeP(ident: PIdent, p: Parser): PNode =
- result = newNodeP(nkIdent, p)
- result.ident = ident
- proc parseExpr(p: var Parser): PNode
- proc parseStmt(p: var Parser): PNode
- proc parseTypeDesc(p: var Parser, fullExpr = false): PNode
- proc parseTypeDefValue(p: var Parser): PNode
- proc parseParamList(p: var Parser, retColon = true): PNode
- proc isSigilLike(tok: Token): bool {.inline.} =
- result = tok.tokType == tkOpr and tok.ident.s[0] == '@'
- proc isRightAssociative(tok: Token): bool {.inline.} =
- ## Determines whether the token is right assocative.
- result = tok.tokType == tkOpr and tok.ident.s[0] == '^'
- # or (tok.ident.s.len > 1 and tok.ident.s[^1] == '>')
- proc isUnary(tok: Token): bool =
- ## Check if the given token is a unary operator
- tok.tokType in {tkOpr, tkDotDot} and
- tok.strongSpaceB == tsNone and
- tok.strongSpaceA
- proc checkBinary(p: Parser) {.inline.} =
- ## Check if the current parser token is a binary operator.
- # we don't check '..' here as that's too annoying
- if p.tok.tokType == tkOpr:
- if p.tok.strongSpaceB == tsTrailing and not p.tok.strongSpaceA:
- parMessage(p, warnInconsistentSpacing, prettyTok(p.tok))
- #| module = stmt ^* (';' / IND{=})
- #|
- #| comma = ',' COMMENT?
- #| semicolon = ';' COMMENT?
- #| colon = ':' COMMENT?
- #| colcom = ':' COMMENT?
- #|
- #| operator = OP0 | OP1 | OP2 | OP3 | OP4 | OP5 | OP6 | OP7 | OP8 | OP9
- #| | 'or' | 'xor' | 'and'
- #| | 'is' | 'isnot' | 'in' | 'notin' | 'of' | 'as' | 'from'
- #| | 'div' | 'mod' | 'shl' | 'shr' | 'not' | 'static' | '..'
- #|
- #| prefixOperator = operator
- #|
- #| optInd = COMMENT? IND?
- #| optPar = (IND{>} | IND{=})?
- #|
- #| simpleExpr = arrowExpr (OP0 optInd arrowExpr)* pragma?
- #| arrowExpr = assignExpr (OP1 optInd assignExpr)*
- #| assignExpr = orExpr (OP2 optInd orExpr)*
- #| orExpr = andExpr (OP3 optInd andExpr)*
- #| andExpr = cmpExpr (OP4 optInd cmpExpr)*
- #| cmpExpr = sliceExpr (OP5 optInd sliceExpr)*
- #| sliceExpr = ampExpr (OP6 optInd ampExpr)*
- #| ampExpr = plusExpr (OP7 optInd plusExpr)*
- #| plusExpr = mulExpr (OP8 optInd mulExpr)*
- #| mulExpr = dollarExpr (OP9 optInd dollarExpr)*
- #| dollarExpr = primary (OP10 optInd primary)*
- proc isOperator(tok: Token): bool =
- #| operatorB = OP0 | OP1 | OP2 | OP3 | OP4 | OP5 | OP6 | OP7 | OP8 | OP9 |
- #| 'div' | 'mod' | 'shl' | 'shr' | 'in' | 'notin' |
- #| 'is' | 'isnot' | 'not' | 'of' | 'as' | 'from' | '..' | 'and' | 'or' | 'xor'
- tok.tokType in {tkOpr, tkDiv, tkMod, tkShl, tkShr, tkIn, tkNotin, tkIs,
- tkIsnot, tkNot, tkOf, tkAs, tkFrom, tkDotDot, tkAnd,
- tkOr, tkXor}
- proc colcom(p: var Parser, n: PNode) =
- eat(p, tkColon)
- skipComment(p, n)
- const tkBuiltInMagics = {tkType, tkStatic, tkAddr}
- proc parseSymbol(p: var Parser, mode = smNormal): PNode =
- #| symbol = '`' (KEYW|IDENT|literal|(operator|'('|')'|'['|']'|'{'|'}'|'=')+)+ '`'
- #| | IDENT | KEYW
- case p.tok.tokType
- of tkSymbol:
- result = newIdentNodeP(p.tok.ident, p)
- getTok(p)
- of tokKeywordLow..tokKeywordHigh:
- if p.tok.tokType in tkBuiltInMagics or mode == smAfterDot:
- # for backwards compatibility these 2 are always valid:
- result = newIdentNodeP(p.tok.ident, p)
- getTok(p)
- elif p.tok.tokType == tkNil and mode == smAllowNil:
- result = newNodeP(nkNilLit, p)
- getTok(p)
- else:
- parMessage(p, errIdentifierExpected, p.tok)
- result = p.emptyNode
- of tkAccent:
- result = newNodeP(nkAccQuoted, p)
- getTok(p)
- # progress guaranteed
- while true:
- case p.tok.tokType
- of tkAccent:
- if result.len == 0:
- parMessage(p, errIdentifierExpected, p.tok)
- break
- of tkOpr, tkDot, tkDotDot, tkEquals, tkParLe..tkParDotRi:
- let lineinfo = parLineInfo(p)
- var accm = ""
- while p.tok.tokType in {tkOpr, tkDot, tkDotDot, tkEquals,
- tkParLe..tkParDotRi}:
- accm.add($p.tok)
- getTok(p)
- let node = newNodeI(nkIdent, lineinfo)
- node.ident = p.lex.cache.getIdent(accm)
- result.add(node)
- of tokKeywordLow..tokKeywordHigh, tkSymbol, tkIntLit..tkCustomLit:
- result.add(newIdentNodeP(p.lex.cache.getIdent($p.tok), p))
- getTok(p)
- else:
- parMessage(p, errIdentifierExpected, p.tok)
- break
- eat(p, tkAccent)
- else:
- parMessage(p, errIdentifierExpected, p.tok)
- # BUGFIX: We must consume a token here to prevent endless loops!
- # But: this really sucks for idetools and keywords, so we don't do it
- # if it is a keyword:
- #if not isKeyword(p.tok.tokType): getTok(p)
- result = p.emptyNode
- proc equals(p: var Parser, a: PNode): PNode =
- if p.tok.tokType == tkEquals:
- result = newNodeP(nkExprEqExpr, p)
- getTok(p)
- #optInd(p, result)
- result.add(a)
- result.add(parseExpr(p))
- else:
- result = a
- proc colonOrEquals(p: var Parser, a: PNode): PNode =
- if p.tok.tokType == tkColon:
- result = newNodeP(nkExprColonExpr, p)
- getTok(p)
- newlineWasSplitting(p)
- #optInd(p, result)
- result.add(a)
- result.add(parseExpr(p))
- else:
- result = equals(p, a)
- proc exprColonEqExpr(p: var Parser): PNode =
- #| exprColonEqExpr = expr (':'|'=' expr)?
- var a = parseExpr(p)
- if p.tok.tokType == tkDo:
- result = postExprBlocks(p, a)
- else:
- result = colonOrEquals(p, a)
- proc exprEqExpr(p: var Parser): PNode =
- #| exprEqExpr = expr ('=' expr)?
- var a = parseExpr(p)
- if p.tok.tokType == tkDo:
- result = postExprBlocks(p, a)
- else:
- result = equals(p, a)
- proc exprList(p: var Parser, endTok: TokType, result: PNode) =
- #| exprList = expr ^+ comma
- when defined(nimpretty):
- inc p.em.doIndentMore
- getTok(p)
- optInd(p, result)
- # progress guaranteed
- var a = parseExpr(p)
- result.add(a)
- while (p.tok.tokType != endTok) and (p.tok.tokType != tkEof):
- if p.tok.tokType != tkComma: break
- getTok(p)
- optInd(p, a)
- var a = parseExpr(p)
- result.add(a)
- when defined(nimpretty):
- dec p.em.doIndentMore
- proc optionalExprList(p: var Parser, endTok: TokType, result: PNode) =
- #| optionalExprList = expr ^* comma
- when defined(nimpretty):
- inc p.em.doIndentMore
- getTok(p)
- optInd(p, result)
- # progress guaranteed
- while (p.tok.tokType != endTok) and (p.tok.tokType != tkEof):
- var a = parseExpr(p)
- result.add(a)
- if p.tok.tokType != tkComma: break
- getTok(p)
- optInd(p, a)
- when defined(nimpretty):
- dec p.em.doIndentMore
- proc exprColonEqExprListAux(p: var Parser, endTok: TokType, result: PNode) =
- assert(endTok in {tkCurlyRi, tkCurlyDotRi, tkBracketRi, tkParRi})
- getTok(p)
- flexComment(p, result)
- optPar(p)
- # progress guaranteed
- while p.tok.tokType != endTok and p.tok.tokType != tkEof:
- var a = exprColonEqExpr(p)
- result.add(a)
- if p.tok.tokType != tkComma: break
- elif result.kind == nkPar:
- result.transitionSonsKind(nkTupleConstr)
- getTok(p)
- skipComment(p, a)
- optPar(p)
- eat(p, endTok)
- proc exprColonEqExprList(p: var Parser, kind: TNodeKind,
- endTok: TokType): PNode =
- #| exprColonEqExprList = exprColonEqExpr (comma exprColonEqExpr)* (comma)?
- result = newNodeP(kind, p)
- exprColonEqExprListAux(p, endTok, result)
- proc dotExpr(p: var Parser, a: PNode): PNode =
- var info = p.parLineInfo
- getTok(p)
- result = newNodeI(nkDotExpr, info)
- optInd(p, result)
- result.add(a)
- result.add(parseSymbol(p, smAfterDot))
- if p.tok.tokType == tkBracketLeColon and not p.tok.strongSpaceA:
- var x = newNodeI(nkBracketExpr, p.parLineInfo)
- # rewrite 'x.y[:z]()' to 'y[z](x)'
- x.add result[1]
- exprList(p, tkBracketRi, x)
- eat(p, tkBracketRi)
- var y = newNodeI(nkCall, p.parLineInfo)
- y.add x
- y.add result[0]
- if p.tok.tokType == tkParLe and not p.tok.strongSpaceA:
- exprColonEqExprListAux(p, tkParRi, y)
- result = y
- proc dotLikeExpr(p: var Parser, a: PNode): PNode =
- var info = p.parLineInfo
- result = newNodeI(nkInfix, info)
- optInd(p, result)
- var opNode = newIdentNodeP(p.tok.ident, p)
- getTok(p)
- result.add(opNode)
- result.add(a)
- result.add(parseSymbol(p, smAfterDot))
- proc qualifiedIdent(p: var Parser): PNode =
- #| qualifiedIdent = symbol ('.' optInd symbol)?
- result = parseSymbol(p)
- if p.tok.tokType == tkDot: result = dotExpr(p, result)
- proc setOrTableConstr(p: var Parser): PNode =
- #| setOrTableConstr = '{' ((exprColonEqExpr comma)* | ':' ) '}'
- result = newNodeP(nkCurly, p)
- getTok(p) # skip '{'
- optInd(p, result)
- if p.tok.tokType == tkColon:
- getTok(p) # skip ':'
- result.transitionSonsKind(nkTableConstr)
- else:
- # progress guaranteed
- while p.tok.tokType notin {tkCurlyRi, tkEof}:
- var a = exprColonEqExpr(p)
- if a.kind == nkExprColonExpr: result.transitionSonsKind(nkTableConstr)
- result.add(a)
- if p.tok.tokType != tkComma: break
- getTok(p)
- skipComment(p, a)
- optPar(p)
- eat(p, tkCurlyRi) # skip '}'
- proc parseCast(p: var Parser): PNode =
- #| castExpr = 'cast' ('[' optInd typeDesc optPar ']' '(' optInd expr optPar ')') /
- # ('(' optInd exprColonEqExpr optPar ')')
- result = newNodeP(nkCast, p)
- getTok(p)
- if p.tok.tokType == tkBracketLe:
- getTok(p)
- optInd(p, result)
- result.add(parseTypeDesc(p))
- optPar(p)
- eat(p, tkBracketRi)
- eat(p, tkParLe)
- optInd(p, result)
- result.add(parseExpr(p))
- else:
- result.add p.emptyNode
- eat(p, tkParLe)
- optInd(p, result)
- result.add(exprColonEqExpr(p))
- optPar(p)
- eat(p, tkParRi)
- proc setBaseFlags(n: PNode, base: NumericalBase) =
- case base
- of base10: discard
- of base2: incl(n.flags, nfBase2)
- of base8: incl(n.flags, nfBase8)
- of base16: incl(n.flags, nfBase16)
- proc parseGStrLit(p: var Parser, a: PNode): PNode =
- case p.tok.tokType
- of tkGStrLit:
- result = newNodeP(nkCallStrLit, p)
- result.add(a)
- result.add(newStrNodeP(nkRStrLit, p.tok.literal, p))
- getTok(p)
- of tkGTripleStrLit:
- result = newNodeP(nkCallStrLit, p)
- result.add(a)
- result.add(newStrNodeP(nkTripleStrLit, p.tok.literal, p))
- getTok(p)
- else:
- result = a
- proc complexOrSimpleStmt(p: var Parser): PNode
- proc simpleExpr(p: var Parser, mode = pmNormal): PNode
- proc parseIfOrWhenExpr(p: var Parser, kind: TNodeKind): PNode
- proc semiStmtList(p: var Parser, result: PNode) =
- inc p.inSemiStmtList
- withInd(p):
- # Be lenient with the first stmt/expr
- let a = case p.tok.tokType
- of tkIf: parseIfOrWhenExpr(p, nkIfStmt)
- of tkWhen: parseIfOrWhenExpr(p, nkWhenStmt)
- else: complexOrSimpleStmt(p)
- result.add a
- while p.tok.tokType != tkEof:
- if p.tok.tokType == tkSemiColon:
- getTok(p)
- if p.tok.tokType == tkParRi:
- break
- elif not (sameInd(p) or realInd(p)):
- parMessage(p, errInvalidIndentation)
- let a = complexOrSimpleStmt(p)
- if a.kind == nkEmpty:
- parMessage(p, errExprExpected, p.tok)
- getTok(p)
- else:
- result.add a
- dec p.inSemiStmtList
- result.transitionSonsKind(nkStmtListExpr)
- proc parsePar(p: var Parser): PNode =
- #| parKeyw = 'discard' | 'include' | 'if' | 'while' | 'case' | 'try'
- #| | 'finally' | 'except' | 'for' | 'block' | 'const' | 'let'
- #| | 'when' | 'var' | 'mixin'
- #| par = '(' optInd
- #| ( &parKeyw (ifExpr / complexOrSimpleStmt) ^+ ';'
- #| | ';' (ifExpr / complexOrSimpleStmt) ^+ ';'
- #| | pragmaStmt
- #| | simpleExpr ( ('=' expr (';' (ifExpr / complexOrSimpleStmt) ^+ ';' )? )
- #| | (':' expr (',' exprColonEqExpr ^+ ',' )? ) ) )
- #| optPar ')'
- #
- # unfortunately it's ambiguous: (expr: expr) vs (exprStmt); however a
- # leading ';' could be used to enforce a 'stmt' context ...
- result = newNodeP(nkPar, p)
- getTok(p)
- optInd(p, result)
- flexComment(p, result)
- if p.tok.tokType in {tkDiscard, tkInclude, tkIf, tkWhile, tkCase,
- tkTry, tkDefer, tkFinally, tkExcept, tkBlock,
- tkConst, tkLet, tkWhen, tkVar, tkFor,
- tkMixin}:
- # XXX 'bind' used to be an expression, so we exclude it here;
- # tests/reject/tbind2 fails otherwise.
- semiStmtList(p, result)
- elif p.tok.tokType == tkSemiColon:
- # '(;' enforces 'stmt' context:
- getTok(p)
- optInd(p, result)
- semiStmtList(p, result)
- elif p.tok.tokType == tkCurlyDotLe:
- result.add(parseStmtPragma(p))
- elif p.tok.tokType == tkParRi:
- # Empty tuple '()'
- result.transitionSonsKind(nkTupleConstr)
- else:
- var a = simpleExpr(p)
- if p.tok.tokType == tkDo:
- result = postExprBlocks(p, a)
- elif p.tok.tokType == tkEquals:
- # special case: allow assignments
- let asgn = newNodeP(nkAsgn, p)
- getTok(p)
- optInd(p, result)
- let b = parseExpr(p)
- asgn.add a
- asgn.add b
- result.add(asgn)
- if p.tok.tokType == tkSemiColon:
- semiStmtList(p, result)
- elif p.tok.tokType == tkSemiColon:
- # stmt context:
- result.add(a)
- semiStmtList(p, result)
- else:
- a = colonOrEquals(p, a)
- if a.kind == nkExprColonExpr:
- result.transitionSonsKind(nkTupleConstr)
- result.add(a)
- if p.tok.tokType == tkComma:
- getTok(p)
- skipComment(p, a)
- # (1,) produces a tuple expression:
- result.transitionSonsKind(nkTupleConstr)
- # progress guaranteed
- while p.tok.tokType != tkParRi and p.tok.tokType != tkEof:
- var a = exprColonEqExpr(p)
- result.add(a)
- if p.tok.tokType != tkComma: break
- getTok(p)
- skipComment(p, a)
- optPar(p)
- eat(p, tkParRi)
- proc identOrLiteral(p: var Parser, mode: PrimaryMode): PNode =
- #| literal = | INT_LIT | INT8_LIT | INT16_LIT | INT32_LIT | INT64_LIT
- #| | UINT_LIT | UINT8_LIT | UINT16_LIT | UINT32_LIT | UINT64_LIT
- #| | FLOAT_LIT | FLOAT32_LIT | FLOAT64_LIT
- #| | STR_LIT | RSTR_LIT | TRIPLESTR_LIT
- #| | CHAR_LIT | CUSTOM_NUMERIC_LIT
- #| | NIL
- #| generalizedLit = GENERALIZED_STR_LIT | GENERALIZED_TRIPLESTR_LIT
- #| identOrLiteral = generalizedLit | symbol | literal
- #| | par | arrayConstr | setOrTableConstr | tupleConstr
- #| | castExpr
- #| tupleConstr = '(' optInd (exprColonEqExpr comma?)* optPar ')'
- #| arrayConstr = '[' optInd (exprColonEqExpr comma?)* optPar ']'
- case p.tok.tokType
- of tkSymbol, tkBuiltInMagics, tkOut:
- 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 tkCustomLit:
- let splitPos = p.tok.iNumber.int
- let str = newStrNodeP(nkRStrLit, p.tok.literal.substr(0, splitPos-1), p)
- let callee = newIdentNodeP(getIdent(p.lex.cache, p.tok.literal.substr(splitPos)), p)
- result = newNodeP(nkDotExpr, p)
- result.add str
- result.add callee
- getTok(p)
- of tkNil:
- result = newNodeP(nkNilLit, p)
- getTok(p)
- of tkParLe:
- # () constructor
- if mode in {pmTypeDesc, pmTypeDef}:
- result = exprColonEqExprList(p, nkPar, tkParRi)
- else:
- result = parsePar(p)
- of tkCurlyLe:
- # {} constructor
- result = setOrTableConstr(p)
- of tkBracketLe:
- # [] constructor
- result = exprColonEqExprList(p, nkBracket, tkBracketRi)
- of tkCast:
- result = parseCast(p)
- else:
- parMessage(p, errExprExpected, p.tok)
- getTok(p) # we must consume a token here to prevent endless loops!
- result = p.emptyNode
- proc namedParams(p: var Parser, callee: PNode,
- kind: TNodeKind, endTok: TokType): PNode =
- let a = callee
- result = newNodeP(kind, p)
- result.add(a)
- # progress guaranteed
- exprColonEqExprListAux(p, endTok, result)
- proc commandParam(p: var Parser, isFirstParam: var bool; mode: PrimaryMode): PNode =
- if mode == pmTypeDesc:
- result = simpleExpr(p, mode)
- elif not isFirstParam:
- result = exprEqExpr(p)
- else:
- result = parseExpr(p)
- if p.tok.tokType == tkDo:
- result = postExprBlocks(p, result)
- isFirstParam = false
- proc commandExpr(p: var Parser; r: PNode; mode: PrimaryMode): PNode =
- if mode == pmTrySimple:
- result = r
- else:
- result = newNodeP(nkCommand, p)
- result.add(r)
- var isFirstParam = true
- # progress NOT guaranteed
- p.hasProgress = false
- result.add commandParam(p, isFirstParam, mode)
- proc isDotLike(tok: Token): bool =
- result = tok.tokType == tkOpr and tok.ident.s.len > 1 and
- tok.ident.s[0] == '.' and tok.ident.s[1] != '.'
- proc primarySuffix(p: var Parser, r: PNode,
- baseIndent: int, mode: PrimaryMode): PNode =
- #| primarySuffix = '(' (exprColonEqExpr comma?)* ')'
- #| | '.' optInd symbol ('[:' exprList ']' ( '(' exprColonEqExpr ')' )?)? generalizedLit?
- #| | DOTLIKEOP optInd symbol generalizedLit?
- #| | '[' optInd exprColonEqExprList optPar ']'
- #| | '{' optInd exprColonEqExprList optPar '}'
- # XXX strong spaces need to be reflected above
- result = r
- # progress guaranteed
- while p.tok.indent < 0 or
- (p.tok.tokType == tkDot and p.tok.indent >= baseIndent):
- case p.tok.tokType
- of tkParLe:
- # progress guaranteed
- if p.tok.strongSpaceA:
- result = commandExpr(p, result, mode)
- break
- result = namedParams(p, result, nkCall, tkParRi)
- if result.len > 1 and result[1].kind == nkExprColonExpr:
- result.transitionSonsKind(nkObjConstr)
- of tkDot:
- # progress guaranteed
- result = dotExpr(p, result)
- result = parseGStrLit(p, result)
- of tkBracketLe:
- # progress guaranteed
- if p.tok.strongSpaceA:
- result = commandExpr(p, result, mode)
- break
- result = namedParams(p, result, nkBracketExpr, tkBracketRi)
- of tkCurlyLe:
- # progress guaranteed
- if p.tok.strongSpaceA:
- result = commandExpr(p, result, mode)
- break
- result = namedParams(p, result, nkCurlyExpr, tkCurlyRi)
- of tkSymbol, tkAccent, tkIntLit..tkCustomLit, tkNil, tkCast,
- tkOpr, tkDotDot, tkVar, tkOut, tkStatic, tkType, tkEnum, tkTuple,
- tkObject, tkProc:
- # XXX: In type sections we allow the free application of the
- # command syntax, with the exception of expressions such as
- # `foo ref` or `foo ptr`. Unfortunately, these two are also
- # used as infix operators for the memory regions feature and
- # the current parsing rules don't play well here.
- let isDotLike2 = p.tok.isDotLike
- if isDotLike2 and p.lex.config.isDefined("nimPreviewDotLikeOps"):
- # synchronize with `tkDot` branch
- result = dotLikeExpr(p, result)
- result = parseGStrLit(p, result)
- else:
- if isDotLike2:
- parMessage(p, warnDotLikeOps, "dot-like operators will be parsed differently with `-d:nimPreviewDotLikeOps`")
- if p.inPragma == 0 and (isUnary(p.tok) or p.tok.tokType notin {tkOpr, tkDotDot}):
- # actually parsing {.push hints:off.} as {.push(hints:off).} is a sweet
- # solution, but pragmas.nim can't handle that
- result = commandExpr(p, result, mode)
- break
- else:
- break
- proc parseOperators(p: var Parser, headNode: PNode,
- limit: int, mode: PrimaryMode): PNode =
- result = headNode
- # expand while operators have priorities higher than 'limit'
- var opPrec = getPrecedence(p.tok)
- let modeB = if mode == pmTypeDef: pmTypeDesc else: mode
- # the operator itself must not start on a new line:
- # progress guaranteed
- while opPrec >= limit and p.tok.indent < 0 and not isUnary(p.tok):
- checkBinary(p)
- let leftAssoc = ord(not isRightAssociative(p.tok))
- var a = newNodeP(nkInfix, p)
- var opNode = newIdentNodeP(p.tok.ident, p) # skip operator:
- getTok(p)
- flexComment(p, a)
- optPar(p)
- # read sub-expression with higher priority:
- var b = simpleExprAux(p, opPrec + leftAssoc, modeB)
- a.add(opNode)
- a.add(result)
- a.add(b)
- result = a
- opPrec = getPrecedence(p.tok)
- proc simpleExprAux(p: var Parser, limit: int, mode: PrimaryMode): PNode =
- var mode = mode
- result = primary(p, mode)
- if mode == pmTrySimple:
- mode = pmNormal
- if p.tok.tokType == tkCurlyDotLe and (p.tok.indent < 0 or realInd(p)) and
- mode == pmNormal:
- var pragmaExp = newNodeP(nkPragmaExpr, p)
- pragmaExp.add result
- pragmaExp.add p.parsePragma
- result = pragmaExp
- result = parseOperators(p, result, limit, mode)
- proc simpleExpr(p: var Parser, mode = pmNormal): PNode =
- when defined(nimpretty):
- inc p.em.doIndentMore
- result = simpleExprAux(p, -1, mode)
- when defined(nimpretty):
- dec p.em.doIndentMore
- proc parsePragma(p: var Parser): PNode =
- #| pragma = '{.' optInd (exprColonEqExpr comma?)* optPar ('.}' | '}')
- result = newNodeP(nkPragma, p)
- inc p.inPragma
- when defined(nimpretty):
- inc p.em.doIndentMore
- inc p.em.keepIndents
- getTok(p)
- optInd(p, result)
- while p.tok.tokType notin {tkCurlyDotRi, tkCurlyRi, tkEof}:
- p.hasProgress = false
- var a = exprColonEqExpr(p)
- if not p.hasProgress: break
- result.add(a)
- if p.tok.tokType == tkComma:
- getTok(p)
- skipComment(p, a)
- optPar(p)
- if p.tok.tokType in {tkCurlyDotRi, tkCurlyRi}:
- when defined(nimpretty):
- if p.tok.tokType == tkCurlyRi: curlyRiWasPragma(p.em)
- getTok(p)
- else:
- parMessage(p, "expected '.}'")
- dec p.inPragma
- when defined(nimpretty):
- dec p.em.doIndentMore
- dec p.em.keepIndents
- proc identVis(p: var Parser; allowDot=false): PNode =
- #| identVis = symbol OPR? # postfix position
- #| identVisDot = symbol '.' optInd symbol OPR?
- var a = parseSymbol(p)
- if p.tok.tokType == tkOpr:
- when defined(nimpretty):
- starWasExportMarker(p.em)
- result = newNodeP(nkPostfix, p)
- result.add(newIdentNodeP(p.tok.ident, p))
- result.add(a)
- getTok(p)
- elif p.tok.tokType == tkDot and allowDot:
- result = dotExpr(p, a)
- else:
- result = a
- proc identWithPragma(p: var Parser; allowDot=false): PNode =
- #| identWithPragma = identVis pragma?
- #| identWithPragmaDot = identVisDot pragma?
- var a = identVis(p, allowDot)
- if p.tok.tokType == tkCurlyDotLe:
- result = newNodeP(nkPragmaExpr, p)
- result.add(a)
- result.add(parsePragma(p))
- else:
- result = a
- type
- DeclaredIdentFlag = enum
- withPragma, # identifier may have pragma
- withBothOptional # both ':' and '=' parts are optional
- withDot # allow 'var ident.ident = value'
- DeclaredIdentFlags = set[DeclaredIdentFlag]
- proc parseIdentColonEquals(p: var Parser, flags: DeclaredIdentFlags): PNode =
- #| declColonEquals = identWithPragma (comma identWithPragma)* comma?
- #| (':' optInd typeDescExpr)? ('=' optInd expr)?
- #| identColonEquals = IDENT (comma IDENT)* comma?
- #| (':' optInd typeDescExpr)? ('=' optInd expr)?)
- var a: PNode
- result = newNodeP(nkIdentDefs, p)
- # progress guaranteed
- while true:
- case p.tok.tokType
- of tkSymbol, tkAccent:
- if withPragma in flags: a = identWithPragma(p, allowDot=withDot in flags)
- else: a = parseSymbol(p)
- if a.kind == nkEmpty: return
- else: break
- result.add(a)
- if p.tok.tokType != tkComma: break
- getTok(p)
- optInd(p, a)
- if p.tok.tokType == tkColon:
- getTok(p)
- optInd(p, result)
- result.add(parseTypeDesc(p, fullExpr = true))
- else:
- result.add(newNodeP(nkEmpty, p))
- if p.tok.tokType != tkEquals and withBothOptional notin flags:
- parMessage(p, "':' or '=' expected, but got '$1'", p.tok)
- if p.tok.tokType == tkEquals:
- getTok(p)
- optInd(p, result)
- result.add(parseExpr(p))
- else:
- result.add(newNodeP(nkEmpty, p))
- proc parseTuple(p: var Parser, indentAllowed = false): PNode =
- #| tupleTypeBracket = '[' optInd (identColonEquals (comma/semicolon)?)* optPar ']'
- #| tupleType = 'tuple' tupleTypeBracket
- #| tupleDecl = 'tuple' (tupleTypeBracket /
- #| COMMENT? (IND{>} identColonEquals (IND{=} identColonEquals)*)?)
- result = newNodeP(nkTupleTy, p)
- getTok(p)
- if p.tok.tokType == tkBracketLe:
- getTok(p)
- optInd(p, result)
- # progress guaranteed
- while p.tok.tokType in {tkSymbol, tkAccent}:
- var a = parseIdentColonEquals(p, {})
- result.add(a)
- if p.tok.tokType notin {tkComma, tkSemiColon}: break
- when defined(nimpretty):
- commaWasSemicolon(p.em)
- getTok(p)
- skipComment(p, a)
- optPar(p)
- eat(p, tkBracketRi)
- elif indentAllowed:
- skipComment(p, result)
- if realInd(p):
- withInd(p):
- rawSkipComment(p, result)
- # progress guaranteed
- while true:
- case p.tok.tokType
- of tkSymbol, tkAccent:
- var a = parseIdentColonEquals(p, {})
- if p.tok.indent < 0 or p.tok.indent >= p.currInd:
- rawSkipComment(p, a)
- result.add(a)
- of tkEof: break
- else:
- parMessage(p, errIdentifierExpected, p.tok)
- break
- if not sameInd(p): break
- elif p.tok.tokType == tkParLe:
- parMessage(p, errGenerated, "the syntax for tuple types is 'tuple[...]', not 'tuple(...)'")
- else:
- result = newNodeP(nkTupleClassTy, p)
- proc parseParamList(p: var Parser, retColon = true): PNode =
- #| paramList = '(' declColonEquals ^* (comma/semicolon) ')'
- #| paramListArrow = paramList? ('->' optInd typeDesc)?
- #| paramListColon = paramList? (':' optInd typeDesc)?
- var a: PNode
- result = newNodeP(nkFormalParams, p)
- result.add(p.emptyNode) # return type
- when defined(nimpretty):
- inc p.em.doIndentMore
- inc p.em.keepIndents
- let hasParLe = p.tok.tokType == tkParLe and p.tok.indent < 0
- if hasParLe:
- getTok(p)
- optInd(p, result)
- # progress guaranteed
- while true:
- case p.tok.tokType
- of tkSymbol, tkAccent:
- a = parseIdentColonEquals(p, {withBothOptional, withPragma})
- of tkParRi:
- break
- of tkVar:
- parMessage(p, errGenerated, "the syntax is 'parameter: var T', not 'var parameter: T'")
- break
- else:
- parMessage(p, "expected closing ')'")
- break
- result.add(a)
- if p.tok.tokType notin {tkComma, tkSemiColon}: break
- when defined(nimpretty):
- commaWasSemicolon(p.em)
- getTok(p)
- skipComment(p, a)
- optPar(p)
- eat(p, tkParRi)
- let hasRet = if retColon: p.tok.tokType == tkColon
- else: p.tok.tokType == tkOpr and p.tok.ident.s == "->"
- if hasRet and p.tok.indent < 0:
- getTok(p)
- optInd(p, result)
- result[0] = parseTypeDesc(p)
- elif not retColon and not hasParLe:
- # Mark as "not there" in order to mark for deprecation in the semantic pass:
- result = p.emptyNode
- when defined(nimpretty):
- dec p.em.doIndentMore
- dec p.em.keepIndents
- proc optPragmas(p: var Parser): PNode =
- if p.tok.tokType == tkCurlyDotLe and (p.tok.indent < 0 or realInd(p)):
- result = parsePragma(p)
- else:
- result = p.emptyNode
- proc parseDoBlock(p: var Parser; info: TLineInfo): PNode =
- #| doBlock = 'do' paramListArrow pragma? colcom stmt
- var params = parseParamList(p, retColon=false)
- let pragmas = optPragmas(p)
- colcom(p, result)
- result = parseStmt(p)
- if params.kind != nkEmpty or pragmas.kind != nkEmpty:
- if params.kind == nkEmpty:
- params = newNodeP(nkFormalParams, p)
- params.add(p.emptyNode) # return type
- result = newProcNode(nkDo, info,
- body = result, params = params, name = p.emptyNode, pattern = p.emptyNode,
- genericParams = p.emptyNode, pragmas = pragmas, exceptions = p.emptyNode)
- proc parseProcExpr(p: var Parser; isExpr: bool; kind: TNodeKind): PNode =
- #| routineExpr = ('proc' | 'func' | 'iterator') paramListColon pragma? ('=' COMMENT? stmt)?
- #| routineType = ('proc' | 'iterator') paramListColon pragma?
- # either a proc type or a anonymous proc
- let info = parLineInfo(p)
- let hasSignature = p.tok.tokType in {tkParLe, tkColon} and p.tok.indent < 0
- let params = parseParamList(p)
- let pragmas = optPragmas(p)
- if p.tok.tokType == tkEquals and isExpr:
- getTok(p)
- skipComment(p, result)
- result = newProcNode(kind, info, body = parseStmt(p),
- params = params, name = p.emptyNode, pattern = p.emptyNode,
- genericParams = p.emptyNode, pragmas = pragmas, exceptions = p.emptyNode)
- else:
- result = newNodeI(if kind == nkIteratorDef: nkIteratorTy else: nkProcTy, info)
- if hasSignature:
- result.add(params)
- if kind == nkFuncDef:
- parMessage(p, "func keyword is not allowed in type descriptions, use proc with {.noSideEffect.} pragma instead")
- result.add(pragmas)
- proc isExprStart(p: Parser): bool =
- case p.tok.tokType
- of tkSymbol, tkAccent, tkOpr, tkNot, tkNil, tkCast, tkIf, tkFor,
- tkProc, tkFunc, tkIterator, tkBind, tkBuiltInMagics,
- tkParLe, tkBracketLe, tkCurlyLe, tkIntLit..tkCustomLit, tkVar, tkRef, tkPtr,
- tkEnum, tkTuple, tkObject, tkWhen, tkCase, tkOut, tkTry, tkBlock:
- result = true
- else: result = false
- proc parseSymbolList(p: var Parser, result: PNode) =
- # progress guaranteed
- while true:
- var s = parseSymbol(p, smAllowNil)
- if s.kind == nkEmpty: break
- result.add(s)
- if p.tok.tokType != tkComma: break
- getTok(p)
- optInd(p, s)
- proc parseTypeDescKAux(p: var Parser, kind: TNodeKind,
- mode: PrimaryMode): PNode =
- result = newNodeP(kind, p)
- getTok(p)
- if p.tok.indent != -1 and p.tok.indent <= p.currInd: return
- optInd(p, result)
- let isTypedef = mode == pmTypeDef and p.tok.tokType in {tkObject, tkTuple}
- if not isOperator(p.tok) and isExprStart(p):
- if isTypedef:
- result.add(parseTypeDefValue(p))
- else:
- result.add(primary(p, mode))
- if kind == nkDistinctTy and p.tok.tokType == tkSymbol:
- # XXX document this feature!
- var nodeKind: TNodeKind
- if p.tok.ident.s == "with":
- nodeKind = nkWith
- elif p.tok.ident.s == "without":
- nodeKind = nkWithout
- else:
- return result
- getTok(p)
- let list = newNodeP(nodeKind, p)
- result.add list
- parseSymbolList(p, list)
- if mode == pmTypeDef and not isTypedef:
- result = parseOperators(p, result, -1, mode)
- proc parseVarTuple(p: var Parser): PNode
- proc parseFor(p: var Parser): PNode =
- #| forStmt = 'for' ((varTuple / identWithPragma) ^+ comma) 'in' expr colcom stmt
- #| forExpr = forStmt
- getTokNoInd(p)
- result = newNodeP(nkForStmt, p)
- if p.tok.tokType == tkParLe:
- result.add(parseVarTuple(p))
- else:
- var a = identWithPragma(p)
- result.add(a)
- while p.tok.tokType == tkComma:
- getTok(p)
- optInd(p, a)
- if p.tok.tokType == tkParLe:
- result.add(parseVarTuple(p))
- break
- a = identWithPragma(p)
- result.add(a)
- eat(p, tkIn)
- result.add(parseExpr(p))
- colcom(p, result)
- result.add(parseStmt(p))
- template nimprettyDontTouch(body) =
- when defined(nimpretty):
- inc p.em.keepIndents
- body
- when defined(nimpretty):
- dec p.em.keepIndents
- proc parseExpr(p: var Parser): PNode =
- #| expr = (blockExpr
- #| | ifExpr
- #| | whenExpr
- #| | caseStmt
- #| | forExpr
- #| | tryExpr)
- #| / simpleExpr
- case p.tok.tokType
- of tkBlock:
- nimprettyDontTouch:
- result = parseBlock(p)
- of tkIf:
- nimprettyDontTouch:
- result = parseIfOrWhenExpr(p, nkIfExpr)
- of tkFor:
- nimprettyDontTouch:
- result = parseFor(p)
- of tkWhen:
- nimprettyDontTouch:
- result = parseIfOrWhenExpr(p, nkWhenExpr)
- of tkCase:
- # Currently we think nimpretty is good enough with case expressions,
- # so it is allowed to touch them:
- #nimprettyDontTouch:
- result = parseCase(p)
- of tkTry:
- nimprettyDontTouch:
- result = parseTry(p, isExpr=true)
- else: result = simpleExpr(p)
- proc parseEnum(p: var Parser): PNode
- proc parseObject(p: var Parser): PNode
- proc parseTypeClass(p: var Parser): PNode
- proc primary(p: var Parser, mode: PrimaryMode): PNode =
- #| simplePrimary = SIGILLIKEOP? identOrLiteral primarySuffix*
- #| commandStart = &('`'|IDENT|literal|'cast'|'addr'|'type'|'var'|'out'|
- #| 'static'|'enum'|'tuple'|'object'|'proc')
- #| primary = simplePrimary (commandStart expr)
- #| / operatorB primary
- #| / routineExpr
- #| / rawTypeDesc
- #| / prefixOperator primary
- # XXX strong spaces need to be reflected in commandStart
- # command part is handled in the primarySuffix proc
- # prefix operators:
- if isOperator(p.tok):
- # Note 'sigil like' operators are currently not reflected in the grammar
- # and should be removed for Nim 2.0, I don't think anybody uses them.
- let isSigil = isSigilLike(p.tok)
- result = newNodeP(nkPrefix, p)
- var a = newIdentNodeP(p.tok.ident, p)
- result.add(a)
- getTok(p)
- optInd(p, a)
- const identOrLiteralKinds = tkBuiltInMagics + {tkSymbol, tkAccent, tkNil,
- tkIntLit..tkCustomLit, tkCast, tkOut, tkParLe, tkBracketLe, tkCurlyLe}
- if isSigil and p.tok.tokType in identOrLiteralKinds:
- let baseInd = p.lex.currLineIndent
- result.add(identOrLiteral(p, mode))
- result = primarySuffix(p, result, baseInd, mode)
- else:
- result.add(primary(p, pmNormal))
- return
- case p.tok.tokType
- of tkProc:
- getTok(p)
- result = parseProcExpr(p, mode != pmTypeDesc, nkLambda)
- of tkFunc:
- getTok(p)
- result = parseProcExpr(p, mode != pmTypeDesc, nkFuncDef)
- of tkIterator:
- getTok(p)
- result = parseProcExpr(p, mode != pmTypeDesc, nkIteratorDef)
- of tkBind:
- # legacy syntax, no-op in current nim
- result = newNodeP(nkBind, p)
- getTok(p)
- optInd(p, result)
- result.add(primary(p, pmNormal))
- of tkTuple, tkEnum, tkObject, tkConcept,
- tkVar, tkOut, tkRef, tkPtr, tkDistinct:
- result = parseTypeDesc(p)
- else:
- let baseInd = p.lex.currLineIndent
- result = identOrLiteral(p, mode)
- result = primarySuffix(p, result, baseInd, mode)
- proc binaryNot(p: var Parser; a: PNode): PNode =
- if p.tok.tokType == tkNot:
- let notOpr = newIdentNodeP(p.tok.ident, p)
- getTok(p)
- optInd(p, notOpr)
- let b = parseExpr(p)
- result = newNodeP(nkInfix, p)
- result.add notOpr
- result.add a
- result.add b
- else:
- result = a
- proc parseTypeDesc(p: var Parser, fullExpr = false): PNode =
- #| rawTypeDesc = (tupleType | routineType | 'enum' | 'object' |
- #| ('var' | 'out' | 'ref' | 'ptr' | 'distinct') typeDesc?)
- #| ('not' expr)?
- #| typeDescExpr = (routineType / simpleExpr) ('not' expr)?
- #| typeDesc = rawTypeDesc / typeDescExpr
- newlineWasSplitting(p)
- if fullExpr:
- result = simpleExpr(p, pmTypeDesc)
- else:
- case p.tok.tokType
- of tkTuple:
- result = parseTuple(p, false)
- of tkProc:
- getTok(p)
- result = parseProcExpr(p, false, nkLambda)
- of tkIterator:
- getTok(p)
- result = parseProcExpr(p, false, nkIteratorDef)
- of tkEnum:
- result = newNodeP(nkEnumTy, p)
- getTok(p)
- of tkObject:
- result = newNodeP(nkObjectTy, p)
- getTok(p)
- of tkConcept:
- parMessage(p, "the 'concept' keyword is only valid in 'type' sections")
- of tkVar: result = parseTypeDescKAux(p, nkVarTy, pmTypeDesc)
- of tkOut: result = parseTypeDescKAux(p, nkOutTy, pmTypeDesc)
- of tkRef: result = parseTypeDescKAux(p, nkRefTy, pmTypeDesc)
- of tkPtr: result = parseTypeDescKAux(p, nkPtrTy, pmTypeDesc)
- of tkDistinct: result = parseTypeDescKAux(p, nkDistinctTy, pmTypeDesc)
- else:
- result = simpleExpr(p, pmTypeDesc)
- result = binaryNot(p, result)
- proc parseTypeDefValue(p: var Parser): PNode =
- #| typeDefValue = ((tupleDecl | enumDecl | objectDecl | conceptDecl |
- #| ('ref' | 'ptr' | 'distinct') (tupleDecl | objectDecl))
- #| / (simpleExpr (exprEqExpr ^+ comma postExprBlocks)?))
- #| ('not' expr)?
- case p.tok.tokType
- of tkTuple: result = parseTuple(p, true)
- of tkRef: result = parseTypeDescKAux(p, nkRefTy, pmTypeDef)
- of tkPtr: result = parseTypeDescKAux(p, nkPtrTy, pmTypeDef)
- of tkDistinct: result = parseTypeDescKAux(p, nkDistinctTy, pmTypeDef)
- of tkEnum:
- prettySection:
- result = parseEnum(p)
- of tkObject:
- prettySection:
- result = parseObject(p)
- of tkConcept:
- result = parseTypeClass(p)
- else:
- result = simpleExpr(p, pmTypeDef)
- if p.tok.tokType != tkNot:
- if result.kind == nkCommand:
- var isFirstParam = false
- while p.tok.tokType == tkComma:
- getTok(p)
- optInd(p, result)
- result.add(commandParam(p, isFirstParam, pmTypeDef))
- result = postExprBlocks(p, result)
- result = binaryNot(p, result)
- proc makeCall(n: PNode): PNode =
- ## Creates a call if the given node isn't already a call.
- if n.kind in nkCallKinds:
- result = n
- else:
- result = newNodeI(nkCall, n.info)
- result.add n
- proc postExprBlocks(p: var Parser, x: PNode): PNode =
- #| postExprBlocks = ':' stmt? ( IND{=} doBlock
- #| | IND{=} 'of' exprList ':' stmt
- #| | IND{=} 'elif' expr ':' stmt
- #| | IND{=} 'except' optionalExprList ':' stmt
- #| | IND{=} 'finally' ':' stmt
- #| | IND{=} 'else' ':' stmt )*
- result = x
- if p.tok.indent >= 0: return
- var
- openingParams = p.emptyNode
- openingPragmas = p.emptyNode
- if p.tok.tokType == tkDo:
- getTok(p)
- openingParams = parseParamList(p, retColon=false)
- openingPragmas = optPragmas(p)
- if p.tok.tokType == tkColon:
- result = makeCall(result)
- getTok(p)
- skipComment(p, result)
- if p.tok.tokType notin {tkOf, tkElif, tkElse, tkExcept}:
- var stmtList = newNodeP(nkStmtList, p)
- stmtList.add parseStmt(p)
- # to keep backwards compatibility (see tests/vm/tstringnil)
- if stmtList[0].kind == nkStmtList: stmtList = stmtList[0]
- stmtList.flags.incl nfBlockArg
- if openingParams.kind != nkEmpty or openingPragmas.kind != nkEmpty:
- if openingParams.kind == nkEmpty:
- openingParams = newNodeP(nkFormalParams, p)
- openingParams.add(p.emptyNode) # return type
- result.add newProcNode(nkDo, stmtList.info, body = stmtList,
- params = openingParams,
- name = p.emptyNode, pattern = p.emptyNode,
- genericParams = p.emptyNode,
- pragmas = openingPragmas,
- exceptions = p.emptyNode)
- else:
- result.add stmtList
- while sameInd(p):
- var nextBlock: PNode
- let nextToken = p.tok.tokType
- if nextToken == tkDo:
- let info = parLineInfo(p)
- getTok(p)
- nextBlock = parseDoBlock(p, info)
- else:
- case nextToken
- of tkOf:
- nextBlock = newNodeP(nkOfBranch, p)
- exprList(p, tkColon, nextBlock)
- of tkElif:
- nextBlock = newNodeP(nkElifBranch, p)
- getTok(p)
- optInd(p, nextBlock)
- nextBlock.add parseExpr(p)
- of tkExcept:
- nextBlock = newNodeP(nkExceptBranch, p)
- optionalExprList(p, tkColon, nextBlock)
- of tkFinally:
- nextBlock = newNodeP(nkFinally, p)
- getTok(p)
- of tkElse:
- nextBlock = newNodeP(nkElse, p)
- getTok(p)
- else: break
- eat(p, tkColon)
- nextBlock.add parseStmt(p)
- nextBlock.flags.incl nfBlockArg
- result.add nextBlock
- if nextBlock.kind in {nkElse, nkFinally}: break
- else:
- if openingParams.kind != nkEmpty:
- parMessage(p, "expected ':'")
- proc parseExprStmt(p: var Parser): PNode =
- #| exprStmt = simpleExpr postExprBlocks?
- #| / simplePrimary (exprEqExpr ^+ comma) postExprBlocks?
- #| / simpleExpr '=' optInd (expr postExprBlocks?)
- var a = simpleExpr(p, pmTrySimple)
- if p.tok.tokType == tkEquals:
- result = newNodeP(nkAsgn, p)
- getTok(p)
- optInd(p, result)
- var b = parseExpr(p)
- b = postExprBlocks(p, b)
- result.add(a)
- result.add(b)
- else:
- var isFirstParam = false
- # if an expression is starting here, a simplePrimary was parsed and
- # this is the start of a command
- if p.tok.indent < 0 and isExprStart(p):
- result = newTreeI(nkCommand, a.info, a)
- let baseIndent = p.currInd
- while true:
- result.add(commandParam(p, isFirstParam, pmNormal))
- if p.tok.tokType != tkComma or
- (p.tok.indent >= 0 and p.tok.indent < baseIndent):
- break
- getTok(p)
- optInd(p, result)
- else:
- result = a
- result = postExprBlocks(p, result)
- proc parseModuleName(p: var Parser, kind: TNodeKind): PNode =
- result = parseExpr(p)
- when false:
- # parseExpr already handles 'as' syntax ...
- if p.tok.tokType == tkAs and kind == nkImportStmt:
- let a = result
- result = newNodeP(nkImportAs, p)
- getTok(p)
- result.add(a)
- result.add(parseExpr(p))
- proc parseImport(p: var Parser, kind: TNodeKind): PNode =
- #| importStmt = 'import' optInd expr
- #| ((comma expr)*
- #| / 'except' optInd (expr ^+ comma))
- #| exportStmt = 'export' optInd expr
- #| ((comma expr)*
- #| / 'except' optInd (expr ^+ comma))
- result = newNodeP(kind, p)
- getTok(p) # skip `import` or `export`
- optInd(p, result)
- var a = parseModuleName(p, kind)
- result.add(a)
- if p.tok.tokType in {tkComma, tkExcept}:
- if p.tok.tokType == tkExcept:
- result.transitionSonsKind(succ(kind))
- getTok(p)
- optInd(p, result)
- while true:
- # was: while p.tok.tokType notin {tkEof, tkSad, tkDed}:
- p.hasProgress = false
- a = parseModuleName(p, kind)
- if a.kind == nkEmpty or not p.hasProgress: break
- result.add(a)
- if p.tok.tokType != tkComma: break
- getTok(p)
- optInd(p, a)
- #expectNl(p)
- proc parseIncludeStmt(p: var Parser): PNode =
- #| includeStmt = 'include' optInd expr ^+ comma
- result = newNodeP(nkIncludeStmt, p)
- getTok(p) # skip `import` or `include`
- optInd(p, result)
- while true:
- # was: while p.tok.tokType notin {tkEof, tkSad, tkDed}:
- p.hasProgress = false
- var a = parseExpr(p)
- if a.kind == nkEmpty or not p.hasProgress: break
- result.add(a)
- if p.tok.tokType != tkComma: break
- getTok(p)
- optInd(p, a)
- #expectNl(p)
- proc parseFromStmt(p: var Parser): PNode =
- #| fromStmt = 'from' expr 'import' optInd expr (comma expr)*
- result = newNodeP(nkFromStmt, p)
- getTok(p) # skip `from`
- optInd(p, result)
- var a = parseModuleName(p, nkImportStmt)
- result.add(a) #optInd(p, a);
- eat(p, tkImport)
- optInd(p, result)
- while true:
- # p.tok.tokType notin {tkEof, tkSad, tkDed}:
- p.hasProgress = false
- a = parseExpr(p)
- if a.kind == nkEmpty or not p.hasProgress: break
- result.add(a)
- if p.tok.tokType != tkComma: break
- getTok(p)
- optInd(p, a)
- #expectNl(p)
- proc parseReturnOrRaise(p: var Parser, kind: TNodeKind): PNode =
- #| returnStmt = 'return' optInd expr?
- #| raiseStmt = 'raise' optInd expr?
- #| yieldStmt = 'yield' optInd expr?
- #| discardStmt = 'discard' optInd expr?
- #| breakStmt = 'break' optInd expr?
- #| continueStmt = 'continue' optInd expr?
- result = newNodeP(kind, p)
- getTok(p)
- if p.tok.tokType == tkComment:
- skipComment(p, result)
- result.add(p.emptyNode)
- elif p.tok.indent >= 0 and p.tok.indent <= p.currInd or not isExprStart(p):
- # NL terminates:
- result.add(p.emptyNode)
- # nimpretty here!
- else:
- var e = parseExpr(p)
- e = postExprBlocks(p, e)
- result.add(e)
- proc parseIfOrWhen(p: var Parser, kind: TNodeKind): PNode =
- #| condStmt = expr colcom stmt COMMENT?
- #| (IND{=} 'elif' expr colcom stmt)*
- #| (IND{=} 'else' colcom stmt)?
- #| ifStmt = 'if' condStmt
- #| whenStmt = 'when' condStmt
- result = newNodeP(kind, p)
- while true:
- getTok(p) # skip `if`, `when`, `elif`
- var branch = newNodeP(nkElifBranch, p)
- optInd(p, branch)
- branch.add(parseExpr(p))
- colcom(p, branch)
- branch.add(parseStmt(p))
- skipComment(p, branch)
- result.add(branch)
- if p.tok.tokType != tkElif or not sameOrNoInd(p): break
- if p.tok.tokType == tkElse and sameOrNoInd(p):
- var branch = newNodeP(nkElse, p)
- eat(p, tkElse)
- colcom(p, branch)
- branch.add(parseStmt(p))
- result.add(branch)
- proc parseIfOrWhenExpr(p: var Parser, kind: TNodeKind): PNode =
- #| condExpr = expr colcom expr optInd
- #| ('elif' expr colcom expr optInd)*
- #| 'else' colcom expr
- #| ifExpr = 'if' condExpr
- #| whenExpr = 'when' condExpr
- result = newNodeP(kind, p)
- while true:
- getTok(p) # skip `if`, `when`, `elif`
- var branch = newNodeP(nkElifExpr, p)
- optInd(p, branch)
- branch.add(parseExpr(p))
- colcom(p, branch)
- branch.add(parseStmt(p))
- skipComment(p, branch)
- result.add(branch)
- if p.tok.tokType != tkElif: break
- if p.tok.tokType == tkElse:
- var branch = newNodeP(nkElseExpr, p)
- eat(p, tkElse)
- colcom(p, branch)
- branch.add(parseStmt(p))
- result.add(branch)
- proc parseWhile(p: var Parser): PNode =
- #| whileStmt = 'while' expr colcom stmt
- result = newNodeP(nkWhileStmt, p)
- getTok(p)
- optInd(p, result)
- result.add(parseExpr(p))
- colcom(p, result)
- result.add(parseStmt(p))
- proc parseCase(p: var Parser): PNode =
- #| ofBranch = 'of' exprList colcom stmt
- #| ofBranches = ofBranch (IND{=} ofBranch)*
- #| (IND{=} 'elif' expr colcom stmt)*
- #| (IND{=} 'else' colcom stmt)?
- #| caseStmt = 'case' expr ':'? COMMENT?
- #| (IND{>} ofBranches DED
- #| | IND{=} ofBranches)
- var
- b: PNode
- inElif = false
- wasIndented = false
- result = newNodeP(nkCaseStmt, p)
- getTok(p)
- result.add(parseExpr(p))
- if p.tok.tokType == tkColon: getTok(p)
- skipComment(p, result)
- let oldInd = p.currInd
- if realInd(p):
- p.currInd = p.tok.indent
- wasIndented = true
- while sameInd(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)
- b.add(parseExpr(p))
- of tkElse:
- b = newNodeP(nkElse, p)
- getTok(p)
- else: break
- colcom(p, b)
- b.add(parseStmt(p))
- result.add(b)
- if b.kind == nkElse: break
- if wasIndented:
- p.currInd = oldInd
- proc parseTry(p: var Parser; isExpr: bool): PNode =
- #| tryStmt = 'try' colcom stmt &(IND{=}? 'except'|'finally')
- #| (IND{=}? 'except' optionalExprList colcom stmt)*
- #| (IND{=}? 'finally' colcom stmt)?
- #| tryExpr = 'try' colcom stmt &(optInd 'except'|'finally')
- #| (optInd 'except' optionalExprList colcom stmt)*
- #| (optInd 'finally' colcom stmt)?
- result = newNodeP(nkTryStmt, p)
- let parentIndent = p.currInd # isExpr
- getTok(p)
- colcom(p, result)
- result.add(parseStmt(p))
- var b: PNode = nil
- while sameOrNoInd(p) or (isExpr and parentIndent <= p.tok.indent):
- case p.tok.tokType
- of tkExcept:
- b = newNodeP(nkExceptBranch, p)
- optionalExprList(p, tkColon, b)
- of tkFinally:
- b = newNodeP(nkFinally, p)
- getTok(p)
- else: break
- colcom(p, b)
- b.add(parseStmt(p))
- result.add(b)
- if b == nil: parMessage(p, "expected 'except'")
- proc parseExceptBlock(p: var Parser, kind: TNodeKind): PNode =
- result = newNodeP(kind, p)
- getTok(p)
- colcom(p, result)
- result.add(parseStmt(p))
- proc parseBlock(p: var Parser): PNode =
- #| blockStmt = 'block' symbol? colcom stmt
- #| blockExpr = 'block' symbol? colcom stmt
- result = newNodeP(nkBlockStmt, p)
- getTokNoInd(p)
- if p.tok.tokType == tkColon: result.add(p.emptyNode)
- else: result.add(parseSymbol(p))
- colcom(p, result)
- result.add(parseStmt(p))
- proc parseStaticOrDefer(p: var Parser; k: TNodeKind): PNode =
- #| staticStmt = 'static' colcom stmt
- #| deferStmt = 'defer' colcom stmt
- result = newNodeP(k, p)
- getTok(p)
- colcom(p, result)
- result.add(parseStmt(p))
- proc parseAsm(p: var Parser): PNode =
- #| asmStmt = 'asm' pragma? (STR_LIT | RSTR_LIT | TRIPLESTR_LIT)
- result = newNodeP(nkAsmStmt, p)
- getTokNoInd(p)
- if p.tok.tokType == tkCurlyDotLe: result.add(parsePragma(p))
- else: result.add(p.emptyNode)
- case p.tok.tokType
- of tkStrLit: result.add(newStrNodeP(nkStrLit, p.tok.literal, p))
- of tkRStrLit: result.add(newStrNodeP(nkRStrLit, p.tok.literal, p))
- of tkTripleStrLit: result.add(newStrNodeP(nkTripleStrLit, p.tok.literal, p))
- else:
- parMessage(p, "the 'asm' statement takes a string literal")
- result.add(p.emptyNode)
- return
- getTok(p)
- proc parseGenericParam(p: var Parser): PNode =
- #| genericParam = symbol (comma symbol)* (colon expr)? ('=' optInd expr)?
- var a: PNode
- result = newNodeP(nkIdentDefs, p)
- # progress guaranteed
- while true:
- case p.tok.tokType
- of tkIn, tkOut:
- let x = p.lex.cache.getIdent(if p.tok.tokType == tkIn: "in" else: "out")
- a = newNodeP(nkPrefix, p)
- a.add newIdentNodeP(x, p)
- getTok(p)
- expectIdent(p)
- a.add(parseSymbol(p))
- of tkSymbol, tkAccent:
- a = parseSymbol(p)
- if a.kind == nkEmpty: return
- else: break
- result.add(a)
- if p.tok.tokType != tkComma: break
- getTok(p)
- optInd(p, a)
- if p.tok.tokType == tkColon:
- getTok(p)
- optInd(p, result)
- result.add(parseExpr(p))
- else:
- result.add(p.emptyNode)
- if p.tok.tokType == tkEquals:
- getTok(p)
- optInd(p, result)
- result.add(parseExpr(p))
- else:
- result.add(p.emptyNode)
- proc parseGenericParamList(p: var Parser): PNode =
- #| genericParamList = '[' optInd
- #| genericParam ^* (comma/semicolon) optPar ']'
- result = newNodeP(nkGenericParams, p)
- getTok(p)
- optInd(p, result)
- # progress guaranteed
- while p.tok.tokType in {tkSymbol, tkAccent, tkIn, tkOut}:
- var a = parseGenericParam(p)
- result.add(a)
- if p.tok.tokType notin {tkComma, tkSemiColon}: break
- when defined(nimpretty):
- commaWasSemicolon(p.em)
- getTok(p)
- skipComment(p, a)
- optPar(p)
- eat(p, tkBracketRi)
- proc parsePattern(p: var Parser): PNode =
- #| pattern = '{' stmt '}'
- eat(p, tkCurlyLe)
- result = parseStmt(p)
- eat(p, tkCurlyRi)
- proc parseRoutine(p: var Parser, kind: TNodeKind): PNode =
- #| indAndComment = (IND{>} COMMENT)? | COMMENT?
- #| routine = optInd identVis pattern? genericParamList?
- #| paramListColon pragma? ('=' COMMENT? stmt)? indAndComment
- result = newNodeP(kind, p)
- getTok(p)
- optInd(p, result)
- if kind in {nkProcDef, nkLambda, nkIteratorDef, nkFuncDef} and
- p.tok.tokType notin {tkSymbol, tokKeywordLow..tokKeywordHigh, tkAccent}:
- # no name; lambda or proc type
- # in every context that we can parse a routine, we can also parse these
- result = parseProcExpr(p, true, if kind == nkProcDef: nkLambda else: kind)
- return
- result.add(identVis(p))
- if p.tok.tokType == tkCurlyLe and p.validInd: result.add(p.parsePattern)
- else: result.add(p.emptyNode)
- if p.tok.tokType == tkBracketLe and p.validInd:
- result.add(p.parseGenericParamList)
- else:
- result.add(p.emptyNode)
- result.add(p.parseParamList)
- if p.tok.tokType == tkCurlyDotLe and p.validInd: result.add(p.parsePragma)
- else: result.add(p.emptyNode)
- # empty exception tracking:
- result.add(p.emptyNode)
- let maybeMissEquals = p.tok.tokType != tkEquals
- if (not maybeMissEquals) and p.validInd:
- getTok(p)
- skipComment(p, result)
- result.add(parseStmt(p))
- else:
- result.add(p.emptyNode)
- indAndComment(p, result, maybeMissEquals)
- let body = result[^1]
- if body.kind == nkStmtList and body.len > 0 and body[0].comment.len > 0 and body[0].kind != nkCommentStmt:
- if result.comment.len == 0:
- # proc fn*(a: int): int = a ## foo
- # => moves comment `foo` to `fn`
- result.comment = body[0].comment
- body[0].comment = ""
- #else:
- # assert false, p.lex.config$body.info # avoids hard to track bugs, fail early.
- # Yeah, that worked so well. There IS a bug in this logic, now what?
- proc newCommentStmt(p: var Parser): PNode =
- #| commentStmt = COMMENT
- result = newNodeP(nkCommentStmt, p)
- result.comment = p.tok.literal
- getTok(p)
- proc parseSection(p: var Parser, kind: TNodeKind,
- defparser: proc (p: var Parser): PNode {.nimcall.}): PNode =
- #| section(RULE) = COMMENT? RULE / (IND{>} (RULE / COMMENT)^+IND{=} DED)
- result = newNodeP(kind, p)
- if kind != nkTypeSection: getTok(p)
- skipComment(p, result)
- if realInd(p):
- withInd(p):
- skipComment(p, result)
- # progress guaranteed
- while sameInd(p):
- case p.tok.tokType
- of tkSymbol, tkAccent, tkParLe:
- var a = defparser(p)
- skipComment(p, a)
- result.add(a)
- of tkComment:
- var a = newCommentStmt(p)
- result.add(a)
- else:
- parMessage(p, errIdentifierExpected, p.tok)
- break
- if result.len == 0: parMessage(p, errIdentifierExpected, p.tok)
- elif p.tok.tokType in {tkSymbol, tkAccent, tkParLe} and p.tok.indent < 0:
- # tkParLe is allowed for ``var (x, y) = ...`` tuple parsing
- result.add(defparser(p))
- else:
- parMessage(p, errIdentifierExpected, p.tok)
- proc parseEnum(p: var Parser): PNode =
- #| enumDecl = 'enum' optInd (symbol pragma? optInd ('=' optInd expr COMMENT?)? comma?)+
- result = newNodeP(nkEnumTy, p)
- getTok(p)
- result.add(p.emptyNode)
- optInd(p, result)
- flexComment(p, result)
- # progress guaranteed
- while true:
- var a = parseSymbol(p)
- if a.kind == nkEmpty: return
- var symPragma = a
- var pragma: PNode
- if (p.tok.indent < 0 or p.tok.indent >= p.currInd) and p.tok.tokType == tkCurlyDotLe:
- pragma = optPragmas(p)
- symPragma = newNodeP(nkPragmaExpr, p)
- symPragma.add(a)
- symPragma.add(pragma)
- # nimpretty support here
- if p.tok.indent >= 0 and p.tok.indent <= p.currInd:
- result.add(symPragma)
- break
- if p.tok.tokType == tkEquals and p.tok.indent < 0:
- getTok(p)
- optInd(p, symPragma)
- var b = symPragma
- symPragma = newNodeP(nkEnumFieldDef, p)
- symPragma.add(b)
- symPragma.add(parseExpr(p))
- if p.tok.indent < 0 or p.tok.indent >= p.currInd:
- rawSkipComment(p, symPragma)
- if p.tok.tokType == tkComma and p.tok.indent < 0:
- getTok(p)
- rawSkipComment(p, symPragma)
- else:
- if p.tok.indent < 0 or p.tok.indent >= p.currInd:
- rawSkipComment(p, symPragma)
- result.add(symPragma)
- if p.tok.indent >= 0 and p.tok.indent <= p.currInd or
- p.tok.tokType == tkEof:
- break
- if result.len <= 1:
- parMessage(p, errIdentifierExpected, p.tok)
- proc parseObjectPart(p: var Parser): PNode
- proc parseObjectWhen(p: var Parser): PNode =
- #| objectWhen = 'when' expr colcom objectPart COMMENT?
- #| ('elif' expr colcom objectPart COMMENT?)*
- #| ('else' colcom objectPart COMMENT?)?
- result = newNodeP(nkRecWhen, p)
- # progress guaranteed
- while sameInd(p):
- getTok(p) # skip `when`, `elif`
- var branch = newNodeP(nkElifBranch, p)
- optInd(p, branch)
- branch.add(parseExpr(p))
- colcom(p, branch)
- branch.add(parseObjectPart(p))
- flexComment(p, branch)
- result.add(branch)
- if p.tok.tokType != tkElif: break
- if p.tok.tokType == tkElse and sameInd(p):
- var branch = newNodeP(nkElse, p)
- eat(p, tkElse)
- colcom(p, branch)
- branch.add(parseObjectPart(p))
- flexComment(p, branch)
- result.add(branch)
- proc parseObjectCase(p: var Parser): PNode =
- #| objectBranch = 'of' exprList colcom objectPart
- #| objectBranches = objectBranch (IND{=} objectBranch)*
- #| (IND{=} 'elif' expr colcom objectPart)*
- #| (IND{=} 'else' colcom objectPart)?
- #| objectCase = 'case' declColonEquals ':'? COMMENT?
- #| (IND{>} objectBranches DED
- #| | IND{=} objectBranches)
- result = newNodeP(nkRecCase, p)
- getTokNoInd(p)
- var a = parseIdentColonEquals(p, {withPragma})
- result.add(a)
- if p.tok.tokType == tkColon: getTok(p)
- flexComment(p, result)
- var wasIndented = false
- let oldInd = p.currInd
- if realInd(p):
- p.currInd = p.tok.indent
- wasIndented = true
- # progress guaranteed
- while sameInd(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)
- else: break
- colcom(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
- b.add(fields)
- result.add(b)
- if b.kind == nkElse: break
- if wasIndented:
- p.currInd = oldInd
- proc parseObjectPart(p: var Parser): PNode =
- #| objectPart = IND{>} objectPart^+IND{=} DED
- #| / objectWhen / objectCase / 'nil' / 'discard' / declColonEquals
- if realInd(p):
- result = newNodeP(nkRecList, p)
- withInd(p):
- rawSkipComment(p, result)
- while sameInd(p):
- case p.tok.tokType
- of tkCase, tkWhen, tkSymbol, tkAccent, tkNil, tkDiscard:
- result.add(parseObjectPart(p))
- else:
- parMessage(p, errIdentifierExpected, p.tok)
- break
- elif sameOrNoInd(p):
- case p.tok.tokType
- of tkWhen:
- result = parseObjectWhen(p)
- of tkCase:
- result = parseObjectCase(p)
- of tkSymbol, tkAccent:
- result = parseIdentColonEquals(p, {withPragma})
- if p.tok.indent < 0 or p.tok.indent >= p.currInd:
- rawSkipComment(p, result)
- of tkNil, tkDiscard:
- result = newNodeP(nkNilLit, p)
- getTok(p)
- else:
- result = p.emptyNode
- else:
- result = p.emptyNode
- proc parseObject(p: var Parser): PNode =
- #| objectDecl = 'object' ('of' typeDesc)? COMMENT? objectPart
- result = newNodeP(nkObjectTy, p)
- getTok(p)
- result.add(p.emptyNode) # compatibility with old pragma node
- if p.tok.tokType == tkOf and p.tok.indent < 0:
- var a = newNodeP(nkOfInherit, p)
- getTok(p)
- a.add(parseTypeDesc(p))
- result.add(a)
- else:
- result.add(p.emptyNode)
- if p.tok.tokType == tkComment:
- skipComment(p, result)
- # an initial IND{>} HAS to follow:
- if not realInd(p):
- result.add(p.emptyNode)
- else:
- result.add(parseObjectPart(p))
- proc parseTypeClassParam(p: var Parser): PNode =
- let modifier =
- case p.tok.tokType
- of tkOut, tkVar: nkVarTy
- of tkPtr: nkPtrTy
- of tkRef: nkRefTy
- of tkStatic: nkStaticTy
- of tkType: nkTypeOfExpr
- else: nkEmpty
- if modifier != nkEmpty:
- result = newNodeP(modifier, p)
- getTok(p)
- result.add(p.parseSymbol)
- else:
- result = p.parseSymbol
- proc parseTypeClass(p: var Parser): PNode =
- #| conceptParam = ('var' | 'out')? symbol
- #| conceptDecl = 'concept' conceptParam ^* ',' (pragma)? ('of' typeDesc ^* ',')?
- #| &IND{>} stmt
- result = newNodeP(nkTypeClassTy, p)
- getTok(p)
- if p.tok.tokType == tkComment:
- skipComment(p, result)
- if p.tok.indent < 0:
- var args = newNodeP(nkArgList, p)
- result.add(args)
- args.add(p.parseTypeClassParam)
- while p.tok.tokType == tkComma:
- getTok(p)
- args.add(p.parseTypeClassParam)
- else:
- result.add(p.emptyNode) # see ast.isNewStyleConcept
- if p.tok.tokType == tkCurlyDotLe and p.validInd:
- result.add(parsePragma(p))
- else:
- result.add(p.emptyNode)
- if p.tok.tokType == tkOf and p.tok.indent < 0:
- var a = newNodeP(nkOfInherit, p)
- getTok(p)
- # progress guaranteed
- while true:
- a.add(parseTypeDesc(p))
- if p.tok.tokType != tkComma: break
- getTok(p)
- result.add(a)
- else:
- result.add(p.emptyNode)
- if p.tok.tokType == tkComment:
- skipComment(p, result)
- # an initial IND{>} HAS to follow:
- if not realInd(p):
- if result.isNewStyleConcept:
- parMessage(p, "routine expected, but found '$1' (empty new-styled concepts are not allowed)", p.tok)
- result.add(p.emptyNode)
- else:
- result.add(parseStmt(p))
- proc parseTypeDef(p: var Parser): PNode =
- #|
- #| typeDef = identVisDot genericParamList? pragma '=' optInd typeDefValue
- #| indAndComment?
- result = newNodeP(nkTypeDef, p)
- var identifier = identVis(p, allowDot=true)
- var identPragma = identifier
- var pragma: PNode
- var genericParam: PNode
- if p.tok.tokType == tkBracketLe and p.validInd:
- genericParam = parseGenericParamList(p)
- else:
- genericParam = p.emptyNode
- pragma = optPragmas(p)
- if pragma.kind != nkEmpty:
- identPragma = newNodeP(nkPragmaExpr, p)
- identPragma.add(identifier)
- identPragma.add(pragma)
- result.add(identPragma)
- result.add(genericParam)
- if p.tok.tokType == tkEquals:
- result.info = parLineInfo(p)
- getTok(p)
- optInd(p, result)
- result.add(parseTypeDefValue(p))
- else:
- result.add(p.emptyNode)
- indAndComment(p, result) # special extension!
- proc parseVarTuple(p: var Parser): PNode =
- #| varTuple = '(' optInd identWithPragma ^+ comma optPar ')' '=' optInd expr
- result = newNodeP(nkVarTuple, p)
- getTok(p) # skip '('
- optInd(p, result)
- # progress guaranteed
- while p.tok.tokType in {tkSymbol, tkAccent}:
- var a = identWithPragma(p, allowDot=true)
- result.add(a)
- if p.tok.tokType != tkComma: break
- getTok(p)
- skipComment(p, a)
- result.add(p.emptyNode) # no type desc
- optPar(p)
- eat(p, tkParRi)
- proc parseVariable(p: var Parser): PNode =
- #| colonBody = colcom stmt postExprBlocks?
- #| variable = (varTuple / identColonEquals) colonBody? indAndComment
- if p.tok.tokType == tkParLe:
- result = parseVarTuple(p)
- eat(p, tkEquals)
- optInd(p, result)
- result.add(parseExpr(p))
- else: result = parseIdentColonEquals(p, {withPragma, withDot})
- result[^1] = postExprBlocks(p, result[^1])
- indAndComment(p, result)
- proc parseConstant(p: var Parser): PNode =
- #| constant = (varTuple / identWithPragma) (colon typeDesc)? '=' optInd expr indAndComment
- if p.tok.tokType == tkParLe: result = parseVarTuple(p)
- else:
- result = newNodeP(nkConstDef, p)
- result.add(identWithPragma(p))
- if p.tok.tokType == tkColon:
- getTok(p)
- optInd(p, result)
- result.add(parseTypeDesc(p))
- else:
- result.add(p.emptyNode)
- eat(p, tkEquals)
- optInd(p, result)
- #add(result, parseStmtListExpr(p))
- result.add(parseExpr(p))
- result[^1] = postExprBlocks(p, result[^1])
- indAndComment(p, result)
- proc parseBind(p: var Parser, k: TNodeKind): PNode =
- #| bindStmt = 'bind' optInd qualifiedIdent ^+ comma
- #| mixinStmt = 'mixin' optInd qualifiedIdent ^+ comma
- result = newNodeP(k, p)
- getTok(p)
- optInd(p, result)
- # progress guaranteed
- while true:
- var a = qualifiedIdent(p)
- result.add(a)
- if p.tok.tokType != tkComma: break
- getTok(p)
- optInd(p, a)
- #expectNl(p)
- proc parseStmtPragma(p: var Parser): PNode =
- #| pragmaStmt = pragma (':' COMMENT? stmt)?
- result = parsePragma(p)
- if p.tok.tokType == tkColon and p.tok.indent < 0:
- let a = result
- result = newNodeI(nkPragmaBlock, a.info)
- getTok(p)
- skipComment(p, result)
- result.add a
- result.add parseStmt(p)
- proc simpleStmt(p: var Parser): PNode =
- #| simpleStmt = ((returnStmt | raiseStmt | yieldStmt | discardStmt | breakStmt
- #| | continueStmt | pragmaStmt | importStmt | exportStmt | fromStmt
- #| | includeStmt | commentStmt) / exprStmt) COMMENT?
- #|
- case p.tok.tokType
- of tkReturn: result = parseReturnOrRaise(p, nkReturnStmt)
- of tkRaise: result = parseReturnOrRaise(p, nkRaiseStmt)
- of tkYield: result = parseReturnOrRaise(p, nkYieldStmt)
- of tkDiscard: result = parseReturnOrRaise(p, nkDiscardStmt)
- of tkBreak: result = parseReturnOrRaise(p, nkBreakStmt)
- of tkContinue: result = parseReturnOrRaise(p, nkContinueStmt)
- of tkCurlyDotLe: result = parseStmtPragma(p)
- of tkImport: result = parseImport(p, nkImportStmt)
- of tkExport: result = parseImport(p, nkExportStmt)
- of tkFrom: result = parseFromStmt(p)
- of tkInclude: result = parseIncludeStmt(p)
- of tkComment: result = newCommentStmt(p)
- else:
- if isExprStart(p): result = parseExprStmt(p)
- else: result = p.emptyNode
- if result.kind notin {nkEmpty, nkCommentStmt}: skipComment(p, result)
- proc complexOrSimpleStmt(p: var Parser): PNode =
- #| complexOrSimpleStmt = (ifStmt | whenStmt | whileStmt
- #| | tryStmt | forStmt
- #| | blockStmt | staticStmt | deferStmt | asmStmt
- #| | 'proc' routine
- #| | 'method' routine
- #| | 'func' routine
- #| | 'iterator' routine
- #| | 'macro' routine
- #| | 'template' routine
- #| | 'converter' routine
- #| | 'type' section(typeDef)
- #| | 'const' section(constant)
- #| | ('let' | 'var' | 'using') section(variable)
- #| | bindStmt | mixinStmt)
- #| / simpleStmt
- 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, isExpr=false)
- 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 = parseStaticOrDefer(p, nkStaticStmt)
- of tkDefer: result = parseStaticOrDefer(p, nkDefer)
- of tkAsm: result = parseAsm(p)
- of tkProc: result = parseRoutine(p, nkProcDef)
- of tkFunc: result = parseRoutine(p, nkFuncDef)
- 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:
- getTok(p)
- if p.tok.tokType == tkParLe:
- getTok(p)
- result = newNodeP(nkTypeOfExpr, p)
- result.add(primary(p, pmTypeDesc))
- eat(p, tkParRi)
- result = parseOperators(p, result, -1, pmNormal)
- else:
- result = parseSection(p, nkTypeSection, parseTypeDef)
- of tkConst:
- prettySection:
- result = parseSection(p, nkConstSection, parseConstant)
- of tkLet:
- prettySection:
- result = parseSection(p, nkLetSection, parseVariable)
- of tkVar:
- prettySection:
- result = parseSection(p, nkVarSection, parseVariable)
- of tkWhen: result = parseIfOrWhen(p, nkWhenStmt)
- of tkBind: result = parseBind(p, nkBindStmt)
- of tkMixin: result = parseBind(p, nkMixinStmt)
- of tkUsing: result = parseSection(p, nkUsingStmt, parseVariable)
- else: result = simpleStmt(p)
- proc parseStmt(p: var Parser): PNode =
- #| stmt = (IND{>} complexOrSimpleStmt^+(IND{=} / ';') DED)
- #| / simpleStmt ^+ ';'
- if p.tok.indent > p.currInd:
- # nimpretty support here
- result = newNodeP(nkStmtList, p)
- withInd(p):
- while true:
- if p.tok.indent == p.currInd:
- discard
- elif p.tok.tokType == tkSemiColon:
- getTok(p)
- if p.tok.indent < 0 or p.tok.indent == p.currInd: discard
- else: break
- else:
- if p.tok.indent > p.currInd and p.tok.tokType != tkDot:
- parMessage(p, errInvalidIndentation)
- break
- if p.tok.tokType in {tkCurlyRi, tkParRi, tkCurlyDotRi, tkBracketRi}:
- # XXX this ensures tnamedparamanonproc still compiles;
- # deprecate this syntax later
- break
- p.hasProgress = false
- if p.tok.tokType in {tkElse, tkElif}:
- break # Allow this too, see tests/parser/tifexprs
- let a = complexOrSimpleStmt(p)
- if a.kind == nkEmpty and not p.hasProgress:
- parMessage(p, errExprExpected, p.tok)
- break
- else:
- result.add a
- if not p.hasProgress and p.tok.tokType == tkEof: break
- else:
- # the case statement is only needed for better error messages:
- case p.tok.tokType
- of tkIf, tkWhile, tkCase, tkTry, tkFor, tkBlock, tkAsm, tkProc, tkFunc,
- tkIterator, tkMacro, tkType, tkConst, tkWhen, tkVar:
- parMessage(p, "nestable statement requires indentation")
- result = p.emptyNode
- else:
- if p.inSemiStmtList > 0:
- result = simpleStmt(p)
- if result.kind == nkEmpty: parMessage(p, errExprExpected, p.tok)
- else:
- result = newNodeP(nkStmtList, p)
- while true:
- if p.tok.indent >= 0:
- parMessage(p, errInvalidIndentation)
- p.hasProgress = false
- let a = simpleStmt(p)
- let err = not p.hasProgress
- if a.kind == nkEmpty: parMessage(p, errExprExpected, p.tok)
- result.add(a)
- if p.tok.tokType != tkSemiColon: break
- getTok(p)
- if err and p.tok.tokType == tkEof: break
- proc parseAll(p: var Parser): PNode =
- ## Parses the rest of the input stream held by the parser into a PNode.
- result = newNodeP(nkStmtList, p)
- while p.tok.tokType != tkEof:
- p.hasProgress = false
- var a = complexOrSimpleStmt(p)
- if a.kind != nkEmpty and p.hasProgress:
- result.add(a)
- else:
- parMessage(p, errExprExpected, p.tok)
- # bugfix: consume a token here to prevent an endless loop:
- getTok(p)
- if p.tok.indent != 0:
- parMessage(p, errInvalidIndentation)
- proc checkFirstLineIndentation*(p: var Parser) =
- if p.tok.indent != 0 and p.tok.strongSpaceA:
- parMessage(p, errInvalidIndentation)
- proc parseTopLevelStmt(p: var Parser): PNode =
- ## Implements an iterator which, when called repeatedly, returns the next
- ## top-level statement or emptyNode if end of stream.
- result = p.emptyNode
- # progress guaranteed
- while true:
- # nimpretty support here
- if p.tok.indent != 0:
- if p.firstTok and p.tok.indent < 0: discard
- elif p.tok.tokType != tkSemiColon:
- # special casing for better error messages:
- if p.tok.tokType == tkOpr and p.tok.ident.s == "*":
- parMessage(p, errGenerated,
- "invalid indentation; an export marker '*' follows the declared identifier")
- else:
- parMessage(p, errInvalidIndentation)
- p.firstTok = false
- case p.tok.tokType
- of tkSemiColon:
- getTok(p)
- if p.tok.indent <= 0: discard
- else: parMessage(p, errInvalidIndentation)
- p.firstTok = true
- of tkEof: break
- else:
- result = complexOrSimpleStmt(p)
- if result.kind == nkEmpty: parMessage(p, errExprExpected, p.tok)
- break
- proc parseString*(s: string; cache: IdentCache; config: ConfigRef;
- filename: string = ""; line: int = 0;
- errorHandler: ErrorHandler = nil): PNode =
- ## Parses a string into an AST, returning the top node.
- ## `filename` and `line`, although optional, provide info so that the
- ## compiler can generate correct error messages referring to the original
- ## source.
- var stream = llStreamOpen(s)
- stream.lineOffset = line
- var parser: Parser
- parser.lex.errorHandler = errorHandler
- openParser(parser, AbsoluteFile filename, stream, cache, config)
- result = parser.parseAll
- closeParser(parser)
|