123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505 |
- #
- #
- # 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.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)
- getTok(p)
- colcom(p, result)
- result.add(parseStmt(p))
- var b: PNode = nil
- while sameOrNoInd(p) or isExpr:
- 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)
|