#ifndef V8_DATE_DATEPARSER_H_
#define V8_DATE_DATEPARSER_H_
#include "src/base/vector.h"
#include "src/strings/char-predicates.h"
#include "src/utils/allocation.h"
namespace v8 {
namespace internal {
class DateParser : public AllStatic {
public:
enum {
YEAR,
MONTH,
DAY,
HOUR,
MINUTE,
SECOND,
MILLISECOND,
UTC_OFFSET,
OUTPUT_SIZE
};
template <typename Char>
static bool Parse(Isolate* isolate, base::Vector<Char> str, double* output);
private:
static inline bool Between(int x, int lo, int hi) {
return static_cast<unsigned>(x - lo) <= static_cast<unsigned>(hi - lo);
}
static const int kNone = kMaxInt;
static const int kMaxSignificantDigits = 9;
template <typename Char>
class InputReader {
public:
explicit InputReader(base::Vector<Char> s) : index_(0), buffer_(s) {
Next();
}
int position() { return index_; }
void Next() {
ch_ = (index_ < buffer_.length()) ? buffer_[index_] : 0;
index_++;
}
int ReadUnsignedNumeral() {
int n = 0;
int i = 0;
while (ch_ == '0') Next();
while (IsAsciiDigit()) {
if (i < kMaxSignificantDigits) n = n * 10 + ch_ - '0';
i++;
Next();
}
return n;
}
int ReadWord(uint32_t* prefix, int prefix_size) {
int len;
for (len = 0; IsAsciiAlphaOrAbove() && !IsWhiteSpaceChar();
Next(), len++) {
if (len < prefix_size) prefix[len] = AsciiAlphaToLower(ch_);
}
for (int i = len; i < prefix_size; i++) prefix[i] = 0;
return len;
}
bool Skip(uint32_t c) {
if (ch_ == c) {
Next();
return true;
}
return false;
}
inline bool SkipWhiteSpace();
inline bool SkipParentheses();
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 IsWhiteSpaceChar() const { return IsWhiteSpace(ch_); }
bool IsAsciiSign() const { return ch_ == '+' || ch_ == '-'; }
int GetAsciiSignValue() const { return 44 - static_cast<int>(ch_); }
private:
int index_;
base::Vector<Char> buffer_;
uint32_t ch_;
};
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; }
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_;
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);
class KeywordTable : public AllStatic {
public:
static int Lookup(const uint32_t* pre, int len);
static KeywordType GetType(int i) {
return static_cast<KeywordType>(array[i][kTypeOffset]);
}
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 {
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(double* output);
bool IsEmpty() { return hour_ == kNone; }
private:
int sign_;
int hour_;
int minute_;
};
class TimeComposer {
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(double* 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 {
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(double* 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_;
bool is_iso_date_;
};
template <typename Char>
static DateParser::DateToken ParseES5DateTime(
DateStringTokenizer<Char>* scanner, DayComposer* day, TimeComposer* time,
TimeZoneComposer* tz);
};
}
}
#endif