///|
fn internal_node(context : FunctionContext) -> ParseNode {
Internal(mode=context.mode)
}
///|
fn check_control_sequence(token : Token) -> String raise ParseFailure {
match token.text {
"\\" | "{" | "}" | "$" | "&" | "#" | "^" | "_" | "EOF" =>
raise InvalidArgument(
message="Expected a control sequence",
loc=token.loc,
)
name => name
}
}
///|
fn macro_prefix_spec() -> FunctionSpec {
FunctionSpec::make(
["\\global", "\\long", "\\\\globallong"],
0,
allowed_in_text=true,
handler=macro_prefix_handler,
)
}
///|
fn prefixed_macro_name(prefix : String, next : String) -> String? {
let global = prefix == "\\global" || prefix == "\\\\globallong"
match next {
"\\global" => Some("\\global")
"\\long" => Some(if global { "\\\\globallong" } else { "\\long" })
"\\\\globallong" => Some("\\\\globallong")
"\\def" => Some(if global { "\\gdef" } else { "\\def" })
"\\gdef" => Some("\\gdef")
"\\edef" => Some(if global { "\\xdef" } else { "\\edef" })
"\\xdef" => Some("\\xdef")
"\\let" => Some(if global { "\\\\globallet" } else { "\\let" })
"\\futurelet" =>
Some(if global { "\\\\globalfuture" } else { "\\futurelet" })
_ => None
}
}
///|
fn macro_prefix_handler(
context : FunctionContext,
_ : Array[ParseNode],
_ : Array[ParseNode?],
) -> ParseNode raise ParseFailure {
(context.consume_spaces)()
let token = (context.pop_token)()
guard prefixed_macro_name(context.func_name, token.text) is Some(name) else {
raise InvalidArgument(
message="Invalid token after macro prefix",
loc=token.loc,
)
}
(context.parse_prefixed_function)(name)
}
///|
fn definition_spec() -> FunctionSpec {
FunctionSpec::make(
["\\def", "\\gdef", "\\edef", "\\xdef"],
0,
allowed_in_text=true,
primitive=true,
handler=definition_handler,
)
}
///|
fn definition_handler(
context : FunctionContext,
_ : Array[ParseNode],
_ : Array[ParseNode?],
) -> ParseNode raise ParseFailure {
let name = check_control_sequence((context.pop_token)())
let delimiters : Array[Array[String]] = [[]]
let mut count = 0
let mut insert : Token? = None
while (context.future_token)().text != "{" {
let token = (context.pop_token)()
if token.text == "#" {
if (context.future_token)().text == "{" {
let brace = (context.future_token)()
delimiters[count].push("{")
insert = Some(brace)
break
}
let number = (context.pop_token)()
if number.text.length() != 1 ||
number.text[0] < '1' ||
number.text[0] > '9' {
raise InvalidArgument(
message="Invalid argument number \"" + number.text + "\"",
loc=number.loc,
)
}
if number.text[0].to_int() - ('0' : UInt16).to_int() != count + 1 {
raise InvalidArgument(
message="Argument number \"" + number.text + "\" out of order",
loc=number.loc,
)
}
count = count + 1
delimiters.push([])
} else if token.text == "EOF" {
raise InvalidArgument(
message="Expected a macro definition",
loc=token.loc,
)
} else {
delimiters[count].push(token.text)
}
}
let mut tokens = (context.consume_macro_arg)()
match insert {
Some(token) => {
let prefixed = [token]
prefixed.append(tokens)
tokens = prefixed
}
None => ()
}
let tokens = if context.func_name == "\\edef" || context.func_name == "\\xdef" {
let expanded = (context.expand_tokens)(tokens)
expanded.rev_in_place()
expanded
} else {
tokens
}
(context.set_macro_definition)(
name,
MacroDefinition::expansion(
MacroExpansion::make(tokens, num_args=count, delimiters~),
),
context.func_name == "\\gdef" || context.func_name == "\\xdef",
)
internal_node(context)
}
///|
fn let_spec() -> FunctionSpec {
FunctionSpec::make(
["\\let", "\\\\globallet"],
0,
allowed_in_text=true,
primitive=true,
handler=let_handler,
)
}
///|
fn let_rhs(context : FunctionContext) -> Token raise ParseFailure {
let mut token = (context.pop_token)()
if token.text == "=" {
token = (context.pop_token)()
if token.text == " " {
token = (context.pop_token)()
}
}
token
}
///|
fn assign_let(
context : FunctionContext,
name : String,
token : Token,
global : Bool,
) -> Unit {
let definition = match (context.get_macro)(token.text) {
Some(value) => value
None => {
token.noexpand = true
MacroDefinition::expansion(
MacroExpansion::make(
[token],
unexpandable=!(context.is_expandable)(token.text),
),
)
}
}
(context.set_macro_definition)(name, definition, global)
}
///|
fn let_handler(
context : FunctionContext,
_ : Array[ParseNode],
_ : Array[ParseNode?],
) -> ParseNode raise ParseFailure {
let name = check_control_sequence((context.pop_token)())
(context.consume_spaces)()
assign_let(
context,
name,
let_rhs(context),
context.func_name == "\\\\globallet",
)
internal_node(context)
}
///|
fn futurelet_spec() -> FunctionSpec {
FunctionSpec::make(
["\\futurelet", "\\\\globalfuture"],
0,
allowed_in_text=true,
primitive=true,
handler=futurelet_handler,
)
}
///|
fn futurelet_handler(
context : FunctionContext,
_ : Array[ParseNode],
_ : Array[ParseNode?],
) -> ParseNode raise ParseFailure {
let name = check_control_sequence((context.pop_token)())
let middle = (context.pop_token)()
let token = (context.pop_token)()
assign_let(context, name, token, context.func_name == "\\\\globalfuture")
(context.push_token)(token)
(context.push_token)(middle)
internal_node(context)
}