/- Copyright (c) 2018 Microsoft Corporation. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Author: Sebastian Ullrich Tokenizer for the Lean language Even though our reader architecture does not statically depend on a tokenizer but works directly on the input string, we still use a "tokenizer" parser in the Lean reader in some circumstances: * to distinguish between identifiers and keywords * for error recovery: advance until next command token * ...? -/ prelude import init.lean.parser.reader.basic init.util namespace lean.parser namespace reader open lean.parser.monad_parser open string def match_token : reader (option token_config) := do st ← get, it ← left_over, -- the slowest longest prefix matcher on Earth pure $ st.tokens.foldl (λ acc tk, if tk.prefix.mk_iterator.is_prefix_of_remaining it then match acc with | some tk' := if tk.prefix > tk'.prefix then tk else tk' | none := tk else acc) none private def finish_comment_block_aux : nat → nat → reader unit | nesting (n+1) := str "/-" *> finish_comment_block_aux (nesting + 1) n <|> str "-/" *> if nesting = 1 then pure () else finish_comment_block_aux (nesting - 1) n | _ _ := error def finish_comment_block (nesting := 1) : reader unit := remaining >>= finish_comment_block_aux nesting private def whitespace_aux : nat → reader unit | (n+1) := do start ← pos, tk ← whitespace *> match_token, (match tk with | some ⟨"--", _⟩ := str "--" *> take_while' (= '\n') *> whitespace_aux n | some ⟨"/-", _⟩ := str "/-" *> finish_comment_block *> whitespace_aux n | _ := pure ()) | 0 := error /-- Skip whitespace and comments. -/ --TODO(Sebastian): store whitespace prefix and suffix in syntax objects def whitespace : reader unit := -- every `whitespace_aux` loop reads at least one char do r ← remaining, whitespace_aux r /-- Match a string literally without consulting the token table. -/ def raw_symbol (sym : string) : reader syntax := do start ← pos, try (whitespace *> str sym), stop ← pos, pure $ syntax.atom ⟨some ⟨start, stop⟩, atomic_val.string sym⟩ --TODO(Sebastian): other bases private def number' (start : position) : reader syntax := do num ← take_while1 char.is_digit, stop ← pos, pure $ syntax.node ⟨`base10_lit, [syntax.atom ⟨some ⟨start, stop⟩, atomic_val.string num⟩]⟩ private def ident' (start : position) : reader syntax := do n ← identifier, stop ← pos, pure $ syntax.ident ⟨some ⟨start, stop⟩, n, none, none⟩ def token : reader syntax := do start ← pos, tk ← whitespace *> match_token, match tk with -- constant-length token | some ⟨tk, none⟩ := do str tk, stop ← pos, pure $ syntax.atom ⟨some ⟨start, stop⟩, atomic_val.string tk⟩ -- variable-length token | some ⟨tk, some r⟩ := str tk *> monad_parser.lift (r start) | none := number' start <|> ident' start --TODO(Sebastian): error messages def symbol (sym : string) : reader syntax := try $ do stx@(syntax.atom ⟨_, atomic_val.string sym'⟩) ← token | error "" (dlist.singleton sym), when (sym ≠ sym') $ error "" (dlist.singleton sym), pure stx def keyword (kw : string) : reader syntax := symbol kw def number : reader syntax := try $ do stx@(syntax.node ⟨`base10_lit, _⟩) ← token | error, pure stx def ident : reader syntax := try $ do stx@(syntax.ident _) ← token | error, pure stx end reader end lean.parser