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