From 21aba13d7126f01ae4cc87746dfca662b4ced770 Mon Sep 17 00:00:00 2001 From: PoiScript Date: Thu, 27 Jun 2019 20:05:54 +0800 Subject: [PATCH] refactor(iter): use indextree::Traverse for iterating nodes --- examples/custom.rs | 21 +-- src/elements/mod.rs | 1 - src/export/html.rs | 232 +++++++++++--------------- src/export/mod.rs | 4 +- src/iter.rs | 384 ++------------------------------------------ src/lib.rs | 3 +- src/org.rs | 31 +--- 7 files changed, 124 insertions(+), 552 deletions(-) diff --git a/examples/custom.rs b/examples/custom.rs index 1ae53d2..a7a798b 100644 --- a/examples/custom.rs +++ b/examples/custom.rs @@ -5,8 +5,8 @@ use std::io::{Error as IOError, Write}; use std::result::Result; use std::string::FromUtf8Error; -use orgize::export::*; -use orgize::{Container, Org}; +use orgize::export::{html::Escape, DefaultHtmlHandler, HtmlHandler}; +use orgize::{Element, Org}; use slugify::slugify; #[derive(Debug)] @@ -32,22 +32,25 @@ impl From for MyError { struct MyHtmlHandler; impl HtmlHandler for MyHtmlHandler { - fn start(&mut self, mut w: W, container: Container<'_>) -> Result<(), MyError> { + fn start(&mut self, mut w: W, element: &Element<'_>) -> Result<(), MyError> { let mut default_handler = DefaultHtmlHandler; - match container { - Container::Headline(hdl) => { - if hdl.level > 6 { + match element { + Element::Headline { headline, .. } => { + if headline.level > 6 { return Err(MyError::Heading); } else { - let slugify = slugify!(hdl.title); + let slugify = slugify!(headline.title); write!( w, "{2}", - hdl.level, slugify, hdl.title, + headline.level, + slugify, + Escape(headline.title), )?; } } - _ => default_handler.start(w, container)?, + // fallthrough to default handler + _ => default_handler.start(w, element)?, } Ok(()) } diff --git a/src/elements/mod.rs b/src/elements/mod.rs index 665f76e..03eb75a 100644 --- a/src/elements/mod.rs +++ b/src/elements/mod.rs @@ -52,7 +52,6 @@ use indextree::NodeId; #[cfg_attr(feature = "serde", serde(tag = "type"))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] pub enum Element<'a> { - Root, Block { #[cfg_attr(feature = "serde", serde(flatten))] block: Block<'a>, diff --git a/src/export/html.rs b/src/export/html.rs index 8232a9b..bd52702 100644 --- a/src/export/html.rs +++ b/src/export/html.rs @@ -1,171 +1,127 @@ -#![allow(unused_variables)] -#![allow(unused_mut)] - -use crate::elements::*; -use crate::iter::Container; +use crate::elements::Element; use jetscii::bytes; +use std::fmt; use std::io::{Error, Write}; -pub trait HtmlHandler> { - fn escape(&mut self, mut w: W, text: &str) -> Result<(), E> { +pub struct Escape<'a>(pub &'a str); + +impl fmt::Display for Escape<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut pos = 0; - let bytes = text.as_bytes(); + let bytes = self.0.as_bytes(); while let Some(off) = bytes!(b'<', b'>', b'&', b'\'', b'"').find(&bytes[pos..]) { - w.write_all(&bytes[pos..pos + off])?; + write!(f, "{}", &self.0[pos..pos + off])?; pos += off + 1; - match text.as_bytes()[pos - 1] { - b'<' => w.write_all(b"<")?, - b'>' => w.write_all(b">")?, - b'&' => w.write_all(b"&")?, - b'\'' => w.write_all(b"'")?, - b'"' => w.write_all(b""")?, + match bytes[pos - 1] { + b'<' => write!(f, "<")?, + b'>' => write!(f, ">")?, + b'&' => write!(f, "&")?, + b'\'' => write!(f, "'")?, + b'"' => write!(f, """)?, _ => unreachable!(), } } - Ok(w.write_all(&bytes[pos..])?) + write!(f, "{}", &self.0[pos..]) } - fn start(&mut self, mut w: W, container: Container) -> Result<(), E> { - match container { - Container::Block(block) => write!(w, "
")?, - Container::Bold => write!(w, "")?, - Container::Document => write!(w, "
")?, - Container::DynBlock(_) => (), - Container::Headline(hdl) => { - let level = if hdl.level <= 6 { hdl.level } else { 6 }; - write!(&mut w, "", level)?; - self.text(&mut w, hdl.title)?; - write!(&mut w, "", level)?; +} + +pub trait HtmlHandler> { + fn start(&mut self, mut w: W, element: &Element) -> Result<(), E> { + use Element::*; + + match element { + // container elements + Block { .. } => write!(w, "
")?, + Bold { .. } => write!(w, "")?, + Document { .. } => write!(w, "
")?, + DynBlock { .. } => (), + Headline { headline, .. } => { + let level = if headline.level <= 6 { + headline.level + } else { + 6 + }; + write!(w, "{1}", level, Escape(headline.title))?; } - Container::Italic => write!(w, "")?, - Container::List(list) => { + List { list, .. } => { if list.ordered { write!(w, "
    ")?; } else { write!(w, "
      ")?; } } - Container::ListItem(_) => write!(w, "
    • ")?, - Container::Paragraph => write!(w, "

      ")?, - Container::Section => write!(w, "

      ")?, - Container::Strike => write!(w, "")?, - Container::Underline => write!(w, "")?, + Italic { .. } => write!(w, "")?, + ListItem { .. } => write!(w, "
    • ")?, + Paragraph { .. } => write!(w, "

      ")?, + Section { .. } => write!(w, "

      ")?, + Strike { .. } => write!(w, "")?, + Underline { .. } => write!(w, "")?, + // non-container elements + BabelCall { .. } => (), + InlineSrc { inline_src, .. } => write!(w, "{}", Escape(inline_src.body))?, + Code { value, .. } => write!(w, "{}", Escape(value))?, + FnRef { .. } => (), + InlineCall { .. } => (), + Link { link, .. } => write!( + w, + "{}", + Escape(link.path), + Escape(link.desc.unwrap_or(link.path)), + )?, + Macros { .. } => (), + Planning { .. } => (), + RadioTarget { .. } => (), + Snippet { snippet, .. } => { + if snippet.name.eq_ignore_ascii_case("HTML") { + write!(w, "{}", snippet.value)?; + } + } + Target { .. } => (), + Text { value, .. } => write!(w, "{}", Escape(value))?, + Timestamp { .. } => (), + Verbatim { value, .. } => write!(&mut w, "{}", Escape(value))?, + FnDef { .. } => (), + Clock { .. } => (), + Comment { value, .. } => write!(w, "", Escape(value))?, + FixedWidth { value, .. } => write!(w, "
      {}
      ", Escape(value))?, + Keyword { .. } => (), + Drawer { .. } => (), + Rule { .. } => write!(w, "
      ")?, + Cookie { .. } => (), } + Ok(()) } - fn end(&mut self, mut w: W, container: Container) -> Result<(), E> { - match container { - Container::Block(block) => write!(w, "
