const Splitter = require("split-tools");
var text = "x > 5 && (y == 7.5) || sin(PI*a/360) || isNumber(y) && true ";
// var text = "x+-y"; // это тоже работает
const splitter = new Splitter({
matchers: {
function: /\s*([a-z]\w*)\(\s*(.*)\s*\)/g,
bracket: /\(\s*(.*)\s*\)/g,
operator: /\s*(\!|\*|\/|\%|\+|\-|\=\=\=|\!\=\=|\<\=|\<|\>\=|\>|\=\=|\!\=|\&\&|\|\||\&|\|)\s*/g,
boolean: /\s*(true|false)\s*/gi,
sting: /\s*\"(.*?)\"\s*/gi,
variable: /\s*([a-z][\w\.]*)\s*/gi,
value: /\s*(\d[\d\.]*)\s*/gi
}
});
const tree = {
"brackets": ["function", "bracket", "operator", "boolean", "variable", "sting", "value"]
};
function subSplit(id, text) {
console.log("subSplit:", id, "[", text, "]");
if(tree[id] && tree[id].length)
return splitter.process(text, tree[id]);
return text;
}
splitter.addParser("function",(match,name,body)=>{
return {
type: "function",
name: name,
body: subSplit("brackets", body)
};
});
splitter.addParser("bracket",(match,body)=>{
return {
type: "bracket",
body: subSplit("brackets", body)
};
});
splitter.addParser("operator",(match,body)=>{
return {
type: "operator",
body: body
};
});
splitter.addParser("variable",(match,body)=>{
return {
type: "variable",
body: body
};
});
splitter.addParser("string",(match,body)=>{
return {
type: "string",
body: body
};
});
splitter.addParser("boolean",(match,body)=>{
return {
type: "boolean",
body: body==="true"?true:false
};
});
splitter.addParser("value",(match,body)=>{
return {
type: "value",
body: +body
};
});
const list = splitter.process(text, tree.brackets);
console.log("\n\nresult:\n",JSON.stringify(list, null, ' '));
// тут я не придумал ничего умнее :)))
var OPERATOR_PRIORITY = {
'!': (src, dst)=>{ dst.push({type: "value", body: !src.shift()}) },
'*': (src, dst)=>{ dst[dst.length-1] = {type: "value", body: dst[dst.length-1].body*src.shift().body} },
'/': (src, dst)=>{ dst[dst.length-1] = {type: "value", body: dst[dst.length-1].body/src.shift().body} },
'%': (src, dst)=>{ dst[dst.length-1] = {type: "value", body: dst[dst.length-1].body%src.shift().body} },
'-': (src, dst)=>{ dst[dst.length-1] = {type: "operator", body: "+" }; dst.push({type: "value", body: 0-src.shift().body}) },
'+': (src, dst)=>{ dst[dst.length-1] = {type: "value", body: dst[dst.length-1].body+src.shift().body} },
'<': (src, dst)=>{ dst[dst.length-1] = {type: "boolean", body: (dst[dst.length-1].body<src.shift().body?true:false) } },
'<=': (src, dst)=>{ dst[dst.length-1] = {type: "boolean", body: (dst[dst.length-1].body<=src.shift().body?true:false) } },
'>': (src, dst)=>{ dst[dst.length-1] = {type: "boolean", body: (dst[dst.length-1].body>src.shift().body?true:false) } },
'>=': (src, dst)=>{ dst[dst.length-1] = {type: "boolean", body: (dst[dst.length-1].body>=src.shift().body?true:false) } },
'==': (src, dst)=>{ dst[dst.length-1] = {type: "boolean", body: (dst[dst.length-1].body==src.shift().body?true:false) } },
'!=': (src, dst)=>{ dst[dst.length-1] = {type: "boolean", body: (dst[dst.length-1].body!=src.shift().body?true:false) } },
'===': (src, dst)=>{ dst[dst.length-1] = {type: "boolean", body: (dst[dst.length-1].body===src.shift().body?true:false) } },
'!==': (src, dst)=>{ dst[dst.length-1] = {type: "boolean", body: (dst[dst.length-1].body!==src.shift().body?true:false) } },
'&': (src, dst)=>{ dst[dst.length-1] = {type: "value", body: dst[dst.length-1].body&src.shift().body} },
'|': (src, dst)=>{ dst[dst.length-1] = {type: "value", body: dst[dst.length-1].body|src.shift().body} },
'&&': (src, dst)=>{ dst[dst.length-1] = {type: "boolean", body: (dst[dst.length-1].body&&src.shift().body) } },
'||': (src, dst)=>{ dst[dst.length-1] = {type: "boolean", body: (dst[dst.length-1].body||src.shift().body) } }
};
// это набор разрешенных функций
var ALLOWED_FUNCTIONS = {
"sin": (v)=>{ return {type: "value", body: Math.sin(v)} },
"cos": (v)=>{ return {type: "value", body: Math.cos(v)} },
"isNumber": (v)=>{ return {type: "boolean", body: !isNaN(parseFloat(v)) && isFinite(v) } }
};
// это набор разрешенных переменных
var ALLOWED_VARIABLES = {
"PI": Math.PI,
"x": 6,
"y": 7.5,
"a": 180
};
// парсер результатов разбора строки
function resCalc(src){
src = src
// заменяем переменные на их значения
.map(item=>{
// если не переменная возвращаем как есть
if(item.type !== "variable")
return item;
// иначе преобразуем переменную в ее значение
// тут надо еще сделать проверку на наличие переменной в списке разрешенных
const value = ALLOWED_VARIABLES[item.body];
let type = "value";
if( typeof value !== "number" ) type = typeof value;
return {
type: type,
body: ALLOWED_VARIABLES[item.body]
};
})
// заменяем функции на их значения
.map(item=>{
// если не функция возвращаем как есть
if(item.type !== "function")
return item;
// иначе преобразуем функции в ее значение
// тут надо еще сделать проверку на наличие функции в списке разрешенных
return ALLOWED_FUNCTIONS[item.name]( resCalc(item.body).body );
})
// заменяем скобки на их значения
.map(item=>{
// если не переменная возвращаем как есть
if(item.type !== "bracket")
return item;
// иначе преобразуем содержимое скобки в ее значение
return resCalc(item.body);
});
let dst;
Object.keys(OPERATOR_PRIORITY).forEach(operator=>{
const _calc = OPERATOR_PRIORITY[operator];
dst = [];
while( src.length ){
const item = src.shift();
if(item.type === "operator" && item.body === operator){
// если оператор
// тут надо еще сделать проверку на наличие обработчика оператора
_calc(src, dst);
}else{
// если все остальное
dst.push(item);
}
}
src = dst;
});
return src[0];
}
console.log("\n\nresult:\n",JSON.stringify(resCalc(list), null, ' '));
"x > 5 && (y == 7.5) || sin(PI*a/360) || isNumber(y) && true "const Splitter = require("split-tools");
var text = "x > 5 && (y == 7.5) || sin(PI*a/360) || isNumber(y) && true ";
// var text = "x > 5 && (y == 7.5) || sin(PI*a/360)";
const splitter = new Splitter({
matchers: {
function: /\s*([a-z]\w*)\(\s*(.*)\s*\)/g,
bracket: /\(\s*(.*)\s*\)/g,
operator: /\s*(\!|\*|\/|\%|\+|\-|\=\=\=|\!\=\=|\<\=|\<|\>\=|\>|\=\=|\!\=|\&\&|\|\||\&|\|)\s*/g,
boolean: /\s*(true|false)\s*/gi,
sting: /\s*\"(.*?)\"\s*/gi,
variable: /\s*([a-z][\w\.]*)\s*/gi,
value: /\s*(\d[\d\.]*)\s*/gi
}
});
const tree = {
"brackets": ["function", "bracket", "operator", "boolean", "variable", "sting", "value"]
};
function subSplit(id, text) {
console.log("subSplit:", id, "[", text, "]");
if(tree[id] && tree[id].length)
return splitter.process(text, tree[id]);
return text;
}
splitter.addParser("function",(match,name,body)=>{
return {
type: "function",
name: name,
body: subSplit("brackets", body)
};
});
splitter.addParser("bracket",(match,body)=>{
return {
type: "bracket",
body: subSplit("brackets", body)
};
});
splitter.addParser("operator",(match,body)=>{
return {
type: "operator",
body: body
};
});
splitter.addParser("variable",(match,body)=>{
return {
type: "variable",
body: body
};
});
splitter.addParser("string",(match,body)=>{
return {
type: "string",
body: body
};
});
splitter.addParser("boolean",(match,body)=>{
return {
type: "boolean",
body: body==="true"?true:false
};
});
splitter.addParser("value",(match,body)=>{
return {
type: "value",
body: +body
};
});
const list = splitter.process(text, tree.tags);
console.log("\n\nresult:\n",JSON.stringify(list, null, ' '));
// тут я не придумал ничего умнее :)))
var OPERATOR_PRIORITY = {
'!': (src, dst)=>{ dst.push({type: "value", body: !src.shift()}) },
'*': (src, dst)=>{ dst[dst.length-1] = {type: "value", body: dst[dst.length-1].body*src.shift().body} },
'/': (src, dst)=>{ dst[dst.length-1] = {type: "value", body: dst[dst.length-1].body/src.shift().body} },
'%': (src, dst)=>{ dst[dst.length-1] = {type: "value", body: dst[dst.length-1].body%src.shift().body} },
'-': (src, dst)=>{ dst[dst.length-1] = {type: "operator", body: "+" }; dst.push({type: "value", body: 0-src.shift().body}) },
'+': (src, dst)=>{ dst[dst.length-1] = {type: "value", body: dst[dst.length-1].body+src.shift().body} },
'<': (src, dst)=>{ dst[dst.length-1] = {type: "boolean", body: (dst[dst.length-1].body<src.shift().body?true:false) } },
'<=': (src, dst)=>{ dst[dst.length-1] = {type: "boolean", body: (dst[dst.length-1].body<=src.shift().body?true:false) } },
'>': (src, dst)=>{ dst[dst.length-1] = {type: "boolean", body: (dst[dst.length-1].body>src.shift().body?true:false) } },
'>=': (src, dst)=>{ dst[dst.length-1] = {type: "boolean", body: (dst[dst.length-1].body>=src.shift().body?true:false) } },
'==': (src, dst)=>{ dst[dst.length-1] = {type: "boolean", body: (dst[dst.length-1].body==src.shift().body?true:false) } },
'!=': (src, dst)=>{ dst[dst.length-1] = {type: "boolean", body: (dst[dst.length-1].body!=src.shift().body?true:false) } },
'===': (src, dst)=>{ dst[dst.length-1] = {type: "boolean", body: (dst[dst.length-1].body===src.shift().body?true:false) } },
'!==': (src, dst)=>{ dst[dst.length-1] = {type: "boolean", body: (dst[dst.length-1].body!==src.shift().body?true:false) } },
'&': (src, dst)=>{ dst[dst.length-1] = {type: "value", body: dst[dst.length-1].body&src.shift().body} },
'|': (src, dst)=>{ dst[dst.length-1] = {type: "value", body: dst[dst.length-1].body|src.shift().body} },
'&&': (src, dst)=>{ dst[dst.length-1] = {type: "boolean", body: (dst[dst.length-1].body&&src.shift().body) } },
'||': (src, dst)=>{ dst[dst.length-1] = {type: "boolean", body: (dst[dst.length-1].body||src.shift().body) } }
};
// это набор разрешенных функций
var ALLOWED_FUNCTIONS = {
"sin": (v)=>{ return {type: "value", body: Math.sin(v)} },
"cos": (v)=>{ return {type: "value", body: Math.cos(v)} },
"isNumber": (v)=>{ return {type: "boolean", body: !isNaN(parseFloat(v)) && isFinite(v) } }
};
// это набор разрешенных переменных
var ALLOWED_VARIABLES = {
"PI": Math.PI,
"x": 6,
"y": 7.5,
"a": 180
};
// парсер результатов разбора строки
function resCalc(src){
// заменяем переменные на их значения
src = src.map(item=>{
// если не переменная возвращаем как есть
if(item.type !== "variable")
return item;
// иначе преобразуем переменную в ее значение
// тут надо еще сделать проверку на наличие переменной в списке разрешенных
const value = ALLOWED_VARIABLES[item.body];
let type = "value";
if( typeof value !== "number" ) type = typeof value;
return {
type: type,
body: ALLOWED_VARIABLES[item.body]
};
});
let dst;
Object.keys(OPERATOR_PRIORITY).forEach(operator=>{
const _calc = OPERATOR_PRIORITY[operator];
dst = [];
while( src.length ){
const item = src.shift();
if(item.type === "function"){
// если функция
// тут надо еще сделать проверку на наличие функции в списке разрешенных
dst.push(ALLOWED_FUNCTIONS[item.name]( resCalc(item.body).body ) );
}else if(item.type === "bracket"){
// если скобки
dst.push( resCalc(item.body).body );
}else if(item.type === "operator" && item.body === operator){
// если оператор
// тут надо еще сделать проверку на наличие обработчика оператора
_calc(src, dst);
}else{
// если все остальное
dst.push(item);
}
}
src = dst;
});
return src[0];
}
console.log("\n\nresult:\n",JSON.stringify(resCalc(list), null, ' '));[
{ "type": "variable", "body": "x" },
{ "type": "operator", "body": ">" },
{ "type": "value", "body": 5 },
{ "type": "operator", "body": "&&" },
{ "type": "bracket", "body": [
{ "type": "variable", "body": "y" },
{ "type": "operator", "body": "==" },
{ "type": "value", "body": 7.5 }
]
},
{ "type": "operator", "body": "||" },
{ "type": "function", "name": "sin", "body": [
{ "type": "variable", "body": "PI" },
{ "type": "operator", "body": "*" },
{ "type": "variable", "body": "a" },
{ "type": "operator", "body": "/" },
{ "type": "value", "body": 360 },
{ "type": "operator", "body": "||" },
{ "type": "variable", "body": "isNumber" },
{ "type": "variable", "body": "y" }
]
},
{ "type": "operator", "body": "&&" },
{ "type": "boolean", "body": true }
]{
"type": "boolean",
"body": true
}



