use(“atomy”)

require(“stringio”)

hl = require(“hl”) token = require(“hl/token”) ruby = hl load(“ruby”)

use(“anatomy/html”) base = require(“anatomy/base”) data = require(“anatomy/data”)

def = require(“define”)

fn(format-inline(tokens, link-only = nil)):

tokens collect [t]:
  data Element new(
    -- constants, identifiers, and operators
    if((link-only && link-only include?(t contents)) ||
        (!link-only && ["no", "nf", "n", "o"] include?(t type tag)))
      then:
        data ResolveElement new(
          [part]:
            tag = part find-tag(t contents)
            if(tag)
              then: data Reference new(tag, t contents)
              else: t contents)
      else: t contents
    .class(t type tag))

fn(format-block(tokens, link-only = nil)):

data Block new(
  base verbatim(format-inline(tokens, link-only))
  .class("highlight"))

fn(with-all-output-to(out, err) &action):

before-out = $stdout
before-err = $stderr

{ $stdout = out
  $stderr = err

  action call
} ensuring:
  $stdout = before-out
  $stderr = before-err

fn(interactive-line(bnd, input, line, context)):

out = StringIO new
err = StringIO new

output-tokens =
  with-restarts(use-tokens(ts): ts) {
    res =
      with-all-output-to(out, err):
        bnd eval(input)

    -- shorten the long-form inspections that show the ivars
    ruby lex(res inspect sub(r"#<([^\s]+)\s+@.*$", "#<\\1>"))
  } bind:
    (e & Error):
      restart(
        .use-tokens
        [ token Token new(
            token Tagged new(.gr)
            i"#{e name}: #{e message}")
        ])

[ format-inline(
    token Token new(token Tagged new(.caret), "> ") .
      ruby lex(input))
  "\n"
  out string
  unless(err string empty?):
    data Element new(err string, .class("gr"))
  format-inline(output-tokens)
  "\n"
]

environments = #{} fn(new-interaction-env): binding fn(take-environment(name)):

if(name)
  then:
    environments[name] ||= new-interaction-env
  else:
    new-interaction-env

def(hl(x)): base code(format-inline(ruby new(x) run))

def(ruby(x)): format-block(ruby new(x) run)

def(evaluate(code, where = nil)):

with-restarts(err(msg): data Element new(msg, .class("gr"))) {
  take-environment(where) eval(code)
  nil
} bind:
  (e & Error):
    /restart(.err, i"#{e name}: #{e message}")

def(interaction(x, where = nil)):

bnd = take-environment(where)

data Block new(
  data Block new(
    x split("\n") collect with-index [input, line]:
      interactive-line(bnd, input, line + 1, where)
    .tt)
  .class("interaction"))

def(example(x, where = nil)):

data Block new(
  [ data Paragraph new([data Element new("Example:", .italic)])
    interaction(x, where)
  ]
  .class("example"))

def(example-segment(x)):

data Block new(
  [ data Paragraph new([data Element new("Example:", .italic)])
    format-block(ruby new(x) run)
  ]
  .class("example"))

def(define(what, *rules, returns, body)):

thumb = what to-ast

message-name = thumb name to-s

display = format-inline(ruby lex(what), [message-name])
data Block new(
  [ base target-element(message-name)
    data Block new(
      [ data Block new(
          [ display
            data Element new(
              " => "
              .class("definition_result_arrow"))
            format-inline(ruby lex(returns))
            rules collect [rule]:
              ["\n  | ", format-inline(ruby lex(rule))]
          ]
          .tt)
      ]
      .class("thumb"))
    body
  ]
  .class("definition"))

def(assign(name, to, body)):

display = format-inline(ruby lex(name), [name])
data Block new(
  [ base target-element(name)
    data Block new(
      [ data Block new(
          [ display
            data Element new(
              " = "
              .class("definition_result_arrow"))
            format-inline(ruby lex(to))
          ]
          .tt)
      ]
      .class("thumb"))
    body
  ]
  .class("definition", "assign"))