///|
pub struct Position {
  line : Int
  column : Int
} derive(Eq)

///|
pub impl Show for Position with fn output(self, logger) {
  logger
  ..write_string("Position(line=")
  ..write_object(self.line)
  ..write_string(", column=")
  ..write_object(self.column)
  .write_char(')')
}

///|
pub suberror JsonParseError {
  InvalidChar(Position, Char)
  InvalidEof
  DepthLimitExceeded
  InvalidNumericLiteral(Position)
  InvalidNumericLiteralAtEof(Position)
  InvalidStringLiteral(Position)
  InvalidStringLiteralAtEof(Position)
} derive(Eq)

///|
pub impl Show for JsonParseError with fn output(self, logger) {
  match self {
    InvalidChar({ line, column }, c) =>
      logger
      ..write_string("Invalid character ")
      ..write_string(Repr(c).to_string())
      ..write_string(" at line ")
      ..write_object(line)
      ..write_string(", column ")
      .write_object(column)
    InvalidEof => logger.write_string("Unexpected end of file")
    DepthLimitExceeded => logger.write_string("Exceeds depth limit for parsing")
    InvalidNumericLiteral({ line, column }) =>
      logger
      ..write_string("Invalid numeric literal at line ")
      ..write_object(line)
      ..write_string(", column ")
      .write_object(column)
    InvalidNumericLiteralAtEof({ line, column }) =>
      logger
      ..write_string("Invalid numeric literal at EOF at line ")
      ..write_object(line)
      ..write_string(", column ")
      .write_object(column)
    InvalidStringLiteral({ line, column }) =>
      logger
      ..write_string("Invalid string literal; expected \", but got ' at line ")
      ..write_object(line)
      ..write_string(", column ")
      .write_object(column)
    InvalidStringLiteralAtEof({ line, column }) =>
      logger
      ..write_string(
        "Invalid string literal; expected \", but got ' at EOF at line ",
      )
      ..write_object(line)
      ..write_string(", column ")
      .write_object(column)
  }
}

///|
pub suberror CompileError {
  InvalidChar(Position, Char)
  InvalidEof
  InvalidNumber(Position, String)
  Diagnostics(Array[String])
} derive(Eq)

///|
pub impl Show for CompileError with fn output(self, logger) {
  match self {
    InvalidChar({ line, column }, c) =>
      logger
      ..write_string("Invalid character ")
      ..write_string(Repr(c).to_string())
      ..write_string(" at line ")
      ..write_object(line)
      ..write_string(", column ")
      .write_object(column)
    InvalidEof => logger.write_string("Unexpected end of file")
    InvalidNumber({ line, column }, s) =>
      logger
      ..write_string("Invalid number ")
      ..write_string(s)
      ..write_string(" at line ")
      ..write_object(line)
      ..write_string(", column ")
      .write_object(column)
    Diagnostics(lines) =>
      for i in 0.. 0 {
          logger.write_char('\n')
        }
        logger.write_string(lines[i])
      }
  }
}

///|
pub suberror RuntimeError {
  TypeError(String)
  Thrown(Json)
  UnknownFunction(String)
  UnknownVariable(String)
  BreakSignal(String, Array[Json])
  Partial(Array[Json], Json, String)
} derive(Eq)

///|
pub impl Show for RuntimeError with fn output(self, logger) {
  match self {
    TypeError(msg) => logger.write_string(msg)
    Thrown(value) =>
      match value {
        String(s) => logger.write_string(s)
        _ => logger.write_string(value.stringify())
      }
    UnknownFunction(name) => logger.write_string("Unknown function: " + name)
    UnknownVariable(name) => logger.write_string("Unknown variable: $" + name)
    BreakSignal(name, _) => logger.write_string("break $" + name)
    Partial(_, _, message) => logger.write_string(message)
  }
}

///|
pub struct CompiledFilter {
  priv filter : @core.Filter
}

///|
pub suberror RunError {
  Compile(CompileError)
  Runtime(RuntimeError)
} derive(Eq)

///|
pub impl Show for RunError with fn output(self, logger) {
  match self {
    Compile(err) => Show::output(err, logger)
    Runtime(err) => Show::output(err, logger)
  }
}

///|
pub suberror JsonTextRunError {
  JsonParse(JsonParseError)
  Compile(CompileError)
  Runtime(RuntimeError)
} derive(Eq)

///|
pub impl Show for JsonTextRunError with fn output(self, logger) {
  match self {
    JsonParse(err) => Show::output(err, logger)
    Compile(err) => Show::output(err, logger)
    Runtime(err) => Show::output(err, logger)
  }
}

///|
pub suberror CompiledJsonTextRunError {
  JsonParse(JsonParseError)
  Runtime(RuntimeError)
} derive(Eq)

///|
pub impl Show for CompiledJsonTextRunError with fn output(self, logger) {
  match self {
    JsonParse(err) => Show::output(err, logger)
    Runtime(err) => Show::output(err, logger)
  }
}

///|
fn compiled_json_text_run_error_from_core(
  input : @core.RunError,
) -> CompiledJsonTextRunError {
  match input {
    Parse(err) => JsonParse(json_parse_error_from_core(err))
    Eval(err) => Runtime(runtime_error_from_core(err))
    Filter(_) =>
      abort("unreachable: compiled JSON-text run does not recompile filters")
  }
}

///|
fn position_from_core(input : @core.Position) -> Position {
  { line: input.line, column: input.column }
}

///|
fn json_parse_error_from_core(input : @core.ParseError) -> JsonParseError {
  match input {
    InvalidChar(pos, ch) => InvalidChar(position_from_core(pos), ch)
    InvalidEof => InvalidEof
    DepthLimitExceeded => DepthLimitExceeded
    InvalidNumericLiteral(pos) => InvalidNumericLiteral(position_from_core(pos))
    InvalidNumericLiteralAtEof(pos) =>
      InvalidNumericLiteralAtEof(position_from_core(pos))
    InvalidStringLiteral(pos) => InvalidStringLiteral(position_from_core(pos))
    InvalidStringLiteralAtEof(pos) =>
      InvalidStringLiteralAtEof(position_from_core(pos))
  }
}

///|
fn compile_error_from_core(input : @core.FilterError) -> CompileError {
  match input {
    InvalidChar(pos, ch) => InvalidChar(position_from_core(pos), ch)
    InvalidEof => InvalidEof
    InvalidNumber(pos, raw) => InvalidNumber(position_from_core(pos), raw)
    Diagnostics(lines) => Diagnostics(lines)
  }
}

///|
fn runtime_error_from_core(input : @core.EvalError) -> RuntimeError {
  match input {
    TypeError(msg) => TypeError(msg)
    Thrown(value) => Thrown(@core.Value::to_json(value))
    UnknownFunction(name) => UnknownFunction(name)
    UnknownVariable(name) => UnknownVariable(name)
    BreakSignal(name, values) => BreakSignal(name, @core.values_to_json(values))
    Partial(values, catch_input, message) =>
      Partial(
        @core.values_to_json(values),
        @core.Value::to_json(catch_input),
        message,
      )
  }
}