#if defined(_USE_PCH_)
#include "pch.hpp"
#else
#include <bits/stdc++.h>
#endif
#define RNG(V_, A_, B_, ...) for (int V_ = (A_), V_##_END = (B_) __VA_OPT__(, ) __VA_ARGS__; V_ <= V_##_END; V_++)
#define IRNG(V_, A_, B_, ...) for (int V_ = (A_), V_##_END = (B_) __VA_OPT__(, ) __VA_ARGS__; V_ >= V_##_END; V_--)
#ifdef _WIN32
#define long int64_t
#endif
#define fir first
#define sec second
#define _UN using namespace
using namespace std;
typedef pair<int, int> pii;
typedef __int128 i128;
#define PRINTF_STRINGVIEW(X_) (int) ((X_).size()), (X_).data()
static __attribute__((format(printf, 1, 2))) int eprintf(const char* fmt, ...) {
va_list args;
va_start(args, fmt);
int ret = vfprintf(stderr, fmt, args);
va_end(args);
return ret;
}
static bool is_debugger_attached() {
static int result_cache = -1;
if (result_cache != -1) return result_cache;
static char buf[1024];
memset(buf, 0, sizeof(buf));
auto f = fopen("/proc/self/status", "r");
if (!f) return false;
fread(buf, 1, sizeof(buf) - 1, f);
if (auto p = strstr(buf, "TracerPid:")) {
p += 10;
int a = 0;
sscanf(p, "%d", &a);
if (a) return (bool) (result_cache = true);
}
return (bool) (result_cache = false);
}
static void debug_break_or_exit() {
if (is_debugger_attached()) {
__asm__ volatile("int3");
} else {
eprintf("Critical error occurred. Program terminated.\n");
exit(1);
}
}
static void my_report_error(const char* expr, const char* file, int line, const char* function) {
eprintf("Error: %s\n file %s, line %d, function %s\n", expr, file, line, function);
if (errno) eprintf(" errno: %s (%d)\n", strerror(errno), errno);
if (is_debugger_attached()) { __asm__ volatile("int3"); }
}
#define __REPORT_ERR(_EXPR_S) my_report_error(_EXPR_S, __FILE__, __LINE__, __ASSERT_FUNCTION)
#define CHK(_EXPR) \
do { \
if (!static_cast<bool>((_EXPR))) return __REPORT_ERR(#_EXPR), false; \
} while (0)
#define CHK_EXIT(_EXPR) \
do { \
if (!static_cast<bool>((_EXPR))) return __REPORT_ERR(#_EXPR), debug_break_or_exit(); \
} while (0)
template<typename T, typename U>
bool elem_in(const T& x, initializer_list<U> lst) {
for (auto& y : lst) {
if (x == y) return true;
}
return false;
}
enum TokenType {
TK_NL,
TK_COMMENT,
TK_STR,
TK_WORD,
TK_SYM,
TK_EOF,
};
struct Token {
int pos, len, ln, col;
TokenType type;
string_view content;
int data;
int child, next, next_comma;
};
constexpr int BUFFER_SIZE = 1048576, N_TOKEN = 1048576;
char buf[BUFFER_SIZE], tmp[BUFFER_SIZE];
int file_size;
Token tokens[N_TOKEN];
int n_token;
int line_pos[BUFFER_SIZE], n_line;
bool verify_ascii(string_view str) {
for (int i = 0; i < str.size(); i++) {
int ch = ((uint8_t*) str.data())[i];
if ((ch < 32 && (ch != '\n' && ch != '\t' && ch != '\r')) || ch > 126) return false;
}
return true;
}
bool init(const char* filename) {
auto file = fopen(filename, "rb");
CHK(file);
CHK(!fseek(file, 0, SEEK_END));
constexpr auto FILE_SIZE_LIM = BUFFER_SIZE - 10;
long len = ftell(file);
CHK(len >= 0 && len <= FILE_SIZE_LIM);
file_size = int(len);
CHK(!fseek(file, 0, SEEK_SET));
CHK(file_size == fread(buf, 1, BUFFER_SIZE, file));
fclose(file);
CHK(verify_ascii(string_view(buf, file_size)));
n_token = 0;
line_pos[n_line = 1] = 0;
for (int i = 1; i < file_size; i++) {
if (buf[i - 1] == '\n') { line_pos[++n_line] = i; }
}
line_pos[n_line + 1] = file_size;
return true;
}
pair<int, int> find_ln_col(int pos) {
int ln = upper_bound(line_pos + 1, line_pos + n_line + 1, pos) - line_pos - 1;
int col = pos - line_pos[ln] + 1;
return {ln, col};
}
bool ascii_isspace(int ch) { return elem_in(ch, {' ', '\t', '\r', '\n'}); }
bool ascii_isdigit(int ch) { return ch >= '0' && ch <= '9'; }
bool ascii_isalpha(int ch) { return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'); }
bool tokenize() {
auto char_category = [](int ch) {
if (!ch) return 0;
if (ascii_isspace(ch)) return 1;
if (ascii_isdigit(ch)) return 2;
if (ascii_isalpha(ch) || ch == '_') return 3;
return 4;
};
auto append_token = [](int pos, int len, TokenType type, int data) {
assert(len >= 0 && pos >= 0 && pos + len <= file_size);
auto [ln, col] = find_ln_col(pos);
tokens[++n_token] = {pos, len, ln, col, type, string_view(buf + pos, len), data, 0, 0, 0};
};
for (int i = 0; i < file_size;) {
int ch = buf[i];
int a = char_category(ch);
if (a == 1) {
#if 0
if (ch == '\n') append_token(i, 1, TK_NL, 0);
#endif
i++;
continue;
}
if (a == 4) {
if (ch == '/' && buf[i + 1] == '/') {
int l = i;
while (buf[i] && buf[i] != '\n') i++;
append_token(l, i - l, TK_COMMENT, 0);
i++;
continue;
}
if (ch == '/' && buf[i + 1] == '*') {
int l = i;
while (buf[i] && (buf[i] != '*' || buf[i + 1] != '/')) i++;
i += 2;
append_token(l, i - l, TK_COMMENT, 0);
continue;
}
if (ch == '\'') {
i++;
int l = i;
while (buf[i] && !(buf[i] == '\'' && (buf[i - 1] != '\\' || (i >= 2 && buf[i - 2] == '\\')))) i++;
CHK(buf[i] == '\'');
append_token(l, i - l, TK_STR, 0);
i++;
continue;
}
if (ch == '\"') {
if (i > 0 && buf[i - 1] == 'R') {
int l = i;
i++;
auto p = strchr(buf + i, '(');
CHK(p);
snprintf(tmp, BUFFER_SIZE, ")%.*s\"", int(p - (buf + i)), buf + i);
i = p + 1 - buf;
p = strstr(buf + i, tmp);
CHK(p);
i = p + strlen(tmp) - buf;
append_token(l, i - l, TK_STR, 0);
continue;
} else {
int l = i;
i++;
while (buf[i] && !(buf[i] == '\"' && (buf[i - 1] != '\\' || (i >= 2 && buf[i - 2] == '\\')))) i++;
CHK(buf[i] == '\"');
i++;
append_token(l, i - l, TK_STR, 0);
continue;
}
}
append_token(i, 1, TK_SYM, ch);
i++;
continue;
}
{
int l = i;
while (buf[i] && !elem_in(char_category(buf[i]), {1, 4})) i++;
append_token(l, i - l, TK_WORD, 0);
continue;
}
}
append_token(file_size, 0, TK_EOF, 0);
return true;
}
bool _do_build_token_tree(int& p, bool toplvl) {
int to_match = -1;
if (!toplvl) {
auto x = tokens[p++];
assert(x.type == TK_SYM);
switch (x.data) {
case '(': to_match = ')'; break;
case '[': to_match = ']'; break;
case '{': to_match = '}'; break;
default: assert(0);
}
}
int prv = 0;
while (true) {
auto& x = tokens[p];
if (prv) tokens[prv].next = p;
prv = p, p++;
if (x.type == TK_EOF) {
if (toplvl) return true;
else return false;
}
if (x.type == TK_SYM) {
if (x.data == to_match) return true;
switch (x.data) {
case ')':
case ']':
case '}': debug_break_or_exit(); return false; // Unmatched
case '(':
case '[':
case '{':
p--;
tokens[prv].child = p + 1;
if (!_do_build_token_tree(p, false)) return false;
continue;
default: break;
}
}
}
}
bool build_token_tree() {
int p = 1;
return _do_build_token_tree(p, 1);
}
int main() {
#if defined(_LOCAL_)
// freopen("in", "r", stdin);
// freopen("out","w",stdout);
// freopen("/dev/null","w",stderr);
#else
freopen("paint.in", "r", stdin);
freopen("paint.out", "w", stdout);
#endif
// ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
init("main.cpp");
assert(tokenize());
assert(build_token_tree());
auto f = fopen("out", "w");
for (int i = 1; i <= n_token; i++) {
auto x = tokens[i];
if (x.type != TK_NL)
fprintf(f, "%d %d %d %d %d\n\t%.*s\n", i, x.ln, x.col, x.next, x.child, PRINTF_STRINGVIEW(x.content));
}
fclose(f);
/*COMMENT*/
}