///|
fn Parser::consume_array_hlines(
self : Parser,
) -> Array[Bool] raise ParseFailure {
let lines : Array[Bool] = []
self.consume_spaces()
while self.fetch().text == "\\hline" || self.fetch().text == "\\hdashline" {
let dashed = self.fetch().text == "\\hdashline"
self.consume()
lines.push(dashed)
self.consume_spaces()
}
lines
}
///|
fn Parser::take_array_tag(
self : Parser,
auto_tag : Bool?,
) -> (Array[ParseNode]?, Bool) raise ParseFailure {
guard auto_tag is Some(automatic) else { (None, false) }
guard self.gullet.macros.get("\\df@tag") is Some(_) else { (None, automatic) }
let tag = self.subparse([Token::make("\\df@tag")])
self.gullet.macros.set("\\df@tag", None, global=true)
(Some(tag), false)
}
///|
fn Parser::parse_array_environment(
self : Parser,
options : ArrayEnvironmentOptions,
) -> ParseNode raise ParseFailure {
self.gullet.begin_group()
self.gullet.macros.set("\\cr", Some(MacroDefinition::text("\\\\\\relax")))
self.gullet.begin_group()
let result : Result[ParseNode, ParseFailure] = capture_parse_result(() => {
let body : Array[Array[ParseNode]] = [[]]
let row_gaps : Array[Measurement?] = []
let hlines_before_row : Array[Array[Bool]] = [self.consume_array_hlines()]
let tags : Array[Array[ParseNode]?] = []
let auto_tags : Array[Bool] = []
for ;; {
let cell_body = self.parse_expression(false, Some("\\\\"))
let cell : ParseNode = Styling(
mode=self.mode,
body=[
OrdGroup(mode=self.mode, loc=None, body=cell_body, semisimple=false),
],
style=options.cell_style,
reset_font=true,
)
self.gullet.end_group()
self.gullet.begin_group()
guard body.last() is Some(row) else {
raise InternalInvariant(message="Missing array row")
}
row.push(cell)
match self.fetch().text {
"&" =>
match options.max_columns {
Some(maximum) if body.last() is Some(row) && row.length() >= maximum =>
raise InvalidArgument(
message="Too many tab characters: &",
loc=None,
)
_ => self.consume()
}
"\\end" => {
let (tag, automatic) = self.take_array_tag(options.auto_tag)
if options.auto_tag is Some(_) {
tags.push(tag)
auto_tags.push(automatic)
}
break
}
"\\\\" => {
guard !options.single_row else {
raise InvalidArgument(message="Expected \\end", loc=None)
}
self.consume()
let gap = if self.gullet.future().text == " " {
None
} else {
match self.parse_size_group(true) {
Some(Size(value~, ..)) => Some(value)
Some(_) =>
raise InternalInvariant(message="Expected array row gap")
None => None
}
}
row_gaps.push(gap)
let (tag, automatic) = self.take_array_tag(options.auto_tag)
if options.auto_tag is Some(_) {
tags.push(tag)
auto_tags.push(automatic)
}
hlines_before_row.push(self.consume_array_hlines())
body.push([])
}
token =>
raise InvalidArgument(
message="Expected & or \\\\ or \\end, got " + token,
loc=None,
)
}
}
if hlines_before_row.length() < body.length() + 1 {
hlines_before_row.push([])
}
Array(
mode=self.mode,
body~,
add_jot=options.add_jot,
array_stretch=options.array_stretch,
columns=options.columns,
row_gaps~,
hskip_before_and_after=options.hskip_before_and_after,
hlines_before_row~,
column_separation_type=options.column_separation_type,
tags=if options.auto_tag is Some(_) { Some(tags) } else { None },
auto_tags=if options.auto_tag is Some(_) { Some(auto_tags) } else { None },
leqno=options.leqno,
)
})
let close_cell : Result[Unit, ParseFailure] = capture_parse_result(() => {
self.gullet.end_group()
})
let close_array : Result[Unit, ParseFailure] = capture_parse_result(() => {
self.gullet.end_group()
})
match (result, close_cell, close_array) {
(Err(err), _, _) => raise err
(_, Err(err), _) => raise err
(_, _, Err(err)) => raise err
(Ok(node), Ok(_), Ok(_)) => node
}
}