#if true
#ifndef ONLINE_JUDGE
#define GNU_DEBUG
#define _GLIBCXX_DEBUG 1
#define _GLIBCXX_DEBUG_PEDANTIC 1
#define _GLIBCXX_SANITIZE_VECTOR 1
#endif
#endif
#include <bits/stdc++.h>
bool DEBUG_MODE=false;
#define debug if(DEBUG_MODE)
template <typename T> inline auto chkMax(T& base, const T& cmp) -> T { return (base = std::max(base, cmp)); }
template <typename T> inline auto chkMin(T& base, const T& cmp) -> T { return (base = std::min(base, cmp)); }
#define never if constexpr(0)
const int inf = 0x3f3f3f3f; const long long infLL = 0x3f3f3f3f3f3f3f3fLL; using ll = long long; using ull = unsigned long long;
const char endl = '\n';
#define __lambda_1(expr) [&](){return expr;}
#define __lambda_2(a, expr) [&](auto a){return expr;}
#define __lambda_3(a, b, expr) [&](auto a, auto b){return expr;}
#define __lambda_4(a, b, c, expr) [&](auto a, auto b, auto c){return expr;}
#define __lambda_overload(a, b, c, d, e, args...) __lambda_##e
#define lambda(...) __lambda_overload(__VA_ARGS__, 4, 3, 2, 1)(__VA_ARGS__)
#define lam lambda
#define unreachable() (assert(false), __builtin_unreachable())
namespace lib{
#if __cplusplus > 201703LL
namespace ranges = std::ranges;
namespace views = std::views;
#endif
}
using namespace lib;
namespace MyStd {
template <typename T>
auto constexpr max(T const &x, T const &y) -> T { return x > y? x :y; }
template <typename ...Ts>
struct __MaxSizeof;
template <>
struct __MaxSizeof<> {
static constexpr size_t value = 0;
};
template <typename T, typename ...Ts>
struct __MaxSizeof<T, Ts...> {
static constexpr size_t value = max(sizeof(T), __MaxSizeof<Ts...>::value);
};
template <typename ...Ts>
struct __MaxAlignof;
template <>
struct __MaxAlignof<> {
static constexpr size_t value = 1;
};
template <typename T, typename ...Ts>
struct __MaxAlignof<T, Ts...> {
static constexpr size_t value = max(alignof(T), __MaxAlignof<Ts...>::value);
};
template <int index, typename ...Ts>
struct __IndexOfPack;
template <int index, typename T, typename ...Ts>
struct __IndexOfPack<index, T, Ts...> {
using type = typename __IndexOfPack<index - 1, Ts...>::type;
};
template <typename T, typename ...Ts>
struct __IndexOfPack<0, T, Ts...> {
using type = T;
};
template <typename T, typename ...Ts>
struct __FindInPack;
template <typename T, typename U, typename ...Ts>
struct __FindInPack<T, U, Ts...> {
static constexpr int value = __FindInPack<T, Ts...>::value + 1;
};
template <typename T, typename ...Ts>
struct __FindInPack<T, T, Ts...> {
static constexpr int value = 0;
};
template <typename T>
struct __FindInPack<T> {
static constexpr int value = 0;
};
template <typename ...Ts>
struct __Destroyer;
template <>
struct __Destroyer<> {
static auto destroy(int, char *) -> void {
assert(false);
}
};
template <typename T, typename ...Ts>
struct __Destroyer<T, Ts...> {
static auto destroy(int index, char *data) -> void {
if (index == 0) {
reinterpret_cast<T *>(data)->~T();
} else {
__Destroyer<Ts...>::destroy(index - 1, data);
}
}
};
template <typename ...Ts>
struct __Copier;
template <>
struct __Copier<> {
static auto copy(int, const char *, char *) -> void {
assert(false);
}
};
template <typename T, typename ...Ts>
struct __Copier<T, Ts...> {
static auto copy(int index, char const *src, char *dst) -> void {
if (index == 0) {
new (dst) T(*reinterpret_cast<T const *>(src));
} else {
__Copier<Ts...>::copy(index - 1, src, dst);
}
}
};
template <typename ...Ts>
struct __Mover;
template <>
struct __Mover<> {
static auto move(int, char *, char *) -> void {
assert(false);
}
};
template <typename T, typename ...Ts>
struct __Mover<T, Ts...> {
static auto move(int index, char *src, char *dst) -> void {
if (index == 0) {
new (dst) T(std::move(*reinterpret_cast<T *>(src)));
} else {
__Mover<Ts...>::move(index - 1, src, dst);
}
}
};
template <typename ...Ts>
struct __CountPack;
template <>
struct __CountPack<> {
static constexpr int value = 0;
};
template <typename T, typename ...Ts>
struct __CountPack<T, Ts...> {
static constexpr int value = 1 + __CountPack<Ts...>::value;
};
template <typename ...Ts>
struct alignas(__MaxAlignof<Ts...>) variant {
char data[__MaxSizeof<Ts...>::value];
int index = -1;
static constexpr int count = __CountPack<Ts...>::value;
variant() = default;
variant(variant<Ts...> const &other) {
if (other.index != -1) __Copier<Ts...>::copy(other.index, other.data, data);
index = other.index;
}
variant(variant<Ts...> &&other) {
if (other.index != -1) __Mover<Ts...>::move(other.index, other.data, data);
index = other.index;
other.index = -1;
}
template <typename T, typename = typename std::enable_if<not std::is_same<typename std::remove_reference<T>::type, MyStd::variant<Ts...>>::value>::type>
variant(T &&x) {
set(std::forward<T>(x));
}
~variant() {
if (index != -1) __Destroyer<Ts...>::destroy(index, data);
}
template <typename T>
auto get() -> T & {
static_assert(__FindInPack<T, Ts...>::value != count, "type not found");
assert((index == __FindInPack<T, Ts...>::value));
return *reinterpret_cast<T *>(data);
}
template <typename T>
auto get() const -> T const & {
static_assert(__FindInPack<T, Ts...>::value != count, "type not found");
assert((index == __FindInPack<T, Ts...>::value));
return *reinterpret_cast<T const *>(data);
}
template <typename T>
auto set(T &&x) -> void {
using rm_ref = typename std::remove_reference<T>::type;
static_assert(__FindInPack<rm_ref, Ts...>::value != count, "type not found");
index = __FindInPack<rm_ref, Ts...>::value;
new (data) rm_ref(std::forward<T>(x));
}
template <typename T, typename = typename std::enable_if<not std::is_same<typename std::remove_reference<T>::type, MyStd::variant<Ts...>>::value>::type>
auto operator= (T &&x) -> variant & {
set(std::forward<T>(x));
return *this;
}
auto operator= (variant<Ts...> const &other) -> variant & {
if (other.index != -1) __Copier<Ts...>::copy(other.index, other.data, data);
index = other.index;
return *this;
}
auto operator= (variant<Ts...> &&other) -> variant & {
if (other.index != -1) __Mover<Ts...>::move(other.index, other.data, data);
index = other.index, other.index = -1;
return *this;
}
};
}
template <typename T, typename ...Ts>
auto get(MyStd::variant<Ts...> &v) -> T & {
return v.template get<T>();
}
template <typename T, typename ...Ts>
auto get(MyStd::variant<Ts...> const &v) -> T const & {
return v.template get<T>();
}
namespace IO {
#ifdef __linux__
#include <sys/stat.h>
#include <sys/mman.h>
#endif
#if __cplusplus < 202002L
#define requires(...)
#endif
struct EOFError: public std::exception {
const char *what() const throw() {
return "EOF when reading a char";
}
};
struct IntegerOverflowError: public std::exception {
const char *what() const throw() override {
return "Integer Overflow";
}
};
template <typename T> struct is_integral_or_int128 { constexpr static bool value = std::is_integral<T>::value; };
template <> struct is_integral_or_int128<__int128> { constexpr static bool value = true; };
template <> struct is_integral_or_int128<unsigned __int128> { constexpr static bool value = true; };
template <typename T> struct is_floating_point_or_float128 { constexpr static bool value = std::is_floating_point<T>::value; };
template <> struct is_floating_point_or_float128<__float128> { constexpr static bool value = true; };
template <typename T> struct is_number {
constexpr static bool value = is_integral_or_int128<T>::value || is_floating_point_or_float128<T>::value;
};
template <typename T, typename U>
bool addOverflow(T &x, U y) {
return __builtin_add_overflow(x, y, &x);
}
template <typename T, typename U>
bool subOverflow(T &x, U y) {
return __builtin_sub_overflow(x, y, &x);
}
template <typename T, typename U>
bool mulOverflow(T &x, U y) {
return __builtin_mul_overflow(x, y, &x);
}
template <typename T, typename U>
bool leftShiftOverflow(T &x, U y) {
if (x == 0) return false;
bool flag = std::__lg(x) + y >= std::numeric_limits<T>::digits;
return x <<= y, flag;
}
struct Scanner {
private:
char prev[2] = {'\0', '\0'};
int ungetFlag = 0;
virtual char gc() = 0;
static bool isDigit(char ch) { return '0' <= ch and ch <= '9'; }
static bool isBlank(char ch) { return ch <= 32 or ch == 127; }
public:
char get() {
if (ungetFlag) {
return prev[--ungetFlag];
}
return (prev[1] = prev[0], prev[0] = gc());
}
Scanner &unget() {
if (ungetFlag == 2) throw std::logic_error("Cannot unget twice");
ungetFlag++;
return *this;
}
template <typename T, typename std::enable_if<is_number<T>::value>::type* = nullptr, int base = 10>
Scanner &read(T &x) {
bool sign = false; x = 0; char ch = get();
for (; not isDigit(ch); ch = get()) {
if (ch == '-') sign = true;
if (is_floating_point_or_float128<T>::value) {
if (ch == '.') break;
}
}
if (sign) {
for (; isDigit(ch); ch = get()) {
if (is_integral_or_int128<T>::value) {
if (mulOverflow(x, 10)) throw IntegerOverflowError{};
if (subOverflow(x, ch ^ 48)) throw IntegerOverflowError{};
} else {
x = x * 10 - (ch ^ 48);
}
}
if (is_integral_or_int128<T>::value) return unget(), *this;
T tmp = 1;
if (ch == '.') {
for (ch = get(); isDigit(ch); ch = get()) {
tmp *= 0.1, x -= tmp * (ch ^ 48);
}
}
} else {
for (; isDigit(ch); ch = get()) {
if (is_integral_or_int128<T>::value) {
if (mulOverflow(x, 10)) throw IntegerOverflowError{};
if (addOverflow(x, ch ^ 48)) throw IntegerOverflowError{};
} else {
x = x * 10 + (ch ^ 48);
}
}
if (is_integral_or_int128<T>::value) return unget(), *this;
T tmp = 1;
if (ch == '.') {
for (ch = get(); isDigit(ch); ch = get()) {
tmp *= 0.1, x += tmp * (ch ^ 48);
}
}
}
if (ch == 'e' or ch == 'E') {
int y; read(y);
x *= pow(10, y);
}
return unget(), *this;
}
Scanner &read(char &x) {
for (x = get(); isBlank(x); x = get());
return *this;
}
Scanner &read(char *s) {
char ch = get();
for (; isBlank(ch); ch = get());
for (; not isBlank(ch); ch = get()) *s++ = ch;
*s = '\0';
return unget(), *this;
}
Scanner &read(std::string &s, int reserve = 0) {
char ch = get();
s.clear(), s.reserve(reserve);
for (; isBlank(ch); ch = get());
for (; not isBlank(ch); ch = get()) s.push_back(ch);
return unget(), *this;
}
template <typename T, typename std::enable_if<
is_number<T>::value || std::is_convertible<T, const char *>::value || std::is_convertible<T, std::string const &>::value
>::type* = nullptr
>
Scanner &operator>> (T &x) {
return read(x);
}
};
template <size_t MaxSize>
struct FileReadScanner: public Scanner {
private:
char buf[MaxSize], *p1, *p2;
bool eofFlag = false;
char gc_fread() {
if (p1 == p2) {
if (eofFlag) throw EOFError{};
p1 = buf;
p2 = buf + std::fread(buf, sizeof(char), sizeof(buf), stdin);
if (std::feof(stdin)) eofFlag = true;
}
return p1 == p2? '\0': *p1++;
}
protected:
char gc() { return gc_fread(); }
public:
FileReadScanner(): p1(buf), p2(buf) {}
};
struct GetCharScanner: public Scanner {
private:
char gc_getchar() {
char ch = getchar();
if (ch != EOF) return ch;
else {
throw EOFError{};
return '\0';
}
}
protected:
char gc() { return gc_getchar(); }
};
#ifdef IO_ENABLE_MMAP
struct MemoryMapScanner: public Scanner {
struct stat s;
char *c;
MemoryMapScanner() {
fstat(0, &s);
c = (char *)mmap(nullptr, s.st_size, 1, 2, 0, 0);
}
char gc() { return *c++; }
};
#endif
struct Printer {
virtual void put(char) = 0;
virtual void flush() {}
template <typename T, typename std::enable_if<is_floating_point_or_float128<T>::value>::type* = nullptr>
Printer &write(T x) {
if (std::isnan(x)) return write("nan");
static char st[std::numeric_limits<T>::max_exponent10+1];
char *top = st;
if (x < 0) x = -x, put('-');
if (std::isinf(x)) return write("Infinity");
x += 5e-7;
auto y = std::floor(x);
while (y >= 1) {
auto cur = y - (std::floor(y / 10) * 10);
y = (y - cur) / 10;
*top++ = (int)(cur) ^ 48;
}
if (top == st) put('0');
while (top != st) put(*--top);
x -= std::floor(x);
put('.');
for (auto i = 0; i < 6; i++) {
x = x * 10;
auto cur = std::floor(x);
x -= cur;
put((int)cur ^ 48);
}
return *this;
}
template <typename T, typename std::enable_if<is_integral_or_int128<T>::value>::type* = nullptr>
Printer &write(T x) {
static char st[std::numeric_limits<T>::digits10+1];
char *top = st;
if (x < 0) {
put('-');
*top++ = -(x % 10) ^ 48, x = -(x / 10);
if (x == 0) {
put(*--top);
return *this;
}
}
do {
*top++ = x % 10 ^ 48, x /= 10;
} while (x);
while (top != st) put(*--top);
return *this;
}
Printer &write(char ch) {
put(ch);
return *this;
}
Printer &write(const char *s) {
for (; *s; s++) put(*s);
return *this;
}
Printer &write(std::string const &s) {
for (auto ch: s) put(ch);
return *this;
}
template <typename T, typename std::enable_if<
is_number<T>::value || std::is_convertible<T, const char *>::value || std::is_convertible<T, std::string const &>::value
>::type* = nullptr>
Printer &operator<< (const T &x) {
if (
is_number<T>::value || std::is_convertible<T, const char *>::value || std::is_convertible<T, std::string const &>::value
) {
return write(x);
}
}
};
struct PutCharPrinter: public Printer {
void put(char ch) {
std::putchar(ch);
}
};
template <size_t MaxSize>
struct FileWritePrinter: public Printer {
char pbuf[MaxSize], *pp;
FileWritePrinter(): pp(pbuf) {}
~FileWritePrinter() {
std::fwrite(pbuf, 1, pp - pbuf, stdout);
}
void flush() {
std::fwrite(pbuf, 1, MaxSize, stdout);
pp = pbuf;
}
void put(char ch) {
if (pp - pbuf == MaxSize) flush();
*pp++ = ch;
}
};
#ifdef IO_ENABLE_MMAP
template <size_t MaxSize>
struct DefaultIO: public MemoryMapScanner, FileWritePrinter<MaxSize> {};
DefaultIO<1<<20> io;
#else
struct DefaultIO: public GetCharScanner, PutCharPrinter {};
DefaultIO io;
#endif
}
using IO::io;
using i32 = int32_t; using i64 = int64_t; using u32 = uint32_t; using u64 = uint64_t;
using i128 = __int128; using u128 = unsigned __int128;
using f32 = float; using f64 = double;
namespace GenshinLang {
template <size_t MaxSize>
struct FileWritePrinterStdandardError: public IO::Printer {
char pbuf[MaxSize], *pp;
FileWritePrinterStdandardError(): pp(pbuf) {}
~FileWritePrinterStdandardError() {
std::fwrite(pbuf, 1, pp - pbuf, stderr);
}
void flush() {
std::fwrite(pbuf, 1, MaxSize, stderr);
pp = pbuf;
}
void put(char ch) override {
if (pp - pbuf == MaxSize) flush();
*pp++ = ch;
}
};
FileWritePrinterStdandardError<1<<20> ioError;
struct StringScanner: public IO::Scanner {
std::string &s;
size_t index;
bool eofFlag = false;
StringScanner(std::string &s): s(s), index(0) {}
char gc() override {
if (index < s.size()) eofFlag = false;
if (index == s.size()) {
if (eofFlag) throw IO::EOFError{};
else eofFlag = true;
return '\0';
}
return index == s.size()? '\0': s[index++];
}
};
struct StringPrinter: public IO::Printer {
std::string s;
StringPrinter(): s() {}
void put(char ch) {
s.push_back(ch);
}
};
namespace Trie {
class Trie {
public:
struct Node {
std::array<Node *, 96> next;
int match;
};
std::deque<Node> nodes;
Node *root;
Trie(): nodes(), root(nullptr) {
nodes.push_back({});
root = &nodes.back();
}
Trie(std::initializer_list<std::string> &&init): nodes(), root(nullptr) {
nodes.push_back({});
root = &nodes.back();
for (const auto &s: init) insert(s);
}
int match(const std::string &s) const {
Node *cur = root;
for (auto ch: s) {
assert(ch > 32), ch -= 32;
if (cur->next[ch]) cur = cur->next[ch];
else return 0;
}
return cur->match;
}
void insert(const std::string &s) {
Node *cur = root;
for (auto ch: s) {
assert(ch > 32), ch -= 32;
cur->match++;
if (cur->next[ch]) cur = cur->next[ch];
else nodes.push_back({}), cur = cur->next[ch] = &nodes.back();
}
cur->match++;
}
};
}
using IdentifierIndexType = int;
class IdentifierMap {
struct IndexError: public std::exception {
const char *err = nullptr;
IndexError(const char *err): err(err) {}
const char *what() const noexcept override {
return err;
}
};
std::unordered_map<std::string, IdentifierIndexType> mappingStringToIndex;
std::unordered_map<int, std::string> mappingIndexToString;
public:
IdentifierMap();
IdentifierIndexType getIndex(std::string const &s, IdentifierIndexType default_val) {
auto it = mappingStringToIndex.find(s);
if (it != mappingStringToIndex.end()) return it->second;
else return default_val;
}
IdentifierIndexType getIndex(std::string const &s) {
auto it = mappingStringToIndex.find(s);
if (it != mappingStringToIndex.end()) return it->second;
else throw IndexError("IdentifierMap::getIndex: name not found");
}
const std::string &getString(IdentifierIndexType index) {
auto it = mappingIndexToString.find(index);
if (it != mappingIndexToString.end()) return it->second;
else throw IndexError("IdentifierMap::getString: index out of range");
}
IdentifierIndexType insert(std::string const &s) {
auto index = mappingIndexToString.size() + 1;
while (containsIndex(index)) index++;
auto pair = mappingStringToIndex.insert({s, index});
auto new_it = pair.first;
auto success = pair.second;
if (success) mappingIndexToString.insert({index, s});
return new_it->second;
}
bool containsString(std::string const &s) {
return mappingStringToIndex.find(s) != mappingStringToIndex.end();
}
bool containsIndex(IdentifierIndexType index) {
return mappingIndexToString.find(index) != mappingIndexToString.end();
}
void join(IdentifierIndexType index, const std::string &s) {
assert(not containsIndex(index) and not containsString(s));
mappingStringToIndex.insert({s, index});
mappingIndexToString.insert({index, s});
}
};
namespace Keywords {
enum KeywordType: int {
None, If, While, For, Var, Def, Return, Else
};
void joinKeywords(IdentifierMap &idMap) {
idMap.join(If, "if");
idMap.join(While, "while");
idMap.join(For, "for");
idMap.join(Var, "var");
idMap.join(Def, "def");
idMap.join(Return, "return");
idMap.join(Else, "else");
}
}
IdentifierMap::IdentifierMap(): mappingStringToIndex(), mappingIndexToString() {
Keywords::joinKeywords(*this);
}
IdentifierMap identifierMap;
struct Program;
void compileError(std::string const &s) {
ioError << s << endl;
std::exit(-1);
}
class Tokenizer {
public:
static bool isBlank(char ch) {
return ch <= 32 or ch == 127;
}
static bool isIdentifierStart(char ch) {
return ('A' <= ch and ch <= 'Z') or ('a' <= ch and ch <= 'z') or ch == '_';
}
static bool isDigit(char ch) {
return '0' <= ch and ch <= '9';
}
static Trie::Trie symbols;
struct ParseAble {
virtual void parse(IO::Scanner &io) = 0;
friend IO::Scanner &operator>>(IO::Scanner &io, ParseAble &pa) {
return pa.parse(io), io;
}
};
struct DumpAble {
virtual void dump(IO::Printer &io) const = 0;
friend IO::Printer &operator<<(IO::Printer &io, const DumpAble &da) {
return da.dump(io), io;
}
};
struct Identifier: public ParseAble, public DumpAble {
IdentifierIndexType name;
Identifier(): name() {}
Identifier(IdentifierIndexType name): name(name) {}
Identifier(const std::string &str_name) {
auto index = identifierMap.insert(str_name);
name = index;
}
void parse(IO::Scanner &io) {
std::string str_name;
char ch = io.get();
for (; not isIdentifierStart(ch); ch = io.get());
for (; isIdentifierStart(ch) or isDigit(ch); ch = io.get()) {
str_name.push_back(ch);
}
io.unget();
auto index = identifierMap.insert(str_name);
name = index;
}
void dump(IO::Printer &io) const override {
io << name;
}
bool operator< (Identifier const &other) const { return name < other.name; }
bool operator== (std::string const &other) const {
return identifierMap.getString(name) == other;
}
bool operator== (IdentifierIndexType const &x) const {
return name == x;
}
};
struct Integer: public ParseAble, public DumpAble {
std::string value;
std::vector<Identifier> suffixOperators;
Integer(): value("") {}
Integer(std::string value): value(value) {}
void parse(IO::Scanner &io) override {
value.clear();
suffixOperators.clear();
char ch = io.get();
try {
if (ch == '0') {
value.push_back(ch);
ch = io.get();
value.push_back(ch);
if (ch == 'x' or ch == 'X') {
for (ch = io.get(); isDigit(ch) or ('A' <= ch and ch <= 'F') or ('a' <= ch and ch <= 'f') or ch == '\''; ch = io.get()) {
if (ch == '\'') continue;
value.push_back(ch);
}
io.unget();
} else if (ch == 'b' or ch == 'B') {
for (ch = io.get(); ch == '0' or ch == '1' or ch == '\''; ch = io.get()) {
if (ch == '\'') continue;
value.push_back(ch);
}
io.unget();
} else if (ch == 'o' or ch == 'O') {
for (ch = io.get(); ('0' <= ch and ch <= '7') or ch == '\''; ch = io.get()) {
if (ch == '\'') continue;
value.push_back(ch);
}
io.unget();
} else {
value.pop_back();
goto egg;
}
} else {
egg:
for (; isDigit(ch) or ch == '\''; ch = io.get()) {
if (ch == '\'') continue;
value.push_back(ch);
}
io.unget();
}
} catch (IO::IntegerOverflowError &) {
compileError("Number is too large");
}
}
void dump(IO::Printer &io) const override {
io << value;
for (auto &op: suffixOperators) {
io << "'" << op;
}
}
};
struct String: public ParseAble, public DumpAble {
std::string value;
std::vector<Identifier> suffixOperators;
String(): value() {}
String(std::string value): value(std::move(value)) {}
String(std::string &&value): value(std::move(value)) {}
void parse(IO::Scanner &io) {
value.clear();
suffixOperators.clear();
char ch = io.get();
if (ch != '"') throw "String literal should starts with '\"'";
for (ch = io.get(); ch != '"'; ch = io.get()) {
if (ch == '\\') {
ch = io.get();
switch (ch) {
case 'a': value.push_back('\a'); break;
case 'b': value.push_back('\b'); break;
case 'f': value.push_back('\f'); break;
case 'n': value.push_back('\n'); break;
case 'r': value.push_back('\r'); break;
case 't': value.push_back('\t'); break;
case 'v': value.push_back('\v'); break;
case '\\': value.push_back('\\'); break;
case '\'': value.push_back('\''); break;
case '"': value.push_back('"'); break;
case '?': value.push_back('\?'); break;
case 'x': {
int x = 0;
for (int i = 0; i < 2; i++) {
ch = io.get();
if ('0' <= ch and ch <= '9') x = x * 16 + (ch ^ 48);
else if ('A' <= ch and ch <= 'F') x = x * 16 + (ch & 15) + 9;
else if ('a' <= ch and ch <= 'f') x = x * 16 + (ch & 15) + 9;
else compileError("Invalid hex digit");
}
value.push_back(x);
} break;
case '0': [[fallthrough]];
case '1': [[fallthrough]];
case '2': [[fallthrough]];
case '3': [[fallthrough]];
case '4': [[fallthrough]];
case '5': [[fallthrough]];
case '6': [[fallthrough]];
case '7': {
int x = ch ^ 48;
for (int i = 0; i < 2; i++) {
ch = io.get();
if ('0' <= ch and ch <= '7') x = x * 8 + (ch ^ 48);
else {
io.unget();
break;
}
}
value.push_back(x);
} break;
default: value.push_back(ch); break;
}
} else {
value.push_back(ch);
}
}
Identifier id;
ch = io.get();
if (isIdentifierStart(ch)) {
io.unget();
io >> id;
suffixOperators.push_back(id);
} else {
io.unget();
}
while (io.get() == '\'') {
io >> id;
suffixOperators.push_back(id.name);
}
io.unget();
}
void dump(IO::Printer &io) const override {
io << '"';
for (auto x: value) {
switch (x) {
case '\n': io << "\\n"; break;
case '\t': io << "\\t"; break;
case '\r': io << "\\r"; break;
case '\0': io << "\\0"; break;
case '\\': io << "\\\\"; break;
case '\"': io << "\\\""; break;
case '\x20': io << '\x20'; break;
[[likely]] default:
io << x;
}
}
io << '"';
if (not suffixOperators.empty()) {
io << suffixOperators.front();
}
}
};
struct Symbol: public ParseAble, public DumpAble {
std::string value;
Symbol(): value({}) {}
Symbol(std::string value): value(value) {}
void parse(IO::Scanner &io) {
value.clear();
char ch = io.get();
value.push_back(ch);
auto *cur = symbols.root->next[ch - 32];
bool flag = false;
while (cur) {
ch = io.get(), flag = true;
if (isBlank(ch) or isDigit(ch) or isIdentifierStart(ch)) break;
if (cur->next[ch - 32]) cur = cur->next[ch - 32];
else break;
value.push_back(ch);
}
if (flag) io.unget();
}
void dump(IO::Printer &io) const override {
io << value;
}
};
struct FloatingPointNumber: public ParseAble, public DumpAble {
std::string value;
std::vector<Identifier> suffixOperators;
FloatingPointNumber(): value() {}
FloatingPointNumber(std::string value): value(value) {}
void parse(IO::Scanner &io) {
char ch = io.get();
value.clear();
for (; isDigit(ch); ch = io.get()) value.push_back(ch);
if (ch == '.') {
value.push_back('.');
for (ch = io.get(); isDigit(ch); ch = io.get()) value.push_back(ch);
}
Identifier id;
if (isIdentifierStart(ch) and ch != 'E' and ch != 'e') {
io.unget();
io >> id;
suffixOperators.push_back(id);
} else {
io.unget();
}
while (io.get() == '\'') {
io >> id;
suffixOperators.push_back(id.name);
}
io.unget();
}
void dump(IO::Printer &io) const override {
io << value;
for (auto i: suffixOperators) io << '\'' << i;
}
};
struct Token {
enum Tag {
NoneTag, IdentifierTag, SymbolTag, KeywordTag, IntegerTag, StringTag, EndOfLineTag, FloatingPointTag
} tag = NoneTag;
MyStd::variant<int, Identifier, Integer, String, Symbol, FloatingPointNumber> value = 0;
Token() = default;
Token(Tag tag, MyStd::variant<int, Identifier, Integer, String, Symbol, FloatingPointNumber> value = 0): tag(tag), value(value) {}
friend IO::Printer &operator<<(IO::Printer &io, const Token &token) {
switch (token.tag) {
case Token::IdentifierTag:
{
auto s = identifierMap.getString(get<Identifier>(token.value).name);
static std::set<std::string> set{
"and", "break", "do", "else", "elseif", "end", "false", "for", "function",
"if", "in", "local", "nil", "not", "or", "repeat", "return", "then",
"true", "until", "while"
};
if (set.find(s) != set.end()) return io << "[RESERVED] " << s;
return io << "[NAME] " << s;
}
case Token::IntegerTag:
return io << "[NUMBER] " << get<Integer>(token.value);
case Token::StringTag:
return io << "[STRING] " << get<String>(token.value);
case Token::SymbolTag:
return io << "[SYMBOL] " << get<Symbol>(token.value);
case Token::EndOfLineTag:
return io << "[EOL]";
case Token::FloatingPointTag:
return io << "[NUMBER] " << get<FloatingPointNumber>(token.value);
default:
unreachable();
return io << "[UNKNOWN] ";
}
}
};
static std::vector<Token> tokenize(IO::Scanner &io) {
std::vector<Token> tokens;
try {
while (true) {
char ch = io.get();
io.unget();
if (ch == '\0') {
break;
} if (ch == '\n') {
io.get();
tokens.push_back(Token{Token::EndOfLineTag});
} else if (isBlank(ch)) {
io.get();
} else if (ch == '"') {
String str;
io >> str;
tokens.push_back({Token::StringTag, str});
} else if (isDigit(ch) or ch == '.') {
Integer integer;
FloatingPointNumber fp;
bool isInteger = true;
if (ch != '.') io >> integer;
else {
io.get();
if (io.get() == '.') {
io.unget(), io.unget();
goto readSymbol;
continue;
}
io.unget(), io.unget();
}
if (integer.suffixOperators.empty() and integer.value.substr(0, 2) != "0x") {
if (io.get() == '.') {
fp.value += '.';
isInteger = false;
if (not isDigit(io.get())) {
if (integer.value.empty()) {
io.unget(), io.unget();
goto readSymbol;
}
io.unget();
fp.value = integer.value + fp.value;
goto egg;
}
io.unget();
io.unget(), io >> fp;
fp.value = integer.value + fp.value;
} else {
io.unget();
}
egg:
ch = io.get();
if (ch == 'e' or ch == 'E') {
fp.value.push_back(ch);
if (isInteger) fp.value = integer.value + fp.value;
ch = io.get();
if (ch == '+') fp.value.push_back(ch), ch = io.get();
else if (ch == '-') fp.value.push_back(ch), ch = io.get();
isInteger = false;
if (isDigit(ch)) {
io.unget(), io >> integer;
StringPrinter print; print << integer;
fp.value += print.s;
fp.suffixOperators = std::move(integer.suffixOperators);
} else {
compileError("Invalid decimal literal");
}
} else {
io.unget();
}
}
if (isInteger) tokens.push_back({Token::IntegerTag, integer});
else tokens.push_back({Token::FloatingPointTag, fp});
} else if (isIdentifierStart(ch)) {
Identifier identifier;
io >> identifier;
tokens.push_back({Token::IdentifierTag, identifier});
} else {
readSymbol:
Symbol symbol;
io >> symbol;
if (symbol.value == "--") {
while (io.get() != '\n');
io.unget();
continue;
}
tokens.push_back({Token::SymbolTag, symbol});
}
}
} catch (IO::EOFError &) {}
return tokens;
}
};
Trie::Trie Tokenizer::symbols {
"==", ">=", "<=", "~=", "..", "..."
};
using Token = Tokenizer::Token;
using Identifier = Tokenizer::Identifier;
using Symbol = Tokenizer::Symbol;
void solve() {
auto tokens = Tokenizer{}.tokenize(io);
for (auto x: tokens) {
io << x << endl;
}
}
}
int main(int argc, char const *argv[]) {
DEBUG_MODE = (argc-1) and not strcmp("-d", argv[1]);
GenshinLang::solve();
return 0;
}