diff --git a/crates/codegen/parser/runtime/src/cst.rs b/crates/codegen/parser/runtime/src/cst.rs index 194432b0fa..28f072953f 100644 --- a/crates/codegen/parser/runtime/src/cst.rs +++ b/crates/codegen/parser/runtime/src/cst.rs @@ -51,6 +51,14 @@ impl Node { Cursor::new(self.clone(), text_offset) } + /// Reconstructs the original source code from the parse tree. + pub fn reconstruct(self) -> String { + match self { + Self::Rule(rule) => rule.reconstruct(), + Self::Token(token) => token.text.clone(), + } + } + pub fn as_rule(&self) -> Option<&Rc> { match self { Self::Rule(node) => Some(node), @@ -58,6 +66,13 @@ impl Node { } } + pub fn into_rule(self) -> Option> { + match self { + Self::Rule(node) => Some(node), + _ => None, + } + } + pub fn as_token(&self) -> Option<&Rc> { match self { Self::Token(node) => Some(node), @@ -65,6 +80,13 @@ impl Node { } } + pub fn into_token(self) -> Option> { + match self { + Self::Token(node) => Some(node), + _ => None, + } + } + pub fn as_token_with_kind(&self, kinds: &[TokenKind]) -> Option<&Rc> { self.as_token_matching(|token| kinds.contains(&token.kind)) } @@ -111,4 +133,16 @@ impl RuleNode { pub fn cursor_with_offset(self: Rc, text_offset: TextIndex) -> Cursor { Cursor::new(Node::Rule(self), text_offset) } + + /// Reconstructs the original source code from the parse tree. + pub fn reconstruct(self: Rc) -> String { + let acc = String::with_capacity(self.text_len.utf8); + + self.cursor_with_offset(TextIndex::ZERO) + .filter_map(Node::into_token) + .fold(acc, |mut acc, token| { + acc.push_str(&token.text); + acc + }) + } } diff --git a/crates/codegen/parser/runtime/src/text_index.rs b/crates/codegen/parser/runtime/src/text_index.rs index 4fc3616433..2e96f65534 100644 --- a/crates/codegen/parser/runtime/src/text_index.rs +++ b/crates/codegen/parser/runtime/src/text_index.rs @@ -12,6 +12,15 @@ pub struct TextIndex { pub char: usize, } +impl TextIndex { + /// Shorthand for `TextIndex { utf8: 0, utf16: 0, char: 0 }`. + pub const ZERO: TextIndex = TextIndex { + utf8: 0, + utf16: 0, + char: 0, + }; +} + impl PartialOrd for TextIndex { fn partial_cmp(&self, other: &Self) -> Option { self.utf8.partial_cmp(&other.utf8) diff --git a/crates/solidity/outputs/cargo/crate/src/generated/cst.rs b/crates/solidity/outputs/cargo/crate/src/generated/cst.rs index 82a9f1a2e6..fed956e66a 100644 --- a/crates/solidity/outputs/cargo/crate/src/generated/cst.rs +++ b/crates/solidity/outputs/cargo/crate/src/generated/cst.rs @@ -53,6 +53,14 @@ impl Node { Cursor::new(self.clone(), text_offset) } + /// Reconstructs the original source code from the parse tree. + pub fn reconstruct(self) -> String { + match self { + Self::Rule(rule) => rule.reconstruct(), + Self::Token(token) => token.text.clone(), + } + } + pub fn as_rule(&self) -> Option<&Rc> { match self { Self::Rule(node) => Some(node), @@ -60,6 +68,13 @@ impl Node { } } + pub fn into_rule(self) -> Option> { + match self { + Self::Rule(node) => Some(node), + _ => None, + } + } + pub fn as_token(&self) -> Option<&Rc> { match self { Self::Token(node) => Some(node), @@ -67,6 +82,13 @@ impl Node { } } + pub fn into_token(self) -> Option> { + match self { + Self::Token(node) => Some(node), + _ => None, + } + } + pub fn as_token_with_kind(&self, kinds: &[TokenKind]) -> Option<&Rc> { self.as_token_matching(|token| kinds.contains(&token.kind)) } @@ -113,4 +135,16 @@ impl RuleNode { pub fn cursor_with_offset(self: Rc, text_offset: TextIndex) -> Cursor { Cursor::new(Node::Rule(self), text_offset) } + + /// Reconstructs the original source code from the parse tree. + pub fn reconstruct(self: Rc) -> String { + let acc = String::with_capacity(self.text_len.utf8); + + self.cursor_with_offset(TextIndex::ZERO) + .filter_map(Node::into_token) + .fold(acc, |mut acc, token| { + acc.push_str(&token.text); + acc + }) + } } diff --git a/crates/solidity/outputs/cargo/crate/src/generated/text_index.rs b/crates/solidity/outputs/cargo/crate/src/generated/text_index.rs index 1d9b8427b1..839a104ac2 100644 --- a/crates/solidity/outputs/cargo/crate/src/generated/text_index.rs +++ b/crates/solidity/outputs/cargo/crate/src/generated/text_index.rs @@ -14,6 +14,15 @@ pub struct TextIndex { pub char: usize, } +impl TextIndex { + /// Shorthand for `TextIndex { utf8: 0, utf16: 0, char: 0 }`. + pub const ZERO: TextIndex = TextIndex { + utf8: 0, + utf16: 0, + char: 0, + }; +} + impl PartialOrd for TextIndex { fn partial_cmp(&self, other: &Self) -> Option { self.utf8.partial_cmp(&other.utf8) diff --git a/crates/solidity/outputs/cargo/tests/src/doc_examples/simple_contract.rs b/crates/solidity/outputs/cargo/tests/src/doc_examples/simple_contract.rs index e64ae1d6b3..ef2710f82e 100644 --- a/crates/solidity/outputs/cargo/tests/src/doc_examples/simple_contract.rs +++ b/crates/solidity/outputs/cargo/tests/src/doc_examples/simple_contract.rs @@ -14,12 +14,13 @@ fn simple_contract() -> Result<()> { let parse_tree = parse_output.tree(); - let children = if let Node::Rule(rule) = &parse_tree { + let rule = if let Node::Rule(rule) = parse_tree { assert_eq!(rule.kind, RuleKind::ContractDefinition); - &rule.children + rule } else { panic!("Unexpected parse_tree"); }; + let children = &rule.children; assert_eq!(children.len(), 6); @@ -30,5 +31,7 @@ fn simple_contract() -> Result<()> { assert!(matches!(&children[4], Node::Token(token) if token.kind == TokenKind::OpenBrace)); assert!(matches!(&children[5], Node::Token(token) if token.kind == TokenKind::CloseBrace)); + assert_eq!(rule.reconstruct(), "contract Foo {}"); + return Ok(()); } diff --git a/crates/solidity/outputs/npm/crate/src/generated/cst.rs b/crates/solidity/outputs/npm/crate/src/generated/cst.rs index 82a9f1a2e6..fed956e66a 100644 --- a/crates/solidity/outputs/npm/crate/src/generated/cst.rs +++ b/crates/solidity/outputs/npm/crate/src/generated/cst.rs @@ -53,6 +53,14 @@ impl Node { Cursor::new(self.clone(), text_offset) } + /// Reconstructs the original source code from the parse tree. + pub fn reconstruct(self) -> String { + match self { + Self::Rule(rule) => rule.reconstruct(), + Self::Token(token) => token.text.clone(), + } + } + pub fn as_rule(&self) -> Option<&Rc> { match self { Self::Rule(node) => Some(node), @@ -60,6 +68,13 @@ impl Node { } } + pub fn into_rule(self) -> Option> { + match self { + Self::Rule(node) => Some(node), + _ => None, + } + } + pub fn as_token(&self) -> Option<&Rc> { match self { Self::Token(node) => Some(node), @@ -67,6 +82,13 @@ impl Node { } } + pub fn into_token(self) -> Option> { + match self { + Self::Token(node) => Some(node), + _ => None, + } + } + pub fn as_token_with_kind(&self, kinds: &[TokenKind]) -> Option<&Rc> { self.as_token_matching(|token| kinds.contains(&token.kind)) } @@ -113,4 +135,16 @@ impl RuleNode { pub fn cursor_with_offset(self: Rc, text_offset: TextIndex) -> Cursor { Cursor::new(Node::Rule(self), text_offset) } + + /// Reconstructs the original source code from the parse tree. + pub fn reconstruct(self: Rc) -> String { + let acc = String::with_capacity(self.text_len.utf8); + + self.cursor_with_offset(TextIndex::ZERO) + .filter_map(Node::into_token) + .fold(acc, |mut acc, token| { + acc.push_str(&token.text); + acc + }) + } } diff --git a/crates/solidity/outputs/npm/crate/src/generated/text_index.rs b/crates/solidity/outputs/npm/crate/src/generated/text_index.rs index 1d9b8427b1..839a104ac2 100644 --- a/crates/solidity/outputs/npm/crate/src/generated/text_index.rs +++ b/crates/solidity/outputs/npm/crate/src/generated/text_index.rs @@ -14,6 +14,15 @@ pub struct TextIndex { pub char: usize, } +impl TextIndex { + /// Shorthand for `TextIndex { utf8: 0, utf16: 0, char: 0 }`. + pub const ZERO: TextIndex = TextIndex { + utf8: 0, + utf16: 0, + char: 0, + }; +} + impl PartialOrd for TextIndex { fn partial_cmp(&self, other: &Self) -> Option { self.utf8.partial_cmp(&other.utf8)