///|
fn environment_name(arg : ParseNode) -> String raise ParseFailure {
  guard arg is OrdGroup(body~, ..) else {
    raise InvalidArgument(message="Invalid environment name", loc=None)
  }
  let builder = StringBuilder()
  for node in body {
    guard node is TextOrd(text~, ..) else {
      raise InvalidArgument(message="Invalid environment name", loc=None)
    }
    builder.write_string(text)
  }
  builder.to_string()
}

///|
fn begin_end_spec() -> FunctionSpec {
  FunctionSpec::make(
    ["\\begin", "\\end"],
    1,
    arg_types=[TextArg],
    handler=environment_handler,
  )
}

///|
fn environment_handler(
  context : FunctionContext,
  args : Array[ParseNode],
  _ : Array[ParseNode?],
) -> ParseNode raise ParseFailure {
  let name = environment_name(require_function_arg(args, 0, context.func_name))
  if context.func_name == "\\begin" {
    (context.parse_environment)(name)
  } else {
    EnvironmentEnd(mode=context.mode, name~)
  }
}

///|
fn hline_spec() -> FunctionSpec {
  FunctionSpec::make(
    ["\\hline", "\\hdashline"],
    0,
    allowed_in_text=true,
    handler=hline_handler,
  )
}

///|
fn hline_handler(
  context : FunctionContext,
  _ : Array[ParseNode],
  _ : Array[ParseNode?],
) -> ParseNode raise ParseFailure {
  raise InvalidArgument(
    message=context.func_name + " valid only within array environment",
    loc=None,
  )
}