aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_editor/src/completion.rs32
-rw-r--r--crates/ra_editor/src/extend_selection.rs16
-rw-r--r--crates/ra_editor/src/scope/fn_scope.rs9
-rw-r--r--crates/ra_syntax/src/lib.rs3
-rw-r--r--crates/ra_syntax/src/parser_impl/event.rs210
-rw-r--r--crates/ra_syntax/src/parser_impl/mod.rs25
-rw-r--r--crates/ra_syntax/src/reparsing.rs3
-rw-r--r--crates/ra_syntax/src/yellow/builder.rs32
-rw-r--r--crates/ra_syntax/tests/data/parser/err/0025_nope.txt6
-rw-r--r--crates/ra_syntax/tests/data/parser/ok/0033_label_break.txt6
10 files changed, 236 insertions, 106 deletions
diff --git a/crates/ra_editor/src/completion.rs b/crates/ra_editor/src/completion.rs
index 62a63fb04..570d72d66 100644
--- a/crates/ra_editor/src/completion.rs
+++ b/crates/ra_editor/src/completion.rs
@@ -39,6 +39,12 @@ pub fn scope_completion(file: &File, offset: TextUnit) -> Option<Vec<CompletionI
39 if is_node::<ast::Param>(name_ref.syntax()) { 39 if is_node::<ast::Param>(name_ref.syntax()) {
40 param_completions(name_ref.syntax(), &mut res); 40 param_completions(name_ref.syntax(), &mut res);
41 } 41 }
42 let name_range = name_ref.syntax().range();
43 let top_node = name_ref.syntax().ancestors().take_while(|it| it.range() == name_range).last().unwrap();
44 match top_node.parent().map(|it| it.kind()) {
45 Some(ROOT) | Some(ITEM_LIST) => complete_mod_item_snippets(&mut res),
46 _ => (),
47 }
42 } 48 }
43 if let Some(name) = find_node_at_offset::<ast::Name>(file.syntax(), offset) { 49 if let Some(name) = find_node_at_offset::<ast::Name>(file.syntax(), offset) {
44 if is_node::<ast::Param>(name.syntax()) { 50 if is_node::<ast::Param>(name.syntax()) {
@@ -216,6 +222,15 @@ fn complete_expr_snippets(acc: &mut Vec<CompletionItem>) {
216 ); 222 );
217} 223}
218 224
225fn complete_mod_item_snippets(acc: &mut Vec<CompletionItem>) {
226 acc.push(CompletionItem {
227 label: "tfn".to_string(),
228 lookup: None,
229 snippet: Some("#[test]\nfn $1() {\n $0\n}".to_string()),
230 }
231 );
232}
233
219fn complete_fn(name_ref: ast::NameRef, scopes: &FnScopes, acc: &mut Vec<CompletionItem>) { 234fn complete_fn(name_ref: ast::NameRef, scopes: &FnScopes, acc: &mut Vec<CompletionItem>) {
220 let mut shadowed = HashSet::new(); 235 let mut shadowed = HashSet::new();
221 acc.extend( 236 acc.extend(
@@ -506,4 +521,21 @@ mod tests {
506 CompletionItem { label: "SourceRoot", lookup: None, snippet: None }, 521 CompletionItem { label: "SourceRoot", lookup: None, snippet: None },
507 CompletionItem { label: "file_id: FileId", lookup: Some("file_id"), snippet: None }]"#); 522 CompletionItem { label: "file_id: FileId", lookup: Some("file_id"), snippet: None }]"#);
508 } 523 }
524
525 #[test]
526 fn test_tfn_snippet() {
527 // check_snippet_completion(r"
528 // <|>
529 // ",
530 // r##"[CompletionItem { label: "tfn", lookup: None, snippet: Some("#[test]\nfn $1() {\n $0\n}") }]"##,
531 // );
532 check_snippet_completion(r"
533 #[cfg(test)]
534 mod tests {
535 <|>
536 }
537 ",
538 r##"[CompletionItem { label: "tfn", lookup: None, snippet: Some("#[test]\nfn $1() {\n $0\n}") }]"##,
539 );
540 }
509} 541}
diff --git a/crates/ra_editor/src/extend_selection.rs b/crates/ra_editor/src/extend_selection.rs
index 6977900e6..e12346cb6 100644
--- a/crates/ra_editor/src/extend_selection.rs
+++ b/crates/ra_editor/src/extend_selection.rs
@@ -154,6 +154,22 @@ impl S {
154 } 154 }
155 155
156 #[test] 156 #[test]
157 fn test_extend_selection_doc_comments() {
158 do_check(
159 r#"
160struct A;
161
162/// bla
163/// bla
164struct B {
165 <|>
166}
167 "#,
168 &["\n \n", "{\n \n}", "/// bla\n/// bla\nstruct B {\n \n}"]
169 )
170 }
171
172 #[test]
157 fn test_extend_selection_comments() { 173 fn test_extend_selection_comments() {
158 do_check( 174 do_check(
159 r#" 175 r#"
diff --git a/crates/ra_editor/src/scope/fn_scope.rs b/crates/ra_editor/src/scope/fn_scope.rs
index a99bd1822..65d85279f 100644
--- a/crates/ra_editor/src/scope/fn_scope.rs
+++ b/crates/ra_editor/src/scope/fn_scope.rs
@@ -245,11 +245,13 @@ pub fn resolve_local_name<'a>(name_ref: ast::NameRef, scopes: &'a FnScopes) -> O
245 use std::collections::HashSet; 245 use std::collections::HashSet;
246 246
247 let mut shadowed = HashSet::new(); 247 let mut shadowed = HashSet::new();
248 scopes.scope_chain(name_ref.syntax()) 248 let ret = scopes.scope_chain(name_ref.syntax())
249 .flat_map(|scope| scopes.entries(scope).iter()) 249 .flat_map(|scope| scopes.entries(scope).iter())
250 .filter(|entry| shadowed.insert(entry.name())) 250 .filter(|entry| shadowed.insert(entry.name()))
251 .filter(|entry| entry.name() == name_ref.text()) 251 .filter(|entry| entry.name() == name_ref.text())
252 .nth(0) 252 .nth(0);
253 eprintln!("ret = {:?}", ret);
254 ret
253} 255}
254 256
255#[cfg(test)] 257#[cfg(test)]
@@ -357,7 +359,6 @@ mod tests {
357 let scopes = FnScopes::new(fn_def); 359 let scopes = FnScopes::new(fn_def);
358 360
359 let local_name = resolve_local_name(name_ref, &scopes).unwrap().ast().name().unwrap(); 361 let local_name = resolve_local_name(name_ref, &scopes).unwrap().ast().name().unwrap();
360
361 let expected_name = find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into()).unwrap(); 362 let expected_name = find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into()).unwrap();
362 assert_eq!(local_name.syntax().range(), expected_name.syntax().range()); 363 assert_eq!(local_name.syntax().range(), expected_name.syntax().range());
363 } 364 }
@@ -394,4 +395,4 @@ mod tests {
394 }", 395 }",
395 46); 396 46);
396 } 397 }
397} \ No newline at end of file 398}
diff --git a/crates/ra_syntax/src/lib.rs b/crates/ra_syntax/src/lib.rs
index 738664afd..703469629 100644
--- a/crates/ra_syntax/src/lib.rs
+++ b/crates/ra_syntax/src/lib.rs
@@ -74,7 +74,8 @@ impl File {
74 } 74 }
75 pub fn parse(text: &str) -> File { 75 pub fn parse(text: &str) -> File {
76 let tokens = tokenize(&text); 76 let tokens = tokenize(&text);
77 let (green, errors) = parser_impl::parse_with::<yellow::GreenBuilder>( 77 let (green, errors) = parser_impl::parse_with(
78 yellow::GreenBuilder::new(),
78 text, &tokens, grammar::root, 79 text, &tokens, grammar::root,
79 ); 80 );
80 File::new(green, errors) 81 File::new(green, errors)
diff --git a/crates/ra_syntax/src/parser_impl/event.rs b/crates/ra_syntax/src/parser_impl/event.rs
index 9fd56b996..95e5ce4cc 100644
--- a/crates/ra_syntax/src/parser_impl/event.rs
+++ b/crates/ra_syntax/src/parser_impl/event.rs
@@ -9,9 +9,10 @@
9//! this stream to a real tree. 9//! this stream to a real tree.
10use std::mem; 10use std::mem;
11use { 11use {
12 TextUnit, TextRange, SmolStr,
12 lexer::Token, 13 lexer::Token,
13 parser_impl::Sink, 14 parser_impl::Sink,
14 SyntaxKind::{self, TOMBSTONE}, 15 SyntaxKind::{self, *},
15}; 16};
16 17
17 18
@@ -78,77 +79,162 @@ pub(crate) enum Event {
78 }, 79 },
79} 80}
80 81
82pub(super) struct EventProcessor<'a, S: Sink> {
83 sink: S,
84 text_pos: TextUnit,
85 text: &'a str,
86 token_pos: usize,
87 tokens: &'a [Token],
88 events: &'a mut [Event],
89}
81 90
82pub(super) fn process<'a, S: Sink<'a>>(builder: &mut S, tokens: &[Token], mut events: Vec<Event>) { 91impl<'a, S: Sink> EventProcessor<'a, S> {
83 fn tombstone() -> Event { 92 pub(super) fn new(sink: S, text: &'a str, tokens: &'a[Token], events: &'a mut [Event]) -> EventProcessor<'a, S> {
84 Event::Start { kind: TOMBSTONE, forward_parent: None } 93 EventProcessor {
94 sink,
95 text_pos: 0.into(),
96 text,
97 token_pos: 0,
98 tokens,
99 events
100 }
85 } 101 }
86 let eat_ws = |idx: &mut usize, builder: &mut S| { 102
87 while let Some(token) = tokens.get(*idx) { 103 pub(super) fn process(mut self) -> S {
88 if !token.kind.is_trivia() { 104 fn tombstone() -> Event {
89 break; 105 Event::Start { kind: TOMBSTONE, forward_parent: None }
90 }
91 builder.leaf(token.kind, token.len);
92 *idx += 1
93 } 106 }
94 }; 107 let mut forward_parents = Vec::new();
95 108
96 let events: &mut [Event] = &mut events; 109 for i in 0..self.events.len() {
97 let mut depth = 0; 110 match mem::replace(&mut self.events[i], tombstone()) {
98 let mut forward_parents = Vec::new(); 111 Event::Start {
99 let mut next_tok_idx = 0; 112 kind: TOMBSTONE, ..
100 for i in 0..events.len() { 113 } => (),
101 match mem::replace(&mut events[i], tombstone()) { 114
102 Event::Start { 115 Event::Start { kind, forward_parent } => {
103 kind: TOMBSTONE, .. 116 forward_parents.push(kind);
104 } => (), 117 let mut idx = i;
105 118 let mut fp = forward_parent;
106 Event::Start { kind, forward_parent } => { 119 while let Some(fwd) = fp {
107 forward_parents.push(kind); 120 idx += fwd as usize;
108 let mut idx = i; 121 fp = match mem::replace(&mut self.events[idx], tombstone()) {
109 let mut fp = forward_parent; 122 Event::Start {
110 while let Some(fwd) = fp { 123 kind,
111 idx += fwd as usize; 124 forward_parent,
112 fp = match mem::replace(&mut events[idx], tombstone()) { 125 } => {
113 Event::Start { 126 forward_parents.push(kind);
114 kind, 127 forward_parent
115 forward_parent, 128 },
116 } => { 129 _ => unreachable!(),
117 forward_parents.push(kind); 130 };
118 forward_parent 131 }
119 }, 132 for kind in forward_parents.drain(..).rev() {
120 _ => unreachable!(), 133 self.start(kind);
121 };
122 }
123 for kind in forward_parents.drain(..).rev() {
124 if depth > 0 {
125 eat_ws(&mut next_tok_idx, builder);
126 } 134 }
127 depth += 1;
128 builder.start_internal(kind);
129 } 135 }
130 } 136 Event::Finish => {
131 Event::Finish => { 137 let last = i == self.events.len() - 1;
132 depth -= 1; 138 self.finish(last);
133 if depth == 0 { 139 },
134 eat_ws(&mut next_tok_idx, builder); 140 Event::Token { kind, n_raw_tokens } => {
141 self.eat_ws();
142 let n_raw_tokens = n_raw_tokens as usize;
143 let len = self.tokens[self.token_pos..self.token_pos + n_raw_tokens]
144 .iter()
145 .map(|it| it.len)
146 .sum::<TextUnit>();
147 self.leaf(kind, len, n_raw_tokens);
135 } 148 }
149 Event::Error { msg } => self.sink.error(msg, self.text_pos),
150 }
151 }
152 self.sink
153 }
154
155 fn start(&mut self, kind: SyntaxKind) {
156 if kind == ROOT {
157 self.sink.start_internal(kind);
158 return;
159 }
160 let n_trivias = self.tokens[self.token_pos..]
161 .iter()
162 .take_while(|it| it.kind.is_trivia())
163 .count();
164 let leading_trivias = &self.tokens[self.token_pos..self.token_pos + n_trivias];
165 let mut trivia_end = self.text_pos + leading_trivias
166 .iter()
167 .map(|it| it.len)
168 .sum::<TextUnit>();
169
170 let n_attached_trivias = {
171 let leading_trivias = leading_trivias.iter().rev()
172 .map(|it| {
173 let next_end = trivia_end - it.len;
174 let range = TextRange::from_to(next_end, trivia_end);
175 trivia_end = next_end;
176 (it.kind, &self.text[range])
177 });
178 n_attached_trivias(kind, leading_trivias)
179 };
180 self.eat_n_trivias(n_trivias - n_attached_trivias);
181 self.sink.start_internal(kind);
182 self.eat_n_trivias(n_attached_trivias);
183 }
136 184
137 builder.finish_internal(); 185 fn finish(&mut self, last: bool) {
186 if last {
187 self.eat_ws()
188 }
189 self.sink.finish_internal();
190 }
191
192 fn eat_ws(&mut self) {
193 while let Some(&token) = self.tokens.get(self.token_pos) {
194 if !token.kind.is_trivia() {
195 break;
138 } 196 }
139 Event::Token { 197 self.leaf(token.kind, token.len, 1);
140 kind, 198 }
141 mut n_raw_tokens, 199 }
142 } => { 200
143 eat_ws(&mut next_tok_idx, builder); 201 fn eat_n_trivias(&mut self, n: usize) {
144 let mut len = 0.into(); 202 for _ in 0..n {
145 for _ in 0..n_raw_tokens { 203 let token = self.tokens[self.token_pos];
146 len += tokens[next_tok_idx].len; 204 assert!(token.kind.is_trivia());
147 next_tok_idx += 1; 205 self.leaf(token.kind, token.len, 1);
206 }
207 }
208
209 fn leaf(&mut self, kind: SyntaxKind, len: TextUnit, n_tokens: usize) {
210 let range = TextRange::offset_len(self.text_pos, len);
211 let text: SmolStr = self.text[range].into();
212 self.text_pos += len;
213 self.token_pos += n_tokens;
214 self.sink.leaf(kind, text);
215 }
216}
217
218fn n_attached_trivias<'a>(kind: SyntaxKind, trivias: impl Iterator<Item=(SyntaxKind, &'a str)>) -> usize {
219 match kind {
220 STRUCT_DEF | ENUM_DEF | FN_DEF | TRAIT_DEF | MODULE => {
221 let mut res = 0;
222 for (i, (kind, text)) in trivias.enumerate() {
223 match kind {
224 WHITESPACE => {
225 if text.contains("\n\n") {
226 break;
227 }
228 }
229 COMMENT => {
230 res = i + 1;
231 }
232 _ => (),
148 } 233 }
149 builder.leaf(kind, len);
150 } 234 }
151 Event::Error { msg } => builder.error(msg), 235 res
152 } 236 }
237 _ => 0,
153 } 238 }
239
154} 240}
diff --git a/crates/ra_syntax/src/parser_impl/mod.rs b/crates/ra_syntax/src/parser_impl/mod.rs
index b343b404f..8d74cef0e 100644
--- a/crates/ra_syntax/src/parser_impl/mod.rs
+++ b/crates/ra_syntax/src/parser_impl/mod.rs
@@ -4,45 +4,44 @@ mod input;
4use std::cell::Cell; 4use std::cell::Cell;
5 5
6use { 6use {
7 TextUnit, SmolStr,
7 lexer::Token, 8 lexer::Token,
8 parser_api::Parser, 9 parser_api::Parser,
9 parser_impl::{ 10 parser_impl::{
10 event::{process, Event}, 11 event::{EventProcessor, Event},
11 input::{InputPosition, ParserInput}, 12 input::{InputPosition, ParserInput},
12 }, 13 },
13 TextUnit,
14}; 14};
15 15
16use SyntaxKind::{self, EOF, TOMBSTONE}; 16use SyntaxKind::{self, EOF, TOMBSTONE};
17 17
18pub(crate) trait Sink<'a> { 18pub(crate) trait Sink {
19 type Tree; 19 type Tree;
20 20
21 fn new(text: &'a str) -> Self; 21 fn leaf(&mut self, kind: SyntaxKind, text: SmolStr);
22
23 fn leaf(&mut self, kind: SyntaxKind, len: TextUnit);
24 fn start_internal(&mut self, kind: SyntaxKind); 22 fn start_internal(&mut self, kind: SyntaxKind);
25 fn finish_internal(&mut self); 23 fn finish_internal(&mut self);
26 fn error(&mut self, err: String); 24 fn error(&mut self, message: String, offset: TextUnit);
27 fn finish(self) -> Self::Tree; 25 fn finish(self) -> Self::Tree;
28} 26}
29 27
30/// Parse a sequence of tokens into the representative node tree 28/// Parse a sequence of tokens into the representative node tree
31pub(crate) fn parse_with<'a, S: Sink<'a>>( 29pub(crate) fn parse_with<S: Sink>(
32 text: &'a str, 30 sink: S,
31 text: &str,
33 tokens: &[Token], 32 tokens: &[Token],
34 parser: fn(&mut Parser), 33 parser: fn(&mut Parser),
35) -> S::Tree { 34) -> S::Tree {
36 let events = { 35 let mut events = {
37 let input = input::ParserInput::new(text, tokens); 36 let input = input::ParserInput::new(text, tokens);
38 let parser_impl = ParserImpl::new(&input); 37 let parser_impl = ParserImpl::new(&input);
39 let mut parser_api = Parser(parser_impl); 38 let mut parser_api = Parser(parser_impl);
40 parser(&mut parser_api); 39 parser(&mut parser_api);
41 parser_api.0.into_events() 40 parser_api.0.into_events()
42 }; 41 };
43 let mut sink = S::new(text); 42 EventProcessor::new(sink, text, tokens, &mut events)
44 process(&mut sink, tokens, events); 43 .process()
45 sink.finish() 44 .finish()
46} 45}
47 46
48/// Implementation details of `Parser`, extracted 47/// Implementation details of `Parser`, extracted
diff --git a/crates/ra_syntax/src/reparsing.rs b/crates/ra_syntax/src/reparsing.rs
index dcafd2c40..d8b6a6a10 100644
--- a/crates/ra_syntax/src/reparsing.rs
+++ b/crates/ra_syntax/src/reparsing.rs
@@ -84,7 +84,8 @@ fn reparse_block<'node>(
84 return None; 84 return None;
85 } 85 }
86 let (green, new_errors) = 86 let (green, new_errors) =
87 parser_impl::parse_with::<yellow::GreenBuilder>( 87 parser_impl::parse_with(
88 yellow::GreenBuilder::new(),
88 &text, &tokens, reparser, 89 &text, &tokens, reparser,
89 ); 90 );
90 Some((node, green, new_errors)) 91 Some((node, green, new_errors))
diff --git a/crates/ra_syntax/src/yellow/builder.rs b/crates/ra_syntax/src/yellow/builder.rs
index 35dbaec05..c307b2bd0 100644
--- a/crates/ra_syntax/src/yellow/builder.rs
+++ b/crates/ra_syntax/src/yellow/builder.rs
@@ -1,33 +1,29 @@
1use rowan::GreenNodeBuilder; 1use rowan::GreenNodeBuilder;
2use { 2use {
3 TextUnit, SmolStr,
3 parser_impl::Sink, 4 parser_impl::Sink,
4 yellow::{GreenNode, SyntaxError, RaTypes}, 5 yellow::{GreenNode, SyntaxError, RaTypes},
5 SyntaxKind, TextRange, TextUnit, 6 SyntaxKind,
6}; 7};
7 8
8pub(crate) struct GreenBuilder<'a> { 9pub(crate) struct GreenBuilder {
9 text: &'a str,
10 pos: TextUnit,
11 errors: Vec<SyntaxError>, 10 errors: Vec<SyntaxError>,
12 inner: GreenNodeBuilder<RaTypes>, 11 inner: GreenNodeBuilder<RaTypes>,
13} 12}
14 13
15impl<'a> Sink<'a> for GreenBuilder<'a> { 14impl GreenBuilder {
16 type Tree = (GreenNode, Vec<SyntaxError>); 15 pub(crate) fn new() -> GreenBuilder {
17
18 fn new(text: &'a str) -> Self {
19 GreenBuilder { 16 GreenBuilder {
20 text,
21 pos: 0.into(),
22 errors: Vec::new(), 17 errors: Vec::new(),
23 inner: GreenNodeBuilder::new(), 18 inner: GreenNodeBuilder::new(),
24 } 19 }
25 } 20 }
21}
22
23impl Sink for GreenBuilder {
24 type Tree = (GreenNode, Vec<SyntaxError>);
26 25
27 fn leaf(&mut self, kind: SyntaxKind, len: TextUnit) { 26 fn leaf(&mut self, kind: SyntaxKind, text: SmolStr) {
28 let range = TextRange::offset_len(self.pos, len);
29 self.pos += len;
30 let text = self.text[range].into();
31 self.inner.leaf(kind, text); 27 self.inner.leaf(kind, text);
32 } 28 }
33 29
@@ -39,11 +35,9 @@ impl<'a> Sink<'a> for GreenBuilder<'a> {
39 self.inner.finish_internal(); 35 self.inner.finish_internal();
40 } 36 }
41 37
42 fn error(&mut self, message: String) { 38 fn error(&mut self, message: String, offset: TextUnit) {
43 self.errors.push(SyntaxError { 39 let error = SyntaxError { msg: message, offset };
44 msg: message, 40 self.errors.push(error)
45 offset: self.pos,
46 })
47 } 41 }
48 42
49 fn finish(self) -> (GreenNode, Vec<SyntaxError>) { 43 fn finish(self) -> (GreenNode, Vec<SyntaxError>) {
diff --git a/crates/ra_syntax/tests/data/parser/err/0025_nope.txt b/crates/ra_syntax/tests/data/parser/err/0025_nope.txt
index c30b8585f..6879c8d0a 100644
--- a/crates/ra_syntax/tests/data/parser/err/0025_nope.txt
+++ b/crates/ra_syntax/tests/data/parser/err/0025_nope.txt
@@ -137,9 +137,9 @@ ROOT@[0; 575)
137 BLOCK@[306; 459) 137 BLOCK@[306; 459)
138 L_CURLY@[306; 307) 138 L_CURLY@[306; 307)
139 WHITESPACE@[307; 316) 139 WHITESPACE@[307; 316)
140 COMMENT@[316; 329) 140 ENUM_DEF@[316; 453)
141 WHITESPACE@[329; 338) 141 COMMENT@[316; 329)
142 ENUM_DEF@[338; 453) 142 WHITESPACE@[329; 338)
143 ENUM_KW@[338; 342) 143 ENUM_KW@[338; 342)
144 WHITESPACE@[342; 343) 144 WHITESPACE@[342; 343)
145 NAME@[343; 348) 145 NAME@[343; 348)
diff --git a/crates/ra_syntax/tests/data/parser/ok/0033_label_break.txt b/crates/ra_syntax/tests/data/parser/ok/0033_label_break.txt
index 6abb9234c..201eca644 100644
--- a/crates/ra_syntax/tests/data/parser/ok/0033_label_break.txt
+++ b/crates/ra_syntax/tests/data/parser/ok/0033_label_break.txt
@@ -1,7 +1,7 @@
1ROOT@[0; 506) 1ROOT@[0; 506)
2 COMMENT@[0; 33) 2 FN_DEF@[0; 505)
3 WHITESPACE@[33; 34) 3 COMMENT@[0; 33)
4 FN_DEF@[34; 505) 4 WHITESPACE@[33; 34)
5 FN_KW@[34; 36) 5 FN_KW@[34; 36)
6 WHITESPACE@[36; 37) 6 WHITESPACE@[36; 37)
7 NAME@[37; 41) 7 NAME@[37; 41)