[21: 31, 22: 14, 23: 1, 24: 35, 25: 6, 26: 16, 27: 13, 28: 78, 29: 1,30: 42, 31: 21, 32: 31, 33: 15,34: 20, 35: 24] и [[24: 35], [21: 31]]{"21":31,"22":14,"23":1,"24":35,"25":6,"26":16,"27":13,"28":78,"29":1,"30":42,"31":21,"32":31,"33":15,"34":20,"35":24} и {"21":31,"24":35} соответственно.
const net = require("net");
function send(event, data){
this.write(
JSON.stringify({
event: event,
data: data
})+"\n"
);
}
class Server extends EventEmitter {
constructor(options={}){
super();
this.clients = {};
this.host=options.host||'0.0.0.0';
this.port=options.port||'8020';
this.server = net.createServer ((client)=>{
client.send = send;
client.on('data', data=>{
try{
const str = data.toString();
const json = JSON.parse(""+str);
if( json && json.event && json.data!==undefined){
this.emit(json.event, json.data, client);
}
}catch(err){
console.log('\n\nError parse json', err);
}
});
client.on('close', data=>{
// ...
});
});
}
listen(host, port){
this.host=host||this.host;
this.port=port||this.port;
this.server.listen(this.port, this.host);
console.log('Server was ben started as ' + this.host + ':' + this.port);
}
}
module.exports.Server = Server;
class Client extends EventEmitter {
constructor(options={}){
super();
this.host=options.host||'192.168.0.1';
this.port=options.port||'8020';
this.client = new net.Socket();
this.client.send = send;
this.client.on('data', data=>{
data.toString().split(/[\n\r]+/).forEach(str=>{
if( !str || str.search(/^[\s\n\r]+$/)+1 ) return;
try{
const json = JSON.parse(""+str);
if( json && json.event && json.data!==undefined){
this.emit(json.event, json.data, this.client);
}
}catch(err){
console.log('\n\nError parse json ', err);
}
});
});
this.client.on('close', ()=>{
console.log('Connection closed');
this.emit("disconnect", {});
setTimeout(()=>{
this.connect()
}, 6000);
});
this.client.on('error', ()=>{
console.log('Connection error');
this.emit("disconnect", {});
this.client.destroy();
});
}
connect(host, port){
this.host=host||this.host;
this.port=port||this.port;
this.client.connect(this.port, this.host, (err)=>{
this.emit('connect', {}, this.client);
if(err) reject(err);
});
}
}
module.exports.Client = Client;const storrage = require("./modules/storrage.js");
const users = storrage("../config/users.json",{});
const config = storrage("../config/config.server.json",{});
const NU = require("./modules/net-union.js");
const server = new NU.Server();
const clients = {};
function getUser(data){
if( !data || !data.login || !data.password )
return false;
const user = users[data.login];
if( !user || user.password != data.password)
return false;
return user;
}
server.on("avance", (data, client )=>{
const user = getUser(data.auth);
if( !user ) return client.end();
const username = data.auth.login;
clients[username] = client;
user.points-=data.avance;
client.send("points", user.points);
});
server.on("unlock", (data, client )=>{
console.log("unlock", data);
const user = getUser(data);
if( !user ) return client.end();
const username = data.login;
clients[username] = client;
user.points--;
client.send("points", user.points);
});
function update_points(username,amount) {
if( !amount ) return;
const user = users[username];
if( !user ) return;
const client = clients[username];
if( !client ) return;
client.send("points", user.points);
client.send("message", "Количество минут "+(amount<0?"уменьшено":"увеличено")+" на "+amount+".\nОсталось "+user.points+" минут");
}
server.listen(config.url.host, config.url.port);
module.exports.server = server;
module.exports.update_points = update_points;const manage = require("./modules/manage.js");
const storrage = require("./modules/storrage.js");
const config = storrage("./config/config.client.json",{});
const NU = require("./modules/net-union.js");
const client = new NU.Client();
var count = 0;
var avance = 0;
var server;
var isConnect = false;
// при подключении к серверу отправить запрос на авторизацию
client.on("connect", (data, serv )=>{
if(isConnect) return;
isConnect = true;
server = serv;
console.log('\n\nПодключился к ' + client.host + ":" + client.port);
if(avance)
server.send("avance", {auth: config.auth, avance: avance});
});
// при отключении к серверу переходим в автономный режим
client.on("disconnect", (data)=>{
console.log("client.on disconnect");
isConnect = false;
});
// при получении количества баллов обновить счетчик
client.on("points", (points, serv )=>{
console.log("client.on points", points);
count = points;
notify();
});
// при получении сообщения - вывести уведомление
client.on("message", (message, serv )=>{
console.log("client.on message", message);
manage.notify("Таймер", message);
});
client.connect(config.url.host, config.url.port);
function notify() {
if(count>8 && count%10===0){
manage.notify("Таймер", "Осталось "+count+" минут");
}else if(count==5){
manage.notify("Таймер", "Осталось "+count+" минут");
}else if(count>1 && count<5){
manage.notify("Таймер", "Осталось "+count+" минуты");
}else if(count==1){
manage.notify("Таймер", "Осталась 1 минута");
}else if(count<1){
manage.notify("Таймер", "ВРЕМЯ ВЫШЛО");
lock()
}
}
function lock() {
setTimeout(function() {
manage.lock(config.auth.login);
}, 20000);
}
function observe_session() {
console.log("next time");
manage.isLocked(config.auth.login, isLocked=>{
if(isLocked) return;
if( isConnect ){
server.send("unlock", config.auth);
}else{
count--;
avance++;
notify();
}
});
setTimeout( observe_session , 60000 );
}
setTimeout( observe_session , 10000 );