| // Copyright 2011 the V8 project authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #ifndef V8_DATEPARSER_H_ | |
| #define V8_DATEPARSER_H_ | |
| #include "src/allocation.h" | |
| #include "src/char-predicates.h" | |
| #include "src/unicode-cache.h" | |
| namespace v8 { | |
| namespace internal { | |
| class DateParser : public AllStatic { | |
| public: | |
| // Parse the string as a date. If parsing succeeds, return true after | |
| // filling out the output array as follows (all integers are Smis): | |
| // [0]: year | |
| // [1]: month (0 = Jan, 1 = Feb, ...) | |
| // [2]: day | |
| // [3]: hour | |
| // [4]: minute | |
| // [5]: second | |
| // [6]: millisecond | |
| // [7]: UTC offset in seconds, or null value if no timezone specified | |
| // If parsing fails, return false (content of output array is not defined). | |
| template <typename Char> | |
| static bool Parse(Isolate* isolate, Vector<Char> str, FixedArray* output); | |
| enum { | |
| YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, MILLISECOND, UTC_OFFSET, OUTPUT_SIZE | |
| }; | |
| private: | |
| // Range testing | |
| static inline bool Between(int x, int lo, int hi) { | |
| return static_cast<unsigned>(x - lo) <= static_cast<unsigned>(hi - lo); | |
| } | |
| // Indicates a missing value. | |
| static const int kNone = kMaxInt; | |
| // Maximal number of digits used to build the value of a numeral. | |
| // Remaining digits are ignored. | |
| static const int kMaxSignificantDigits = 9; | |
| // InputReader provides basic string parsing and character classification. | |
| template <typename Char> | |
| class InputReader BASE_EMBEDDED { | |
| public: | |
| InputReader(UnicodeCache* unicode_cache, Vector<Char> s) | |
| : index_(0), | |
| buffer_(s), | |
| unicode_cache_(unicode_cache) { | |
| Next(); | |
| } | |
| int position() { return index_; } | |
| // Advance to the next character of the string. | |
| void Next() { | |
| ch_ = (index_ < buffer_.length()) ? buffer_[index_] : 0; | |
| index_++; | |
| } | |
| // Read a string of digits as an unsigned number. Cap value at | |
| // kMaxSignificantDigits, but skip remaining digits if the numeral | |
| // is longer. | |
| int ReadUnsignedNumeral() { | |
| int n = 0; | |
| int i = 0; | |
| while (IsAsciiDigit()) { | |
| if (i < kMaxSignificantDigits) n = n * 10 + ch_ - '0'; | |
| i++; | |
| Next(); | |
| } | |
| return n; | |
| } | |
| // Read a word (sequence of chars. >= 'A'), fill the given buffer with a | |
| // lower-case prefix, and pad any remainder of the buffer with zeroes. | |
| // Return word length. | |
| int ReadWord(uint32_t* prefix, int prefix_size) { | |
| int len; | |
| for (len = 0; IsAsciiAlphaOrAbove(); Next(), len++) { | |
| if (len < prefix_size) prefix[len] = AsciiAlphaToLower(ch_); | |
| } | |
| for (int i = len; i < prefix_size; i++) prefix[i] = 0; | |
| return len; | |
| } | |
| // The skip methods return whether they actually skipped something. | |
| bool Skip(uint32_t c) { | |
| if (ch_ == c) { | |
| Next(); | |
| return true; | |
| } | |
| return false; | |
| } | |
| inline bool SkipWhiteSpace(); | |
| inline bool SkipParentheses(); | |
| // Character testing/classification. Non-ASCII digits are not supported. | |
| bool Is(uint32_t c) const { return ch_ == c; } | |
| bool IsEnd() const { return ch_ == 0; } | |
| bool IsAsciiDigit() const { return IsDecimalDigit(ch_); } | |
| bool IsAsciiAlphaOrAbove() const { return ch_ >= 'A'; } | |
| bool IsAsciiSign() const { return ch_ == '+' || ch_ == '-'; } | |
| // Return 1 for '+' and -1 for '-'. | |
| int GetAsciiSignValue() const { return 44 - static_cast<int>(ch_); } | |
| private: | |
| int index_; | |
| Vector<Char> buffer_; | |
| uint32_t ch_; | |
| UnicodeCache* unicode_cache_; | |
| }; | |
| enum KeywordType { | |
| INVALID, MONTH_NAME, TIME_ZONE_NAME, TIME_SEPARATOR, AM_PM | |
| }; | |
| struct DateToken { | |
| public: | |
| bool IsInvalid() { return tag_ == kInvalidTokenTag; } | |
| bool IsUnknown() { return tag_ == kUnknownTokenTag; } | |
| bool IsNumber() { return tag_ == kNumberTag; } | |
| bool IsSymbol() { return tag_ == kSymbolTag; } | |
| bool IsWhiteSpace() { return tag_ == kWhiteSpaceTag; } | |
| bool IsEndOfInput() { return tag_ == kEndOfInputTag; } | |
| bool IsKeyword() { return tag_ >= kKeywordTagStart; } | |
| int length() { return length_; } | |
| int number() { | |
| DCHECK(IsNumber()); | |
| return value_; | |
| } | |
| KeywordType keyword_type() { | |
| DCHECK(IsKeyword()); | |
| return static_cast<KeywordType>(tag_); | |
| } | |
| int keyword_value() { | |
| DCHECK(IsKeyword()); | |
| return value_; | |
| } | |
| char symbol() { | |
| DCHECK(IsSymbol()); | |
| return static_cast<char>(value_); | |
| } | |
| bool IsSymbol(char symbol) { | |
| return IsSymbol() && this->symbol() == symbol; | |
| } | |
| bool IsKeywordType(KeywordType tag) { | |
| return tag_ == tag; | |
| } | |
| bool IsFixedLengthNumber(int length) { | |
| return IsNumber() && length_ == length; | |
| } | |
| bool IsAsciiSign() { | |
| return tag_ == kSymbolTag && (value_ == '-' || value_ == '+'); | |
| } | |
| int ascii_sign() { | |
| DCHECK(IsAsciiSign()); | |
| return 44 - value_; | |
| } | |
| bool IsKeywordZ() { | |
| return IsKeywordType(TIME_ZONE_NAME) && length_ == 1 && value_ == 0; | |
| } | |
| bool IsUnknown(int character) { | |
| return IsUnknown() && value_ == character; | |
| } | |
| // Factory functions. | |
| static DateToken Keyword(KeywordType tag, int value, int length) { | |
| return DateToken(tag, length, value); | |
| } | |
| static DateToken Number(int value, int length) { | |
| return DateToken(kNumberTag, length, value); | |
| } | |
| static DateToken Symbol(char symbol) { | |
| return DateToken(kSymbolTag, 1, symbol); | |
| } | |
| static DateToken EndOfInput() { | |
| return DateToken(kEndOfInputTag, 0, -1); | |
| } | |
| static DateToken WhiteSpace(int length) { | |
| return DateToken(kWhiteSpaceTag, length, -1); | |
| } | |
| static DateToken Unknown() { | |
| return DateToken(kUnknownTokenTag, 1, -1); | |
| } | |
| static DateToken Invalid() { | |
| return DateToken(kInvalidTokenTag, 0, -1); | |
| } | |
| private: | |
| enum TagType { | |
| kInvalidTokenTag = -6, | |
| kUnknownTokenTag = -5, | |
| kWhiteSpaceTag = -4, | |
| kNumberTag = -3, | |
| kSymbolTag = -2, | |
| kEndOfInputTag = -1, | |
| kKeywordTagStart = 0 | |
| }; | |
| DateToken(int tag, int length, int value) | |
| : tag_(tag), | |
| length_(length), | |
| value_(value) { } | |
| int tag_; | |
| int length_; // Number of characters. | |
| int value_; | |
| }; | |
| template <typename Char> | |
| class DateStringTokenizer { | |
| public: | |
| explicit DateStringTokenizer(InputReader<Char>* in) | |
| : in_(in), next_(Scan()) { } | |
| DateToken Next() { | |
| DateToken result = next_; | |
| next_ = Scan(); | |
| return result; | |
| } | |
| DateToken Peek() { | |
| return next_; | |
| } | |
| bool SkipSymbol(char symbol) { | |
| if (next_.IsSymbol(symbol)) { | |
| next_ = Scan(); | |
| return true; | |
| } | |
| return false; | |
| } | |
| private: | |
| DateToken Scan(); | |
| InputReader<Char>* in_; | |
| DateToken next_; | |
| }; | |
| static int ReadMilliseconds(DateToken number); | |
| // KeywordTable maps names of months, time zones, am/pm to numbers. | |
| class KeywordTable : public AllStatic { | |
| public: | |
| // Look up a word in the keyword table and return an index. | |
| // 'pre' contains a prefix of the word, zero-padded to size kPrefixLength | |
| // and 'len' is the word length. | |
| static int Lookup(const uint32_t* pre, int len); | |
| // Get the type of the keyword at index i. | |
| static KeywordType GetType(int i) { | |
| return static_cast<KeywordType>(array[i][kTypeOffset]); | |
| } | |
| // Get the value of the keyword at index i. | |
| static int GetValue(int i) { return array[i][kValueOffset]; } | |
| static const int kPrefixLength = 3; | |
| static const int kTypeOffset = kPrefixLength; | |
| static const int kValueOffset = kTypeOffset + 1; | |
| static const int kEntrySize = kValueOffset + 1; | |
| static const int8_t array[][kEntrySize]; | |
| }; | |
| class TimeZoneComposer BASE_EMBEDDED { | |
| public: | |
| TimeZoneComposer() : sign_(kNone), hour_(kNone), minute_(kNone) {} | |
| void Set(int offset_in_hours) { | |
| sign_ = offset_in_hours < 0 ? -1 : 1; | |
| hour_ = offset_in_hours * sign_; | |
| minute_ = 0; | |
| } | |
| void SetSign(int sign) { sign_ = sign < 0 ? -1 : 1; } | |
| void SetAbsoluteHour(int hour) { hour_ = hour; } | |
| void SetAbsoluteMinute(int minute) { minute_ = minute; } | |
| bool IsExpecting(int n) const { | |
| return hour_ != kNone && minute_ == kNone && TimeComposer::IsMinute(n); | |
| } | |
| bool IsUTC() const { return hour_ == 0 && minute_ == 0; } | |
| bool Write(FixedArray* output); | |
| bool IsEmpty() { return hour_ == kNone; } | |
| private: | |
| int sign_; | |
| int hour_; | |
| int minute_; | |
| }; | |
| class TimeComposer BASE_EMBEDDED { | |
| public: | |
| TimeComposer() : index_(0), hour_offset_(kNone) {} | |
| bool IsEmpty() const { return index_ == 0; } | |
| bool IsExpecting(int n) const { | |
| return (index_ == 1 && IsMinute(n)) || | |
| (index_ == 2 && IsSecond(n)) || | |
| (index_ == 3 && IsMillisecond(n)); | |
| } | |
| bool Add(int n) { | |
| return index_ < kSize ? (comp_[index_++] = n, true) : false; | |
| } | |
| bool AddFinal(int n) { | |
| if (!Add(n)) return false; | |
| while (index_ < kSize) comp_[index_++] = 0; | |
| return true; | |
| } | |
| void SetHourOffset(int n) { hour_offset_ = n; } | |
| bool Write(FixedArray* output); | |
| static bool IsMinute(int x) { return Between(x, 0, 59); } | |
| static bool IsHour(int x) { return Between(x, 0, 23); } | |
| static bool IsSecond(int x) { return Between(x, 0, 59); } | |
| private: | |
| static bool IsHour12(int x) { return Between(x, 0, 12); } | |
| static bool IsMillisecond(int x) { return Between(x, 0, 999); } | |
| static const int kSize = 4; | |
| int comp_[kSize]; | |
| int index_; | |
| int hour_offset_; | |
| }; | |
| class DayComposer BASE_EMBEDDED { | |
| public: | |
| DayComposer() : index_(0), named_month_(kNone), is_iso_date_(false) {} | |
| bool IsEmpty() const { return index_ == 0; } | |
| bool Add(int n) { | |
| if (index_ < kSize) { | |
| comp_[index_] = n; | |
| index_++; | |
| return true; | |
| } | |
| return false; | |
| } | |
| void SetNamedMonth(int n) { named_month_ = n; } | |
| bool Write(FixedArray* output); | |
| void set_iso_date() { is_iso_date_ = true; } | |
| static bool IsMonth(int x) { return Between(x, 1, 12); } | |
| static bool IsDay(int x) { return Between(x, 1, 31); } | |
| private: | |
| static const int kSize = 3; | |
| int comp_[kSize]; | |
| int index_; | |
| int named_month_; | |
| // If set, ensures that data is always parsed in year-month-date order. | |
| bool is_iso_date_; | |
| }; | |
| // Tries to parse an ES5 Date Time String. Returns the next token | |
| // to continue with in the legacy date string parser. If parsing is | |
| // complete, returns DateToken::EndOfInput(). If terminally unsuccessful, | |
| // returns DateToken::Invalid(). Otherwise parsing continues in the | |
| // legacy parser. | |
| template <typename Char> | |
| static DateParser::DateToken ParseES5DateTime( | |
| DateStringTokenizer<Char>* scanner, DayComposer* day, TimeComposer* time, | |
| TimeZoneComposer* tz); | |
| }; | |
| } // namespace internal | |
| } // namespace v8 | |
| #endif // V8_DATEPARSER_H_ |