| OLD | NEW |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 part of scanner; | 5 part of scanner; |
| 6 | 6 |
| 7 /** | 7 /** |
| 8 * Scanner that reads from a UTF-8 encoded list of bytes and creates tokens | 8 * Scanner that reads from a UTF-8 encoded list of bytes and creates tokens |
| 9 * that points to substrings. | 9 * that points to substrings. |
| 10 */ | 10 */ |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 62 * string text of the source file is decoded. | 62 * string text of the source file is decoded. |
| 63 * | 63 * |
| 64 * The list of UTF-8 bytes [file.slowUtf8Bytes()] is expected to return an | 64 * The list of UTF-8 bytes [file.slowUtf8Bytes()] is expected to return an |
| 65 * array whose last element is '0' to signal the end of the file. If this | 65 * array whose last element is '0' to signal the end of the file. If this |
| 66 * is not the case, the entire array is copied before scanning. | 66 * is not the case, the entire array is copied before scanning. |
| 67 */ | 67 */ |
| 68 Utf8BytesScanner(SourceFile file, {bool includeComments: false}) | 68 Utf8BytesScanner(SourceFile file, {bool includeComments: false}) |
| 69 : bytes = file.slowUtf8Bytes(), | 69 : bytes = file.slowUtf8Bytes(), |
| 70 super(file, includeComments) { | 70 super(file, includeComments) { |
| 71 ensureZeroTermination(); | 71 ensureZeroTermination(); |
| 72 // Skip a leading BOM. |
| 73 if (_containsBomAt(0)) byteOffset += 3; |
| 72 } | 74 } |
| 73 | 75 |
| 74 /** | 76 /** |
| 75 * Creates a new Utf8BytesScanner from a list of UTF-8 bytes. | 77 * Creates a new Utf8BytesScanner from a list of UTF-8 bytes. |
| 76 * | 78 * |
| 77 * The last element of the list is expected to be '0' to signal the end of | 79 * The last element of the list is expected to be '0' to signal the end of |
| 78 * the file. If this is not the case, the entire array is copied before | 80 * the file. If this is not the case, the entire array is copied before |
| 79 * scanning. | 81 * scanning. |
| 80 */ | 82 */ |
| 81 Utf8BytesScanner.fromBytes(this.bytes, {bool includeComments: false}) | 83 Utf8BytesScanner.fromBytes(this.bytes, {bool includeComments: false}) |
| 82 : super(null, includeComments) { | 84 : super(null, includeComments) { |
| 83 ensureZeroTermination(); | 85 ensureZeroTermination(); |
| 84 } | 86 } |
| 85 | 87 |
| 86 void ensureZeroTermination() { | 88 void ensureZeroTermination() { |
| 87 if (bytes.isEmpty || bytes[bytes.length - 1] != 0) { | 89 if (bytes.isEmpty || bytes[bytes.length - 1] != 0) { |
| 88 // TODO(lry): abort instead of copying the array, or warn? | 90 // TODO(lry): abort instead of copying the array, or warn? |
| 89 var newBytes = new Uint8List(bytes.length + 1); | 91 var newBytes = new Uint8List(bytes.length + 1); |
| 90 for (int i = 0; i < bytes.length; i++) { | 92 for (int i = 0; i < bytes.length; i++) { |
| 91 newBytes[i] = bytes[i]; | 93 newBytes[i] = bytes[i]; |
| 92 } | 94 } |
| 93 newBytes[bytes.length] = 0; | 95 newBytes[bytes.length] = 0; |
| 94 bytes = newBytes; | 96 bytes = newBytes; |
| 95 } | 97 } |
| 96 } | 98 } |
| 97 | 99 |
| 100 bool _containsBomAt(int offset) { |
| 101 const BOM_UTF8 = const [0xEF, 0xBB, 0xBF]; |
| 102 |
| 103 return offset + 3 < bytes.length && |
| 104 bytes[offset] == BOM_UTF8[0] && |
| 105 bytes[offset + 1] == BOM_UTF8[1] && |
| 106 bytes[offset + 2] == BOM_UTF8[2]; |
| 107 } |
| 108 |
| 98 int advance() => bytes[++byteOffset]; | 109 int advance() => bytes[++byteOffset]; |
| 99 | 110 |
| 100 int peek() => bytes[byteOffset + 1]; | 111 int peek() => bytes[byteOffset + 1]; |
| 101 | 112 |
| 102 /** | 113 /** |
| 103 * Returns the unicode code point starting at the byte offset [startOffset] | 114 * Returns the unicode code point starting at the byte offset [startOffset] |
| 104 * with the byte [nextByte]. If [advance] is true the current [byteOffset] | 115 * with the byte [nextByte]. If [advance] is true the current [byteOffset] |
| 105 * is advanced to the last byte of the code point. | 116 * is advanced to the last byte of the code point. |
| 106 */ | 117 */ |
| 107 int nextCodePoint(int startOffset, int nextByte, bool advance) { | 118 int nextCodePoint(int startOffset, int nextByte, bool advance) { |
| 108 // The number of 1s in the first byte indicate the number of bytes, at | 119 // The number of 1s in the first byte indicate the number of bytes, at |
| 109 // least 2. | 120 // least 2. |
| 110 int numBytes = 2; | 121 int numBytes = 2; |
| 111 int bit = 0x20; | 122 int bit = 0x20; |
| 112 while ((nextByte & bit) != 0) { | 123 while ((nextByte & bit) != 0) { |
| 113 numBytes++; | 124 numBytes++; |
| 114 bit >>= 1; | 125 bit >>= 1; |
| 115 } | 126 } |
| 116 int end = startOffset + numBytes; | 127 int end = startOffset + numBytes; |
| 117 if (advance) { | 128 if (advance) { |
| 118 byteOffset = end - 1; | 129 byteOffset = end - 1; |
| 119 } | 130 } |
| 120 // TODO(lry): measurably slow, decode creates first a Utf8Decoder and a | 131 // TODO(lry): measurably slow, decode creates first a Utf8Decoder and a |
| 121 // _Utf8Decoder instance. Also the sublist is eagerly allocated. | 132 // _Utf8Decoder instance. Also the sublist is eagerly allocated. |
| 122 String codePoint = UTF8.decode(bytes.sublist(startOffset, end)); | 133 String codePoint = UTF8.decode(bytes.sublist(startOffset, end)); |
| 134 if (codePoint.length == 0) { |
| 135 // The UTF-8 decoder discards leading BOM characters. |
| 136 // TODO(floitsch): don't just assume that removed characters were the |
| 137 // BOM. |
| 138 assert(_containsBomAt(startOffset)); |
| 139 codePoint = new String.fromCharCode(UNICODE_BOM_CHARACTER_RUNE); |
| 140 } |
| 123 if (codePoint.length == 1) { | 141 if (codePoint.length == 1) { |
| 124 if (advance) { | 142 if (advance) { |
| 125 utf8Slack += (numBytes - 1); | 143 utf8Slack += (numBytes - 1); |
| 126 scanSlack = numBytes - 1; | 144 scanSlack = numBytes - 1; |
| 127 scanSlackOffset = byteOffset; | 145 scanSlackOffset = byteOffset; |
| 128 } | 146 } |
| 129 return codePoint.codeUnitAt(0); | 147 return codePoint.codeUnitAt(0); |
| 130 } else if (codePoint.length == 2) { | 148 } else if (codePoint.length == 2) { |
| 131 if (advance) { | 149 if (advance) { |
| 132 utf8Slack += (numBytes - 2); | 150 utf8Slack += (numBytes - 2); |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 186 | 204 |
| 187 void appendSubstringToken(PrecedenceInfo info, int start, bool asciiOnly, | 205 void appendSubstringToken(PrecedenceInfo info, int start, bool asciiOnly, |
| 188 [int extraOffset = 0]) { | 206 [int extraOffset = 0]) { |
| 189 tail.next = new StringToken.fromUtf8Bytes( | 207 tail.next = new StringToken.fromUtf8Bytes( |
| 190 info, bytes, start, byteOffset + extraOffset, asciiOnly, tokenStart); | 208 info, bytes, start, byteOffset + extraOffset, asciiOnly, tokenStart); |
| 191 tail = tail.next; | 209 tail = tail.next; |
| 192 } | 210 } |
| 193 | 211 |
| 194 bool atEndOfFile() => byteOffset >= bytes.length - 1; | 212 bool atEndOfFile() => byteOffset >= bytes.length - 1; |
| 195 } | 213 } |
| OLD | NEW |