From 23d587c6992dfae4772bfa8c44caa90f24c07a48 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Mon, 24 Jul 2023 16:29:31 -0400 Subject: [PATCH] Implement parser for subscript/superscript with braces. --- src/parser/parser_context.rs | 18 ++++++ src/parser/subscript_and_superscript.rs | 83 +++++++++++++++++++++++++ 2 files changed, 101 insertions(+) diff --git a/src/parser/parser_context.rs b/src/parser/parser_context.rs index a4809e7..e7063e4 100644 --- a/src/parser/parser_context.rs +++ b/src/parser/parser_context.rs @@ -194,6 +194,18 @@ pub enum ContextElement<'r, 's> { /// unbalanced brackets can be detected in the middle of an /// object. InlineSourceBlockBracket(InlineSourceBlockBracket<'s>), + + /// Stores the current bracket or parenthesis depth inside a + /// superscript or superscript. + /// + /// Inside the braces of a subscript or superscript there must be + /// balanced braces {}, so this stores the amount of opening + /// braces subtracted by the amount of closing braces within the + /// definition must equal zero. + /// + /// A reference to the position in the string is also included so + /// unbalanced braces can be detected in the middle of an object. + SubscriptSuperscriptBrace(SubscriptSuperscriptBrace<'s>), } pub struct ExitMatcherNode<'r> { @@ -225,6 +237,12 @@ pub struct InlineSourceBlockBracket<'s> { pub depth: usize, } +#[derive(Debug)] +pub struct SubscriptSuperscriptBrace<'s> { + pub position: &'s str, + pub depth: usize, +} + impl<'r> std::fmt::Debug for ExitMatcherNode<'r> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let mut formatter = f.debug_struct("ExitMatcherNode"); diff --git a/src/parser/subscript_and_superscript.rs b/src/parser/subscript_and_superscript.rs index 1af3970..5ac1df7 100644 --- a/src/parser/subscript_and_superscript.rs +++ b/src/parser/subscript_and_superscript.rs @@ -16,7 +16,13 @@ use super::Object; use crate::error::CustomError; use crate::error::MyError; use crate::error::Res; +use crate::parser::exiting::ExitClass; +use crate::parser::object_parser::standard_set_object; +use crate::parser::parser_context::ContextElement; +use crate::parser::parser_context::ExitMatcherNode; +use crate::parser::parser_context::SubscriptSuperscriptBrace; use crate::parser::parser_with_context::parser_with_context; +use crate::parser::util::exit_matcher_parser; use crate::parser::util::get_consumed; use crate::parser::util::get_one_before; use crate::parser::Subscript; @@ -79,6 +85,9 @@ fn script_body<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, map(parser_with_context!(script_alphanum)(context), |body| { ScriptBody::Braceless(body) }), + map(parser_with_context!(script_with_braces)(context), |body| { + ScriptBody::WithBraces(body) + }), ))(input) } @@ -119,3 +128,77 @@ fn end_script_alphanum_character<'r, 's>( )))(remaining)?; Ok((remaining, final_char)) } + +#[tracing::instrument(ret, level = "debug")] +fn script_with_braces<'r, 's>( + context: Context<'r, 's>, + input: &'s str, +) -> Res<&'s str, Vec>> { + let (remaining, _) = tag("{")(input)?; + let parser_context = context + .with_additional_node(ContextElement::SubscriptSuperscriptBrace( + SubscriptSuperscriptBrace { + position: remaining, + depth: 0, + }, + )) + .with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { + class: ExitClass::Beta, + exit_matcher: &script_with_braces_end, + })); + + let (remaining, (children, _exit_contents)) = many_till( + parser_with_context!(standard_set_object)(&parser_context), + parser_with_context!(exit_matcher_parser)(&parser_context), + )(remaining)?; + + let (remaining, _) = tag("}")(remaining)?; + Ok((remaining, children)) +} + +#[tracing::instrument(ret, level = "debug")] +fn script_with_braces_end<'r, 's>( + context: Context<'r, 's>, + input: &'s str, +) -> Res<&'s str, &'s str> { + let context_depth = get_bracket_depth(context) + .expect("This function should only be called from inside a subscript or superscript."); + let text_since_context_entry = get_consumed(context_depth.position, input); + let mut current_depth = context_depth.depth; + for c in text_since_context_entry.chars() { + match c { + '{' => { + current_depth += 1; + } + '}' if current_depth == 0 => { + panic!("Exceeded subscript or superscript brace depth.") + } + '}' if current_depth > 0 => { + current_depth -= 1; + } + _ => {} + } + } + if current_depth == 0 { + let close_bracket = tag::<&str, &str, CustomError<&str>>("}")(input); + if close_bracket.is_ok() { + return close_bracket; + } + } + return Err(nom::Err::Error(CustomError::MyError(MyError( + "Not a valid end for subscript or superscript.", + )))); +} + +#[tracing::instrument(ret, level = "debug")] +fn get_bracket_depth<'r, 's>( + context: Context<'r, 's>, +) -> Option<&'r SubscriptSuperscriptBrace<'s>> { + for node in context.iter() { + match node.get_data() { + ContextElement::SubscriptSuperscriptBrace(depth) => return Some(depth), + _ => {} + } + } + None +}