Line data Source code
1 : // Copyright 2011 the V8 project authors. All rights reserved.
2 : // Use of this source code is governed by a BSD-style license that can be
3 : // found in the LICENSE file.
4 :
5 : #ifndef V8_DATEPARSER_H_
6 : #define V8_DATEPARSER_H_
7 :
8 : #include "src/allocation.h"
9 : #include "src/char-predicates.h"
10 : #include "src/unicode-cache.h"
11 :
12 : namespace v8 {
13 : namespace internal {
14 :
15 : class DateParser : public AllStatic {
16 : public:
17 : // Parse the string as a date. If parsing succeeds, return true after
18 : // filling out the output array as follows (all integers are Smis):
19 : // [0]: year
20 : // [1]: month (0 = Jan, 1 = Feb, ...)
21 : // [2]: day
22 : // [3]: hour
23 : // [4]: minute
24 : // [5]: second
25 : // [6]: millisecond
26 : // [7]: UTC offset in seconds, or null value if no timezone specified
27 : // If parsing fails, return false (content of output array is not defined).
28 : template <typename Char>
29 : static bool Parse(Isolate* isolate, Vector<Char> str, FixedArray* output);
30 :
31 : enum {
32 : YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, MILLISECOND, UTC_OFFSET, OUTPUT_SIZE
33 : };
34 :
35 : private:
36 : // Range testing
37 : static inline bool Between(int x, int lo, int hi) {
38 971493 : return static_cast<unsigned>(x - lo) <= static_cast<unsigned>(hi - lo);
39 : }
40 :
41 : // Indicates a missing value.
42 : static const int kNone = kMaxInt;
43 :
44 : // Maximal number of digits used to build the value of a numeral.
45 : // Remaining digits are ignored.
46 : static const int kMaxSignificantDigits = 9;
47 :
48 : // InputReader provides basic string parsing and character classification.
49 : template <typename Char>
50 : class InputReader BASE_EMBEDDED {
51 : public:
52 : InputReader(UnicodeCache* unicode_cache, Vector<Char> s)
53 : : index_(0),
54 : buffer_(s),
55 96949 : unicode_cache_(unicode_cache) {
56 : Next();
57 : }
58 :
59 : int position() { return index_; }
60 :
61 : // Advance to the next character of the string.
62 : void Next() {
63 7353707 : ch_ = (index_ < buffer_.length()) ? buffer_[index_] : 0;
64 3773624 : index_++;
65 : }
66 :
67 : // Read a string of digits as an unsigned number. Cap value at
68 : // kMaxSignificantDigits, but skip remaining digits if the numeral
69 : // is longer.
70 2696431 : int ReadUnsignedNumeral() {
71 : int n = 0;
72 : int i = 0;
73 2696431 : while (IsAsciiDigit()) {
74 1534969 : if (i < kMaxSignificantDigits) n = n * 10 + ch_ - '0';
75 1534969 : i++;
76 : Next();
77 : }
78 580731 : return n;
79 : }
80 :
81 : // Read a word (sequence of chars. >= 'A'), fill the given buffer with a
82 : // lower-case prefix, and pad any remainder of the buffer with zeroes.
83 : // Return word length.
84 1383940 : int ReadWord(uint32_t* prefix, int prefix_size) {
85 : int len;
86 2210356 : for (len = 0; IsAsciiAlphaOrAbove(); Next(), len++) {
87 1648444 : if (len < prefix_size) prefix[len] = AsciiAlphaToLower(ch_);
88 : }
89 14258 : for (int i = len; i < prefix_size; i++) prefix[i] = 0;
90 278762 : return len;
91 : }
92 :
93 : // The skip methods return whether they actually skipped something.
94 : bool Skip(uint32_t c) {
95 5023756 : if (ch_ == c) {
96 : Next();
97 : return true;
98 : }
99 : return false;
100 : }
101 :
102 : inline bool SkipWhiteSpace();
103 : inline bool SkipParentheses();
104 :
105 : // Character testing/classification. Non-ASCII digits are not supported.
106 : bool Is(uint32_t c) const { return ch_ == c; }
107 : bool IsEnd() const { return ch_ == 0; }
108 3916681 : bool IsAsciiDigit() const { return IsDecimalDigit(ch_); }
109 : bool IsAsciiAlphaOrAbove() const { return ch_ >= 'A'; }
110 : bool IsAsciiSign() const { return ch_ == '+' || ch_ == '-'; }
111 :
112 : // Return 1 for '+' and -1 for '-'.
113 : int GetAsciiSignValue() const { return 44 - static_cast<int>(ch_); }
114 :
115 : private:
116 : int index_;
117 : Vector<Char> buffer_;
118 : uint32_t ch_;
119 : UnicodeCache* unicode_cache_;
120 : };
121 :
122 : enum KeywordType {
123 : INVALID, MONTH_NAME, TIME_ZONE_NAME, TIME_SEPARATOR, AM_PM
124 : };
125 :
126 : struct DateToken {
127 : public:
128 : bool IsInvalid() { return tag_ == kInvalidTokenTag; }
129 : bool IsUnknown() { return tag_ == kUnknownTokenTag; }
130 : bool IsNumber() { return tag_ == kNumberTag; }
131 : bool IsSymbol() { return tag_ == kSymbolTag; }
132 : bool IsWhiteSpace() { return tag_ == kWhiteSpaceTag; }
133 : bool IsEndOfInput() { return tag_ == kEndOfInputTag; }
134 : bool IsKeyword() { return tag_ >= kKeywordTagStart; }
135 :
136 : int length() { return length_; }
137 :
138 : int number() {
139 : DCHECK(IsNumber());
140 : return value_;
141 : }
142 : KeywordType keyword_type() {
143 : DCHECK(IsKeyword());
144 273079 : return static_cast<KeywordType>(tag_);
145 : }
146 : int keyword_value() {
147 : DCHECK(IsKeyword());
148 : return value_;
149 : }
150 : char symbol() {
151 : DCHECK(IsSymbol());
152 216133 : return static_cast<char>(value_);
153 : }
154 1419204 : bool IsSymbol(char symbol) {
155 2340183 : return IsSymbol() && this->symbol() == symbol;
156 : }
157 : bool IsKeywordType(KeywordType tag) {
158 : return tag_ == tag;
159 : }
160 : bool IsFixedLengthNumber(int length) {
161 128733 : return IsNumber() && length_ == length;
162 : }
163 : bool IsAsciiSign() {
164 1470966 : return tag_ == kSymbolTag && (value_ == '-' || value_ == '+');
165 : }
166 : int ascii_sign() {
167 : DCHECK(IsAsciiSign());
168 88799 : return 44 - value_;
169 : }
170 : bool IsKeywordZ() {
171 4545 : return IsKeywordType(TIME_ZONE_NAME) && length_ == 1 && value_ == 0;
172 : }
173 : bool IsUnknown(int character) {
174 : return IsUnknown() && value_ == character;
175 : }
176 : // Factory functions.
177 : static DateToken Keyword(KeywordType tag, int value, int length) {
178 278762 : return DateToken(tag, length, value);
179 : }
180 : static DateToken Number(int value, int length) {
181 : return DateToken(kNumberTag, length, value);
182 : }
183 : static DateToken Symbol(char symbol) {
184 : return DateToken(kSymbolTag, 1, symbol);
185 : }
186 : static DateToken EndOfInput() {
187 : return DateToken(kEndOfInputTag, 0, -1);
188 : }
189 : static DateToken WhiteSpace(int length) {
190 : return DateToken(kWhiteSpaceTag, length, -1);
191 : }
192 : static DateToken Unknown() {
193 : return DateToken(kUnknownTokenTag, 1, -1);
194 : }
195 : static DateToken Invalid() {
196 : return DateToken(kInvalidTokenTag, 0, -1);
197 : }
198 :
199 : private:
200 : enum TagType {
201 : kInvalidTokenTag = -6,
202 : kUnknownTokenTag = -5,
203 : kWhiteSpaceTag = -4,
204 : kNumberTag = -3,
205 : kSymbolTag = -2,
206 : kEndOfInputTag = -1,
207 : kKeywordTagStart = 0
208 : };
209 : DateToken(int tag, int length, int value)
210 : : tag_(tag),
211 : length_(length),
212 : value_(value) { }
213 :
214 : int tag_;
215 : int length_; // Number of characters.
216 : int value_;
217 : };
218 :
219 : template <typename Char>
220 : class DateStringTokenizer {
221 : public:
222 : explicit DateStringTokenizer(InputReader<Char>* in)
223 96949 : : in_(in), next_(Scan()) { }
224 : DateToken Next() {
225 1489638 : DateToken result = next_;
226 1681421 : next_ = Scan();
227 : return result;
228 : }
229 :
230 : DateToken Peek() {
231 685984 : return next_;
232 : }
233 1211423 : bool SkipSymbol(char symbol) {
234 1211423 : if (next_.IsSymbol(symbol)) {
235 206161 : next_ = Scan();
236 206161 : return true;
237 : }
238 : return false;
239 : }
240 :
241 : private:
242 : DateToken Scan();
243 :
244 : InputReader<Char>* in_;
245 : DateToken next_;
246 : };
247 :
248 : static int ReadMilliseconds(DateToken number);
249 :
250 : // KeywordTable maps names of months, time zones, am/pm to numbers.
251 : class KeywordTable : public AllStatic {
252 : public:
253 : // Look up a word in the keyword table and return an index.
254 : // 'pre' contains a prefix of the word, zero-padded to size kPrefixLength
255 : // and 'len' is the word length.
256 : static int Lookup(const uint32_t* pre, int len);
257 : // Get the type of the keyword at index i.
258 : static KeywordType GetType(int i) {
259 278762 : return static_cast<KeywordType>(array[i][kTypeOffset]);
260 : }
261 : // Get the value of the keyword at index i.
262 278762 : static int GetValue(int i) { return array[i][kValueOffset]; }
263 :
264 : static const int kPrefixLength = 3;
265 : static const int kTypeOffset = kPrefixLength;
266 : static const int kValueOffset = kTypeOffset + 1;
267 : static const int kEntrySize = kValueOffset + 1;
268 : static const int8_t array[][kEntrySize];
269 : };
270 :
271 : class TimeZoneComposer BASE_EMBEDDED {
272 : public:
273 96949 : TimeZoneComposer() : sign_(kNone), hour_(kNone), minute_(kNone) {}
274 : void Set(int offset_in_hours) {
275 91860 : sign_ = offset_in_hours < 0 ? -1 : 1;
276 91860 : hour_ = offset_in_hours * sign_;
277 91860 : minute_ = 0;
278 : }
279 91364 : void SetSign(int sign) { sign_ = sign < 0 ? -1 : 1; }
280 91334 : void SetAbsoluteHour(int hour) { hour_ = hour; }
281 91364 : void SetAbsoluteMinute(int minute) { minute_ = minute; }
282 : bool IsExpecting(int n) const {
283 274954 : return hour_ != kNone && minute_ == kNone && TimeComposer::IsMinute(n);
284 : }
285 88303 : bool IsUTC() const { return hour_ == 0 && minute_ == 0; }
286 : bool Write(FixedArray* output);
287 : bool IsEmpty() { return hour_ == kNone; }
288 : private:
289 : int sign_;
290 : int hour_;
291 : int minute_;
292 : };
293 :
294 : class TimeComposer BASE_EMBEDDED {
295 : public:
296 96949 : TimeComposer() : index_(0), hour_offset_(kNone) {}
297 : bool IsEmpty() const { return index_ == 0; }
298 : bool IsExpecting(int n) const {
299 2329 : return (index_ == 1 && IsMinute(n)) ||
300 363783 : (index_ == 2 && IsSecond(n)) ||
301 0 : (index_ == 3 && IsMillisecond(n));
302 : }
303 : bool Add(int n) {
304 108312 : return index_ < kSize ? (comp_[index_++] = n, true) : false;
305 : }
306 : bool AddFinal(int n) {
307 91218 : if (!Add(n)) return false;
308 93547 : while (index_ < kSize) comp_[index_++] = 0;
309 : return true;
310 : }
311 768 : void SetHourOffset(int n) { hour_offset_ = n; }
312 : bool Write(FixedArray* output);
313 :
314 : static bool IsMinute(int x) { return Between(x, 0, 59); }
315 : static bool IsHour(int x) { return Between(x, 0, 23); }
316 : static bool IsSecond(int x) { return Between(x, 0, 59); }
317 :
318 : private:
319 : static bool IsHour12(int x) { return Between(x, 0, 12); }
320 : static bool IsMillisecond(int x) { return Between(x, 0, 999); }
321 :
322 : static const int kSize = 4;
323 : int comp_[kSize];
324 : int index_;
325 : int hour_offset_;
326 : };
327 :
328 : class DayComposer BASE_EMBEDDED {
329 : public:
330 96949 : DayComposer() : index_(0), named_month_(kNone), is_iso_date_(false) {}
331 : bool IsEmpty() const { return index_ == 0; }
332 : bool Add(int n) {
333 197507 : if (index_ < kSize) {
334 197507 : comp_[index_] = n;
335 197507 : index_++;
336 : return true;
337 : }
338 : return false;
339 : }
340 91519 : void SetNamedMonth(int n) { named_month_ = n; }
341 : bool Write(FixedArray* output);
342 4559 : void set_iso_date() { is_iso_date_ = true; }
343 : static bool IsMonth(int x) { return Between(x, 1, 12); }
344 : static bool IsDay(int x) { return Between(x, 1, 31); }
345 :
346 : private:
347 : static const int kSize = 3;
348 : int comp_[kSize];
349 : int index_;
350 : int named_month_;
351 : // If set, ensures that data is always parsed in year-month-date order.
352 : bool is_iso_date_;
353 : };
354 :
355 : // Tries to parse an ES5 Date Time String. Returns the next token
356 : // to continue with in the legacy date string parser. If parsing is
357 : // complete, returns DateToken::EndOfInput(). If terminally unsuccessful,
358 : // returns DateToken::Invalid(). Otherwise parsing continues in the
359 : // legacy parser.
360 : template <typename Char>
361 : static DateParser::DateToken ParseES5DateTime(
362 : DateStringTokenizer<Char>* scanner, DayComposer* day, TimeComposer* time,
363 : TimeZoneComposer* tz);
364 : };
365 :
366 :
367 : } // namespace internal
368 : } // namespace v8
369 :
370 : #endif // V8_DATEPARSER_H_
|