Basic Lean parser infrastructure #
The Lean parser was developed with the following primary goals in mind:
- flexibility: Lean's grammar is complex and includes indentation and other whitespace sensitivity. It should be possible to introduce such custom "tweaks" locally without having to adjust the fundamental parsing approach.
- extensibility: Lean's grammar can be extended on the fly within a Lean file, and with Lean 4 we want to extend this to cover embedding domain-specific languages that may look nothing like Lean, down to using a separate set of tokens.
- losslessness: The parser should produce a concrete syntax tree that preserves all whitespace and other "sub-token" information for the use in tooling.
- performance: The overhead of the parser building blocks, and the overall parser performance on average-complexity input, should be comparable with that of the previous parser hand-written in C++. No fancy optimizations should be necessary for this.
Given these constraints, we decided to implement a combinatoric, non-monadic, lexer-less, memoizing recursive-descent
parser. Using combinators instead of some more formal and introspectible grammar representation ensures ultimate
flexibility as well as efficient extensibility: there is (almost) no pre-processing necessary when extending the grammar
with a new parser. However, because the all results the combinators produce are of the homogeneous Syntax
type, the
basic parser type is not actually a monad but a monomorphic linear function ParserState → ParserState
, avoiding
constructing and deconstructing countless monadic return values. Instead of explicitly returning syntax objects, parsers
push (zero or more of) them onto a syntax stack inside the linear state. Chaining parsers via >>
accumulates their
output on the stack. Combinators such as node
then pop off all syntax objects produced during their invocation and
wrap them in a single Syntax.node
object that is again pushed on this stack. Instead of calling node
directly, we
usually use the macro leading_parser p
, which unfolds to node k p
where the new syntax node kind k
is the name of the
declaration being defined.
The lack of a dedicated lexer ensures we can modify and replace the lexical grammar at any point, and simplifies
detecting and propagating whitespace. The parser still has a concept of "tokens", however, and caches the most recent
one for performance: when tokenFn
is called twice at the same position in the input, it will reuse the result of the
first call. tokenFn
recognizes some built-in variable-length tokens such as identifiers as well as any fixed token in
the ParserContext
's TokenTable
(a trie); however, the same cache field and strategy could be reused by custom token
parsers. Tokens also play a central role in the prattParser
combinator, which selects a leading parser followed by
zero or more trailing parsers based on the current token (via peekToken
); see the documentation of prattParser
for more details. Tokens are specified via the symbol
parser, or with symbolNoWs
for tokens that should not be preceded by whitespace.
The Parser
type is extended with additional metadata over the mere parsing function to propagate token information:
collectTokens
collects all tokens within a parser for registering. firstTokens
holds information about the "FIRST"
token set used to speed up parser selection in prattParser
. This approach of combining static and dynamic information
in the parser type is inspired by the paper "Deterministic, Error-Correcting Combinator Parsers" by Swierstra and Duponcheel.
If multiple parsers accept the same current token, prattParser
tries all of them using the backtracking longestMatchFn
combinator.
This is the only case where standard parsers might execute arbitrary backtracking. At the moment there is no memoization shared by these
parallel parsers apart from the first token, though we might change this in the future if the need arises.
Finally, error reporting follows the standard combinatoric approach of collecting a single unexpected token/... and zero
or more expected tokens (see Error
below). Expected tokens are e.g. set by symbol
and merged by <|>
. Combinators
running multiple parsers should check if an error message is set in the parser state (hasError
) and act accordingly.
Error recovery is left to the designer of the specific language; for example, Lean's top-level parseCommand
loop skips
tokens until the next command keyword on error.
Equations
- Lean.Parser.mkAtom info val = Lean.Syntax.atom info val
Equations
- Lean.Parser.mkIdent info rawVal val = Lean.Syntax.ident info rawVal val []
Equations
- Lean.Parser.getNext input pos = String.get input (String.next input pos)
- startPos : String.Pos
- stopPos : String.Pos
- token : Lean.Syntax
Equations
- Lean.Parser.initCacheForInput input = { tokenCache := { startPos := String.endPos input + Char.ofNat 32, stopPos := 0, token := Lean.Syntax.missing } }
Equations
- Lean.Parser.instInhabitedInputContext = { default := { input := default, fileName := default, fileMap := default } }
- env : Lean.Environment
- options : Lean.Options
- currNamespace : Lean.Name
- openDecls : List Lean.OpenDecl
Input context derived from elaboration of previous commands.
- prec : Nat
- tokens : Lean.Parser.TokenTable
- quotDepth : Nat
- suppressInsideQuot : Bool
- savedPos? : Option String.Pos
- forbiddenTk? : Option Lean.Parser.Token
Equations
- Lean.Parser.ParserContext.resolveName ctx id = Lean.ResolveName.resolveGlobalName ctx.toParserModuleContext.env ctx.toParserModuleContext.currNamespace ctx.toParserModuleContext.openDecls id
Equations
- Lean.Parser.instInhabitedError = { default := { unexpected := default, expected := default } }
Equations
- Lean.Parser.instBEqError = { beq := [anonymous] }
Equations
- One or more equations did not get rendered due to their size.
Equations
- Lean.Parser.Error.instToStringError = { toString := Lean.Parser.Error.toString }
Equations
- One or more equations did not get rendered due to their size.
- stxStack : Array Lean.Syntax
Set to the precedence of the preceding (not surrounding) parser by
runLongestMatchParser
for the use ofcheckLhsPrec
in trailing parsers. Note that with chaining, the preceding parser can be another trailing parser: in1 * 2 + 3
, the preceding parser is '*' when '+' is executed.lhsPrec : Nat- pos : String.Pos
- cache : Lean.Parser.ParserCache
- errorMsg : Option Lean.Parser.Error
Equations
- Lean.Parser.ParserState.hasError s = (s.errorMsg != none)
Equations
- Lean.Parser.ParserState.stackSize s = Array.size s.stxStack
Equations
- Lean.Parser.ParserState.restore s iniStackSz iniPos = { stxStack := Array.shrink s.stxStack iniStackSz, lhsPrec := s.lhsPrec, pos := iniPos, cache := s.cache, errorMsg := none }
Equations
- Lean.Parser.ParserState.setPos s pos = { stxStack := s.stxStack, lhsPrec := s.lhsPrec, pos := pos, cache := s.cache, errorMsg := s.errorMsg }
Equations
- Lean.Parser.ParserState.setCache s cache = { stxStack := s.stxStack, lhsPrec := s.lhsPrec, pos := s.pos, cache := cache, errorMsg := s.errorMsg }
Equations
- Lean.Parser.ParserState.pushSyntax s n = { stxStack := Array.push s.stxStack n, lhsPrec := s.lhsPrec, pos := s.pos, cache := s.cache, errorMsg := s.errorMsg }
Equations
- Lean.Parser.ParserState.popSyntax s = { stxStack := Array.pop s.stxStack, lhsPrec := s.lhsPrec, pos := s.pos, cache := s.cache, errorMsg := s.errorMsg }
Equations
- Lean.Parser.ParserState.shrinkStack s iniStackSz = { stxStack := Array.shrink s.stxStack iniStackSz, lhsPrec := s.lhsPrec, pos := s.pos, cache := s.cache, errorMsg := s.errorMsg }
Equations
- Lean.Parser.ParserState.next s input pos = { stxStack := s.stxStack, lhsPrec := s.lhsPrec, pos := String.next input pos, cache := s.cache, errorMsg := s.errorMsg }
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
- Lean.Parser.ParserState.mkEOIError s expected = Lean.Parser.ParserState.mkUnexpectedError s "unexpected end of input" expected
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
- Lean.Parser.instInhabitedParserFn = { default := fun x s => s }
- epsilon: Lean.Parser.FirstTokens
- unknown: Lean.Parser.FirstTokens
- tokens: List Lean.Parser.Token → Lean.Parser.FirstTokens
- optTokens: List Lean.Parser.Token → Lean.Parser.FirstTokens
Equations
- Lean.Parser.instInhabitedFirstTokens = { default := Lean.Parser.FirstTokens.epsilon }
Equations
- One or more equations did not get rendered due to their size.
Equations
- Lean.Parser.FirstTokens.toOptional _fun_discr = match _fun_discr with | Lean.Parser.FirstTokens.tokens tks => Lean.Parser.FirstTokens.optTokens tks | tks => tks
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
- collectTokens : List Lean.Parser.Token → List Lean.Parser.Token
- collectKinds : Lean.Parser.SyntaxNodeKindSet → Lean.Parser.SyntaxNodeKindSet
- firstTokens : Lean.Parser.FirstTokens
Equations
- Lean.Parser.instInhabitedParserInfo = { default := { collectTokens := default, collectKinds := default, firstTokens := default } }
Equations
- Lean.Parser.instInhabitedParser = { default := { info := default, fn := default } }
Equations
- One or more equations did not get rendered due to their size.
Equations
- Lean.Parser.dbgTraceState label p = { info := p.info, fn := Lean.Parser.dbgTraceStateFn label p.fn }
Equations
- Lean.Parser.epsilonInfo = { collectTokens := id, collectKinds := id, firstTokens := Lean.Parser.FirstTokens.epsilon }
Equations
- Lean.Parser.checkStackTopFn p msg x s = if p (Array.back s.stxStack) = true then s else Lean.Parser.ParserState.mkUnexpectedError s msg
Equations
- Lean.Parser.checkStackTop p msg = { info := Lean.Parser.epsilonInfo, fn := Lean.Parser.checkStackTopFn p msg }
Equations
- Lean.Parser.andthenFn p q c s = let s := p c s; if Lean.Parser.ParserState.hasError s = true then s else q c s
Equations
- One or more equations did not get rendered due to their size.
Equations
- Lean.Parser.andthen p q = { info := Lean.Parser.andthenInfo p.info q.info, fn := Lean.Parser.andthenFn p.fn q.fn }
Equations
- Lean.Parser.instAndThenParser = { andThen := fun a b => Lean.Parser.andthen a (b ()) }
Equations
- Lean.Parser.nodeFn n p c s = let iniSz := Lean.Parser.ParserState.stackSize s; let s := p c s; Lean.Parser.ParserState.mkNode s n iniSz
Equations
- Lean.Parser.trailingNodeFn n p c s = let iniSz := Lean.Parser.ParserState.stackSize s; let s := p c s; Lean.Parser.ParserState.mkTrailingNode s n iniSz
Equations
- One or more equations did not get rendered due to their size.
Equations
- Lean.Parser.node n p = { info := Lean.Parser.nodeInfo n p.info, fn := Lean.Parser.nodeFn n p.fn }
Equations
- Lean.Parser.errorFn msg x s = Lean.Parser.ParserState.mkUnexpectedError s msg
Equations
- Lean.Parser.error msg = { info := Lean.Parser.epsilonInfo, fn := Lean.Parser.errorFn msg }
Equations
- One or more equations did not get rendered due to their size.
Equations
- Lean.Parser.errorAtSavedPos msg delta = { info := { collectTokens := id, collectKinds := id, firstTokens := Lean.Parser.FirstTokens.unknown }, fn := Lean.Parser.errorAtSavedPosFn msg delta }
Equations
- One or more equations did not get rendered due to their size.
Equations
- Lean.Parser.checkPrec prec = { info := Lean.Parser.epsilonInfo, fn := Lean.Parser.checkPrecFn prec }
Equations
- One or more equations did not get rendered due to their size.
Equations
- Lean.Parser.checkLhsPrec prec = { info := Lean.Parser.epsilonInfo, fn := Lean.Parser.checkLhsPrecFn prec }
Equations
- Lean.Parser.setLhsPrecFn prec x s = if Lean.Parser.ParserState.hasError s = true then s else { stxStack := s.stxStack, lhsPrec := prec, pos := s.pos, cache := s.cache, errorMsg := s.errorMsg }
Equations
- Lean.Parser.setLhsPrec prec = { info := Lean.Parser.epsilonInfo, fn := Lean.Parser.setLhsPrecFn prec }
Equations
- Lean.Parser.incQuotDepth p = { info := p.info, fn := Lean.Parser.addQuotDepthFn 1 p.fn }
Equations
- Lean.Parser.decQuotDepth p = { info := p.info, fn := Lean.Parser.addQuotDepthFn (-1) p.fn }
Equations
- One or more equations did not get rendered due to their size.
Equations
- Lean.Parser.suppressInsideQuot p = { info := p.info, fn := Lean.Parser.suppressInsideQuotFn p.fn }
Equations
- Lean.Parser.leadingNode n prec p = HAndThen.hAndThen (Lean.Parser.checkPrec prec) fun x => HAndThen.hAndThen (Lean.Parser.node n p) fun x => Lean.Parser.setLhsPrec prec
Equations
- Lean.Parser.trailingNodeAux n p = { info := Lean.Parser.nodeInfo n p.info, fn := Lean.Parser.trailingNodeFn n p.fn }
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Run p
, falling back to q
if p
failed without consuming any input.
NOTE: In order for the pretty printer to retrace an orelse
, p
must be a call to node
or some other parser
producing a single node kind. Nested orelse
calls are flattened for this, i.e. (node k1 p1 <|> node k2 p2) <|> ...
is fine as well.
Equations
- Lean.Parser.orelse p q = { info := Lean.Parser.orelseInfo p.info q.info, fn := Lean.Parser.orelseFn p.fn q.fn }
Equations
- Lean.Parser.instOrElseParser = { orElse := fun a b => Lean.Parser.orelse a (b ()) }
Equations
- Lean.Parser.noFirstTokenInfo info = { collectTokens := info.collectTokens, collectKinds := info.collectKinds, firstTokens := Lean.Parser.FirstTokens.unknown }
Equations
- One or more equations did not get rendered due to their size.
Equations
- Lean.Parser.atomic p = { info := p.info, fn := Lean.Parser.atomicFn p.fn }
Equations
- One or more equations did not get rendered due to their size.
Equations
- Lean.Parser.optionaInfo p = { collectTokens := p.collectTokens, collectKinds := p.collectKinds, firstTokens := Lean.Parser.FirstTokens.toOptional p.firstTokens }
Equations
- Lean.Parser.optionalNoAntiquot p = { info := Lean.Parser.optionaInfo p.info, fn := Lean.Parser.optionalFn p.fn }
Equations
- One or more equations did not get rendered due to their size.
Equations
- Lean.Parser.lookahead p = { info := p.info, fn := Lean.Parser.lookaheadFn p.fn }
Equations
- One or more equations did not get rendered due to their size.
Equations
- Lean.Parser.notFollowedBy p msg = { info := { collectTokens := id, collectKinds := id, firstTokens := Lean.Parser.FirstTokens.unknown }, fn := Lean.Parser.notFollowedByFn p.fn msg }
Equations
- Lean.Parser.manyFn p c s = let iniSz := Lean.Parser.ParserState.stackSize s; let s := Lean.Parser.manyAux p c s; Lean.Parser.ParserState.mkNode s Lean.nullKind iniSz
Equations
- Lean.Parser.manyNoAntiquot p = { info := Lean.Parser.noFirstTokenInfo p.info, fn := Lean.Parser.manyFn p.fn }
Equations
- Lean.Parser.many1Fn p c s = let iniSz := Lean.Parser.ParserState.stackSize s; let s := Lean.Parser.andthenFn p (Lean.Parser.manyAux p) c s; Lean.Parser.ParserState.mkNode s Lean.nullKind iniSz
Equations
- Lean.Parser.many1NoAntiquot p = { info := p.info, fn := Lean.Parser.many1Fn p.fn }
Equations
- Lean.Parser.sepByFn allowTrailingSep p sep c s = let iniSz := Lean.Parser.ParserState.stackSize s; Lean.Parser.sepByFnAux p sep allowTrailingSep iniSz true c s
Equations
- Lean.Parser.sepBy1Fn allowTrailingSep p sep c s = let iniSz := Lean.Parser.ParserState.stackSize s; Lean.Parser.sepByFnAux p sep allowTrailingSep iniSz false c s
Equations
- Lean.Parser.sepByInfo p sep = { collectTokens := p.collectTokens ∘ sep.collectTokens, collectKinds := p.collectKinds ∘ sep.collectKinds, firstTokens := Lean.Parser.FirstTokens.unknown }
Equations
- Lean.Parser.sepBy1Info p sep = { collectTokens := p.collectTokens ∘ sep.collectTokens, collectKinds := p.collectKinds ∘ sep.collectKinds, firstTokens := p.firstTokens }
Equations
- Lean.Parser.sepByNoAntiquot p sep allowTrailingSep = { info := Lean.Parser.sepByInfo p.info sep.info, fn := Lean.Parser.sepByFn allowTrailingSep p.fn sep.fn }
Equations
- Lean.Parser.sepBy1NoAntiquot p sep allowTrailingSep = { info := Lean.Parser.sepBy1Info p.info sep.info, fn := Lean.Parser.sepBy1Fn allowTrailingSep p.fn sep.fn }
Equations
- One or more equations did not get rendered due to their size.
Equations
- Lean.Parser.withResultOfInfo p = { collectTokens := p.collectTokens, collectKinds := p.collectKinds, firstTokens := Lean.Parser.FirstTokens.unknown }
Equations
- Lean.Parser.withResultOf p f = { info := Lean.Parser.withResultOfInfo p.info, fn := Lean.Parser.withResultOfFn p.fn f }
Equations
- Lean.Parser.many1Unbox p = Lean.Parser.withResultOf (Lean.Parser.many1NoAntiquot p) fun stx => if (Lean.Syntax.getNumArgs stx == 1) = true then Lean.Syntax.getArg stx 0 else stx
Equations
- One or more equations did not get rendered due to their size.
Equations
- Lean.Parser.takeWhileFn p = Lean.Parser.takeUntilFn fun c => !p c
Equations
- Lean.Parser.takeWhile1Fn p errorMsg = Lean.Parser.andthenFn (Lean.Parser.satisfyFn p errorMsg) (Lean.Parser.takeWhileFn p)
Equations
- Lean.Parser.finishCommentBlock.eoi s = Lean.Parser.ParserState.mkUnexpectedError s "unterminated comment"
Equations
- Lean.Parser.mkEmptySubstringAt s p = { str := s, startPos := p, stopPos := p }
Match an arbitrary Parser and return the consumed String in a Syntax.atom
.
Equations
- Lean.Parser.rawFn p trailingWs c s = let startPos := s.pos; let s := p c s; if Lean.Parser.ParserState.hasError s = true then s else Lean.Parser.rawAux startPos trailingWs c s
Equations
- Lean.Parser.chFn c trailingWs = Lean.Parser.rawFn (Lean.Parser.satisfyFn (fun d => c == d) ("'" ++ toString c ++ "'")) trailingWs
Equations
- Lean.Parser.rawCh c trailingWs = { info := { collectTokens := id, collectKinds := id, firstTokens := Lean.Parser.FirstTokens.unknown }, fn := Lean.Parser.chFn c trailingWs }
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
- Lean.Parser.isQuotableCharDefault c = (c == Char.ofNat 92 || c == Char.ofNat 34 || c == Char.ofNat 39 || c == Char.ofNat 114 || c == Char.ofNat 110 || c == Char.ofNat 116)
Push (Syntax.node tk
into syntax stack
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
- Lean.Parser.identFnAux startPos tk r = Lean.Parser.identFnAux.parse startPos tk r
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
- Lean.Parser.peekToken c s = let tkc := s.cache.tokenCache; if (tkc.startPos == s.pos) = true then (s, Except.ok tkc.token) else Lean.Parser.peekTokenAux c s
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
- Lean.Parser.symbolFnAux sym errorMsg = Lean.Parser.satisfySymbolFn (fun s => s == sym) [errorMsg]
Equations
- Lean.Parser.symbolInfo sym = { collectTokens := fun tks => sym :: tks, collectKinds := id, firstTokens := Lean.Parser.FirstTokens.tokens [sym] }
Equations
- Lean.Parser.symbolFn sym = Lean.Parser.symbolFnAux sym ("'" ++ sym ++ "'")
Equations
- Lean.Parser.symbolNoAntiquot sym = let sym := String.trim sym; { info := Lean.Parser.symbolInfo sym, fn := Lean.Parser.symbolFn sym }
Equations
- Lean.Parser.checkTailNoWs prev = match Lean.Syntax.getTailInfo prev with | Lean.SourceInfo.original leading pos trailing endPos => trailing.stopPos == trailing.startPos | x => false
Check if the following token is the symbol or identifier sym
. Useful for
parsing local tokens that have not been added to the token table (but may have
been so by some unrelated code).
For example, the universe `max` Function is parsed using this combinator so that
it can still be used as an identifier outside of universe (but registering it
as a token in a Term Syntax would not break the universe Parser).
Equations
- One or more equations did not get rendered due to their size.
Equations
- Lean.Parser.nonReservedSymbolFn sym = Lean.Parser.nonReservedSymbolFnAux sym ("'" ++ sym ++ "'")
Equations
- One or more equations did not get rendered due to their size.
Equations
- Lean.Parser.nonReservedSymbolNoAntiquot sym includeIdent = let sym := String.trim sym; { info := Lean.Parser.nonReservedSymbolInfo sym includeIdent, fn := Lean.Parser.nonReservedSymbolFn sym }
Equations
- Lean.Parser.strAux sym errorMsg j = Lean.Parser.strAux.parse sym errorMsg j
Equations
- Lean.Parser.checkTailWs prev = match Lean.Syntax.getTailInfo prev with | Lean.SourceInfo.original leading pos trailing endPos => decide (trailing.stopPos > trailing.startPos) | x => false
Equations
- Lean.Parser.checkWsBeforeFn errorMsg x s = let prev := Array.back s.stxStack; if Lean.Parser.checkTailWs prev = true then s else Lean.Parser.ParserState.mkError s errorMsg
Equations
- Lean.Parser.checkWsBefore errorMsg = { info := Lean.Parser.epsilonInfo, fn := Lean.Parser.checkWsBeforeFn errorMsg }
Equations
- One or more equations did not get rendered due to their size.
Equations
- Lean.Parser.checkLinebreakBeforeFn errorMsg x s = let prev := Array.back s.stxStack; if Lean.Parser.checkTailLinebreak prev = true then s else Lean.Parser.ParserState.mkError s errorMsg
Equations
- Lean.Parser.checkLinebreakBefore errorMsg = { info := Lean.Parser.epsilonInfo, fn := Lean.Parser.checkLinebreakBeforeFn errorMsg }
Equations
- Lean.Parser.checkNoWsBeforeFn errorMsg x s = let prev := Lean.Parser.pickNonNone s.stxStack; if Lean.Parser.checkTailNoWs prev = true then s else Lean.Parser.ParserState.mkError s errorMsg
Equations
- Lean.Parser.checkNoWsBefore errorMsg = { info := Lean.Parser.epsilonInfo, fn := Lean.Parser.checkNoWsBeforeFn errorMsg }
Equations
- Lean.Parser.unicodeSymbolFnAux sym asciiSym expected = Lean.Parser.satisfySymbolFn (fun s => s == sym || s == asciiSym) expected
Equations
- Lean.Parser.unicodeSymbolInfo sym asciiSym = { collectTokens := fun tks => sym :: asciiSym :: tks, collectKinds := id, firstTokens := Lean.Parser.FirstTokens.tokens [sym, asciiSym] }
Equations
- Lean.Parser.unicodeSymbolFn sym asciiSym = Lean.Parser.unicodeSymbolFnAux sym asciiSym ["'" ++ sym ++ "', '" ++ asciiSym ++ "'"]
Equations
- One or more equations did not get rendered due to their size.
Equations
- Lean.Parser.mkAtomicInfo k = { collectTokens := id, collectKinds := id, firstTokens := Lean.Parser.FirstTokens.tokens [k] }
Equations
- One or more equations did not get rendered due to their size.
Equations
- Lean.Parser.numLitNoAntiquot = { info := Lean.Parser.mkAtomicInfo "num", fn := Lean.Parser.numLitFn }
Equations
- One or more equations did not get rendered due to their size.
Equations
- Lean.Parser.scientificLitNoAntiquot = { info := Lean.Parser.mkAtomicInfo "scientific", fn := Lean.Parser.scientificLitFn }
Equations
- One or more equations did not get rendered due to their size.
Equations
- Lean.Parser.strLitNoAntiquot = { info := Lean.Parser.mkAtomicInfo "str", fn := Lean.Parser.strLitFn }
Equations
- One or more equations did not get rendered due to their size.
Equations
- Lean.Parser.charLitNoAntiquot = { info := Lean.Parser.mkAtomicInfo "char", fn := Lean.Parser.charLitFn }
Equations
- One or more equations did not get rendered due to their size.
Equations
- Lean.Parser.nameLitNoAntiquot = { info := Lean.Parser.mkAtomicInfo "name", fn := Lean.Parser.nameLitFn }
Equations
- One or more equations did not get rendered due to their size.
Equations
- Lean.Parser.identNoAntiquot = { info := Lean.Parser.mkAtomicInfo "ident", fn := Lean.Parser.identFn }
Equations
- Lean.Parser.rawIdentNoAntiquot = { info := { collectTokens := id, collectKinds := id, firstTokens := Lean.Parser.FirstTokens.unknown }, fn := Lean.Parser.rawIdentFn }
Equations
- One or more equations did not get rendered due to their size.
Equations
- Lean.Parser.identEq id = { info := Lean.Parser.mkAtomicInfo "ident", fn := Lean.Parser.identEqFn id }
Equations
- Lean.Parser.ParserState.keepTop s startStackSize = let node := Array.back s; Array.push (Array.shrink s startStackSize) node
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
- Lean.Parser.ParserState.replaceLongest s startStackSize = Lean.Parser.ParserState.keepLatest s startStackSize
Equations
- Lean.Parser.invalidLongestMatchParser s = Lean.Parser.ParserState.mkError s "longestMatch parsers must generate exactly one Syntax node"
Auxiliary function used to execute parsers provided to longestMatchFn
.
Push left?
into the stack if it is not none
, and execute p
.
Remark: p
must produce exactly one syntax node.
Remark: the left?
is not none when we are processing trailing parsers.
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
- Lean.Parser.longestMatchMkResult startSize s = if Lean.Parser.ParserState.stackSize s > startSize + 1 then Lean.Parser.ParserState.mkNode s Lean.choiceKind startSize else s
Equations
- Lean.Parser.longestMatchFnAux left? startSize startLhsPrec startPos prevPrio ps = Lean.Parser.longestMatchFnAux.parse left? startSize startLhsPrec startPos prevPrio ps
Equations
- One or more equations did not get rendered due to their size.
- Lean.Parser.longestMatchFnAux.parse left? startSize startLhsPrec startPos prevPrio [] = fun x s => Lean.Parser.longestMatchMkResult startSize s
Equations
- One or more equations did not get rendered due to their size.
Equations
- Lean.Parser.anyOfFn [] _fun_discr _fun_discr = Lean.Parser.ParserState.mkError _fun_discr "anyOf: empty list"
- Lean.Parser.anyOfFn [p] _fun_discr _fun_discr = Lean.Parser.Parser.fn p _fun_discr _fun_discr
- Lean.Parser.anyOfFn (p :: ps) _fun_discr _fun_discr = Lean.Parser.orelseFn p.fn (Lean.Parser.anyOfFn ps) _fun_discr _fun_discr
Equations
- One or more equations did not get rendered due to their size.
Equations
- Lean.Parser.checkColGe errorMsg = { info := { collectTokens := id, collectKinds := id, firstTokens := Lean.Parser.FirstTokens.unknown }, fn := Lean.Parser.checkColGeFn errorMsg }
Equations
- One or more equations did not get rendered due to their size.
Equations
- Lean.Parser.checkColGt errorMsg = { info := { collectTokens := id, collectKinds := id, firstTokens := Lean.Parser.FirstTokens.unknown }, fn := Lean.Parser.checkColGtFn errorMsg }
Equations
- One or more equations did not get rendered due to their size.
Equations
- Lean.Parser.checkLineEq errorMsg = { info := { collectTokens := id, collectKinds := id, firstTokens := Lean.Parser.FirstTokens.unknown }, fn := Lean.Parser.checkLineEqFn errorMsg }
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
- Lean.Parser.eoiFn c s = let i := s.pos; if String.atEnd c.toInputContext.input i = true then s else Lean.Parser.ParserState.mkError s "expected end of file"
Equations
- Lean.Parser.eoi = { info := { collectTokens := id, collectKinds := id, firstTokens := Lean.Parser.FirstTokens.unknown }, fn := Lean.Parser.eoiFn }
A multimap indexed by tokens. Used for indexing parsers by their leading token.
Equations
Equations
- Lean.Parser.TokenMap.insert map k v = match Std.RBMap.find? map k with | none => Std.RBMap.insert map k [v] | some vs => Std.RBMap.insert map k (v :: vs)
Equations
- Lean.Parser.TokenMap.instInhabitedTokenMap = { default := Std.RBMap.empty }
Equations
- Lean.Parser.TokenMap.instEmptyCollectionTokenMap = { emptyCollection := Std.RBMap.empty }
- leadingTable : Lean.Parser.TokenMap (Lean.Parser.Parser × Nat)
- leadingParsers : List (Lean.Parser.Parser × Nat)
- trailingTable : Lean.Parser.TokenMap (Lean.Parser.Parser × Nat)
- trailingParsers : List (Lean.Parser.Parser × Nat)
Equations
- Lean.Parser.instInhabitedPrattParsingTables = { default := { leadingTable := ∅, leadingParsers := [], trailingTable := ∅, trailingParsers := [] } }
Equations
- Lean.Parser.instBEqLeadingIdentBehavior = { beq := [anonymous] }
Equations
- Lean.Parser.instReprLeadingIdentBehavior = { reprPrec := [anonymous] }
- tables : Lean.Parser.PrattParsingTables
- behavior : Lean.Parser.LeadingIdentBehavior
Each parser category is implemented using a Pratt's parser.
The system comes equipped with the following categories: level
, term
, tactic
, and command
.
Users and plugins may define extra categories.
The method
categoryParser `term prec
executes the Pratt's parser for category term
with precedence prec
.
That is, only parsers with precedence at least prec
are considered.
The method termParser prec
is equivalent to the method above.
Equations
- Lean.Parser.instInhabitedParserCategory = { default := { tables := default, behavior := default } }
Equations
- One or more equations did not get rendered due to their size.
Equations
Equations
- Lean.Parser.categoryParserFn catName ctx s = Lean.EnvExtension.getState Lean.Parser.categoryParserFnExtension ctx.toParserModuleContext.env catName ctx s
Equations
- One or more equations did not get rendered due to their size.
Equations
- Lean.Parser.termParser prec = Lean.Parser.categoryParser `term prec
Fail if previous token is immediately followed by ':'.
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
- Lean.Parser.setExpected expected p = { info := p.info, fn := Lean.Parser.setExpectedFn expected p.fn }
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
- Lean.Parser.antiquotExpr = HOrElse.hOrElse Lean.Parser.identNoAntiquot fun x => HOrElse.hOrElse (Lean.Parser.symbolNoAntiquot "_") fun x => Lean.Parser.antiquotNestedExpr
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
Equations
- Lean.Parser.instCoeStringParser = { coe := fun s => Lean.Parser.symbol s }
Equations
- Lean.Parser.nonReservedSymbol sym includeIdent = Lean.Parser.tokenWithAntiquot (Lean.Parser.nonReservedSymbolNoAntiquot sym includeIdent)
Equations
- Lean.Parser.unicodeSymbol sym asciiSym = Lean.Parser.tokenWithAntiquot (Lean.Parser.unicodeSymbolNoAntiquot sym asciiSym)
Define parser for $e
(if anonymous == true) and $e:name
.
kind
is embedded in the antiquotation's kind, and checked at syntax match
unless isPseudoKind
is false.
Antiquotations can be escaped as in $$e
, which produces the syntax tree for $e
.
Equations
- One or more equations did not get rendered due to their size.
Equations
- Lean.Parser.withAntiquotFn antiquotP p c s = if (String.get c.toInputContext.input s.pos == Char.ofNat 36) = true then Lean.Parser.orelseFnCore antiquotP p false c s else p c s
Optimized version of mkAntiquot ... <|> p
.
Equations
- Lean.Parser.withAntiquot antiquotP p = { info := Lean.Parser.orelseInfo antiquotP.info p.info, fn := Lean.Parser.withAntiquotFn antiquotP.fn p.fn }
Equations
- Lean.Parser.withoutInfo p = { info := { collectTokens := id, collectKinds := id, firstTokens := Lean.Parser.FirstTokens.unknown }, fn := p.fn }
Parse $[p]suffix
, e.g. $[p],*
.
Equations
- One or more equations did not get rendered due to their size.
Parse suffix
after an antiquotation, e.g. $x,*
, and put both into a new node.
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
- Lean.Parser.nodeWithAntiquot name kind p anonymous = Lean.Parser.withAntiquot (Lean.Parser.mkAntiquot name kind anonymous) (Lean.Parser.node kind p)
Equations
- Lean.Parser.sepByElemParser p sep = Lean.Parser.withAntiquotSpliceAndSuffix `sepBy p (Lean.Parser.symbol (String.trim sep ++ "*"))
Equations
- Lean.Parser.sepBy p sep psep allowTrailingSep = Lean.Parser.sepByNoAntiquot (Lean.Parser.sepByElemParser p sep) psep allowTrailingSep
Equations
- Lean.Parser.sepBy1 p sep psep allowTrailingSep = Lean.Parser.sepBy1NoAntiquot (Lean.Parser.sepByElemParser p sep) psep allowTrailingSep
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
- Lean.Parser.leadingParser kind tables behavior antiquotParser = Lean.Parser.withAntiquotFn antiquotParser (Lean.Parser.leadingParserAux kind tables behavior)
Equations
- Lean.Parser.trailingLoopStep tables left ps c s = Lean.Parser.longestMatchFn (some left) (ps ++ tables.trailingParsers) c s
Implements a variant of Pratt's algorithm. In Pratt's algorithms tokens have a right and left binding power.
In our implementation, parsers have precedence instead. This method selects a parser (or more, via
longestMatchFn
) from leadingTable
based on the current token. Note that the unindexed leadingParsers
parsers
are also tried. We have the unidexed leadingParsers
because some parsers do not have a "first token". Example:
syntax term:51 "≤" ident "<" term "|" term : index
Example, in principle, the set of first tokens for this parser is any token that can start a term, but this set
is always changing. Thus, this parsing rule is stored as an unindexed leading parser at leadingParsers
.
After processing the leading parser, we chain with parsers from trailingTable
/trailingParsers
that have precedence
at least c.prec
where c
is the ParsingContext
. Recall that c.prec
is set by categoryParser
.
Note that in the original Pratt's algorith, precedences are only checked before calling trailing parsers. In our implementation, leading and trailing parsers check the precendece. We claim our algorithm is more flexible, modular and easier to understand.
antiquotParser
should be a mkAntiquot
parser (or always fail) and is tried before all other parsers.
It should not be added to the regular leading parsers because it would heavily
overlap with antiquotation parsers nested inside them.
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
- Lean.Parser.fieldIdx = Lean.Parser.withAntiquot (Lean.Parser.mkAntiquot "fieldIdx" `fieldIdx true) { info := Lean.Parser.mkAtomicInfo "fieldIdx", fn := Lean.Parser.fieldIdxFn }
Equations
- Lean.Parser.skip = { info := Lean.Parser.epsilonInfo, fn := fun x s => s }
Equations
- Lean.Syntax.foldArgsM s f b = Array.foldlM (flip f) b (Lean.Syntax.getArgs s) 0 (Array.size (Lean.Syntax.getArgs s))
Equations
- Lean.Syntax.foldArgs s f b = Id.run (Lean.Syntax.foldArgsM s f b)
Equations
- Lean.Syntax.forArgsM s f = Lean.Syntax.foldArgsM s (fun s x => f s) ()