packages feed

atomo-0.4: prelude/repl.atomo

{ *repl* = this

  incomplete-input = ""

  evaluate-all: [] in: _ := error: @no-expressions
  evaluate-all: [e] in: t := t evaluate: e
  evaluate-all: (e . es) in: t :=
    { t evaluate: e
      evaluate-all: es in: t
    } call

  do-input: (s: String) in: env :=
    { parsed =
        { (incomplete-input .. s) parse-expressions
        } with-restarts: {
          incomplete ->
            { *repl* incomplete-input = incomplete-input .. s .. "\n"
              []
            } call
        } bind: {
          @(parse-error: (@(unexpected: e) . _) at: _) ->
            if: (e in?: ["", "ending"])
              then: { restart: 'incomplete }
              else: { *repl* incomplete-input = "" }

          @(parse-error: _ at: _) ->
            (*repl* incomplete-input = "")
        }

      parsed match: {
        [] -> @ok

        es ->
          { *repl* incomplete-input = ""
            (evaluate-all: es in: env) show print
          } call
      }
    } call

  get-value: t :=
    (interaction: "enter value: ") parse-expressions match: {
      [] -> get-value: t
      es -> evaluate-all: es in: t
    }

  get-n-values: 0 := []
  get-n-values: n :=
    (get-value: Lobby) . (get-n-values: (n - 1))

  repl-debugger =
    Object clone do:
      { run: e :=
          { Restart show-options-for: e

            { basic-repl } bind: {
              @prompt ->
                restart: 'use-prompt with: "[!]> "

              @(special: n) ->
                when: (n all?: @digit?) do: {
                  r = Restart get: (n to: Integer)
                  r jump: (get-n-values: r action arguments length)
                }
            }
          } call
      }

  (env: Object) basic-repl :=
    { prompt =
        { signal: @prompt; "> " } with-restarts: {
          use-prompt -> { p | p }
        }

      in =
        { interaction: prompt } handle: {
          @end-of-input -> { signal: @quit; "" } call
          @interrupt -> { signal: @quit; "" } call
        }

      in match: {
        $: . name ->
          signal: @(special: name)

        source ->
          { try :=
              { do-input: source in: env } with-restarts: {
                retry -> try
                abort -> @ok
              }

            try
          } call
      }

      signal: @loop
    } repeat

  (env: Object) repl :=
    { stop |
      with: *debugger* as: repl-debugger do: {
        frame = 0

        { env basic-repl } bind: {
          @prompt ->
            restart: 'use-prompt with: (
              if: incomplete-input empty?
                then: { "[" .. frame show .. "]> " }
                else: { "." .. ($. repeat: frame show length) .. ".. " }
             )

          @loop ->
            (super frame = frame + 1) when: incomplete-input empty?

          @(special: "h") ->
            ":h\thelp" print

          @quit ->
            { ask :=
                { "really quit? (y/n) " display
                  { read-char } (after: { "" print }) match: {
                    $y -> stop yield
                    $n -> @ok
                    _ -> ask
                  }
                } call

              ask
            } call
        }
      }
    } call/cc
} call