///|
fn Parser::switch_mode(self : Parser, mode : Mode) -> Unit {
guard self.mode != mode else { () }
self.mode = mode
self.gullet.switch_mode(mode)
}
///|
/// Unwraps a captured parse result together with its group-close step. The
/// close is checked first: when the parse already unbalanced the group, the
/// close error is the one that surfaces.
fn[V] unwrap_captured(
close : Result[Unit, ParseFailure],
value : Result[V, ParseFailure],
) -> V raise ParseFailure {
match (close, value) {
(Err(err), _) => raise err
(_, Err(err)) => raise err
(Ok(_), Ok(value)) => value
}
}
///|
/// Parses the arguments of a function. Optional arguments come first; a
/// missing optional argument contributes `None` to `opt_args`. The argument
/// type of each position comes from the function spec; when the spec does not
/// declare one, an untyped argument parses as an `OriginalArg` group — except
/// for primitive functions, and for the first mandatory argument when an
/// earlier optional argument was omitted (`primitive_after_missing_optional`),
/// which parse directly.
fn Parser::parse_arguments(
self : Parser,
func : String,
func_data : FunctionSpec,
) -> (Array[ParseNode], Array[ParseNode?]) raise ParseFailure {
let args : Array[ParseNode] = []
let opt_args : Array[ParseNode?] = []
let total_args = func_data.num_args + func_data.num_optional_args
for i in 0.. kind
None if func_data.primitive => PrimitiveArg
None if func_data.primitive_after_missing_optional is Some(optional_index) &&
i == func_data.num_optional_args &&
opt_args.get(optional_index) is Some(None) => PrimitiveArg
None => OriginalArg
}
match
self.parse_group_of_type("argument to '\{func}'", arg_type, optional) {
None if optional => {
opt_args.push(None)
continue
}
None =>
raise InternalInvariant(
message="Null mandatory function argument after parser validation",
)
Some(arg) if optional => {
opt_args.push(Some(arg))
continue
}
Some(arg) => {
args.push(arg)
continue
}
}
} nobreak {
(args, opt_args)
}
}
///|
fn Parser::parse_group_of_type(
self : Parser,
name : String,
arg_type : ArgType,
optional : Bool,
) -> ParseNode? raise ParseFailure {
match arg_type {
ColorArg => self.parse_color_group(optional)
SizeArg => self.parse_size_group(optional)
UrlArg => self.parse_url_group(optional)
RawArg =>
self
.parse_string_group(optional)
.map(token => Raw(mode=Text, string=token.text))
MathArg => self.parse_argument_group(optional, Some(Math))
TextArg => self.parse_argument_group(optional, Some(Text))
HboxArg =>
self
.parse_argument_group(optional, Some(Text))
.map(group => {
Styling(
mode=group.mode(),
body=[group],
style=TextStyle,
reset_font=true,
)
})
PrimitiveArg => self.parse_primitive_group(name, optional)
OriginalArg => self.parse_argument_group(optional, None)
}
}
///|
fn Parser::parse_primitive_group(
self : Parser,
name : String,
optional : Bool,
) -> ParseNode? raise ParseFailure {
guard !optional else {
raise InvalidArgument(
message="A primitive argument cannot be optional",
loc=None,
)
}
guard self.parse_group(name, None) is Some(EmitAtom(group)) else {
let token = self.fetch()
raise InvalidArgument(message="Expected group as \{name}", loc=token.loc)
}
Some(group)
}
///|
fn Parser::parse_argument_group(
self : Parser,
optional : Bool,
mode : Mode?,
) -> ParseNode? raise ParseFailure {
match self.gullet.scan_argument(optional) {
None => None
Some(arg_token) => {
let outer_mode = self.mode
if mode is Some(argument_mode) {
self.switch_mode(argument_mode)
}
self.gullet.begin_group()
let result : Result[ParseNode, ParseFailure] = capture_parse_result(() => {
let body = self.parse_expression(false, Some("EOF"))
self.expect("EOF")
OrdGroup(mode=self.mode, loc=arg_token.loc, body~, semisimple=false)
})
let close_result : Result[Unit, ParseFailure] = capture_parse_result(() => {
self.gullet.end_group()
})
self.switch_mode(outer_mode)
Some(unwrap_captured(close_result, result))
}
}
}