OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 package com.google.dart.compiler.parser; | |
6 | |
7 import com.google.dart.compiler.DartCompilerListener; | |
8 import com.google.dart.compiler.DartSource; | |
9 import com.google.dart.compiler.Source; | |
10 import com.google.dart.compiler.ast.ASTVisitor; | |
11 import com.google.dart.compiler.ast.DartComment; | |
12 import com.google.dart.compiler.ast.DartDeclaration; | |
13 import com.google.dart.compiler.ast.DartField; | |
14 import com.google.dart.compiler.ast.DartMethodDefinition; | |
15 import com.google.dart.compiler.ast.DartNode; | |
16 import com.google.dart.compiler.ast.DartUnit; | |
17 import com.google.dart.compiler.common.SourceInfo; | |
18 import com.google.dart.compiler.metrics.CompilerMetrics; | |
19 import com.google.dart.compiler.util.DartSourceString; | |
20 | |
21 import java.util.ArrayList; | |
22 import java.util.Collections; | |
23 import java.util.Comparator; | |
24 import java.util.List; | |
25 | |
26 /** | |
27 * A parser for Dart that records comment positions. | |
28 */ | |
29 public class CommentPreservingParser extends DartParser { | |
30 | |
31 private static class CommentParserContext extends DartScannerParserContext { | |
32 | |
33 private List<int[]> commentLocs; | |
34 private String source; | |
35 | |
36 CommentParserContext(Source source, String code, | |
37 DartCompilerListener listener) { | |
38 super(source, code, listener); | |
39 this.source = code; | |
40 } | |
41 | |
42 CommentParserContext(Source source, String code, | |
43 DartCompilerListener listener, CompilerMetrics metrics) { | |
44 super(source, code, listener, metrics); | |
45 this.source = code; | |
46 } | |
47 | |
48 List<int[]> getCommentLocs() { | |
49 return commentLocs; | |
50 } | |
51 | |
52 @Override | |
53 protected DartScanner createScanner(String sourceCode) { | |
54 commentLocs = new ArrayList<int[]>(); | |
55 return this.new CommentScanner(sourceCode); | |
56 } | |
57 | |
58 private class CommentScanner extends DartScanner { | |
59 | |
60 CommentScanner(String sourceCode) { | |
61 super(sourceCode); | |
62 } | |
63 | |
64 @Override | |
65 protected void recordCommentLocation(int start, int stop, int line, int co
l) { | |
66 int size = commentLocs.size(); | |
67 if (size > 0) { | |
68 // the parser may re-scan lookahead tokens | |
69 // fortunately, comments are always scanned as comments | |
70 int[] loc = commentLocs.get(size - 1); | |
71 if (start <= loc[0] && stop <= loc[1]) { | |
72 return; | |
73 } | |
74 } | |
75 commentLocs.add(new int[]{start, stop, line, col}); | |
76 } | |
77 } | |
78 } | |
79 | |
80 /** | |
81 * Create a parsing context for the comment-recording parser. | |
82 */ | |
83 public static CommentParserContext createContext(Source source, String code, | |
84 DartCompilerListener listener) { | |
85 return new CommentParserContext(source, code, listener); | |
86 } | |
87 | |
88 /** | |
89 * Create a parsing context for the comment-recording parser. | |
90 */ | |
91 public static CommentParserContext createContext(Source source, String code, | |
92 DartCompilerListener listener, CompilerMetrics metrics) { | |
93 return new CommentParserContext(source, code, listener, metrics); | |
94 } | |
95 | |
96 private CommentParserContext context; | |
97 private boolean onlyDartDoc; | |
98 | |
99 /** | |
100 * Create a parser on the given <code>code</code> that records comment locatio
ns. | |
101 */ | |
102 public CommentPreservingParser(String code) { | |
103 this(code, null, false); | |
104 } | |
105 | |
106 /** | |
107 * Create a parser on the given <code>code</code> that records some comment | |
108 * locations. If <code>onlyDartDoc</code> is <code>true</code> then only | |
109 * DartDoc comments will be recorded, otherwise all comments will be recorded. | |
110 * The given <code>listener</code> will be used to inform clients of errors. | |
111 */ | |
112 public CommentPreservingParser(String code, DartCompilerListener listener, | |
113 boolean onlyDartDoc) { | |
114 this(createContext(null, code, listener), onlyDartDoc); | |
115 } | |
116 | |
117 /** | |
118 * Create a parser with the given parsing context <code>context</code>. | |
119 * If <code>onlyDartDoc</code> is <code>true</code> then only | |
120 * DartDoc comments will be recorded, otherwise all comments will be recorded. | |
121 */ | |
122 public CommentPreservingParser(ParserContext context, | |
123 boolean onlyDartDoc) { | |
124 super(context, onlyDartDoc); | |
125 this.context = (CommentParserContext) context; | |
126 this.onlyDartDoc = onlyDartDoc; | |
127 } | |
128 | |
129 @Override | |
130 public DartUnit parseUnit(DartSource input) { | |
131 DartUnit unit = super.parseUnit(input); | |
132 | |
133 String sourceString = context.source; | |
134 Source source = new DartSourceString(null, sourceString); | |
135 | |
136 for (int[] loc : context.getCommentLocs()) { | |
137 DartComment.Style style = getCommentStyle(sourceString, loc[0]); | |
138 if (!onlyDartDoc || style == DartComment.Style.DART_DOC) { | |
139 unit.getComments().add(new DartComment(source, loc[0], loc[1] - loc[0],
loc[2], loc[3], style)); | |
140 } | |
141 } | |
142 | |
143 List<DartComment> comments = unit.getComments(); | |
144 | |
145 if (comments != null) { | |
146 assignDartComments(unit, comments); | |
147 } | |
148 | |
149 return unit; | |
150 } | |
151 | |
152 private void assignDartComments(DartUnit unit, List<DartComment> comments) { | |
153 // Collect the AST nodes in a list. | |
154 final List<DartNode> astNodes = new ArrayList<DartNode>(); | |
155 unit.accept(new ASTVisitor<DartNode>() { | |
156 @Override | |
157 public DartNode visitDeclaration(DartDeclaration<?> node) { | |
158 astNodes.add(node); | |
159 return super.visitNode(node); | |
160 } | |
161 }); | |
162 | |
163 // Collect all the nodes in one list. | |
164 List<DartNode> nodes = new ArrayList<DartNode>(); | |
165 | |
166 nodes.addAll(comments); | |
167 nodes.addAll(astNodes); | |
168 | |
169 // Sort the nodes by their position in the source file. | |
170 Collections.sort(nodes, new Comparator<DartNode>() { | |
171 @Override | |
172 public int compare(DartNode node1, DartNode node2) { | |
173 return node1.getSourceInfo().getOffset() - node2.getSourceInfo().getOffs
et(); | |
174 } | |
175 }); | |
176 | |
177 // Assign dart docs to their associated DartDeclarations. | |
178 for (int i = 0; i < nodes.size(); i++) { | |
179 DartNode node = nodes.get(i); | |
180 | |
181 if (node instanceof DartComment) { | |
182 DartComment comment = (DartComment)node; | |
183 | |
184 if (comment.isDartDoc() && (i + 1 < nodes.size())) { | |
185 DartNode next = nodes.get(i + 1); | |
186 | |
187 if (next instanceof DartDeclaration) { | |
188 DartDeclaration<?> decl = (DartDeclaration<?>)next; | |
189 | |
190 if (!commentContainedBySibling(comment, decl)) { | |
191 // Dartc creates both a DartField and a DartMethodDefinition for g
etters and setters. | |
192 // They have the same source location; we want to assign the dartd
oc to the method | |
193 // definition and not the field. | |
194 if (i + 2 < nodes.size()) { | |
195 decl = adjustDartdocTarget(next, nodes.get(i + 2)); | |
196 } | |
197 | |
198 decl.setDartDoc(comment); | |
199 } | |
200 } | |
201 } | |
202 } | |
203 } | |
204 } | |
205 | |
206 private DartDeclaration<?> adjustDartdocTarget(DartNode currentNode, DartNode
nextNode) { | |
207 if (currentNode instanceof DartField && nextNode instanceof DartMethodDefini
tion) { | |
208 if (currentNode.getSourceInfo().equals(nextNode.getSourceInfo())) { | |
209 return (DartDeclaration<?>)nextNode; | |
210 } | |
211 } | |
212 | |
213 return (DartDeclaration<?>)currentNode; | |
214 } | |
215 | |
216 private boolean commentContainedBySibling(DartComment comment, DartDeclaration
<?> node) { | |
217 for (DartNode child : getChildren(node.getParent())) { | |
218 if (child != node && !(child instanceof DartComment)) { | |
219 if (isContainedBy(comment, child)) { | |
220 return true; | |
221 } | |
222 } | |
223 } | |
224 | |
225 return false; | |
226 } | |
227 | |
228 private List<DartNode> getChildren(DartNode parent) { | |
229 final List<DartNode> children = new ArrayList<DartNode>(); | |
230 | |
231 parent.visitChildren(new ASTVisitor<DartNode>() { | |
232 @Override | |
233 public DartNode visitNode(DartNode node) { | |
234 children.add(node); | |
235 return null; | |
236 } | |
237 }); | |
238 | |
239 return children; | |
240 } | |
241 | |
242 private static boolean isContainedBy(DartNode node, DartNode containedByNode)
{ | |
243 SourceInfo nodeSource = node.getSourceInfo(); | |
244 SourceInfo containedBySource = containedByNode.getSourceInfo(); | |
245 int nodeEnd = nodeSource.getOffset() + nodeSource.getLength(); | |
246 int containedByEnd = containedBySource.getOffset() + containedBySource.getLe
ngth(); | |
247 return nodeSource.getOffset() >= containedBySource.getOffset() | |
248 && nodeEnd <= containedByEnd; | |
249 } | |
250 | |
251 /** | |
252 * Return the style of the comment in the given string. | |
253 * | |
254 * @param sourceString the source containing the comment | |
255 * @param commentStart the location of the comment in the source | |
256 * | |
257 * @return the style of the comment in the given string | |
258 */ | |
259 private DartComment.Style getCommentStyle(String sourceString, int commentStar
t) { | |
260 if (sourceString.charAt(commentStart + 1) == '/') { | |
261 return DartComment.Style.END_OF_LINE; | |
262 } else if (sourceString.charAt(commentStart + 2) == '*') { | |
263 return DartComment.Style.DART_DOC; | |
264 } | |
265 return DartComment.Style.BLOCK; | |
266 } | |
267 } | |
OLD | NEW |