/-
Copyright (c) 2018 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Sebastian Ullrich

Reader for the Lean language
-/
prelude
import init.lean.parser.parsec init.lean.parser.syntax init.lean.parser.macro
import init.lean.parser.identifier

namespace lean
-- TODO: enhance massively
def message := string

namespace parser

structure token_config :=
(«prefix» : string)
-- reading a token should not need any state
/- An optional parser that is activated after matching `prefix`.
   It should return a syntax tree with a "hole" for the
   `source_info` surrounding the token, which will be supplied
   by the `token` reader. -/
(token_reader : option (parsec (source_info → syntax)) := none)

structure reader_state :=
(tokens : list token_config)
(fatal : bool)
(errors : list lean.message)

def reader_state.empty : reader_state :=
⟨[], ff, []⟩

structure reader_config := mk

@[irreducible] def read_m := reader_t reader_config $ state_t reader_state $ parsec

structure reader :=
(read : read_m syntax)
(tokens : list token_config := [])

namespace read_m
local attribute [reducible] read_m
instance : monad read_m := infer_instance
instance : alternative read_m := infer_instance
instance : monad_reader reader_config read_m := infer_instance
instance : monad_state reader_state read_m := infer_instance
instance : monad_parsec read_m := infer_instance

--TODO(Sebastian): expose `reader_state.errors`
protected def run {α : Type} (cfg : reader_config) (st : reader_state) (s : string) (r : read_m α) :
  except parsec.message α :=
prod.fst <$> ((r.run cfg).run st).parse_with_eoi s
end read_m

namespace reader
open monad_parsec

protected def parse (cfg : reader_config) (s : string) (r : reader) :
  except parsec.message syntax :=
-- the only hardcoded tokens, because they are never directly mentioned by a `reader`
let tokens : list token_config := [⟨"/-", none⟩, ⟨"--", none⟩] in
r.read.run cfg ⟨r.tokens ++ tokens, ff, []⟩ s

def node (m : macro) (ps : list reader) : reader :=
{ read := do {
    args ← ps.mmap reader.read,
    pure $ syntax.node ⟨m.name, args⟩
  },
  tokens := ps.bind reader.tokens }

def many (p : reader) : reader :=
{ p with read := do
    args ← many p.read,
    pure $ syntax.node ⟨name.anonymous, args⟩ }

def many1 (p : reader) : reader :=
{ p with read := do
    args ← many1 p.read,
    pure $ syntax.node ⟨name.anonymous, args⟩ }

def optional (p : reader) : reader :=
{ p with read := do
    r ← optional p.read,
    pure $ match r with
    | some r := syntax.node ⟨name.anonymous, [r]⟩
    | none   := syntax.node ⟨name.anonymous, []⟩ }

end reader
end parser
end lean
