const sql = `
CREATE TABLE \`pf_station_info\` (
\`id\` int unsigned NOT NULL AUTO_INCREMENT,
\`name\` varchar(20) NOT NULL DEFAULT '' COMMENT '站点名称',
\`location\` json NOT NULL COMMENT '站点所在经纬度',
\`ip_address\` varchar(15) NOT NULL DEFAULT '' COMMENT '站点 ip 地址,非此 IP 数据不接收',
\`tiger_shaped\` char(32) NOT NULL DEFAULT '' COMMENT '握手符号',
\`heartbeat\` int unsigned NOT NULL DEFAULT '0' COMMENT '上次心跳时间戳',
\`status\` int NOT NULL DEFAULT '0' COMMENT '站点状态:0 正常,非 0 为停用时间戳',
\`add_time\` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
\`update_time\` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (\`id\`)
) ENGINE=InnoDB AUTO_INCREMENT=22 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
CREATE TABLE \`player\` (
\`user_id\` bigint(20) NOT NULL COMMENT '用户ID',
\`user_name\` varchar(32) COLLATE utf8mb4_bin NOT NULL COMMENT '用户昵称',
\`user_level\` int(11) NOT NULL COMMENT '用户等级',
\`head_skin\` int(11) NOT NULL COMMENT '用户头像皮肤',
\`head_box_skin\` int(11) NOT NULL COMMENT '用户头像框皮肤',
\`world_space_id\` int(11) NOT NULL COMMENT '世界空间ID',
\`is_online\` bit(1) NOT NULL COMMENT '是否在线',
\`login_time\` datetime DEFAULT NULL COMMENT '最近登录时间',
\`login_ip\` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '最近登录IP地址',
\`create_time\` datetime NOT NULL COMMENT '创建时间',
\`revision\` int(11) NOT NULL COMMENT '乐观锁版本号',
PRIMARY KEY (\`user_id\`) USING BTREE,
UNIQUE KEY \`uk_user_name\` (\`user_name\`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
CREATE TABLE \`player_features\` (
\`guid\` char(36) COLLATE utf8mb4_bin NOT NULL COMMENT 'GUID',
\`user_id\` bigint(20) NOT NULL COMMENT '用户ID',
\`feature_type\` bigint(20) NOT NULL COMMENT '特性类型',
\`feature_value\` bigint(20) NOT NULL COMMENT '特性值',
\`create_time\` datetime NOT NULL COMMENT '创建时间',
\`duration\` int(11) NOT NULL COMMENT '持续时间,单位秒,为0则不过期',
PRIMARY KEY (\`guid\`) USING BTREE,
KEY \`idx_user_id\` (\`user_id\`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
`;
function* read_tokens(sql_string) {
sql_string = sql_string.replaceAll('\r', '');
sql_string = sql_string.replaceAll('\n', '');
const singl_token_table = [',', ';', '(', ')', '='];
const chars = [...sql_string];
let token = "";
let in_quote = false;
for(let char of chars) {
if (in_quote || char === '\'') {
if ( char === '\''){
if (in_quote) {
yield token;
token = '';
in_quote = false;
}
else {
in_quote = true;
}
}
else {
token += char;
}
}
else if (char === ' ') {
if (token !== '')
{
yield token;
token = '';
}
}
else if (singl_token_table.includes(char)) {
if (token !== '')
{
yield token;
token = '';
}
yield char;
}
else {
token += char;
}
}
}
function create_token_accessor(tokens) {
return {
_tokens: tokens,
_current_token: tokens.next(),
current_token: function() {
return this._current_token.value;
},
has_next: function() {
return !this._current_token.done;
},
next: function() {
this._current_token = this._tokens.next();
return this;
},
next_while: function(...keys) {
while(this.has_next()) {
if (keys.includes(this.next().current_token())) {
return this;
}
}
throw new Error("Cannot found keys until token finished.");
}
}
}
function read_table_field_type(token_accessor) {
let result = {
name: token_accessor.next().current_token(),
}
if (!token_accessor.has_next()) {
return result
}
if (token_accessor.next().current_token() === "(") {
result.length = token_accessor.next().current_token();
if (token_accessor.next().current_token() !== ")") {
throw new Error("Field type mismatch end [ ) ] of length, field name: " + result);
}
}
return result;
}
function read_table_field(token_accessor) {
let result = {};
result.name = token_accessor.current_token();
result.type = read_table_field_type(token_accessor);
while (token_accessor.has_next()) {
var token = token_accessor.next().current_token();
switch(token.toUpperCase()) {
case "COMMENT":
result.comment = token_accessor.next().current_token();
break;
case ",":
case ")":
return result;
default:
break;
}
}
throw new Error("Read table field not complete");
}
function read_table_key(token_accessor) {
let keyFields = [];
token_accessor.next_while("(");
while (token_accessor.has_next()) {
let token = token_accessor.next().current_token();
if (token === ')') {
break;
}
if (token !== ',') {
keyFields.push(token);
}
}
while (token_accessor.has_next()) {
let token = token_accessor.next().current_token();
if (token === ')' || token === ',') {
break;
}
}
return {
fields: keyFields
}
}
function read_table(token_accessor) {
const key_types = ["PRIMARY", "UNIQUE", "KEY"];
let table_name = token_accessor.next().current_token();
let fields = [];
let keys = [];
token_accessor.next_while("(");
while (token_accessor.has_next()) {
let token = token_accessor.current_token();
if (token === ')') {
break;
}
token = token_accessor.next().current_token();
console.log("TABLE: " + token);
if (key_types.includes(token)) {
let key = read_table_key(token_accessor);
key.type = token;
keys.push(key);
}
else {
var field = read_table_field(token_accessor);
fields.push(field);
}
}
token_accessor.next_while(";");
return {
name: table_name,
keys: keys,
fields: fields
}
}
function read(ddl) {
const tokens = read_tokens(ddl);
const token_accessor = create_token_accessor(tokens);
let result = {
create_tables: [],
};
while (token_accessor.has_next()) {
console.log("GLOBAL: " + token_accessor.current_token());
switch(token_accessor.current_token().toUpperCase()) {
case "CREATE":
if (token_accessor.next().current_token().toUpperCase() === 'TABLE') {
var table = read_table(token_accessor);
result.create_tables.push(table);
token_accessor.next();
}
else {
throw new Error("Invalid token: " + token_accessor.current_token());
}
break;
default:
throw new Error("Invalid token: " + token_accessor.current_token());
}
}
return result;
}
var results = read(sql);
console.log(JSON.stringify(results));
console