Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(61)

Side by Side Diff: pkg/analyzer-experimental/lib/src/generated/html_scanner.dart

Issue 12502002: Update analyzer-experimental. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // This code was auto-generated, is not intended to be edited, and is subject to
2 // significant change. Please see the README file for more information.
3
4 library engine.html.scanner;
5
6 import 'dart:collection';
7 import 'java_core.dart';
8 import 'source.dart';
9 import 'error.dart';
10 import 'instrumentation.dart';
11
12 /**
13 * Instances of the class {@code Token} represent a token that was scanned from the input. Each
14 * token knows which token follows it, acting as the head of a linked list of to kens.
15 */
16 class Token {
17 /**
18 * The offset from the beginning of the file to the first character in the tok en.
19 */
20 int _offset = 0;
21 /**
22 * The previous token in the token stream.
23 */
24 Token _previous;
25 /**
26 * The next token in the token stream.
27 */
28 Token _next;
29 /**
30 * The type of the token.
31 */
32 TokenType _type;
33 /**
34 * The lexeme represented by this token.
35 */
36 String _value;
37 /**
38 * Initialize a newly created token.
39 * @param type the token type (not {@code null})
40 * @param offset the offset from the beginning of the file to the first charac ter in the token
41 */
42 Token.con1(TokenType type, int offset) {
43 _jtd_constructor_143_impl(type, offset);
44 }
45 _jtd_constructor_143_impl(TokenType type, int offset) {
46 _jtd_constructor_144_impl(type, offset, type.lexeme);
47 }
48 /**
49 * Initialize a newly created token.
50 * @param type the token type (not {@code null})
51 * @param offset the offset from the beginning of the file to the first charac ter in the token
52 * @param value the lexeme represented by this token (not {@code null})
53 */
54 Token.con2(TokenType type4, int offset3, String value7) {
55 _jtd_constructor_144_impl(type4, offset3, value7);
56 }
57 _jtd_constructor_144_impl(TokenType type4, int offset3, String value7) {
58 this._type = type4;
59 this._value = value7;
60 this._offset = offset3;
61 }
62 /**
63 * Return the offset from the beginning of the file to the character after las t character of the
64 * token.
65 * @return the offset from the beginning of the file to the first character af ter last character
66 * of the token
67 */
68 int get end => _offset + length;
69 /**
70 * Return the number of characters in the node's source range.
71 * @return the number of characters in the node's source range
72 */
73 int get length => lexeme.length;
74 /**
75 * Return the lexeme that represents this token.
76 * @return the lexeme (not {@code null})
77 */
78 String get lexeme => _value;
79 /**
80 * Return the next token in the token stream.
81 * @return the next token in the token stream
82 */
83 Token get next => _next;
84 /**
85 * Return the offset from the beginning of the file to the first character in the token.
86 * @return the offset from the beginning of the file to the first character in the token
87 */
88 int get offset => _offset;
89 /**
90 * Return the previous token in the token stream.
91 * @return the previous token in the token stream
92 */
93 Token get previous => _previous;
94 /**
95 * Answer the token type for the receiver.
96 * @return the token type (not {@code null})
97 */
98 TokenType get type => _type;
99 /**
100 * Return {@code true} if this token is a synthetic token. A synthetic token i s a token that was
101 * introduced by the parser in order to recover from an error in the code. Syn thetic tokens always
102 * have a length of zero ({@code 0}).
103 * @return {@code true} if this token is a synthetic token
104 */
105 bool isSynthetic() => length == 0;
106 /**
107 * Set the next token in the token stream to the given token. This has the sid e-effect of setting
108 * this token to be the previous token for the given token.
109 * @param token the next token in the token stream
110 * @return the token that was passed in
111 */
112 Token setNext(Token token) {
113 _next = token;
114 token.previous = this;
115 return token;
116 }
117 String toString() => lexeme;
118 /**
119 * Set the previous token in the token stream to the given token.
120 * @param previous the previous token in the token stream
121 */
122 void set previous(Token previous2) {
123 this._previous = previous2;
124 }
125 }
126 /**
127 * Instances of {@code HtmlParseResult} hold the result of parsing an HTML file.
128 */
129 class HtmlParseResult extends HtmlScanResult {
130 /**
131 * The unit containing the parsed information (not {@code null}).
132 */
133 HtmlUnit _unit;
134 HtmlParseResult(Token token, List<int> lineStarts, HtmlUnit unit) : super(toke n, lineStarts) {
135 this._unit = unit;
136 }
137 /**
138 * Answer the unit generated by parsing the source
139 * @return the unit (not {@code null})
140 */
141 HtmlUnit get htmlUnit => _unit;
142 }
143 /**
144 * Instances of the class {@code RecursiveXmlVisitor} implement an XML visitor t hat will recursively
145 * visit all of the nodes in an XML structure. For example, using an instance of this class to visit
146 * a {@link XmlTagNode} will also cause all of the contained {@link XmlAttribute Node}s and{@link XmlTagNode}s to be visited.
147 * <p>
148 * Subclasses that override a visit method must either invoke the overridden vis it method or must
149 * explicitly ask the visited node to visit its children. Failure to do so will cause the children
150 * of the visited node to not be visited.
151 */
152 class RecursiveXmlVisitor<R> implements XmlVisitor<R> {
153 R visitHtmlUnit(HtmlUnit node) {
154 node.visitChildren(this);
155 return null;
156 }
157 R visitXmlAttributeNode(XmlAttributeNode node) {
158 node.visitChildren(this);
159 return null;
160 }
161 R visitXmlTagNode(XmlTagNode node) {
162 node.visitChildren(this);
163 return null;
164 }
165 }
166 /**
167 * The abstract class {@code XmlNode} defines behavior common to all XML/HTML no des.
168 */
169 abstract class XmlNode {
170 /**
171 * The parent of the node, or {@code null} if the node is the root of an AST s tructure.
172 */
173 XmlNode _parent;
174 /**
175 * Use the given visitor to visit this node.
176 * @param visitor the visitor that will visit this node
177 * @return the value returned by the visitor as a result of visiting this node
178 */
179 accept(XmlVisitor visitor);
180 /**
181 * Return the first token included in this node's source range.
182 * @return the first token or {@code null} if none
183 */
184 Token get beginToken;
185 /**
186 * Return the offset of the character immediately following the last character of this node's
187 * source range. This is equivalent to {@code node.getOffset() + node.getLengt h()}. For an html
188 * unit this will be equal to the length of the unit's source.
189 * @return the offset of the character just past the node's source range
190 */
191 int get end => offset + length;
192 /**
193 * Return the last token included in this node's source range.
194 * @return the last token or {@code null} if none
195 */
196 Token get endToken;
197 /**
198 * Return the number of characters in the node's source range.
199 * @return the number of characters in the node's source range
200 */
201 int get length {
202 Token beginToken5 = beginToken;
203 Token endToken4 = endToken;
204 if (beginToken5 == null || endToken4 == null) {
205 return -1;
206 }
207 return endToken4.offset + endToken4.length - beginToken5.offset;
208 }
209 /**
210 * Return the offset from the beginning of the file to the first character in the node's source
211 * range.
212 * @return the offset from the beginning of the file to the first character in the node's source
213 * range
214 */
215 int get offset {
216 Token beginToken6 = beginToken;
217 if (beginToken6 == null) {
218 return -1;
219 }
220 return beginToken.offset;
221 }
222 /**
223 * Return this node's parent node, or {@code null} if this node is the root of an AST structure.
224 * <p>
225 * Note that the relationship between an AST node and its parent node may chan ge over the lifetime
226 * of a node.
227 * @return the parent of this node, or {@code null} if none
228 */
229 XmlNode get parent => _parent;
230 /**
231 * Use the given visitor to visit all of the children of this node. The childr en will be visited
232 * in source order.
233 * @param visitor the visitor that will be used to visit the children of this node
234 */
235 void visitChildren(XmlVisitor<Object> visitor);
236 /**
237 * Make this node the parent of the given child nodes.
238 * @param children the nodes that will become the children of this node
239 * @return the nodes that were made children of this node
240 */
241 List<XmlNode> becomeParentOf(List<XmlNode> children) {
242 if (children != null) {
243 for (JavaIterator<XmlNode> iter = new JavaIterator(children); iter.hasNext ;) {
244 XmlNode node = iter.next();
245 node.parent = this;
246 }
247 }
248 return children;
249 }
250 /**
251 * Make this node the parent of the given child node.
252 * @param child the node that will become a child of this node
253 * @return the node that was made a child of this node
254 */
255 XmlNode becomeParentOf2(XmlNode child) {
256 if (child != null) {
257 XmlNode node = child;
258 node.parent = this;
259 }
260 return child;
261 }
262 /**
263 * Set the parent of this node to the given node.
264 * @param newParent the node that is to be made the parent of this node
265 */
266 void set parent(XmlNode newParent) {
267 _parent = newParent;
268 }
269 }
270 /**
271 * The abstract class {@code AbstractScanner} implements a scanner for HTML code . Subclasses are
272 * required to implement the interface used to access the characters being scann ed.
273 */
274 abstract class AbstractScanner {
275 static List<String> _NO_PASS_THROUGH_ELEMENTS = <String> [];
276 /**
277 * The source being scanned.
278 */
279 Source _source;
280 /**
281 * The token pointing to the head of the linked list of tokens.
282 */
283 Token _tokens;
284 /**
285 * The last token that was scanned.
286 */
287 Token _tail;
288 /**
289 * A list containing the offsets of the first character of each line in the so urce code.
290 */
291 List<int> _lineStarts = new List<int>();
292 /**
293 * An array of element tags for which the content between tags should be consi der a single token.
294 */
295 List<String> _passThroughElements = _NO_PASS_THROUGH_ELEMENTS;
296 /**
297 * Initialize a newly created scanner.
298 * @param source the source being scanned
299 */
300 AbstractScanner(Source source) {
301 this._source = source;
302 _tokens = new Token.con1(TokenType.EOF, -1);
303 _tokens.setNext(_tokens);
304 _tail = _tokens;
305 recordStartOfLine();
306 }
307 /**
308 * Return an array containing the offsets of the first character of each line in the source code.
309 * @return an array containing the offsets of the first character of each line in the source code
310 */
311 List<int> get lineStarts => _lineStarts;
312 /**
313 * Return the current offset relative to the beginning of the file. Return the initial offset if
314 * the scanner has not yet scanned the source code, and one (1) past the end o f the source code if
315 * the source code has been scanned.
316 * @return the current offset of the scanner in the source
317 */
318 int get offset;
319 /**
320 * Answer the source being scanned.
321 * @return the source or {@code null} if undefined
322 */
323 Source get source => _source;
324 /**
325 * Set array of element tags for which the content between tags should be cons ider a single token.
326 */
327 void set passThroughElements(List<String> passThroughElements2) {
328 this._passThroughElements = passThroughElements2 != null ? passThroughElemen ts2 : _NO_PASS_THROUGH_ELEMENTS;
329 }
330 /**
331 * Scan the source code to produce a list of tokens representing the source.
332 * @return the first token in the list of tokens that were produced
333 */
334 Token tokenize() {
335 scan();
336 appendEofToken();
337 return firstToken();
338 }
339 /**
340 * Advance the current position and return the character at the new current po sition.
341 * @return the character at the new current position
342 */
343 int advance();
344 /**
345 * Return the substring of the source code between the start offset and the mo dified current
346 * position. The current position is modified by adding the end delta.
347 * @param start the offset to the beginning of the string, relative to the sta rt of the file
348 * @param endDelta the number of character after the current location to be in cluded in the
349 * string, or the number of characters before the current location to be exclu ded if the
350 * offset is negative
351 * @return the specified substring of the source code
352 */
353 String getString(int start, int endDelta);
354 /**
355 * Return the character at the current position without changing the current p osition.
356 * @return the character at the current position
357 */
358 int peek();
359 /**
360 * Record the fact that we are at the beginning of a new line in the source.
361 */
362 void recordStartOfLine() {
363 _lineStarts.add(offset);
364 }
365 void appendEofToken() {
366 Token eofToken = new Token.con1(TokenType.EOF, offset);
367 eofToken.setNext(eofToken);
368 _tail = _tail.setNext(eofToken);
369 }
370 Token emit(Token token) {
371 _tail.setNext(token);
372 _tail = token;
373 return token;
374 }
375 Token emit2(TokenType type, int start) => emit(new Token.con1(type, start));
376 Token emit3(TokenType type, int start, int count) => emit(new Token.con2(type, start, getString(start, count)));
377 Token firstToken() => _tokens.next;
378 int recordStartOfLineAndAdvance(int c) {
379 if (c == 0xD) {
380 c = advance();
381 if (c == 0xA) {
382 c = advance();
383 }
384 recordStartOfLine();
385 } else if (c == 0xA) {
386 c = advance();
387 recordStartOfLine();
388 } else {
389 c = advance();
390 }
391 return c;
392 }
393 void scan() {
394 bool inBrackets = false;
395 bool passThrough = false;
396 int c = advance();
397 while (c >= 0) {
398 int start = offset;
399 if (c == 0x3C) {
400 c = advance();
401 if (c == 0x21) {
402 c = advance();
403 if (c == 0x2D && peek() == 0x2D) {
404 c = advance();
405 int dashCount = 1;
406 while (c >= 0) {
407 if (c == 0x2D) {
408 dashCount++;
409 } else if (c == 0x3E && dashCount >= 2) {
410 c = advance();
411 break;
412 } else {
413 dashCount = 0;
414 }
415 c = recordStartOfLineAndAdvance(c);
416 }
417 emit3(TokenType.COMMENT, start, -1);
418 if (_tail.length < 7) {
419 }
420 } else {
421 while (c >= 0) {
422 if (c == 0x3E) {
423 c = advance();
424 break;
425 }
426 c = recordStartOfLineAndAdvance(c);
427 }
428 emit3(TokenType.DECLARATION, start, -1);
429 if (!_tail.lexeme.endsWith(">")) {
430 }
431 }
432 } else if (c == 0x3F) {
433 while (c >= 0) {
434 if (c == 0x3F) {
435 c = advance();
436 if (c == 0x3E) {
437 c = advance();
438 break;
439 }
440 } else {
441 c = recordStartOfLineAndAdvance(c);
442 }
443 }
444 emit3(TokenType.DIRECTIVE, start, -1);
445 if (_tail.length < 4) {
446 }
447 } else if (c == 0x2F) {
448 emit2(TokenType.LT_SLASH, start);
449 inBrackets = true;
450 c = advance();
451 } else {
452 inBrackets = true;
453 emit2(TokenType.LT, start);
454 while (Character.isWhitespace(c)) {
455 c = recordStartOfLineAndAdvance(c);
456 }
457 if (Character.isLetterOrDigit(c)) {
458 int tagStart = offset;
459 c = advance();
460 while (Character.isLetterOrDigit(c) || c == 0x2D || c == 0x5F) {
461 c = advance();
462 }
463 emit3(TokenType.TAG, tagStart, -1);
464 String tag = _tail.lexeme;
465 for (String str in _passThroughElements) {
466 if (str == tag) {
467 passThrough = true;
468 break;
469 }
470 }
471 }
472 }
473 } else if (c == 0x3E) {
474 emit2(TokenType.GT, start);
475 inBrackets = false;
476 c = advance();
477 if (passThrough) {
478 while (c >= 0 && (c != 0x3C || peek() != 0x2F)) {
479 c = recordStartOfLineAndAdvance(c);
480 }
481 if (start + 1 < offset) {
482 emit3(TokenType.TEXT, start + 1, -1);
483 }
484 passThrough = false;
485 }
486 } else if (c == 0x2F && peek() == 0x3E) {
487 advance();
488 emit2(TokenType.SLASH_GT, start);
489 inBrackets = false;
490 c = advance();
491 } else if (!inBrackets) {
492 c = recordStartOfLineAndAdvance(c);
493 while (c != 0x3C && c >= 0) {
494 c = recordStartOfLineAndAdvance(c);
495 }
496 emit3(TokenType.TEXT, start, -1);
497 } else if (c == 0x22 || c == 0x27) {
498 int endQuote = c;
499 c = advance();
500 while (c >= 0) {
501 if (c == endQuote) {
502 c = advance();
503 break;
504 }
505 c = recordStartOfLineAndAdvance(c);
506 }
507 emit3(TokenType.STRING, start, -1);
508 } else if (c == 0x3D) {
509 emit2(TokenType.EQ, start);
510 c = advance();
511 } else if (Character.isWhitespace(c)) {
512 do {
513 c = recordStartOfLineAndAdvance(c);
514 } while (Character.isWhitespace(c));
515 } else if (Character.isLetterOrDigit(c)) {
516 c = advance();
517 while (Character.isLetterOrDigit(c) || c == 0x2D || c == 0x5F) {
518 c = advance();
519 }
520 emit3(TokenType.TAG, start, -1);
521 } else {
522 emit3(TokenType.TEXT, start, 0);
523 c = advance();
524 }
525 }
526 }
527 }
528 /**
529 * Instances of {@code HtmlScanResult} hold the result of scanning an HTML file.
530 */
531 class HtmlScanResult {
532 /**
533 * The first token in the token stream (not {@code null}).
534 */
535 Token _token;
536 /**
537 * The line start information that was produced.
538 */
539 List<int> _lineStarts;
540 HtmlScanResult(Token token, List<int> lineStarts) {
541 this._token = token;
542 this._lineStarts = lineStarts;
543 }
544 /**
545 * Answer the line start information that was produced.
546 * @return an array of line starts (not {@code null})
547 */
548 List<int> get lineStarts => _lineStarts;
549 /**
550 * Answer the first token in the token stream.
551 * @return the token (not {@code null})
552 */
553 Token get token => _token;
554 }
555 /**
556 * Instances of the class {@code StringScanner} implement a scanner that reads f rom a string. The
557 * scanning logic is in the superclass.
558 */
559 class StringScanner extends AbstractScanner {
560 /**
561 * The string from which characters will be read.
562 */
563 String _string;
564 /**
565 * The number of characters in the string.
566 */
567 int _stringLength = 0;
568 /**
569 * The index, relative to the string, of the last character that was read.
570 */
571 int _charOffset = 0;
572 /**
573 * Initialize a newly created scanner to scan the characters in the given stri ng.
574 * @param source the source being scanned
575 * @param string the string from which characters will be read
576 */
577 StringScanner(Source source, String string) : super(source) {
578 this._string = string;
579 this._stringLength = string.length;
580 this._charOffset = -1;
581 }
582 int get offset => _charOffset;
583 void set offset(int offset12) {
584 _charOffset = offset12;
585 }
586 int advance() {
587 if (++_charOffset < _stringLength) {
588 return _string.codeUnitAt(_charOffset);
589 }
590 _charOffset = _stringLength;
591 return -1;
592 }
593 String getString(int start, int endDelta) => _string.substring(start, _charOff set + 1 + endDelta);
594 int peek() {
595 if (_charOffset + 1 < _stringLength) {
596 return _string.codeUnitAt(_charOffset + 1);
597 }
598 return -1;
599 }
600 }
601 /**
602 * Instances of the class {@code CharBufferScanner} implement a scanner that rea ds from a character
603 * buffer. The scanning logic is in the superclass.
604 */
605 class CharBufferScanner extends AbstractScanner {
606 /**
607 * The buffer from which characters will be read.
608 */
609 CharBuffer _buffer;
610 /**
611 * The number of characters in the buffer.
612 */
613 int _bufferLength = 0;
614 /**
615 * The index of the last character that was read.
616 */
617 int _charOffset = 0;
618 /**
619 * Initialize a newly created scanner to scan the characters in the given char acter buffer.
620 * @param source the source being scanned
621 * @param buffer the buffer from which characters will be read
622 */
623 CharBufferScanner(Source source, CharBuffer buffer) : super(source) {
624 this._buffer = buffer;
625 this._bufferLength = buffer.length();
626 this._charOffset = -1;
627 }
628 int get offset => _charOffset;
629 int advance() {
630 if (++_charOffset < _bufferLength) {
631 return _buffer.charAt(_charOffset);
632 }
633 _charOffset = _bufferLength;
634 return -1;
635 }
636 String getString(int start, int endDelta) => _buffer.subSequence(start, _charO ffset + 1 + endDelta).toString();
637 int peek() {
638 if (_charOffset + 1 < _bufferLength) {
639 return _buffer.charAt(_charOffset + 1);
640 }
641 return -1;
642 }
643 }
644 /**
645 * The enumeration {@code TokenType} defines the types of tokens that can be ret urned by the
646 * scanner.
647 */
648 class TokenType {
649 /**
650 * The type of the token that marks the end of the input.
651 */
652 static final TokenType EOF = new TokenType_EOF('EOF', 0, "");
653 static final TokenType EQ = new TokenType('EQ', 1, "=");
654 static final TokenType GT = new TokenType('GT', 2, ">");
655 static final TokenType LT_SLASH = new TokenType('LT_SLASH', 3, "</");
656 static final TokenType LT = new TokenType('LT', 4, "<");
657 static final TokenType SLASH_GT = new TokenType('SLASH_GT', 5, "/>");
658 static final TokenType COMMENT = new TokenType('COMMENT', 6, null);
659 static final TokenType DECLARATION = new TokenType('DECLARATION', 7, null);
660 static final TokenType DIRECTIVE = new TokenType('DIRECTIVE', 8, null);
661 static final TokenType STRING = new TokenType('STRING', 9, null);
662 static final TokenType TAG = new TokenType('TAG', 10, null);
663 static final TokenType TEXT = new TokenType('TEXT', 11, null);
664 static final List<TokenType> values = [EOF, EQ, GT, LT_SLASH, LT, SLASH_GT, CO MMENT, DECLARATION, DIRECTIVE, STRING, TAG, TEXT];
665 final String __name;
666 final int __ordinal;
667 /**
668 * The lexeme that defines this type of token, or {@code null} if there is mor e than one possible
669 * lexeme for this type of token.
670 */
671 String _lexeme;
672 TokenType(this.__name, this.__ordinal, String lexeme) {
673 this._lexeme = lexeme;
674 }
675 /**
676 * Return the lexeme that defines this type of token, or {@code null} if there is more than one
677 * possible lexeme for this type of token.
678 * @return the lexeme that defines this type of token
679 */
680 String get lexeme => _lexeme;
681 String toString() => __name;
682 }
683 class TokenType_EOF extends TokenType {
684 TokenType_EOF(String ___name, int ___ordinal, String arg0) : super(___name, __ _ordinal, arg0);
685 String toString() => "-eof-";
686 }
687 /**
688 * Instances of {@code XmlAttributeNode} represent name/value pairs owned by an {@link XmlTagNode}.
689 */
690 class XmlAttributeNode extends XmlNode {
691 Token _name;
692 Token _equals;
693 Token _value;
694 /**
695 * Construct a new instance representing an XML attribute.
696 * @param name the name token (not {@code null}). This may be a zero length to ken if the attribute
697 * is badly formed.
698 * @param equals the equals sign or {@code null} if none
699 * @param value the value token (not {@code null})
700 */
701 XmlAttributeNode(Token name, Token equals, Token value) {
702 this._name = name;
703 this._equals = equals;
704 this._value = value;
705 }
706 accept(XmlVisitor visitor) => visitor.visitXmlAttributeNode(this);
707 Token get beginToken => _name;
708 Token get endToken => _value;
709 /**
710 * Answer the equals sign token that appears between the name and value tokens . This may be{@code null} if the attribute is badly formed.
711 * @return the token or {@code null} if there is no equals sign between the na me and value
712 */
713 Token get equals => _equals;
714 /**
715 * Answer the attribute name. This may be a zero length token if the attribute is badly formed.
716 * @return the name (not {@code null})
717 */
718 Token get name => _name;
719 /**
720 * Answer the attribute value. A properly formed value will start and end with matching quote
721 * characters, but the value returned may not be properly formed.
722 * @return the value or {@code null} if this represents a badly formed attribu te
723 */
724 Token get value => _value;
725 void visitChildren(XmlVisitor<Object> visitor) {
726 }
727 }
728 /**
729 * The interface {@code XmlVisitor} defines the behavior of objects that can be used to visit an{@link XmlNode} structure.
730 */
731 abstract class XmlVisitor<R> {
732 R visitHtmlUnit(HtmlUnit htmlUnit);
733 R visitXmlAttributeNode(XmlAttributeNode xmlAttributeNode);
734 R visitXmlTagNode(XmlTagNode xmlTagNode);
735 }
736 /**
737 * Instances of {@code HtmlScanner} receive and scan HTML content from a {@link Source}.<br/>
738 * For example, the following code scans HTML source and returns the result:
739 * <pre>
740 * HtmlScanner scanner = new HtmlScanner(source);
741 * source.getContents(scanner);
742 * return scanner.getResult();
743 * </pre>
744 */
745 class HtmlScanner implements Source_ContentReceiver {
746 List<String> _SCRIPT_TAG = <String> ["script"];
747 /**
748 * The source being scanned (not {@code null})
749 */
750 Source _source;
751 /**
752 * The scanner used to scan the source
753 */
754 AbstractScanner _scanner;
755 /**
756 * The first token in the token stream.
757 */
758 Token _token;
759 /**
760 * Construct a new instance to scan the specified source.
761 * @param source the source to be scanned (not {@code null})
762 */
763 HtmlScanner(Source source) {
764 this._source = source;
765 }
766 accept(CharBuffer contents) {
767 _scanner = new CharBufferScanner(_source, contents);
768 _scanner.passThroughElements = _SCRIPT_TAG;
769 _token = _scanner.tokenize();
770 }
771 void accept2(String contents) {
772 _scanner = new StringScanner(_source, contents);
773 _scanner.passThroughElements = _SCRIPT_TAG;
774 _token = _scanner.tokenize();
775 }
776 /**
777 * Answer the result of scanning the source
778 * @return the result (not {@code null})
779 */
780 HtmlScanResult get result => new HtmlScanResult(_token, _scanner.lineStarts);
781 }
782 /**
783 * Instances of the class {@code XmlParser} are used to parse tokens into a AST structure comprised
784 * of {@link XmlNode}s.
785 */
786 class XmlParser {
787 /**
788 * The source being parsed.
789 */
790 Source _source;
791 /**
792 * The next token to be parsed.
793 */
794 Token _currentToken;
795 /**
796 * Construct a parser for the specified source.
797 * @param source the source being parsed
798 */
799 XmlParser(Source source) {
800 this._source = source;
801 }
802 /**
803 * Answer the source being parsed.
804 * @return the source
805 */
806 Source get source => _source;
807 /**
808 * Answer {@code true} if the specified tag is self closing and thus should ne ver have content or
809 * child tag nodes.
810 * @param tag the tag (not {@code null})
811 * @return {@code true} if self closing
812 */
813 bool isSelfClosing(Token tag) => false;
814 /**
815 * Parse the entire token stream and in the process, advance the current token to the end of the
816 * token stream.
817 * @return the list of tag nodes found (not {@code null}, contains no {@code n ull})
818 */
819 List<XmlTagNode> parseTopTagNodes(Token firstToken) {
820 _currentToken = firstToken;
821 List<XmlTagNode> tagNodes = new List<XmlTagNode>();
822 while (true) {
823 while (true) {
824 if (_currentToken.type == TokenType.LT) {
825 tagNodes.add(parseTagNode());
826 } else if (_currentToken.type == TokenType.DECLARATION || _currentToken. type == TokenType.DIRECTIVE || _currentToken.type == TokenType.COMMENT) {
827 _currentToken = _currentToken.next;
828 } else if (_currentToken.type == TokenType.EOF) {
829 return tagNodes;
830 } else {
831 reportUnexpectedToken();
832 _currentToken = _currentToken.next;
833 }
834 break;
835 }
836 }
837 }
838 /**
839 * Answer the current token.
840 * @return the current token
841 */
842 Token get currentToken => _currentToken;
843 /**
844 * Insert a synthetic token of the specified type before the current token
845 * @param type the type of token to be inserted (not {@code null})
846 * @return the synthetic token that was inserted (not {@code null})
847 */
848 Token insertSyntheticToken(TokenType type) {
849 Token token = new Token.con2(type, _currentToken.offset, "");
850 _currentToken.previous.setNext(token);
851 token.setNext(_currentToken);
852 return token;
853 }
854 /**
855 * Parse the token stream for an attribute. This method advances the current t oken over the
856 * attribute, but should not be called if the {@link #currentToken} is not {@l ink TokenType#TAG}.
857 * @return the attribute (not {@code null})
858 */
859 XmlAttributeNode parseAttribute() {
860 Token name = _currentToken;
861 _currentToken = _currentToken.next;
862 Token equals;
863 if (identical(_currentToken.type, TokenType.EQ)) {
864 equals = _currentToken;
865 _currentToken = _currentToken.next;
866 } else {
867 reportUnexpectedToken();
868 equals = insertSyntheticToken(TokenType.EQ);
869 }
870 Token value;
871 if (identical(_currentToken.type, TokenType.STRING)) {
872 value = _currentToken;
873 _currentToken = _currentToken.next;
874 } else {
875 reportUnexpectedToken();
876 value = insertSyntheticToken(TokenType.STRING);
877 }
878 return new XmlAttributeNode(name, equals, value);
879 }
880 /**
881 * Parse the stream for a sequence of attributes. This method advances the cur rent token to the
882 * next {@link TokenType#GT}, {@link TokenType#SLASH_GT}, or {@link TokenType# EOF}.
883 * @return a collection of zero or more attributes (not {@code null}, contains no {@code null}s)
884 */
885 List<XmlAttributeNode> parseAttributes() {
886 TokenType type11 = _currentToken.type;
887 if (identical(type11, TokenType.GT) || identical(type11, TokenType.SLASH_GT) || identical(type11, TokenType.EOF)) {
888 return XmlTagNode.NO_ATTRIBUTES;
889 }
890 List<XmlAttributeNode> attributes = new List<XmlAttributeNode>();
891 while (true) {
892 while (true) {
893 if (_currentToken.type == TokenType.GT || _currentToken.type == TokenTyp e.SLASH_GT || _currentToken.type == TokenType.EOF) {
894 return attributes;
895 } else if (_currentToken.type == TokenType.TAG) {
896 attributes.add(parseAttribute());
897 } else {
898 reportUnexpectedToken();
899 _currentToken = _currentToken.next;
900 }
901 break;
902 }
903 }
904 }
905 /**
906 * Parse the stream for a sequence of tag nodes existing within a parent tag n ode. This method
907 * advances the current token to the next {@link TokenType#LT_SLASH} or {@link TokenType#EOF}.
908 * @return a list of nodes (not {@code null}, contains no {@code null}s)
909 */
910 List<XmlTagNode> parseChildTagNodes() {
911 TokenType type12 = _currentToken.type;
912 if (identical(type12, TokenType.LT_SLASH) || identical(type12, TokenType.EOF )) {
913 return XmlTagNode.NO_TAG_NODES;
914 }
915 List<XmlTagNode> nodes = new List<XmlTagNode>();
916 while (true) {
917 while (true) {
918 if (_currentToken.type == TokenType.LT) {
919 nodes.add(parseTagNode());
920 } else if (_currentToken.type == TokenType.LT_SLASH || _currentToken.typ e == TokenType.EOF) {
921 return nodes;
922 } else if (_currentToken.type == TokenType.COMMENT) {
923 _currentToken = _currentToken.next;
924 } else {
925 reportUnexpectedToken();
926 _currentToken = _currentToken.next;
927 }
928 break;
929 }
930 }
931 }
932 /**
933 * Parse the token stream for the next tag node. This method advances current token over the
934 * parsed tag node, but should only be called if the current token is {@link T okenType#LT}
935 * @return the tag node or {@code null} if none found
936 */
937 XmlTagNode parseTagNode() {
938 Token nodeStart = _currentToken;
939 _currentToken = _currentToken.next;
940 Token tag;
941 if (identical(_currentToken.type, TokenType.TAG)) {
942 tag = _currentToken;
943 _currentToken = _currentToken.next;
944 } else {
945 reportUnexpectedToken();
946 tag = insertSyntheticToken(TokenType.TAG);
947 }
948 List<XmlAttributeNode> attributes = parseAttributes();
949 Token attributeEnd;
950 if (identical(_currentToken.type, TokenType.GT) || identical(_currentToken.t ype, TokenType.SLASH_GT)) {
951 attributeEnd = _currentToken;
952 _currentToken = _currentToken.next;
953 } else {
954 reportUnexpectedToken();
955 attributeEnd = insertSyntheticToken(TokenType.SLASH_GT);
956 }
957 if (identical(attributeEnd.type, TokenType.SLASH_GT) || isSelfClosing(tag)) {
958 return new XmlTagNode(nodeStart, tag, attributes, attributeEnd, XmlTagNode .NO_TAG_NODES, _currentToken, null, attributeEnd);
959 }
960 List<XmlTagNode> tagNodes = parseChildTagNodes();
961 Token contentEnd;
962 if (identical(_currentToken.type, TokenType.LT_SLASH)) {
963 contentEnd = _currentToken;
964 _currentToken = _currentToken.next;
965 } else {
966 reportUnexpectedToken();
967 contentEnd = insertSyntheticToken(TokenType.LT_SLASH);
968 }
969 Token closingTag;
970 if (identical(_currentToken.type, TokenType.TAG)) {
971 closingTag = _currentToken;
972 _currentToken = _currentToken.next;
973 } else {
974 reportUnexpectedToken();
975 closingTag = insertSyntheticToken(TokenType.TAG);
976 }
977 Token nodeEnd;
978 if (identical(_currentToken.type, TokenType.GT)) {
979 nodeEnd = _currentToken;
980 _currentToken = _currentToken.next;
981 } else {
982 reportUnexpectedToken();
983 nodeEnd = insertSyntheticToken(TokenType.GT);
984 }
985 return new XmlTagNode(nodeStart, tag, attributes, attributeEnd, tagNodes, co ntentEnd, closingTag, nodeEnd);
986 }
987 /**
988 * Report the current token as unexpected
989 */
990 void reportUnexpectedToken() {
991 }
992 }
993 /**
994 * Instances of {@code XmlTagNode} represent XML or HTML elements such as {@code <p>} and{@code <body foo="bar"> ... </body>}.
995 */
996 class XmlTagNode extends XmlNode {
997 /**
998 * Constant representing empty list of attributes.
999 */
1000 static List<XmlAttributeNode> NO_ATTRIBUTES = new UnmodifiableListView(new Lis t<XmlAttributeNode>());
1001 /**
1002 * Constant representing empty list of tag nodes.
1003 */
1004 static List<XmlTagNode> NO_TAG_NODES = new UnmodifiableListView(new List<XmlTa gNode>());
1005 /**
1006 * The starting {@link TokenType#LT} token (not {@code null}).
1007 */
1008 Token _nodeStart;
1009 /**
1010 * The {@link TokenType#TAG} token after the starting '&lt;' (not {@code null} ).
1011 */
1012 Token _tag;
1013 /**
1014 * The attributes contained by the receiver (not {@code null}, contains no {@c ode null}s).
1015 */
1016 List<XmlAttributeNode> _attributes;
1017 /**
1018 * The {@link TokenType#GT} or {@link TokenType#SLASH_GT} token after the attr ibutes (not{@code null}). The token may be the same token as {@link #nodeEnd} if there are no child{@link #tagNodes}.
1019 */
1020 Token _attributeEnd;
1021 /**
1022 * The tag nodes contained in the receiver (not {@code null}, contains no {@co de null}s).
1023 */
1024 List<XmlTagNode> _tagNodes;
1025 /**
1026 * The token (not {@code null}) after the content, which may be
1027 * <ul>
1028 * <li>(1) {@link TokenType#LT_SLASH} for nodes with open and close tags, or</ li>
1029 * <li>(2) the {@link TokenType#LT} nodeStart of the next sibling node if this node is self
1030 * closing or the attributeEnd is {@link TokenType#SLASH_GT}, or</li>
1031 * <li>(3) {@link TokenType#EOF} if the node does not have a closing tag and i s the last node in
1032 * the stream {@link TokenType#LT_SLASH} token after the content, or {@code nu ll} if there is no
1033 * content and the attributes ended with {@link TokenType#SLASH_GT}.</li>
1034 * </ul>
1035 */
1036 Token _contentEnd;
1037 /**
1038 * The closing {@link TokenType#TAG} after the child elements or {@code null} if there is no
1039 * content and the attributes ended with {@link TokenType#SLASH_GT}
1040 */
1041 Token _closingTag;
1042 /**
1043 * The ending {@link TokenType#GT} or {@link TokenType#SLASH_GT} token (not {@ code null}).
1044 */
1045 Token _nodeEnd;
1046 /**
1047 * Construct a new instance representing an XML or HTML element
1048 * @param nodeStart the starting {@link TokenType#LT} token (not {@code null})
1049 * @param tag the {@link TokenType#TAG} token after the starting '&lt;' (not { @code null}).
1050 * @param attributes the attributes associated with this element or {@link #NO _ATTRIBUTES} (not{@code null}, contains no {@code null}s)
1051 * @param attributeEnd The {@link TokenType#GT} or {@link TokenType#SLASH_GT} token after the
1052 * attributes (not {@code null}). The token may be the same token as {@link #n odeEnd} if
1053 * there are no child {@link #tagNodes}.
1054 * @param tagNodes child tag nodes of the receiver or {@link #NO_TAG_NODES} (n ot {@code null},
1055 * contains no {@code null}s)
1056 * @param contentEnd the token (not {@code null}) after the content, which may be
1057 * <ul>
1058 * <li>(1) {@link TokenType#LT_SLASH} for nodes with open and close tags, or</ li>
1059 * <li>(2) the {@link TokenType#LT} nodeStart of the next sibling node if this node is
1060 * self closing or the attributeEnd is {@link TokenType#SLASH_GT}, or</li>
1061 * <li>(3) {@link TokenType#EOF} if the node does not have a closing tag and i s the last
1062 * node in the stream {@link TokenType#LT_SLASH} token after the content, or { @code null}if there is no content and the attributes ended with {@link TokenType #SLASH_GT}.</li>
1063 * </ul>
1064 * @param closingTag the closing {@link TokenType#TAG} after the child element s or {@code null} if
1065 * there is no content and the attributes ended with {@link TokenType#SLASH_GT }
1066 * @param nodeEnd the ending {@link TokenType#GT} or {@link TokenType#SLASH_GT } token (not{@code null})
1067 */
1068 XmlTagNode(Token nodeStart, Token tag, List<XmlAttributeNode> attributes, Toke n attributeEnd, List<XmlTagNode> tagNodes, Token contentEnd, Token closingTag, T oken nodeEnd) {
1069 this._nodeStart = nodeStart;
1070 this._tag = tag;
1071 this._attributes = becomeParentOf(attributes);
1072 this._attributeEnd = attributeEnd;
1073 this._tagNodes = becomeParentOf(tagNodes);
1074 this._contentEnd = contentEnd;
1075 this._closingTag = closingTag;
1076 this._nodeEnd = nodeEnd;
1077 }
1078 accept(XmlVisitor visitor) => visitor.visitXmlTagNode(this);
1079 /**
1080 * The {@link TokenType#GT} or {@link TokenType#SLASH_GT} token after the attr ibutes (not{@code null}). The token may be the same token as {@link #nodeEnd} if there are no child{@link #tagNodes}.
1081 * @return the token (not {@code null})
1082 */
1083 Token get attributeEnd => _attributeEnd;
1084 /**
1085 * Answer the receiver's attributes. Callers should not manipulate the returne d list to edit the
1086 * AST structure.
1087 * @return the attributes (not {@code null}, contains no {@code null}s)
1088 */
1089 List<XmlAttributeNode> get attributes => _attributes;
1090 Token get beginToken => _nodeStart;
1091 /**
1092 * The the closing {@link TokenType#TAG} after the child elements or {@code nu ll} if there is no
1093 * content and the attributes ended with {@link TokenType#SLASH_GT}
1094 * @return the closing tag or {@code null}
1095 */
1096 Token get closingTag => _closingTag;
1097 /**
1098 * Answer a string representing the content contained in the receiver. This in cludes the textual
1099 * representation of any child tag nodes ({@link #getTagNodes()}). Whitespace between '&lt;',
1100 * '&lt;/', and '>', '/>' is discarded, but all other whitespace is preserved.
1101 * @return the content (not {@code null})
1102 */
1103 String get content {
1104 Token token = _attributeEnd.next;
1105 if (identical(token, _contentEnd)) {
1106 return "";
1107 }
1108 String content = token.lexeme;
1109 token = token.next;
1110 if (identical(token, _contentEnd)) {
1111 return content;
1112 }
1113 StringBuffer buffer = new StringBuffer();
1114 while (token != _contentEnd) {
1115 buffer.write(token.lexeme);
1116 token = token.next;
1117 }
1118 return buffer.toString();
1119 }
1120 /**
1121 * Answer the token (not {@code null}) after the content, which may be
1122 * <ul>
1123 * <li>(1) {@link TokenType#LT_SLASH} for nodes with open and close tags, or</ li>
1124 * <li>(2) the {@link TokenType#LT} nodeStart of the next sibling node if this node is self
1125 * closing or the attributeEnd is {@link TokenType#SLASH_GT}, or</li>
1126 * <li>(3) {@link TokenType#EOF} if the node does not have a closing tag and i s the last node in
1127 * the stream {@link TokenType#LT_SLASH} token after the content, or {@code nu ll} if there is no
1128 * content and the attributes ended with {@link TokenType#SLASH_GT}.</li>
1129 * </ul>
1130 * @return the token (not {@code null})
1131 */
1132 Token get contentEnd => _contentEnd;
1133 Token get endToken {
1134 if (_nodeEnd != null) {
1135 return _nodeEnd;
1136 }
1137 if (_closingTag != null) {
1138 return _closingTag;
1139 }
1140 if (_contentEnd != null) {
1141 return _contentEnd;
1142 }
1143 if (!_tagNodes.isEmpty) {
1144 return _tagNodes[_tagNodes.length - 1].endToken;
1145 }
1146 if (_attributeEnd != null) {
1147 return _attributeEnd;
1148 }
1149 if (!_attributes.isEmpty) {
1150 return _attributes[_attributes.length - 1].endToken;
1151 }
1152 return _tag;
1153 }
1154 /**
1155 * Answer the ending {@link TokenType#GT} or {@link TokenType#SLASH_GT} token.
1156 * @return the token (not {@code null})
1157 */
1158 Token get nodeEnd => _nodeEnd;
1159 /**
1160 * Answer the starting {@link TokenType#LT} token.
1161 * @return the token (not {@code null})
1162 */
1163 Token get nodeStart => _nodeStart;
1164 /**
1165 * Answer the {@link TokenType#TAG} token after the starting '&lt;'.
1166 * @return the token (not {@code null})
1167 */
1168 Token get tag => _tag;
1169 /**
1170 * Answer the tag nodes contained in the receiver. Callers should not manipula te the returned list
1171 * to edit the AST structure.
1172 * @return the children (not {@code null}, contains no {@code null}s)
1173 */
1174 List<XmlTagNode> get tagNodes => _tagNodes;
1175 void visitChildren(XmlVisitor<Object> visitor) {
1176 for (XmlAttributeNode node in _attributes) {
1177 node.accept(visitor);
1178 }
1179 for (XmlTagNode node in _tagNodes) {
1180 node.accept(visitor);
1181 }
1182 }
1183 }
1184 /**
1185 * Instances of the class {@code HtmlParser} are used to parse tokens into a AST structure comprised
1186 * of {@link XmlNode}s.
1187 */
1188 class HtmlParser extends XmlParser {
1189 static Set<String> SELF_CLOSING = new Set<String>();
1190 /**
1191 * Construct a parser for the specified source.
1192 * @param source the source being parsed
1193 */
1194 HtmlParser(Source source) : super(source) {
1195 }
1196 /**
1197 * Parse the tokens specified by the given scan result.
1198 * @param scanResult the result of scanning an HTML source (not {@code null})
1199 * @return the parse result (not {@code null})
1200 */
1201 HtmlParseResult parse(HtmlScanResult scanResult) {
1202 Token firstToken = scanResult.token;
1203 List<XmlTagNode> tagNodes = parseTopTagNodes(firstToken);
1204 HtmlUnit unit = new HtmlUnit(firstToken, tagNodes, currentToken);
1205 return new HtmlParseResult(firstToken, scanResult.lineStarts, unit);
1206 }
1207 /**
1208 * Scan then parse the specified source.
1209 * @param source the source to be scanned and parsed (not {@code null})
1210 * @return the parse result (not {@code null})
1211 */
1212 HtmlParseResult parse2(Source source) {
1213 HtmlScanner scanner = new HtmlScanner(source);
1214 source.getContents(scanner);
1215 return parse(scanner.result);
1216 }
1217 bool isSelfClosing(Token tag) => SELF_CLOSING.contains(tag.lexeme);
1218 }
1219 /**
1220 * Instances of the class {@code HtmlUnit} represent the contents of an HTML fil e.
1221 */
1222 class HtmlUnit extends XmlNode {
1223 /**
1224 * The first token in the token stream that was parsed to form this HTML unit.
1225 */
1226 Token _beginToken;
1227 /**
1228 * The last token in the token stream that was parsed to form this compilation unit. This token
1229 * should always have a type of {@link TokenType.EOF}.
1230 */
1231 Token _endToken;
1232 /**
1233 * The tag nodes contained in the receiver (not {@code null}, contains no {@co de null}s).
1234 */
1235 List<XmlTagNode> _tagNodes;
1236 /**
1237 * Construct a new instance representing the content of an HTML file.
1238 * @param beginToken the first token in the file (not {@code null})
1239 * @param tagNodes child tag nodes of the receiver (not {@code null}, contains no {@code null}s)
1240 * @param endToken the last token in the token stream which should be of type{ @link TokenType.EOF}
1241 */
1242 HtmlUnit(Token beginToken, List<XmlTagNode> tagNodes, Token endToken) {
1243 this._beginToken = beginToken;
1244 this._tagNodes = becomeParentOf(tagNodes);
1245 this._endToken = endToken;
1246 }
1247 accept(XmlVisitor visitor) => visitor.visitHtmlUnit(this);
1248 Token get beginToken => _beginToken;
1249 Token get endToken => _endToken;
1250 /**
1251 * Answer the tag nodes contained in the receiver. Callers should not manipula te the returned list
1252 * to edit the AST structure.
1253 * @return the children (not {@code null}, contains no {@code null}s)
1254 */
1255 List<XmlTagNode> get tagNodes => _tagNodes;
1256 void visitChildren(XmlVisitor<Object> visitor) {
1257 for (XmlTagNode node in _tagNodes) {
1258 node.accept(visitor);
1259 }
1260 }
1261 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698