")?, - Container::Bold => write!(w, "")?, - Container::Document => write!(w, "
")?, - Container::DynBlock(_) => (), - Container::Headline(_) => (), - Container::Italic => write!(w, "")?, - Container::List(list) => { + fn end(&mut self, mut w: W, element: &Element) -> Result<(), E> { + use Element::*; + + match element { + // container elements + Block { .. } => write!(w, "
")?, + Bold { .. } => write!(w, "")?, + Document { .. } => write!(w, "")?, + DynBlock { .. } => (), + Headline { .. } => (), + List { list, .. } => { if list.ordered { write!(w, "")?; } else { write!(w, "")?; } } - Container::ListItem(_) => write!(w, "")?, - Container::Paragraph => write!(w, "

")?, - Container::Section => write!(w, "")?, - Container::Strike => write!(w, "
")?, - Container::Underline => write!(w, "")?, + Italic { .. } => write!(w, "
")?, + ListItem { .. } => write!(w, "")?, + Paragraph { .. } => write!(w, "

")?, + Section { .. } => write!(w, "")?, + Strike { .. } => write!(w, "
")?, + Underline { .. } => write!(w, "")?, + // non-container elements + _ => (), } - Ok(()) - } - fn keyword(&mut self, mut w: W, keyword: &Keyword<'_>) -> Result<(), E> { - Ok(()) - } - fn drawer(&mut self, mut w: W, drawer: &Drawer<'_>) -> Result<(), E> { - Ok(()) - } - fn rule(&mut self, mut w: W) -> Result<(), E> { - Ok(write!(w, "
")?) - } - fn cookie(&mut self, mut w: W, cookie: &Cookie) -> Result<(), E> { - Ok(()) - } - fn fn_ref(&mut self, mut w: W, fn_ref: &FnRef<'_>) -> Result<(), E> { - Ok(()) - } - fn babel_call(&mut self, mut w: W, call: &BabelCall<'_>) -> Result<(), E> { - Ok(()) - } - fn inline_call(&mut self, mut w: W, call: &InlineCall<'_>) -> Result<(), E> { - Ok(()) - } - fn inline_src(&mut self, mut w: W, src: &InlineSrc<'_>) -> Result<(), E> { - write!(&mut w, "")?; - self.text(&mut w, src.body)?; - write!(&mut w, "")?; - Ok(()) - } - fn link(&mut self, mut w: W, link: &Link<'_>) -> Result<(), E> { - write!(&mut w, r#""#)?; - self.text(&mut w, link.desc.unwrap_or(link.path))?; - write!(&mut w, "")?; - Ok(()) - } - fn macros(&mut self, mut w: W, macros: &Macros<'_>) -> Result<(), E> { - Ok(()) - } - fn radio_target(&mut self, mut w: W, target: &RadioTarget<'_>) -> Result<(), E> { - Ok(()) - } - fn snippet(&mut self, mut w: W, snippet: &Snippet<'_>) -> Result<(), E> { - if snippet.name.eq_ignore_ascii_case("HTML") { - write!(w, "{}", snippet.value)?; - } - Ok(()) - } - fn target(&mut self, mut w: W, target: &Target<'_>) -> Result<(), E> { - Ok(()) - } - fn timestamp(&mut self, mut w: W, timestamp: &Timestamp) -> Result<(), E> { - Ok(()) - } - fn verbatim(&mut self, mut w: W, cont: &str) -> Result<(), E> { - write!(&mut w, "")?; - self.text(&mut w, cont)?; - write!(&mut w, "")?; - Ok(()) - } - fn code(&mut self, mut w: W, cont: &str) -> Result<(), E> { - write!(&mut w, "")?; - self.text(&mut w, cont)?; - write!(&mut w, "")?; - Ok(()) - } - fn text(&mut self, mut w: W, cont: &str) -> Result<(), E> { - self.escape(w, cont)?; - Ok(()) - } - fn planning(&mut self, mut w: W, planning: &Planning) -> Result<(), E> { - Ok(()) - } - fn clock(&mut self, mut w: W, clock: &Clock<'_>) -> Result<(), E> { - Ok(()) - } - fn fn_def(&mut self, mut w: W, fn_def: &FnDef<'_>) -> Result<(), E> { - Ok(()) - } - fn comment(&mut self, mut w: W, value: &str) -> Result<(), E> { - write!(&mut w, "")?; - Ok(()) - } - fn fixed_width(&mut self, mut w: W, value: &str) -> Result<(), E> { - write!(&mut w, "
")?;
-        self.text(&mut w, value)?;
-        write!(&mut w, "
")?; + Ok(()) } } diff --git a/src/export/mod.rs b/src/export/mod.rs index 4a0a220..fd87f01 100644 --- a/src/export/mod.rs +++ b/src/export/mod.rs @@ -1,3 +1,3 @@ -mod html; +pub mod html; -pub use html::*; +pub use html::{DefaultHtmlHandler, HtmlHandler}; diff --git a/src/iter.rs b/src/iter.rs index 973ec81..0c94517 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -1,389 +1,25 @@ -use indextree::{Arena, NodeId}; +use indextree::{Arena, NodeEdge, Traverse}; -use crate::elements::*; - -#[derive(Debug)] -pub enum Container<'a> { - Block(&'a Block<'a>), - Bold, - Document, - DynBlock(&'a DynBlock<'a>), - Headline(&'a Headline<'a>), - Italic, - List(&'a List), - ListItem(&'a ListItem<'a>), - Paragraph, - Section, - Strike, - Underline, -} +use crate::elements::Element; #[derive(Debug)] pub enum Event<'a> { - Start(Container<'a>), - End(Container<'a>), - Rule, - BabelCall(&'a BabelCall<'a>), - Clock(&'a Clock<'a>), - Cookie(&'a Cookie<'a>), - Drawer(&'a Drawer<'a>), - FnDef(&'a FnDef<'a>), - FnRef(&'a FnRef<'a>), - InlineCall(&'a InlineCall<'a>), - InlineSrc(&'a InlineSrc<'a>), - Keyword(&'a Keyword<'a>), - Link(&'a Link<'a>), - Macros(&'a Macros<'a>), - Planning(Planning<'a>), - RadioTarget(&'a RadioTarget<'a>), - Snippet(&'a Snippet<'a>), - Target(&'a Target<'a>), - Timestamp(&'a Timestamp<'a>), - Code(&'a str), - Comment(&'a str), - FixedWidth(&'a str), - Text(&'a str), - Verbatim(&'a str), -} - -enum State { - Start, - End, - Empty, - Finished, + Start(&'a Element<'a>), + End(&'a Element<'a>), } pub struct Iter<'a> { - arena: &'a Arena>, - node: NodeId, - state: State, -} - -impl<'a> Iter<'a> { - pub(crate) fn new(arena: &'a Arena>, node: NodeId) -> Self { - Iter { - arena, - node, - state: State::Start, - } - } - - fn start_event(&mut self) -> Option> { - let node = &self.arena[self.node]; - match &node.data { - Element::Root => { - self.state = State::Finished; - None - } - Element::BabelCall { call, .. } => { - self.state = State::Start; - Some(Event::BabelCall(call)) - } - Element::Verbatim { value, .. } => { - self.state = State::Start; - Some(Event::Verbatim(value)) - } - Element::Code { value, .. } => { - self.state = State::Start; - Some(Event::Code(value)) - } - Element::Text { value, .. } => { - self.state = State::Start; - Some(Event::Text(value)) - } - Element::Block { block, .. } => { - if node.first_child().is_none() { - self.state = State::Empty; - } else { - self.state = State::Start; - } - Some(Event::Start(Container::Block(block))) - } - Element::Bold { .. } => { - if node.first_child().is_none() { - self.state = State::Empty; - } else { - self.state = State::Start; - } - Some(Event::Start(Container::Bold)) - } - Element::Document { .. } => { - if node.first_child().is_none() { - self.state = State::Empty; - } else { - self.state = State::Start; - } - - Some(Event::Start(Container::Document)) - } - Element::DynBlock { dyn_block, .. } => { - if node.first_child().is_none() { - self.state = State::Empty; - } else { - self.state = State::Start; - } - Some(Event::Start(Container::DynBlock(dyn_block))) - } - Element::Headline { headline, .. } => { - if node.first_child().is_none() { - self.state = State::Empty; - } else { - self.state = State::Start; - } - Some(Event::Start(Container::Headline(headline))) - } - Element::Italic { .. } => { - if node.first_child().is_none() { - self.state = State::Empty; - } else { - self.state = State::Start; - } - Some(Event::Start(Container::Italic)) - } - Element::List { list, .. } => { - if node.first_child().is_none() { - self.state = State::Empty; - } else { - self.state = State::Start; - } - Some(Event::Start(Container::List(list))) - } - Element::ListItem { list_item, .. } => { - if node.first_child().is_none() { - self.state = State::Empty; - } else { - self.state = State::Start; - } - Some(Event::Start(Container::ListItem(list_item))) - } - Element::Paragraph { .. } => { - if node.first_child().is_none() { - self.state = State::Empty; - } else { - self.state = State::Start; - } - Some(Event::Start(Container::Paragraph)) - } - Element::Section { .. } => { - if node.first_child().is_none() { - self.state = State::Empty; - } else { - self.state = State::Start; - } - Some(Event::Start(Container::Section)) - } - Element::Strike { .. } => { - if node.first_child().is_none() { - self.state = State::Empty; - } else { - self.state = State::Start; - } - Some(Event::Start(Container::Strike)) - } - Element::Underline { .. } => { - if node.first_child().is_none() { - self.state = State::Empty; - } else { - self.state = State::Start; - } - Some(Event::Start(Container::Underline)) - } - Element::Clock { clock, .. } => { - self.state = State::Start; - Some(Event::Clock(clock)) - } - Element::Cookie { cookie, .. } => { - self.state = State::Start; - Some(Event::Cookie(cookie)) - } - Element::Drawer { drawer, .. } => { - self.state = State::Start; - Some(Event::Drawer(drawer)) - } - Element::FnDef { fn_def, .. } => { - self.state = State::Start; - Some(Event::FnDef(fn_def)) - } - Element::FnRef { fn_ref, .. } => { - self.state = State::Start; - Some(Event::FnRef(fn_ref)) - } - Element::InlineCall { inline_call, .. } => { - self.state = State::Start; - Some(Event::InlineCall(inline_call)) - } - Element::InlineSrc { inline_src, .. } => { - self.state = State::Start; - Some(Event::InlineSrc(inline_src)) - } - Element::Keyword { keyword, .. } => { - self.state = State::Start; - Some(Event::Keyword(keyword)) - } - Element::Link { link, .. } => { - self.state = State::Start; - Some(Event::Link(link)) - } - Element::Macros { macros, .. } => { - self.state = State::Start; - Some(Event::Macros(macros)) - } - Element::Planning { - deadline, - scheduled, - closed, - .. - } => { - self.state = State::Start; - Some(Event::Planning(Planning { - deadline: deadline.and_then(|id| { - if let Element::Timestamp { timestamp, .. } = &self.arena[id].data { - Some(timestamp) - } else { - None - } - }), - scheduled: scheduled.and_then(|id| { - if let Element::Timestamp { timestamp, .. } = &self.arena[id].data { - Some(timestamp) - } else { - None - } - }), - closed: closed.and_then(|id| { - if let Element::Timestamp { timestamp, .. } = &self.arena[id].data { - Some(timestamp) - } else { - None - } - }), - })) - } - Element::RadioTarget { radio_target, .. } => { - self.state = State::Start; - Some(Event::RadioTarget(radio_target)) - } - Element::Rule { .. } => { - self.state = State::Start; - Some(Event::Rule) - } - Element::Snippet { snippet, .. } => { - self.state = State::Start; - Some(Event::Snippet(snippet)) - } - Element::Target { target, .. } => { - self.state = State::Start; - Some(Event::Target(target)) - } - Element::Timestamp { timestamp, .. } => { - self.state = State::Start; - Some(Event::Timestamp(timestamp)) - } - Element::FixedWidth { value, .. } => { - self.state = State::Start; - Some(Event::FixedWidth(value)) - } - Element::Comment { value, .. } => { - self.state = State::Start; - Some(Event::Comment(value)) - } - } - } - - fn end_event(&mut self) -> Option> { - let node = &self.arena[self.node]; - match &node.data { - Element::Root => { - self.state = State::Finished; - None - } - Element::Block { block, .. } => { - self.state = State::End; - Some(Event::End(Container::Block(block))) - } - Element::Bold { .. } => { - self.state = State::End; - Some(Event::End(Container::Bold)) - } - Element::Document { .. } => { - self.state = State::End; - Some(Event::End(Container::Document)) - } - Element::DynBlock { dyn_block, .. } => { - self.state = State::End; - Some(Event::End(Container::DynBlock(dyn_block))) - } - Element::Headline { headline, .. } => { - self.state = State::End; - Some(Event::End(Container::Headline(headline))) - } - Element::Italic { .. } => { - self.state = State::End; - Some(Event::End(Container::Italic)) - } - Element::List { list, .. } => { - self.state = State::End; - Some(Event::End(Container::List(list))) - } - Element::ListItem { list_item, .. } => { - self.state = State::End; - Some(Event::End(Container::ListItem(list_item))) - } - Element::Paragraph { .. } => { - self.state = State::End; - Some(Event::End(Container::Paragraph)) - } - Element::Section { .. } => { - self.state = State::End; - Some(Event::End(Container::Section)) - } - Element::Strike { .. } => { - self.state = State::End; - Some(Event::End(Container::Strike)) - } - Element::Underline { .. } => { - self.state = State::End; - Some(Event::End(Container::Underline)) - } - _ => unreachable!(), - } - } + pub(crate) arena: &'a Arena>, + pub(crate) traverse: Traverse<'a, Element<'a>>, } impl<'a> Iterator for Iter<'a> { type Item = Event<'a>; fn next(&mut self) -> Option { - match self.state { - State::Finished => None, - State::End => { - let node = &self.arena[self.node]; - if let Some(sibling_node) = node.next_sibling() { - self.node = sibling_node; - self.start_event() - } else if let Some(parent_node) = node.parent() { - self.node = parent_node; - self.end_event() - } else { - None - } - } - State::Start => { - let node = &self.arena[self.node]; - if let Some(child_node) = node.first_child() { - self.node = child_node; - self.start_event() - } else if let Some(sibling_node) = node.next_sibling() { - self.node = sibling_node; - self.start_event() - } else if let Some(parent_node) = node.parent() { - self.node = parent_node; - self.end_event() - } else { - None - } - } - State::Empty => self.end_event(), - } + self.traverse.next().map(|edge| match edge { + NodeEdge::Start(e) => Event::Start(&self.arena[e].data), + NodeEdge::End(e) => Event::End(&self.arena[e].data), + }) } } diff --git a/src/lib.rs b/src/lib.rs index 71e9f85..866a7ba 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -146,5 +146,6 @@ pub mod org; #[cfg(feature = "serde")] mod serde; -pub use iter::{Container, Event}; +pub use elements::Element; +pub use iter::{Event, Iter}; pub use org::Org; diff --git a/src/org.rs b/src/org.rs index e9b4c0f..ecae501 100644 --- a/src/org.rs +++ b/src/org.rs @@ -10,25 +10,21 @@ use crate::iter::Iter; pub struct Org<'a> { pub(crate) arena: Arena>, pub(crate) document: NodeId, - root: NodeId, text: &'a str, } impl<'a> Org<'a> { pub fn parse(text: &'a str) -> Self { let mut arena = Arena::new(); - let root = arena.new_node(Element::Root); let document = arena.new_node(Element::Document { begin: 0, end: text.len(), contents_begin: 0, contents_end: text.len(), }); - root.append(document, &mut arena).unwrap(); let mut org = Org { arena, - root, document, text, }; @@ -38,7 +34,10 @@ impl<'a> Org<'a> { } pub fn iter(&'a self) -> Iter<'a> { - Iter::new(&self.arena, self.root) + Iter { + arena: &self.arena, + traverse: self.document.traverse(&self.arena), + } } pub fn html(&self, mut writer: W, mut handler: H) -> Result<(), E> @@ -53,28 +52,6 @@ impl<'a> Org<'a> { match event { Start(e) => handler.start(&mut writer, e)?, End(e) => handler.end(&mut writer, e)?, - Clock(e) => handler.clock(&mut writer, e)?, - Cookie(e) => handler.cookie(&mut writer, e)?, - Drawer(e) => handler.drawer(&mut writer, e)?, - FnDef(e) => handler.fn_def(&mut writer, e)?, - FnRef(e) => handler.fn_ref(&mut writer, e)?, - InlineCall(e) => handler.inline_call(&mut writer, e)?, - InlineSrc(e) => handler.inline_src(&mut writer, e)?, - Keyword(e) => handler.keyword(&mut writer, e)?, - Link(e) => handler.link(&mut writer, e)?, - Macros(e) => handler.macros(&mut writer, e)?, - Planning(e) => handler.planning(&mut writer, &e)?, - RadioTarget(e) => handler.radio_target(&mut writer, e)?, - Snippet(e) => handler.snippet(&mut writer, e)?, - Target(e) => handler.target(&mut writer, e)?, - Timestamp(e) => handler.timestamp(&mut writer, e)?, - Text(e) => handler.text(&mut writer, e)?, - Code(e) => handler.code(&mut writer, e)?, - Verbatim(e) => handler.verbatim(&mut writer, e)?, - BabelCall(e) => handler.babel_call(&mut writer, e)?, - Rule => handler.rule(&mut writer)?, - Comment(e) => handler.comment(&mut writer, e)?, - FixedWidth(e) => handler.fixed_width(&mut writer, e)?, } }