From c4cc40f5e1f84e48d5cb634dbb7b86c98d461b58 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 11 Oct 2023 12:06:05 -0400 Subject: [PATCH] Parsing the affiliated keywords. --- src/context/global_settings.rs | 23 ++++++- src/parser/affiliated_keyword.rs | 104 ++++++++++++++++++++----------- src/types/affiliated_keyword.rs | 2 +- 3 files changed, 92 insertions(+), 37 deletions(-) diff --git a/src/context/global_settings.rs b/src/context/global_settings.rs index c9afffe..e011bd7 100644 --- a/src/context/global_settings.rs +++ b/src/context/global_settings.rs @@ -5,6 +5,9 @@ use super::constants::DEFAULT_ORG_ENTITIES; use super::constants::DEFAULT_ORG_LINK_PARAMETERS; use super::FileAccessInterface; use super::LocalFileAccessInterface; +use crate::context::constants::DEFAULT_ORG_ELEMENT_AFFILIATED_KEYWORDS; +use crate::context::constants::DEFAULT_ORG_ELEMENT_DUAL_KEYWORDS; +use crate::context::constants::DEFAULT_ORG_ELEMENT_KEYWORD_TRANSLATION_ALIST; use crate::context::constants::DEFAULT_ORG_ELEMENT_PARSED_KEYWORDS; use crate::types::IndentationLevel; use crate::types::Object; @@ -59,7 +62,22 @@ pub struct GlobalSettings<'g, 's> { /// Keywords that contain the standard set of objects (excluding footnote references). /// /// Corresponds to org-element-parsed-keywords elisp variable. - pub element_parsed_keywords: &'g [&'g str], + pub element_parsed_keywords: &'g [&'s str], + + /// Keywords that can have a secondary value in square brackets. + /// + /// Corresponds to org-element-dual-keywords elisp variable. + pub element_dual_keywords: &'g [&'s str], + + /// Keywords that can be affiliated with an element. + /// + /// Corresponds to org-element-affiliated-keywords elisp variable. + pub element_affiliated_keywords: &'g [&'s str], + + /// Mapping of keyword names. + /// + /// Corresponds to org-element-keyword-translation-alist elisp variable. + pub element_keyword_translation_alist: &'g [(&'s str, &'s str)], } pub const DEFAULT_TAB_WIDTH: IndentationLevel = 8; @@ -93,6 +111,9 @@ impl<'g, 's> GlobalSettings<'g, 's> { link_templates: BTreeMap::new(), entities: &DEFAULT_ORG_ENTITIES, element_parsed_keywords: &DEFAULT_ORG_ELEMENT_PARSED_KEYWORDS, + element_dual_keywords: &DEFAULT_ORG_ELEMENT_DUAL_KEYWORDS, + element_affiliated_keywords: &DEFAULT_ORG_ELEMENT_AFFILIATED_KEYWORDS, + element_keyword_translation_alist: &DEFAULT_ORG_ELEMENT_KEYWORD_TRANSLATION_ALIST, } } } diff --git a/src/parser/affiliated_keyword.rs b/src/parser/affiliated_keyword.rs index 0f88c77..96ce33d 100644 --- a/src/parser/affiliated_keyword.rs +++ b/src/parser/affiliated_keyword.rs @@ -1,11 +1,17 @@ use std::collections::BTreeMap; +use nom::combinator::all_consuming; +use nom::multi::many0; + +use super::object_parser::standard_set_object; +use crate::context::parser_with_context; +use crate::context::Context; +use crate::context::ContextElement; use crate::context::GlobalSettings; +use crate::context::List; use crate::types::AffiliatedKeywordValue; use crate::types::AffiliatedKeywords; use crate::types::Keyword; -use crate::types::Object; -use crate::types::PlainList; pub(crate) fn parse_affiliated_keywords<'g, 's>( global_settings: &'g GlobalSettings<'g, 's>, @@ -13,42 +19,70 @@ pub(crate) fn parse_affiliated_keywords<'g, 's>( ) -> AffiliatedKeywords<'s> { let mut ret = BTreeMap::new(); for kw in input.into_iter() { - // foo + let translated_name = translate_name(global_settings, kw.key); + if is_single_string_keyword(global_settings, translated_name) { + ret.insert( + translated_name, + AffiliatedKeywordValue::SingleString(kw.value), + ); + } else if is_list_of_objects_keyword(global_settings, translated_name) { + let initial_context = ContextElement::document_context(); + let initial_context = Context::new(global_settings, List::new(&initial_context)); + + // TODO: This should be omitting footnote references + let (_remaining, objects) = all_consuming(many0(parser_with_context!( + standard_set_object + )(&initial_context)))(kw.value.into()) + .expect("Object parser should always succeed."); + let list_of_lists = ret.entry(translated_name).or_insert_with(|| { + AffiliatedKeywordValue::ListOfListsOfObjects(Vec::with_capacity(1)) + }); + match list_of_lists { + AffiliatedKeywordValue::ListOfListsOfObjects(list_of_lists) => { + list_of_lists.push(objects); + } + _ => panic!("Invalid AffiliatedKeywordValue type."), + } + } else { + let list_of_strings = ret + .entry(translated_name) + .or_insert_with(|| AffiliatedKeywordValue::ListOfStrings(Vec::with_capacity(1))); + match list_of_strings { + AffiliatedKeywordValue::ListOfStrings(list_of_strings) => { + list_of_strings.push(kw.value); + } + _ => panic!("Invalid AffiliatedKeywordValue type."), + } + } } AffiliatedKeywords { keywords: ret } } -// pub struct AffiliatedKeyword<'s> { -// name: &'s str, -// } +fn translate_name<'g, 's>(global_settings: &'g GlobalSettings<'g, 's>, name: &'s str) -> &'s str { + for (src, dst) in global_settings.element_keyword_translation_alist { + if name.eq_ignore_ascii_case(src) { + return *dst; + } + } + name +} -// pub enum AffiliatedKeywordValue<'s> { -// SingleString(&'s str), -// ListOfStrings(Vec<&'s str>), -// ListOfObjects(Vec>), -// } +fn is_single_string_keyword<'g, 's>( + _global_settings: &'g GlobalSettings<'g, 's>, + name: &'s str, +) -> bool { + // TODO: Is this defined by an elisp variable? I'm only seeing this done for plot. + name.eq_ignore_ascii_case("plot") +} -// pub trait GetAffiliatedKeywords<'s> { -// type ItemIterator: Iterator>; - -// fn get_affiliated_keywords<'r: 's>(&'r self) -> Self::ItemIterator; -// } - -// pub trait HandleAffiliatedKeywords<'s> { -// fn get_stuff(&self) -> &'s str; -// } - -// impl<'s> GetAffiliatedKeywords<'s> for PlainList<'s> { -// type ItemIterator = std::slice::Iter<'s, Keyword<'s>>; - -// fn get_affiliated_keywords<'r: 's>(&'r self) -> Self::ItemIterator { -// self.affiliated_keywords.iter() -// } -// } - -// impl<'s, I: GetAffiliatedKeywords<'s>> HandleAffiliatedKeywords<'s> for I { -// fn get_stuff(&self) -> &'s str { -// self.get_affiliated_keywords().count(); -// todo!() -// } -// } +fn is_list_of_objects_keyword<'g, 's>( + global_settings: &'g GlobalSettings<'g, 's>, + name: &'s str, +) -> bool { + for parsed_keyword in global_settings.element_parsed_keywords { + if name.eq_ignore_ascii_case(parsed_keyword) { + return true; + } + } + false +} diff --git a/src/types/affiliated_keyword.rs b/src/types/affiliated_keyword.rs index c16adf5..b409311 100644 --- a/src/types/affiliated_keyword.rs +++ b/src/types/affiliated_keyword.rs @@ -6,7 +6,7 @@ use super::Object; pub enum AffiliatedKeywordValue<'s> { SingleString(&'s str), ListOfStrings(Vec<&'s str>), - ListOfObjects(Vec>), + ListOfListsOfObjects(Vec>>), } #[derive(Debug)]