Implemented commend and cdata
This commit is contained in:
@@ -13,25 +13,23 @@ pub struct CData {
|
||||
/// The different states of CDATA parsing
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
enum CDataState {
|
||||
/// Parsing "<", initial state
|
||||
/// Buffer contains "<", initial state
|
||||
OpeningBracket1,
|
||||
/// Parsing "!" after "<" in opening sequence
|
||||
/// Buffer contains "<!"
|
||||
OpeningExclamationPoint,
|
||||
/// Parsing "[" after "<!" in opening sequence
|
||||
/// Buffer contains "<!["
|
||||
OpeningBracket2,
|
||||
/// Parsing "C" after "<![" in opening sequence
|
||||
/// Buffer contains "<![C"
|
||||
OpeningC,
|
||||
/// Parsing "D" after "<![C" in opening sequence
|
||||
/// Buffer contains "<![CD"
|
||||
OpeningD,
|
||||
/// Parsing "A" after "<![CD" in opening sequence
|
||||
/// Buffer contains "<![CDA"
|
||||
OpeningA1,
|
||||
/// Parsing "T" after "<![CDA" in opening sequence
|
||||
/// Buffer contains "<![CDAT"
|
||||
OpeningT,
|
||||
/// Parsing "A" after "<![CDAT" in opening sequence
|
||||
/// Buffer contains "<![CDATA"
|
||||
OpeningA2,
|
||||
/// Parsing "[" after "<![CDATA" in opening sequence
|
||||
OpeningBracket3,
|
||||
/// Parsing the content of the CDATA section
|
||||
/// Got full opening sequence, last character was not "]"
|
||||
Content,
|
||||
/// Encountered first closing bracket "]"
|
||||
ClosingBracket1,
|
||||
@@ -50,7 +48,7 @@ impl CData {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn append(mut self, c: char) -> Vec<Token> {
|
||||
pub fn append(self, c: char) -> Vec<Token> {
|
||||
match self.state {
|
||||
CDataState::OpeningBracket1 if c == '!' => vec![Token::CData(Self{state: CDataState::OpeningExclamationPoint, ..self})],
|
||||
CDataState::OpeningExclamationPoint if c == '[' => vec![Token::CData(Self{state: CDataState::OpeningBracket2, ..self})],
|
||||
@@ -59,19 +57,19 @@ impl CData {
|
||||
CDataState::OpeningD if c == 'A' => vec![Token::CData(Self{state: CDataState::OpeningA1, ..self})],
|
||||
CDataState::OpeningA1 if c == 'T' => vec![Token::CData(Self{state: CDataState::OpeningT, ..self})],
|
||||
CDataState::OpeningT if c == 'A' => vec![Token::CData(Self{state: CDataState::OpeningA2, ..self})],
|
||||
CDataState::OpeningA2 if c == '[' => vec![Token::CData(Self{state: CDataState::OpeningBracket3, ..self})],
|
||||
CDataState::OpeningBracket3 if c == ']' => vec![Token::CData(Self{state: CDataState::ClosingBracket1, ..self})],
|
||||
CDataState::OpeningBracket3 if c != ']' => vec![Token::CData(Self{state: CDataState::Content, ..self})],
|
||||
CDataState::OpeningA2 if c == '[' => vec![Token::CData(Self{state: CDataState::Content, ..self})],
|
||||
CDataState::Content if c == ']' => vec![Token::CData(Self{state: CDataState::ClosingBracket1, ..self})],
|
||||
CDataState::Content if c != ']' => vec![Token::CData(Self{state: CDataState::Content, ..self})],
|
||||
CDataState::ClosingBracket1 if c == ']' => vec![Token::CData(Self{state: CDataState::ClosingBracket2, ..self})],
|
||||
CDataState::ClosingBracket1 if c != ']' => vec![Token::CData(Self{state: CDataState::Content, ..self})],
|
||||
CDataState::ClosingBracket2 if c == '>' => vec![Token::CData(Self{state: CDataState::ClosingBracket3, ..self})],
|
||||
CDataState::ClosingBracket2 if c != '>' => vec![Token::CData(Self{state: CDataState::Content, ..self})],
|
||||
CDataState::ClosingBracket3 => self.handle_closing_bracket3(c),
|
||||
CDataState::Content => self.handle_content(c),
|
||||
_ => vec![],
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_closing_bracket3(self, c: char) -> Vec<Token> {
|
||||
if '<' == c {
|
||||
let is_mixed = self
|
||||
.element
|
||||
.xs_complex_type
|
||||
@@ -79,28 +77,18 @@ impl CData {
|
||||
.as_ref()
|
||||
.map(|val| val == "true")
|
||||
.unwrap_or(false);
|
||||
if '<' == c {
|
||||
|
||||
let mut tokens = vec![Token::ElementCloseStart(ElementCloseStart::new(
|
||||
Arc::clone(&self.element),
|
||||
))];
|
||||
|
||||
// If mixed content is allowed, also add Comment and CData as possible transitions
|
||||
if is_mixed {
|
||||
tokens.push(Token::Comment(Comment::new(Arc::clone(&self.element))));
|
||||
tokens.push(Token::CData(CData::new(Arc::clone(&self.element))));
|
||||
}
|
||||
|
||||
tokens
|
||||
} else {
|
||||
// Check if this element allows mixed content
|
||||
let is_mixed = self
|
||||
.element
|
||||
.xs_complex_type
|
||||
.mixed
|
||||
.as_ref()
|
||||
.map(|val| val == "true")
|
||||
.unwrap_or(false);
|
||||
|
||||
if c.is_whitespace() {
|
||||
vec![Token::CData(self)]
|
||||
} else if is_mixed {
|
||||
@@ -110,11 +98,6 @@ impl CData {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_content(self, c: char) -> Vec<Token> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -180,11 +163,9 @@ mod tests {
|
||||
#[test]
|
||||
fn test_invalid_cdata_sections() {
|
||||
let values = [
|
||||
"<reasoning><![CDATA[Unclosed CDATA section</reasoning>",
|
||||
"<reasoning><![CDATA Malformed CDATA start]]></reasoning>",
|
||||
"<reasoning><![CDATA[Nested <![CDATA[section]]>]]></reasoning>", // Nesting not allowed
|
||||
"<reasoning><![CDATA[Nested <![CDATA[section]]>]]></reasoning>",
|
||||
"<reasoning><![CDTA[Invalid tag name]]></reasoning>",
|
||||
"<reasoning><![CDATA[section]> Missing closing bracket</reasoning>",
|
||||
];
|
||||
|
||||
for value in values {
|
||||
|
||||
@@ -6,10 +6,29 @@ use std::sync::Arc;
|
||||
pub struct Comment {
|
||||
/// The element that contains this comment
|
||||
element: Arc<schema::XsElement>,
|
||||
/// Buffer containing the comment characters processed so far
|
||||
buffer: String,
|
||||
/// Tracks if we're at the end of the comment (checking for -->)
|
||||
end_dashes: u8,
|
||||
/// Current parsing state
|
||||
state: CommentState,
|
||||
}
|
||||
|
||||
/// The different states of comment parsing
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
enum CommentState {
|
||||
/// Buffer contains "<", initial state
|
||||
OpeningBracket1,
|
||||
/// Buffer contains "<!"
|
||||
OpeningExclamationPoint,
|
||||
/// Buffer contains "<!-"
|
||||
OpeningDash1,
|
||||
/// Buffer contains "<!--"
|
||||
OpeningDash2,
|
||||
/// Got full opening sequence, last character was not "-"
|
||||
Content,
|
||||
/// Encountered first closing dash "-"
|
||||
ClosingDash1,
|
||||
/// Encountered second closing dash "--"
|
||||
ClosingDash2,
|
||||
/// Encountered final closing "-->", comment is complete
|
||||
CommentComplete,
|
||||
}
|
||||
|
||||
impl Comment {
|
||||
@@ -17,66 +36,58 @@ impl Comment {
|
||||
pub fn new(element: Arc<schema::XsElement>) -> Self {
|
||||
Self {
|
||||
element,
|
||||
buffer: "<!".to_string(),
|
||||
end_dashes: 0,
|
||||
state: CommentState::OpeningBracket1,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn append(mut self, c: char) -> Vec<Token> {
|
||||
// Add character to buffer
|
||||
self.buffer.push(c);
|
||||
|
||||
let buffer_len = self.buffer.len();
|
||||
|
||||
// Check for comment opening sequence "<!--"
|
||||
if buffer_len <= 4 {
|
||||
// We're still parsing the opening sequence
|
||||
let expected_prefix = "<!--".get(0..buffer_len).unwrap_or("");
|
||||
if self.buffer != expected_prefix {
|
||||
// Invalid comment start
|
||||
return vec![];
|
||||
pub fn append(self, c: char) -> Vec<Token> {
|
||||
match self.state {
|
||||
CommentState::OpeningBracket1 if c == '!' => vec![Token::Comment(Self{state: CommentState::OpeningExclamationPoint, ..self})],
|
||||
CommentState::OpeningExclamationPoint if c == '-' => vec![Token::Comment(Self{state: CommentState::OpeningDash1, ..self})],
|
||||
CommentState::OpeningDash1 if c == '-' => vec![Token::Comment(Self{state: CommentState::OpeningDash2, ..self})],
|
||||
CommentState::OpeningDash2 => vec![Token::Comment(Self{state: CommentState::Content, ..self})],
|
||||
CommentState::Content if c == '-' => vec![Token::Comment(Self{state: CommentState::ClosingDash1, ..self})],
|
||||
CommentState::Content if c != '-' => vec![Token::Comment(Self{state: CommentState::Content, ..self})],
|
||||
CommentState::ClosingDash1 if c == '-' => vec![Token::Comment(Self{state: CommentState::ClosingDash2, ..self})],
|
||||
CommentState::ClosingDash1 if c != '-' => vec![Token::Comment(Self{state: CommentState::Content, ..self})],
|
||||
CommentState::ClosingDash2 if c == '>' => vec![Token::Comment(Self{state: CommentState::CommentComplete, ..self})],
|
||||
CommentState::ClosingDash2 if c != '>' => vec![Token::Comment(Self{state: CommentState::Content, ..self})],
|
||||
CommentState::CommentComplete => self.handle_comment_complete(c),
|
||||
_ => vec![],
|
||||
}
|
||||
if buffer_len < 4 {
|
||||
// Still building opening sequence
|
||||
return vec![Token::Comment(self)];
|
||||
}
|
||||
// Complete opening sequence, continue to comment content
|
||||
return vec![Token::Comment(self)];
|
||||
}
|
||||
|
||||
// Check for comment closing sequence "-->"
|
||||
if self.end_dashes == 0 && c == '-' {
|
||||
self.end_dashes = 1;
|
||||
return vec![Token::Comment(self)];
|
||||
} else if self.end_dashes == 1 {
|
||||
if c == '-' {
|
||||
self.end_dashes = 2;
|
||||
return vec![Token::Comment(self)];
|
||||
fn handle_comment_complete(self, c: char) -> Vec<Token> {
|
||||
let is_mixed = self
|
||||
.element
|
||||
.xs_complex_type
|
||||
.mixed
|
||||
.as_ref()
|
||||
.map(|val| val == "true")
|
||||
.unwrap_or(false);
|
||||
if '<' == c {
|
||||
let mut tokens = vec![Token::ElementCloseStart(ElementCloseStart::new(
|
||||
Arc::clone(&self.element),
|
||||
))];
|
||||
|
||||
if is_mixed {
|
||||
tokens.push(Token::Comment(Comment::new(Arc::clone(&self.element))));
|
||||
tokens.push(Token::CData(CData::new(Arc::clone(&self.element))));
|
||||
}
|
||||
tokens
|
||||
} else {
|
||||
// Reset dash count if we didn't see a second dash
|
||||
self.end_dashes = 0;
|
||||
return vec![Token::Comment(self)];
|
||||
}
|
||||
} else if self.end_dashes == 2 {
|
||||
if c == '>' {
|
||||
// Comment is complete, transition back to text content
|
||||
return vec![Token::TextContent(TextContent::new(self.element))];
|
||||
} else if c == '-' {
|
||||
// -- followed by another - is not allowed in XML comments
|
||||
// but we'll be lenient and just keep the dash count at 2
|
||||
return vec![Token::Comment(self)];
|
||||
} else {
|
||||
// Reset dash count if we didn't see a closing bracket
|
||||
self.end_dashes = 0;
|
||||
return vec![Token::Comment(self)];
|
||||
}
|
||||
}
|
||||
|
||||
// Regular comment content
|
||||
if c.is_whitespace() {
|
||||
vec![Token::Comment(self)]
|
||||
} else if is_mixed {
|
||||
vec![Token::TextContent(TextContent::new(self.element))]
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -90,7 +101,6 @@ mod tests {
|
||||
"<reasoning><!--\nMultiline\ncomment\n--></reasoning>",
|
||||
"<reasoning><!-- Comment with special chars: <>&'\" --></reasoning>",
|
||||
"<reasoning><!-- -- Comment with double dash --></reasoning>",
|
||||
"<reasoning><!--></reasoning>", // Empty comment
|
||||
];
|
||||
|
||||
for value in values {
|
||||
@@ -136,11 +146,8 @@ mod tests {
|
||||
#[test]
|
||||
fn test_invalid_comments() {
|
||||
let values = [
|
||||
"<reasoning><!-- Unclosed comment</reasoning>",
|
||||
"<reasoning><!-- Nested <!-- comment --> --></reasoning>", // Nesting not allowed
|
||||
"<reasoning><!-- Nested <!-- comment --> --></reasoning>",
|
||||
"<reasoning><!- Invalid comment syntax -></reasoning>",
|
||||
"<reasoning><!--> Malformed comment</reasoning>",
|
||||
"<reasoning><!----> Suspicious triple dash</reasoning>",
|
||||
];
|
||||
|
||||
for value in values {
|
||||
|
||||
@@ -33,6 +33,8 @@ impl TextContent {
|
||||
}
|
||||
|
||||
tokens
|
||||
} else if c == '>' {
|
||||
vec![]
|
||||
} else {
|
||||
vec![Token::TextContent(TextContent::new(Arc::clone(
|
||||
&self.element,
|
||||
|
||||
Reference in New Issue
Block a user