aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/lib.rs52
-rw-r--r--src/parser/event.rs8
-rw-r--r--src/parser/mod.rs29
-rw-r--r--src/tree/file_builder.rs72
-rw-r--r--src/tree/mod.rs2
-rw-r--r--src/yellow/green.rs194
-rw-r--r--src/yellow/mod.rs42
-rw-r--r--src/yellow/red.rs87
-rw-r--r--src/yellow/syntax.rs132
-rw-r--r--tests/data/parser/err/0000_struct_field_missing_comma.txt2
-rw-r--r--tests/data/parser/err/0001_item_recovery_in_file.txt4
-rw-r--r--tests/data/parser/err/0002_duplicate_shebang.txt2
-rw-r--r--tests/data/parser/err/0003_C++_semicolon.txt4
-rw-r--r--tests/data/parser/err/0004_use_path_bad_segment.txt10
-rw-r--r--tests/data/parser/err/0005_attribute_recover.txt6
-rw-r--r--tests/data/parser/err/0006_named_field_recovery.txt13
-rw-r--r--tests/data/parser/err/0007_stray_curly_in_file.txt6
-rw-r--r--tests/data/parser/err/0008_item_block_recovery.txt8
-rw-r--r--tests/data/parser/err/0009_broken_struct_type_parameter.txt16
-rw-r--r--tests/data/parser/inline/0006_extern_struct.txt2
-rw-r--r--tests/data/parser/inline/0013_unsafe_block_in_mod.txt2
-rw-r--r--tests/data/parser/inline/0023_array_type_missing_semi.txt12
-rw-r--r--tests/data/parser/inline/0029_fn_pointer_type_missing_fn.txt10
-rw-r--r--tests/parser.rs8
24 files changed, 660 insertions, 63 deletions
diff --git a/src/lib.rs b/src/lib.rs
index b90b70c05..cf2e97024 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -12,7 +12,8 @@
12//! [RFC.md]: <https://github.com/matklad/libsyntax2/blob/master/docs/RFC.md> 12//! [RFC.md]: <https://github.com/matklad/libsyntax2/blob/master/docs/RFC.md>
13 13
14#![forbid(missing_debug_implementations, unconditional_recursion, future_incompatible)] 14#![forbid(missing_debug_implementations, unconditional_recursion, future_incompatible)]
15#![deny(bad_style, unsafe_code, missing_docs)] 15#![deny(bad_style, missing_docs)]
16#![allow(missing_docs)]
16//#![warn(unreachable_pub)] // rust-lang/rust#47816 17//#![warn(unreachable_pub)] // rust-lang/rust#47816
17 18
18extern crate unicode_xid; 19extern crate unicode_xid;
@@ -21,19 +22,24 @@ extern crate text_unit;
21mod tree; 22mod tree;
22mod lexer; 23mod lexer;
23mod parser; 24mod parser;
25mod yellow;
24 26
25pub mod syntax_kinds; 27pub mod syntax_kinds;
26pub use text_unit::{TextRange, TextUnit}; 28pub use text_unit::{TextRange, TextUnit};
27pub use tree::{File, Node, SyntaxKind, Token}; 29pub use tree::{File, Node, SyntaxKind, Token};
28pub(crate) use tree::{ErrorMsg, FileBuilder, Sink}; 30pub(crate) use tree::{ErrorMsg, FileBuilder, Sink, GreenBuilder};
29pub use lexer::{next_token, tokenize}; 31pub use lexer::{next_token, tokenize};
30pub use parser::parse; 32pub use yellow::SyntaxNode;
33pub(crate) use yellow::SError;
34pub use parser::{parse, parse_green};
31 35
32/// Utilities for simple uses of the parser. 36/// Utilities for simple uses of the parser.
33pub mod utils { 37pub mod utils {
34 use std::fmt::Write; 38 use std::fmt::Write;
35 39
36 use {File, Node}; 40 use {File, Node, SyntaxNode};
41 use std::collections::BTreeSet;
42 use SError;
37 43
38 /// Parse a file and create a string representation of the resulting parse tree. 44 /// Parse a file and create a string representation of the resulting parse tree.
39 pub fn dump_tree(file: &File) -> String { 45 pub fn dump_tree(file: &File) -> String {
@@ -65,4 +71,42 @@ pub mod utils {
65 } 71 }
66 } 72 }
67 } 73 }
74
75 /// Parse a file and create a string representation of the resulting parse tree.
76 pub fn dump_tree_green(syntax: &SyntaxNode) -> String {
77 let mut errors: BTreeSet<_> = syntax.root.errors.iter().cloned().collect();
78 let mut result = String::new();
79 go(syntax, &mut result, 0, &mut errors);
80 return result;
81
82 fn go(node: &SyntaxNode, buff: &mut String, level: usize, errors: &mut BTreeSet<SError>) {
83 buff.push_str(&String::from(" ").repeat(level));
84 write!(buff, "{:?}\n", node).unwrap();
85// let my_errors = node.errors().filter(|e| e.after_child().is_none());
86// let parent_errors = node.parent()
87// .into_iter()
88// .flat_map(|n| n.errors())
89// .filter(|e| e.after_child() == Some(node));
90//
91 let my_errors: Vec<_> = errors.iter().filter(|e| e.offset == node.range().start())
92 .cloned().collect();
93 for err in my_errors {
94 errors.remove(&err);
95 buff.push_str(&String::from(" ").repeat(level));
96 write!(buff, "err: `{}`\n", err.message).unwrap();
97 }
98
99 for child in node.children().iter() {
100 go(child, buff, level + 1, errors)
101 }
102
103 let my_errors: Vec<_> = errors.iter().filter(|e| e.offset == node.range().end())
104 .cloned().collect();
105 for err in my_errors {
106 errors.remove(&err);
107 buff.push_str(&String::from(" ").repeat(level));
108 write!(buff, "err: `{}`\n", err.message).unwrap();
109 }
110 }
111 }
68} 112}
diff --git a/src/parser/event.rs b/src/parser/event.rs
index ac8a55de9..0fbfaeb9f 100644
--- a/src/parser/event.rs
+++ b/src/parser/event.rs
@@ -1,5 +1,5 @@
1use { 1use {
2 ErrorMsg, File, FileBuilder, Sink, SyntaxKind, Token, 2 ErrorMsg, File, FileBuilder, Sink, SyntaxKind, Token, GreenBuilder,
3 syntax_kinds::TOMBSTONE, 3 syntax_kinds::TOMBSTONE,
4}; 4};
5use super::is_insignificant; 5use super::is_insignificant;
@@ -69,6 +69,11 @@ pub(crate) enum Event {
69 69
70pub(super) fn to_file(text: String, tokens: &[Token], events: Vec<Event>) -> File { 70pub(super) fn to_file(text: String, tokens: &[Token], events: Vec<Event>) -> File {
71 let mut builder = FileBuilder::new(text); 71 let mut builder = FileBuilder::new(text);
72 process(&mut builder, tokens, events);
73 builder.finish()
74}
75
76pub(super) fn process(builder: &mut Sink, tokens: &[Token], events: Vec<Event>) {
72 let mut idx = 0; 77 let mut idx = 0;
73 78
74 let mut holes = Vec::new(); 79 let mut holes = Vec::new();
@@ -145,5 +150,4 @@ pub(super) fn to_file(text: String, tokens: &[Token], events: Vec<Event>) -> Fil
145 &Event::Error { ref msg } => builder.error(ErrorMsg { msg: msg.clone() }), 150 &Event::Error { ref msg } => builder.error(ErrorMsg { msg: msg.clone() }),
146 } 151 }
147 } 152 }
148 builder.finish()
149} 153}
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index 3814837e1..26fbb6e3d 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -1,7 +1,3 @@
1use {File, SyntaxKind, Token};
2
3use syntax_kinds::*;
4
5#[macro_use] 1#[macro_use]
6mod token_set; 2mod token_set;
7mod parser; 3mod parser;
@@ -9,6 +5,16 @@ mod input;
9mod event; 5mod event;
10mod grammar; 6mod grammar;
11 7
8use std::sync::Arc;
9use {
10 File, SyntaxKind, Token,
11 yellow::SyntaxNode,
12 syntax_kinds::*
13};
14use GreenBuilder;
15use parser::event::process;
16
17
12/// Parse a sequence of tokens into the representative node tree 18/// Parse a sequence of tokens into the representative node tree
13pub fn parse(text: String, tokens: &[Token]) -> File { 19pub fn parse(text: String, tokens: &[Token]) -> File {
14 let events = { 20 let events = {
@@ -21,6 +27,21 @@ pub fn parse(text: String, tokens: &[Token]) -> File {
21 event::to_file(text, tokens, events) 27 event::to_file(text, tokens, events)
22} 28}
23 29
30/// Parse a sequence of tokens into the representative node tree
31pub fn parse_green(text: String, tokens: &[Token]) -> SyntaxNode {
32 let events = {
33 let input = input::ParserInput::new(&text, tokens);
34 let parser_impl = parser::imp::ParserImpl::new(&input);
35 let mut parser = parser::Parser(parser_impl);
36 grammar::file(&mut parser);
37 parser.0.into_events()
38 };
39 let mut builder = GreenBuilder::new(text);
40 process(&mut builder, tokens, events);
41 let (green, errors) = builder.finish();
42 SyntaxNode::new(Arc::new(green), errors)
43}
44
24fn is_insignificant(kind: SyntaxKind) -> bool { 45fn is_insignificant(kind: SyntaxKind) -> bool {
25 match kind { 46 match kind {
26 WHITESPACE | COMMENT => true, 47 WHITESPACE | COMMENT => true,
diff --git a/src/tree/file_builder.rs b/src/tree/file_builder.rs
index 712602168..4983006cd 100644
--- a/src/tree/file_builder.rs
+++ b/src/tree/file_builder.rs
@@ -7,8 +7,13 @@
7//! tree builder: the parser produces a stream of events like 7//! tree builder: the parser produces a stream of events like
8//! `start node`, `finish node`, and `FileBuilder` converts 8//! `start node`, `finish node`, and `FileBuilder` converts
9//! this stream to a real tree. 9//! this stream to a real tree.
10use {SyntaxKind, TextRange, TextUnit}; 10use std::sync::Arc;
11use {
12 SyntaxKind, TextRange, TextUnit,
13 yellow::GreenNode
14};
11use super::{File, NodeData, NodeIdx, SyntaxErrorData}; 15use super::{File, NodeData, NodeIdx, SyntaxErrorData};
16use SError;
12 17
13pub(crate) trait Sink { 18pub(crate) trait Sink {
14 fn leaf(&mut self, kind: SyntaxKind, len: TextUnit); 19 fn leaf(&mut self, kind: SyntaxKind, len: TextUnit);
@@ -159,3 +164,68 @@ fn grow(left: &mut TextRange, right: TextRange) {
159pub(crate) struct ErrorMsg { 164pub(crate) struct ErrorMsg {
160 pub(crate) msg: String, 165 pub(crate) msg: String,
161} 166}
167
168pub(crate) struct GreenBuilder {
169 text: String,
170 stack: Vec<GreenNode>,
171 pos: TextUnit,
172 root: Option<GreenNode>,
173 errors: Vec<SError>,
174}
175
176impl GreenBuilder {
177 pub(crate) fn new(text: String) -> GreenBuilder {
178 GreenBuilder {
179 text,
180 stack: Vec::new(),
181 pos: 0.into(),
182 root: None,
183 errors: Vec::new(),
184 }
185 }
186
187 pub(crate) fn finish(self) -> (GreenNode, Vec<SError>) {
188 (self.root.unwrap(), self.errors)
189 }
190}
191
192impl Sink for GreenBuilder {
193 fn leaf(&mut self, kind: SyntaxKind, len: TextUnit) {
194 let range = TextRange::offset_len(self.pos, len);
195 self.pos += len;
196 let text = self.text[range].to_owned();
197 let parent = self.stack.last_mut().unwrap();
198 if kind.is_trivia() {
199 parent.push_trivia(kind, text);
200 } else {
201 let node = GreenNode::new_leaf(kind, text);
202 parent.push_child(Arc::new(node));
203 }
204 }
205
206 fn start_internal(&mut self, kind: SyntaxKind) {
207 self.stack.push(GreenNode::new_branch(kind))
208 }
209
210 fn finish_internal(&mut self) {
211 let node = self.stack.pop().unwrap();
212 if let Some(parent) = self.stack.last_mut() {
213 parent.push_child(Arc::new(node))
214 } else {
215 self.root = Some(node);
216 }
217 }
218
219 fn error(&mut self, err: ErrorMsg) {
220 self.errors.push(SError { message: err.msg, offset: self.pos })
221 }
222}
223impl SyntaxKind {
224 fn is_trivia(self) -> bool {
225 match self {
226 SyntaxKind::WHITESPACE | SyntaxKind::DOC_COMMENT | SyntaxKind::COMMENT => true,
227 _ => false
228 }
229 }
230}
231
diff --git a/src/tree/mod.rs b/src/tree/mod.rs
index f7b16d7b5..7abe17592 100644
--- a/src/tree/mod.rs
+++ b/src/tree/mod.rs
@@ -2,7 +2,7 @@ mod file_builder;
2 2
3use ::{TextRange, TextUnit}; 3use ::{TextRange, TextUnit};
4use std::{fmt, cmp}; 4use std::{fmt, cmp};
5pub(crate) use self::file_builder::{ErrorMsg, FileBuilder, Sink}; 5pub(crate) use self::file_builder::{ErrorMsg, FileBuilder, Sink, GreenBuilder};
6 6
7pub use syntax_kinds::SyntaxKind; 7pub use syntax_kinds::SyntaxKind;
8 8
diff --git a/src/yellow/green.rs b/src/yellow/green.rs
new file mode 100644
index 000000000..ede23b719
--- /dev/null
+++ b/src/yellow/green.rs
@@ -0,0 +1,194 @@
1use std::sync::Arc;
2use text_unit::TextUnit;
3use SyntaxKind;
4
5type TokenText = String;
6
7#[derive(Debug)]
8pub(crate) struct GreenNode {
9 kind: SyntaxKind,
10 data: GreenNodeData,
11}
12
13impl GreenNode {
14 pub(crate) fn new_leaf(kind: SyntaxKind, text: TokenText) -> GreenNode {
15 GreenNode {
16 kind,
17 data: GreenNodeData::Leaf(GreenLeaf { text }),
18 }
19 }
20
21 pub(crate) fn new_branch(
22 kind: SyntaxKind,
23 ) -> GreenNode {
24 let branch = GreenBranch {
25 text_len: 0.into(),
26 leading_trivia: Trivias::default(),
27 children: Vec::new(),
28 };
29 GreenNode {
30 kind,
31 data: GreenNodeData::Branch(branch),
32 }
33 }
34
35 pub(crate) fn push_trivia(&mut self, kind: SyntaxKind, text: TokenText) {
36 let branch = match &mut self.data {
37 GreenNodeData::Branch(branch) => branch,
38 _ => panic!()
39 };
40 branch.text_len += TextUnit::of_str(&text);
41 let leading = &mut branch.leading_trivia;
42 branch.children.last_mut().map(|(_, t)| t).unwrap_or(leading)
43 .push(Arc::new(GreenTrivia { kind, text }));
44 }
45
46 pub(crate) fn push_child(&mut self, node: Arc<GreenNode>) {
47 let branch = match &mut self.data {
48 GreenNodeData::Branch(branch) => branch,
49 _ => panic!()
50 };
51 branch.text_len += node.text_len();
52 branch.children.push((node, Trivias::default()));
53 }
54
55 pub(crate) fn kind(&self) -> SyntaxKind {
56 self.kind
57 }
58
59 pub(crate) fn text_len(&self) -> TextUnit {
60 match &self.data {
61 GreenNodeData::Leaf(l) => l.text_len(),
62 GreenNodeData::Branch(b) => b.text_len(),
63 }
64 }
65
66 pub(crate) fn text(&self) -> String {
67 let mut buff = String::new();
68 go(self, &mut buff);
69 return buff;
70 fn go(node: &GreenNode, buff: &mut String) {
71 match &node.data {
72 GreenNodeData::Leaf(l) => buff.push_str(&l.text),
73 GreenNodeData::Branch(branch) => {
74 add_trivia(&branch.leading_trivia, buff);
75 branch.children.iter().for_each(|(child, trivias)| {
76 go(child, buff);
77 add_trivia(trivias, buff);
78 })
79 }
80 }
81 }
82
83 fn add_trivia(trivias: &Trivias, buff: &mut String) {
84 trivias.iter().for_each(|t| buff.push_str(&t.text))
85 }
86 }
87
88 pub(crate) fn n_children(&self) -> usize {
89 match &self.data {
90 GreenNodeData::Leaf(_) => 0,
91 GreenNodeData::Branch(branch) => branch.children.len(),
92 }
93 }
94
95 pub(crate) fn nth_child(&self, idx: usize) -> &Arc<GreenNode> {
96 match &self.data {
97 GreenNodeData::Leaf(_) => panic!("leaf nodes have no children"),
98 GreenNodeData::Branch(branch) => &branch.children[idx].0,
99 }
100 }
101
102 pub(crate) fn nth_trivias(&self, idx: usize) -> &Trivias {
103 match &self.data {
104 GreenNodeData::Leaf(_) => panic!("leaf nodes have no children"),
105 GreenNodeData::Branch(branch) => if idx == 0 {
106 &branch.leading_trivia
107 } else {
108 &branch.children[idx - 1].1
109 },
110 }
111 }
112
113 pub(crate) fn is_leaf(&self) -> bool {
114 match self.data {
115 GreenNodeData::Leaf(_) => true,
116 GreenNodeData::Branch(_) => false
117 }
118 }
119
120 pub(crate) fn leaf_text(&self) -> &str {
121 match &self.data {
122 GreenNodeData::Leaf(l) => l.text.as_str(),
123 GreenNodeData::Branch(_) => panic!("not a leaf")
124 }
125 }
126}
127
128#[derive(Debug)]
129enum GreenNodeData {
130 Leaf(GreenLeaf),
131 Branch(GreenBranch),
132}
133
134#[derive(Debug)]
135struct GreenLeaf {
136 text: TokenText
137}
138
139#[derive(Debug)]
140struct GreenBranch {
141 text_len: TextUnit,
142 leading_trivia: Trivias,
143 children: Vec<(Arc<GreenNode>, Trivias)>,
144}
145
146#[derive(Debug)]
147pub(crate) struct GreenTrivia {
148 pub(crate) kind: SyntaxKind,
149 pub(crate) text: TokenText,
150}
151
152type Trivias = Vec<Arc<GreenTrivia>>;
153
154
155pub(crate) trait TextLen {
156 fn text_len(&self) -> TextUnit;
157}
158
159impl TextLen for GreenTrivia {
160 fn text_len(&self) -> TextUnit {
161 TextUnit::of_str(&self.text)
162 }
163}
164
165impl<T: TextLen> TextLen for Arc<T> {
166 fn text_len(&self) -> TextUnit {
167 let this: &T = self;
168 this.text_len()
169 }
170}
171
172impl TextLen for GreenNode {
173 fn text_len(&self) -> TextUnit {
174 self.text_len()
175 }
176}
177
178impl TextLen for GreenLeaf {
179 fn text_len(&self) -> TextUnit {
180 TextUnit::of_str(&self.text)
181 }
182}
183
184impl TextLen for GreenBranch {
185 fn text_len(&self) -> TextUnit {
186 self.text_len
187 }
188}
189
190impl<T: TextLen> TextLen for [T] {
191 fn text_len(&self) -> TextUnit {
192 self.iter().map(TextLen::text_len).sum()
193 }
194}
diff --git a/src/yellow/mod.rs b/src/yellow/mod.rs
new file mode 100644
index 000000000..236328a7f
--- /dev/null
+++ b/src/yellow/mod.rs
@@ -0,0 +1,42 @@
1mod green;
2mod red;
3mod syntax;
4
5use std::{
6 sync::{Arc, Weak},
7 ops::Deref,
8 mem
9};
10pub(crate) use self::{
11 green::{GreenNode, TextLen},
12 red::RedNode,
13 syntax::SError,
14};
15pub use self::syntax::SyntaxNode;
16
17// This could be just `*const T`, but we use `Weak` for additional checks
18#[derive(Debug)]
19pub(crate) struct Ptr<T>(Weak<T>);
20
21impl<T> Clone for Ptr<T> {
22 fn clone(&self) -> Self {
23 Ptr(self.0.clone())
24 }
25}
26
27impl<T> Ptr<T> {
28 fn clone(self_: &Ptr<T>) -> Ptr<T> {
29 Ptr(Weak::clone(&self_.0))
30 }
31
32 fn new(arc: &Arc<T>) -> Ptr<T> {
33 Ptr(Arc::downgrade(arc))
34 }
35
36 unsafe fn get(&self) -> &T {
37 let t = self.0.upgrade()
38 .expect("caller must guarantee that Ptr is not null");
39 let t: &T = &*t;
40 mem::transmute(t)
41 }
42}
diff --git a/src/yellow/red.rs b/src/yellow/red.rs
new file mode 100644
index 000000000..feba99faa
--- /dev/null
+++ b/src/yellow/red.rs
@@ -0,0 +1,87 @@
1use std::sync::{Arc, Weak, RwLock};
2use {
3 TextUnit, SyntaxKind, TextRange,
4 yellow::{Ptr, GreenNode, TextLen}
5};
6
7#[derive(Debug)]
8pub(crate) struct RedNode {
9 green: Arc<GreenNode>,
10 parent: Option<ParentData>,
11 children: RwLock<Vec<Option<Arc<RedNode>>>>,
12}
13
14#[derive(Debug)]
15struct ParentData {
16 parent: Ptr<RedNode>,
17 start_offset: TextUnit,
18 index_in_parent: usize,
19}
20
21impl RedNode {
22 pub fn new_root(
23 green: Arc<GreenNode>,
24 ) -> RedNode {
25 RedNode::new(green, None)
26 }
27
28 fn new_child(
29 green: Arc<GreenNode>,
30 parent: Ptr<RedNode>,
31 start_offset: TextUnit,
32 index_in_parent: usize
33 ) -> RedNode {
34 let parent_data = ParentData {
35 parent,
36 start_offset,
37 index_in_parent
38 };
39 RedNode::new(green, Some(parent_data))
40 }
41
42 fn new(
43 green: Arc<GreenNode>,
44 parent: Option<ParentData>,
45 ) -> RedNode {
46 let children = vec![None; green.n_children()];
47 RedNode { green, parent, children: RwLock::new(children) }
48 }
49
50 pub(crate) fn green(&self) -> &GreenNode {
51 &self.green
52 }
53
54 pub(crate) fn start_offset(&self) -> TextUnit {
55 match &self.parent {
56 None => 0.into(),
57 Some(p) => p.start_offset,
58 }
59 }
60
61 pub(crate) fn n_children(&self) -> usize {
62 self.green.n_children()
63 }
64
65 pub(crate) fn nth_child(&self, me: Ptr<RedNode>, n: usize) -> Arc<RedNode> {
66 match &self.children.read().unwrap()[n] {
67 Some(child) => return child.clone(),
68 None => (),
69 }
70 let mut children = self.children.write().unwrap();
71 if children[n].is_none() {
72 let start_offset = {
73 let mut acc = self.start_offset();
74 for i in 0..n {
75 acc += self.green.nth_trivias(i).text_len();
76 acc += self.green.nth_child(i).text_len();
77 }
78 acc += self.green.nth_trivias(n).text_len();
79 acc
80 };
81 let green = self.green.nth_child(n).clone();
82 let child = RedNode::new_child(green, me, start_offset, n);
83 children[n] = Some(Arc::new(child))
84 }
85 children[n].as_ref().unwrap().clone()
86 }
87}
diff --git a/src/yellow/syntax.rs b/src/yellow/syntax.rs
new file mode 100644
index 000000000..0c9ffeb14
--- /dev/null
+++ b/src/yellow/syntax.rs
@@ -0,0 +1,132 @@
1use std::{
2 fmt,
3 sync::Arc,
4};
5
6use {
7 TextRange, TextUnit, SyntaxKind,
8 yellow::{Ptr, RedNode, GreenNode, TextLen},
9};
10use yellow::green::GreenTrivia;
11
12#[derive(Clone)]
13pub struct SyntaxNode {
14 pub(crate) root: SyntaxRoot,
15 red: Ptr<RedNode>,
16 trivia_pos: Option<(usize, usize)>,
17}
18
19#[derive(Clone)]
20pub struct SyntaxRoot {
21 red: Arc<RedNode>,
22 pub(crate) errors: Arc<Vec<SError>>,
23}
24
25#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
26pub(crate) struct SError {
27 pub(crate) message: String,
28 pub(crate) offset: TextUnit,
29}
30
31impl SyntaxNode {
32 pub(crate) fn new(root: Arc<GreenNode>, errors: Vec<SError>) -> SyntaxNode {
33 let root = Arc::new(RedNode::new_root(root));
34 let red = Ptr::new(&root);
35 let root = SyntaxRoot { red: root, errors: Arc::new(errors) };
36 SyntaxNode { root, red, trivia_pos: None }
37 }
38
39 pub fn kind(&self) -> SyntaxKind {
40 let green = self.red().green();
41 match self.trivia_pos {
42 None => green.kind(),
43 Some((i, j)) => green.nth_trivias(i)[j].kind
44 }
45 }
46
47 pub fn range(&self) -> TextRange {
48 let red = self.red();
49 let green = red.green();
50 match self.trivia_pos {
51 None => TextRange::offset_len(red.start_offset(), red.green().text_len()),
52 Some((i, j)) => {
53 let trivias = green.nth_trivias(i);
54 let offset = if i == 0 {
55 red.start_offset()
56 } else {
57 let prev_child = red.nth_child(Ptr::clone(&self.red), i - 1);
58 let mut offset = prev_child.start_offset() + prev_child.green().text_len();
59 for k in 0..j {
60 offset += &trivias[k].text_len();
61 }
62 offset
63 };
64 TextRange::offset_len(offset, trivias[j].text_len())
65 }
66 }
67 }
68
69 pub fn text(&self) -> String {
70 let green = self.red().green();
71 match self.trivia_pos {
72 None => green.text(),
73 Some((i, j)) => green.nth_trivias(i)[j].text.clone()
74 }
75 }
76
77 pub fn children(&self) -> Vec<SyntaxNode> {
78 let mut res = Vec::new();
79 let red = self.red();
80 let green = red.green();
81 if green.is_leaf() || self.trivia_pos.is_some() {
82 return Vec::new();
83 }
84 for (j, _) in green.nth_trivias(0).iter().enumerate() {
85 res.push(SyntaxNode {
86 root: self.root.clone(),
87 red: Ptr::clone(&self.red),
88 trivia_pos: Some((0, j)),
89 })
90 }
91
92 let n_children = red.n_children();
93 for i in 0..n_children {
94 res.push(SyntaxNode {
95 root: self.root.clone(),
96 red: Ptr::new(&red.nth_child(Ptr::clone(&self.red), i)),
97 trivia_pos: None,
98 });
99 for (j, _) in green.nth_trivias(i + 1).iter().enumerate() {
100 res.push(SyntaxNode {
101 root: self.root.clone(),
102 red: self.red.clone(),
103 trivia_pos: Some((i + 1, j)),
104 })
105 }
106 }
107 res
108 }
109
110 fn red(&self) -> &RedNode {
111 // Safe b/c root ptr keeps red alive
112 unsafe { self.red.get() }
113 }
114}
115
116impl fmt::Debug for SyntaxNode {
117 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
118 write!(fmt, "{:?}@{:?}", self.kind(), self.range())?;
119 if has_short_text(self.kind()) {
120 write!(fmt, " \"{}\"", self.text())?;
121 }
122 Ok(())
123 }
124}
125
126fn has_short_text(kind: SyntaxKind) -> bool {
127 use syntax_kinds::*;
128 match kind {
129 IDENT | LIFETIME => true,
130 _ => false,
131 }
132}
diff --git a/tests/data/parser/err/0000_struct_field_missing_comma.txt b/tests/data/parser/err/0000_struct_field_missing_comma.txt
index 5196fd718..2bb3ee6ec 100644
--- a/tests/data/parser/err/0000_struct_field_missing_comma.txt
+++ b/tests/data/parser/err/0000_struct_field_missing_comma.txt
@@ -18,7 +18,7 @@ FILE@[0; 34)
18 WHITESPACE@[17; 18) 18 WHITESPACE@[17; 18)
19 IDENT@[18; 21) "u32" 19 IDENT@[18; 21) "u32"
20 WHITESPACE@[21; 26) 20 WHITESPACE@[21; 26)
21 err: `expected COMMA` 21 err: `expected COMMA`
22 NAMED_FIELD@[26; 33) 22 NAMED_FIELD@[26; 33)
23 NAME@[26; 27) 23 NAME@[26; 27)
24 IDENT@[26; 27) "b" 24 IDENT@[26; 27) "b"
diff --git a/tests/data/parser/err/0001_item_recovery_in_file.txt b/tests/data/parser/err/0001_item_recovery_in_file.txt
index e41ddc009..01bd2abe6 100644
--- a/tests/data/parser/err/0001_item_recovery_in_file.txt
+++ b/tests/data/parser/err/0001_item_recovery_in_file.txt
@@ -1,10 +1,10 @@
1FILE@[0; 21) 1FILE@[0; 21)
2err: `expected item`
2 ERROR@[0; 3) 3 ERROR@[0; 3)
3 err: `expected item`
4 IF_KW@[0; 2) 4 IF_KW@[0; 2)
5 WHITESPACE@[2; 3) 5 WHITESPACE@[2; 3)
6 err: `expected item`
6 ERROR@[3; 10) 7 ERROR@[3; 10)
7 err: `expected item`
8 MATCH_KW@[3; 8) 8 MATCH_KW@[3; 8)
9 WHITESPACE@[8; 10) 9 WHITESPACE@[8; 10)
10 STRUCT_ITEM@[10; 21) 10 STRUCT_ITEM@[10; 21)
diff --git a/tests/data/parser/err/0002_duplicate_shebang.txt b/tests/data/parser/err/0002_duplicate_shebang.txt
index e7cf7187d..1a4b37da8 100644
--- a/tests/data/parser/err/0002_duplicate_shebang.txt
+++ b/tests/data/parser/err/0002_duplicate_shebang.txt
@@ -1,7 +1,7 @@
1FILE@[0; 42) 1FILE@[0; 42)
2 SHEBANG@[0; 20) 2 SHEBANG@[0; 20)
3 ERROR@[20; 42)
4 err: `expected item` 3 err: `expected item`
4 ERROR@[20; 42)
5 WHITESPACE@[20; 21) 5 WHITESPACE@[20; 21)
6 SHEBANG@[21; 41) 6 SHEBANG@[21; 41)
7 WHITESPACE@[41; 42) 7 WHITESPACE@[41; 42)
diff --git a/tests/data/parser/err/0003_C++_semicolon.txt b/tests/data/parser/err/0003_C++_semicolon.txt
index affe8fd09..dc3cf6c73 100644
--- a/tests/data/parser/err/0003_C++_semicolon.txt
+++ b/tests/data/parser/err/0003_C++_semicolon.txt
@@ -32,7 +32,7 @@ FILE@[0; 40)
32 COMMA@[36; 37) 32 COMMA@[36; 37)
33 WHITESPACE@[37; 38) 33 WHITESPACE@[37; 38)
34 R_CURLY@[38; 39) 34 R_CURLY@[38; 39)
35 ERROR@[39; 40) 35 err: `expected item, found `;`
36 err: `expected item, found `;`
37consider removing this semicolon` 36consider removing this semicolon`
37 ERROR@[39; 40)
38 SEMI@[39; 40) 38 SEMI@[39; 40)
diff --git a/tests/data/parser/err/0004_use_path_bad_segment.txt b/tests/data/parser/err/0004_use_path_bad_segment.txt
index 0a67002ac..d9ff79ebe 100644
--- a/tests/data/parser/err/0004_use_path_bad_segment.txt
+++ b/tests/data/parser/err/0004_use_path_bad_segment.txt
@@ -9,13 +9,13 @@ FILE@[0; 12)
9 WHITESPACE@[3; 4) 9 WHITESPACE@[3; 4)
10 IDENT@[4; 7) "foo" 10 IDENT@[4; 7) "foo"
11 COLONCOLON@[7; 9) 11 COLONCOLON@[7; 9)
12 PATH_SEGMENT@[9; 9) 12 err: `expected SEMI`
13 err: `expected identifier` 13 err: `expected identifier`
14 err: `expected SEMI` 14 err: `expected item`
15 PATH_SEGMENT@[9; 9)
15 ERROR@[9; 11) 16 ERROR@[9; 11)
16 err: `expected item`
17 INT_NUMBER@[9; 11) 17 INT_NUMBER@[9; 11)
18 ERROR@[11; 12) 18 err: `expected item, found `;`
19 err: `expected item, found `;`
20consider removing this semicolon` 19consider removing this semicolon`
20 ERROR@[11; 12)
21 SEMI@[11; 12) 21 SEMI@[11; 12)
diff --git a/tests/data/parser/err/0005_attribute_recover.txt b/tests/data/parser/err/0005_attribute_recover.txt
index 731f5f2f8..74dd38959 100644
--- a/tests/data/parser/err/0005_attribute_recover.txt
+++ b/tests/data/parser/err/0005_attribute_recover.txt
@@ -9,12 +9,12 @@ FILE@[0; 54)
9 META_ITEM@[6; 9) 9 META_ITEM@[6; 9)
10 IDENT@[6; 9) "foo" 10 IDENT@[6; 9) "foo"
11 COMMA@[9; 10) 11 COMMA@[9; 10)
12 ERROR@[10; 12)
13 err: `expected attribute` 12 err: `expected attribute`
13 ERROR@[10; 12)
14 WHITESPACE@[10; 11) 14 WHITESPACE@[10; 11)
15 PLUS@[11; 12) 15 PLUS@[11; 12)
16 err: `expected attribute`
16 ERROR@[12; 14) 17 ERROR@[12; 14)
17 err: `expected attribute`
18 COMMA@[12; 13) 18 COMMA@[12; 13)
19 WHITESPACE@[13; 14) 19 WHITESPACE@[13; 14)
20 LITERAL@[14; 16) 20 LITERAL@[14; 16)
@@ -43,7 +43,7 @@ FILE@[0; 54)
43 L_PAREN@[39; 40) 43 L_PAREN@[39; 40)
44 err: `expected attribute` 44 err: `expected attribute`
45 WHITESPACE@[40; 41) 45 WHITESPACE@[40; 41)
46 err: `expected R_BRACK` 46 err: `expected R_BRACK`
47 FN_KW@[41; 43) 47 FN_KW@[41; 43)
48 NAME@[43; 47) 48 NAME@[43; 47)
49 WHITESPACE@[43; 44) 49 WHITESPACE@[43; 44)
diff --git a/tests/data/parser/err/0006_named_field_recovery.txt b/tests/data/parser/err/0006_named_field_recovery.txt
index 2dec74866..cb5a9c32c 100644
--- a/tests/data/parser/err/0006_named_field_recovery.txt
+++ b/tests/data/parser/err/0006_named_field_recovery.txt
@@ -22,23 +22,26 @@ FILE@[0; 74)
22 WHITESPACE@[22; 27) 22 WHITESPACE@[22; 27)
23 PUB_KW@[27; 30) 23 PUB_KW@[27; 30)
24 WHITESPACE@[30; 31) 24 WHITESPACE@[30; 31)
25 err: `expected field declaration`
25 ERROR@[31; 38) 26 ERROR@[31; 38)
26 err: `expected field declaration`
27 INT_NUMBER@[31; 33) 27 INT_NUMBER@[31; 33)
28 WHITESPACE@[33; 38) 28 WHITESPACE@[33; 38)
29 err: `expected COMMA` 29 err: `expected COMMA`
30 err: `expected field declaration`
30 ERROR@[38; 40) 31 ERROR@[38; 40)
31 err: `expected field declaration`
32 PLUS@[38; 39) 32 PLUS@[38; 39)
33 WHITESPACE@[39; 40) 33 WHITESPACE@[39; 40)
34 err: `expected COMMA`
35 err: `expected field declaration`
34 ERROR@[40; 42) 36 ERROR@[40; 42)
35 err: `expected field declaration`
36 MINUS@[40; 41) 37 MINUS@[40; 41)
37 WHITESPACE@[41; 42) 38 WHITESPACE@[41; 42)
39 err: `expected COMMA`
40 err: `expected field declaration`
38 ERROR@[42; 48) 41 ERROR@[42; 48)
39 err: `expected field declaration`
40 STAR@[42; 43) 42 STAR@[42; 43)
41 WHITESPACE@[43; 48) 43 WHITESPACE@[43; 48)
44 err: `expected COMMA`
42 NAMED_FIELD@[48; 58) 45 NAMED_FIELD@[48; 58)
43 VISIBILITY@[48; 52) 46 VISIBILITY@[48; 52)
44 PUB_KW@[48; 51) 47 PUB_KW@[48; 51)
diff --git a/tests/data/parser/err/0007_stray_curly_in_file.txt b/tests/data/parser/err/0007_stray_curly_in_file.txt
index 8a3cb5096..cfc714cc6 100644
--- a/tests/data/parser/err/0007_stray_curly_in_file.txt
+++ b/tests/data/parser/err/0007_stray_curly_in_file.txt
@@ -1,6 +1,6 @@
1FILE@[0; 31) 1FILE@[0; 31)
2err: `expected item`
2 ERROR@[0; 3) 3 ERROR@[0; 3)
3 err: `expected item`
4 R_CURLY@[0; 1) 4 R_CURLY@[0; 1)
5 WHITESPACE@[1; 3) 5 WHITESPACE@[1; 3)
6 STRUCT_ITEM@[3; 14) 6 STRUCT_ITEM@[3; 14)
@@ -10,8 +10,8 @@ FILE@[0; 31)
10 IDENT@[10; 11) "S" 10 IDENT@[10; 11) "S"
11 SEMI@[11; 12) 11 SEMI@[11; 12)
12 WHITESPACE@[12; 14) 12 WHITESPACE@[12; 14)
13 err: `expected item`
13 ERROR@[14; 17) 14 ERROR@[14; 17)
14 err: `expected item`
15 R_CURLY@[14; 15) 15 R_CURLY@[14; 15)
16 WHITESPACE@[15; 17) 16 WHITESPACE@[15; 17)
17 FN_ITEM@[17; 29) 17 FN_ITEM@[17; 29)
@@ -25,7 +25,7 @@ FILE@[0; 31)
25 L_CURLY@[25; 26) 25 L_CURLY@[25; 26)
26 R_CURLY@[26; 27) 26 R_CURLY@[26; 27)
27 WHITESPACE@[27; 29) 27 WHITESPACE@[27; 29)
28 err: `expected item`
28 ERROR@[29; 31) 29 ERROR@[29; 31)
29 err: `expected item`
30 R_CURLY@[29; 30) 30 R_CURLY@[29; 30)
31 WHITESPACE@[30; 31) 31 WHITESPACE@[30; 31)
diff --git a/tests/data/parser/err/0008_item_block_recovery.txt b/tests/data/parser/err/0008_item_block_recovery.txt
index 0e2aae7cc..ca332bcf0 100644
--- a/tests/data/parser/err/0008_item_block_recovery.txt
+++ b/tests/data/parser/err/0008_item_block_recovery.txt
@@ -12,18 +12,18 @@ FILE@[0; 95)
12 WHITESPACE@[10; 11) 12 WHITESPACE@[10; 11)
13 R_CURLY@[11; 12) 13 R_CURLY@[11; 12)
14 WHITESPACE@[12; 14) 14 WHITESPACE@[12; 14)
15 err: `expected item`
15 ERROR@[14; 17) 16 ERROR@[14; 17)
16 err: `expected item`
17 IDENT@[14; 17) "bar" 17 IDENT@[14; 17) "bar"
18 err: `expected item`
18 ERROR@[17; 18) 19 ERROR@[17; 18)
19 err: `expected item`
20 L_PAREN@[17; 18) 20 L_PAREN@[17; 18)
21 err: `expected item`
21 ERROR@[18; 20) 22 ERROR@[18; 20)
22 err: `expected item`
23 R_PAREN@[18; 19) 23 R_PAREN@[18; 19)
24 WHITESPACE@[19; 20) 24 WHITESPACE@[19; 20)
25 err: `expected item`
25 ERROR@[20; 82) 26 ERROR@[20; 82)
26 err: `expected item`
27 L_CURLY@[20; 21) 27 L_CURLY@[20; 21)
28 WHITESPACE@[21; 26) 28 WHITESPACE@[21; 26)
29 IF_KW@[26; 28) 29 IF_KW@[26; 28)
diff --git a/tests/data/parser/err/0009_broken_struct_type_parameter.txt b/tests/data/parser/err/0009_broken_struct_type_parameter.txt
index 9434a764a..c16c6dffe 100644
--- a/tests/data/parser/err/0009_broken_struct_type_parameter.txt
+++ b/tests/data/parser/err/0009_broken_struct_type_parameter.txt
@@ -6,26 +6,26 @@ FILE@[0; 43)
6 IDENT@[7; 8) "S" 6 IDENT@[7; 8) "S"
7 TYPE_PARAM_LIST@[8; 12) 7 TYPE_PARAM_LIST@[8; 12)
8 L_ANGLE@[8; 9) 8 L_ANGLE@[8; 9)
9 ERROR@[9; 12)
10 err: `expected type parameter` 9 err: `expected type parameter`
10 ERROR@[9; 12)
11 INT_NUMBER@[9; 11) 11 INT_NUMBER@[9; 11)
12 WHITESPACE@[11; 12) 12 WHITESPACE@[11; 12)
13 err: `expected COMMA` 13 err: `expected COMMA`
14 err: `expected R_ANGLE` 14 err: `expected R_ANGLE`
15 err: `expected `;`, `{`, or `(`` 15 err: `expected `;`, `{`, or `(``
16 err: `expected item`
16 ERROR@[12; 14) 17 ERROR@[12; 14)
17 err: `expected item`
18 PLUS@[12; 13) 18 PLUS@[12; 13)
19 WHITESPACE@[13; 14) 19 WHITESPACE@[13; 14)
20 err: `expected item`
20 ERROR@[14; 15) 21 ERROR@[14; 15)
21 err: `expected item`
22 INT_NUMBER@[14; 15) 22 INT_NUMBER@[14; 15)
23 err: `expected item`
23 ERROR@[15; 17) 24 ERROR@[15; 17)
24 err: `expected item`
25 R_ANGLE@[15; 16) 25 R_ANGLE@[15; 16)
26 WHITESPACE@[16; 17) 26 WHITESPACE@[16; 17)
27 err: `expected item`
27 ERROR@[17; 33) 28 ERROR@[17; 33)
28 err: `expected item`
29 L_CURLY@[17; 18) 29 L_CURLY@[17; 18)
30 WHITESPACE@[18; 23) 30 WHITESPACE@[18; 23)
31 IDENT@[23; 24) "f" 31 IDENT@[23; 24) "f"
diff --git a/tests/data/parser/inline/0006_extern_struct.txt b/tests/data/parser/inline/0006_extern_struct.txt
index 93a5b0477..f310e1225 100644
--- a/tests/data/parser/inline/0006_extern_struct.txt
+++ b/tests/data/parser/inline/0006_extern_struct.txt
@@ -2,7 +2,7 @@ FILE@[0; 19)
2 ABI@[0; 7) 2 ABI@[0; 7)
3 EXTERN_KW@[0; 6) 3 EXTERN_KW@[0; 6)
4 WHITESPACE@[6; 7) 4 WHITESPACE@[6; 7)
5 err: `expected `fn` or `{`` 5 err: `expected `fn` or `{``
6 STRUCT_ITEM@[7; 19) 6 STRUCT_ITEM@[7; 19)
7 STRUCT_KW@[7; 13) 7 STRUCT_KW@[7; 13)
8 NAME@[13; 17) 8 NAME@[13; 17)
diff --git a/tests/data/parser/inline/0013_unsafe_block_in_mod.txt b/tests/data/parser/inline/0013_unsafe_block_in_mod.txt
index 5ddc1736c..3b56378a3 100644
--- a/tests/data/parser/inline/0013_unsafe_block_in_mod.txt
+++ b/tests/data/parser/inline/0013_unsafe_block_in_mod.txt
@@ -11,8 +11,8 @@ FILE@[0; 33)
11 R_CURLY@[9; 10) 11 R_CURLY@[9; 10)
12 WHITESPACE@[10; 11) 12 WHITESPACE@[10; 11)
13 UNSAFE_KW@[11; 17) 13 UNSAFE_KW@[11; 17)
14 ERROR@[17; 22)
15 err: `expected `trait`, `impl` or `fn`` 14 err: `expected `trait`, `impl` or `fn``
15 ERROR@[17; 22)
16 WHITESPACE@[17; 18) 16 WHITESPACE@[17; 18)
17 L_CURLY@[18; 19) 17 L_CURLY@[18; 19)
18 WHITESPACE@[19; 20) 18 WHITESPACE@[19; 20)
diff --git a/tests/data/parser/inline/0023_array_type_missing_semi.txt b/tests/data/parser/inline/0023_array_type_missing_semi.txt
index bb30a2a2a..cc280d5a7 100644
--- a/tests/data/parser/inline/0023_array_type_missing_semi.txt
+++ b/tests/data/parser/inline/0023_array_type_missing_semi.txt
@@ -13,16 +13,16 @@ FILE@[0; 18)
13 L_PAREN@[10; 11) 13 L_PAREN@[10; 11)
14 R_PAREN@[11; 12) 14 R_PAREN@[11; 12)
15 WHITESPACE@[12; 13) 15 WHITESPACE@[12; 13)
16 err: `expected `;` or `]`` 16 err: `expected SEMI`
17 err: `expected SEMI` 17 err: `expected `;` or `]``
18 err: `expected item`
18 ERROR@[13; 15) 19 ERROR@[13; 15)
19 err: `expected item`
20 INT_NUMBER@[13; 15) 20 INT_NUMBER@[13; 15)
21 err: `expected item`
21 ERROR@[15; 16) 22 ERROR@[15; 16)
22 err: `expected item`
23 R_BRACK@[15; 16) 23 R_BRACK@[15; 16)
24 ERROR@[16; 18) 24 err: `expected item, found `;`
25 err: `expected item, found `;`
26consider removing this semicolon` 25consider removing this semicolon`
26 ERROR@[16; 18)
27 SEMI@[16; 17) 27 SEMI@[16; 17)
28 WHITESPACE@[17; 18) 28 WHITESPACE@[17; 18)
diff --git a/tests/data/parser/inline/0029_fn_pointer_type_missing_fn.txt b/tests/data/parser/inline/0029_fn_pointer_type_missing_fn.txt
index dd6e24096..d6c27cf58 100644
--- a/tests/data/parser/inline/0029_fn_pointer_type_missing_fn.txt
+++ b/tests/data/parser/inline/0029_fn_pointer_type_missing_fn.txt
@@ -8,17 +8,17 @@ FILE@[0; 20)
8 EQ@[7; 8) 8 EQ@[7; 8)
9 WHITESPACE@[8; 9) 9 WHITESPACE@[8; 9)
10 UNSAFE_KW@[9; 15) 10 UNSAFE_KW@[9; 15)
11 err: `expected `fn``
12 err: `expected SEMI` 11 err: `expected SEMI`
12 err: `expected `fn``
13 WHITESPACE@[15; 16) 13 WHITESPACE@[15; 16)
14 err: `expected item`
14 ERROR@[16; 17) 15 ERROR@[16; 17)
15 err: `expected item`
16 L_PAREN@[16; 17) 16 L_PAREN@[16; 17)
17 err: `expected item`
17 ERROR@[17; 18) 18 ERROR@[17; 18)
18 err: `expected item`
19 R_PAREN@[17; 18) 19 R_PAREN@[17; 18)
20 ERROR@[18; 20) 20 err: `expected item, found `;`
21 err: `expected item, found `;`
22consider removing this semicolon` 21consider removing this semicolon`
22 ERROR@[18; 20)
23 SEMI@[18; 19) 23 SEMI@[18; 19)
24 WHITESPACE@[19; 20) 24 WHITESPACE@[19; 20)
diff --git a/tests/parser.rs b/tests/parser.rs
index 68a6434be..35b91436a 100644
--- a/tests/parser.rs
+++ b/tests/parser.rs
@@ -1,15 +1,15 @@
1extern crate libsyntax2; 1extern crate libsyntax2;
2extern crate testutils; 2extern crate testutils;
3 3
4use libsyntax2::{parse, tokenize}; 4use libsyntax2::{parse, tokenize, parse_green};
5use libsyntax2::utils::dump_tree; 5use libsyntax2::utils::{dump_tree, dump_tree_green};
6use testutils::dir_tests; 6use testutils::dir_tests;
7 7
8#[test] 8#[test]
9fn parser_tests() { 9fn parser_tests() {
10 dir_tests(&["parser/inline", "parser/ok", "parser/err"], |text| { 10 dir_tests(&["parser/inline", "parser/ok", "parser/err"], |text| {
11 let tokens = tokenize(text); 11 let tokens = tokenize(text);
12 let file = parse(text.to_string(), &tokens); 12 let file = parse_green(text.to_string(), &tokens);
13 dump_tree(&file) 13 dump_tree_green(&file)
14 }) 14 })
15} 15}