|
|
@@ -0,0 +1,312 @@ |
|
|
|
#!/usr/bin/awk -f |
|
|
|
# |
|
|
|
# ZyklonB eval plugin, LISP-like expression evaluator |
|
|
|
# |
|
|
|
# Copyright 2013, 2014 Přemysl Janouch. All rights reserved. |
|
|
|
# See the file LICENSE for licensing information. |
|
|
|
# |
|
|
|
|
|
|
|
BEGIN \ |
|
|
|
{ |
|
|
|
RS = "\r" |
|
|
|
ORS = "\r\n" |
|
|
|
IGNORECASE = 1 |
|
|
|
srand() |
|
|
|
|
|
|
|
prefix = get_config("prefix") |
|
|
|
|
|
|
|
print "ZYKLONB register" |
|
|
|
fflush("") |
|
|
|
|
|
|
|
# All functions have to be in this particular array |
|
|
|
min_args["int"] = 1 |
|
|
|
min_args["+"] = 1 |
|
|
|
min_args["-"] = 1 |
|
|
|
min_args["*"] = 1 |
|
|
|
min_args["/"] = 1 |
|
|
|
min_args["%"] = 1 |
|
|
|
min_args["^"] = 1 |
|
|
|
min_args["**"] = 1 |
|
|
|
min_args["exp"] = 1 |
|
|
|
min_args["sin"] = 1 |
|
|
|
min_args["cos"] = 1 |
|
|
|
min_args["atan2"] = 2 |
|
|
|
min_args["log"] = 1 |
|
|
|
min_args["rand"] = 0 |
|
|
|
min_args["sqrt"] = 1 |
|
|
|
|
|
|
|
min_args["pi"] = 0 |
|
|
|
min_args["e"] = 0 |
|
|
|
|
|
|
|
min_args["min"] = 1 |
|
|
|
min_args["max"] = 1 |
|
|
|
|
|
|
|
# Whereas here their presence is only optional |
|
|
|
max_args["int"] = 1 |
|
|
|
max_args["sin"] = 1 |
|
|
|
max_args["cos"] = 1 |
|
|
|
max_args["atan2"] = 2 |
|
|
|
max_args["log"] = 1 |
|
|
|
max_args["rand"] = 0 |
|
|
|
max_args["sqrt"] = 1 |
|
|
|
|
|
|
|
max_args["pi"] = 0 |
|
|
|
max_args["e"] = 0 |
|
|
|
} |
|
|
|
|
|
|
|
{ |
|
|
|
parse($0) |
|
|
|
} |
|
|
|
|
|
|
|
msg_command == "PRIVMSG" \ |
|
|
|
{ |
|
|
|
# Context = either channel or user nickname |
|
|
|
match(msg_prefix, /^[^!]+/) |
|
|
|
ctx = substr(msg_prefix, RSTART, RLENGTH) |
|
|
|
if (msg_param[0] ~ /^[#&!+]/) |
|
|
|
{ |
|
|
|
ctxquote = ctx ": " |
|
|
|
ctx = msg_param[0] |
|
|
|
} |
|
|
|
else |
|
|
|
ctxquote = "" |
|
|
|
|
|
|
|
|
|
|
|
if (substr(msg_param[1], 1, length(prefix)) == prefix) |
|
|
|
{ |
|
|
|
keyword = "eval" |
|
|
|
text = substr(msg_param[1], 1 + length(prefix)) |
|
|
|
if (match(text, "^" keyword "([^A-Za-z0-9].*|$)")) |
|
|
|
process_request(substr(text, 1 + length(keyword))) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
{ |
|
|
|
fflush("") |
|
|
|
} |
|
|
|
|
|
|
|
function pmrespond (text) |
|
|
|
{ |
|
|
|
print "PRIVMSG " ctx " :" ctxquote text |
|
|
|
} |
|
|
|
|
|
|
|
function process_request (input, res, x) |
|
|
|
{ |
|
|
|
delete funs |
|
|
|
delete accumulator |
|
|
|
delete n_args |
|
|
|
|
|
|
|
res = "" |
|
|
|
fun_top = 0 |
|
|
|
funs[0] = "" |
|
|
|
accumulator[0] = 0 |
|
|
|
n_args[0] = 0 |
|
|
|
|
|
|
|
if (match(input, "^[ \t]*")) |
|
|
|
input = substr(input, RLENGTH + 1) |
|
|
|
if (input == "") |
|
|
|
res = "expression missing" |
|
|
|
|
|
|
|
while (res == "" && input != "") { |
|
|
|
if (match(input, "^-?[0-9]+\\.?[0-9]*")) { |
|
|
|
x = substr(input, RSTART, RLENGTH) |
|
|
|
input = substr(input, RLENGTH + 1) |
|
|
|
|
|
|
|
match(input, "^ *") |
|
|
|
input = substr(input, RLENGTH + 1) |
|
|
|
|
|
|
|
res = process_argument(x) |
|
|
|
} else if (match(input, "^[(]([^ ()]+)")) { |
|
|
|
x = substr(input, RSTART + 1, RLENGTH - 1) |
|
|
|
input = substr(input, RLENGTH + 1) |
|
|
|
|
|
|
|
match(input, "^ *") |
|
|
|
input = substr(input, RLENGTH + 1) |
|
|
|
|
|
|
|
if (!(x in min_args)) { |
|
|
|
res = "undefined function '" x "'" |
|
|
|
} else { |
|
|
|
fun_top++ |
|
|
|
funs[fun_top] = x |
|
|
|
accumulator[fun_top] = 636363 |
|
|
|
n_args[fun_top] = 0 |
|
|
|
} |
|
|
|
} else if (match(input, "^[)] *")) { |
|
|
|
input = substr(input, RLENGTH + 1) |
|
|
|
res = process_end() |
|
|
|
} else |
|
|
|
res = "invalid input at '" substr(input, 1, 10) "...'" |
|
|
|
} |
|
|
|
|
|
|
|
if (res == "") { |
|
|
|
if (fun_top != 0) |
|
|
|
res = "unclosed '" funs[fun_top] "'" |
|
|
|
else if (n_args[0] != 1) |
|
|
|
res = "internal error, expected one result" \ |
|
|
|
", got " n_args[0] " instead" |
|
|
|
} |
|
|
|
|
|
|
|
if (res == "") |
|
|
|
pmrespond(accumulator[0]) |
|
|
|
else |
|
|
|
pmrespond(res) |
|
|
|
} |
|
|
|
|
|
|
|
function process_argument (arg) |
|
|
|
{ |
|
|
|
if (fun_top == 0) { |
|
|
|
if (n_args[0]++ != 0) |
|
|
|
return "too many results, I only expect one" |
|
|
|
|
|
|
|
accumulator[0] = arg |
|
|
|
return "" |
|
|
|
} |
|
|
|
|
|
|
|
fun = funs[fun_top] |
|
|
|
if (fun in max_args && max_args[fun] <= n_args[fun_top]) |
|
|
|
return "too many operands for " fun |
|
|
|
|
|
|
|
if (fun == "int") { |
|
|
|
accumulator[fun_top] = int(arg) |
|
|
|
} else if (fun == "+") { |
|
|
|
if (n_args[fun_top] == 0) |
|
|
|
accumulator[fun_top] = arg |
|
|
|
else |
|
|
|
accumulator[fun_top] += arg |
|
|
|
} else if (fun == "-") { |
|
|
|
if (n_args[fun_top] == 0) |
|
|
|
accumulator[fun_top] = arg |
|
|
|
else |
|
|
|
accumulator[fun_top] -= arg |
|
|
|
} else if (fun == "*") { |
|
|
|
if (n_args[fun_top] == 0) |
|
|
|
accumulator[fun_top] = arg |
|
|
|
else |
|
|
|
accumulator[fun_top] *= arg |
|
|
|
} else if (fun == "/") { |
|
|
|
if (n_args[fun_top] == 0) |
|
|
|
accumulator[fun_top] = arg |
|
|
|
else if (arg == 0) |
|
|
|
return "division by zero" |
|
|
|
else |
|
|
|
accumulator[fun_top] /= arg |
|
|
|
} else if (fun == "%") { |
|
|
|
if (n_args[fun_top] == 0) |
|
|
|
accumulator[fun_top] = arg |
|
|
|
else if (arg == 0) |
|
|
|
return "division by zero" |
|
|
|
else |
|
|
|
accumulator[fun_top] %= arg |
|
|
|
} else if (fun == "^" || fun == "**" || fun == "exp") { |
|
|
|
if (n_args[fun_top] == 0) |
|
|
|
accumulator[fun_top] = arg |
|
|
|
else |
|
|
|
accumulator[fun_top] ^= arg |
|
|
|
} else if (fun == "sin") { |
|
|
|
accumulator[fun_top] = sin(arg) |
|
|
|
} else if (fun == "cos") { |
|
|
|
accumulator[fun_top] = cos(arg) |
|
|
|
} else if (fun == "atan2") { |
|
|
|
if (n_args[fun_top] == 0) |
|
|
|
accumulator[fun_top] = arg |
|
|
|
else |
|
|
|
accumulator[fun_top] = atan2(accumulator[fun_top], arg) |
|
|
|
} else if (fun == "log") { |
|
|
|
accumulator[fun_top] = log(arg) |
|
|
|
} else if (fun == "rand") { |
|
|
|
# Just for completeness, execution never gets here |
|
|
|
} else if (fun == "sqrt") { |
|
|
|
accumulator[fun_top] = sqrt(arg) |
|
|
|
} else if (fun == "min") { |
|
|
|
if (n_args[fun_top] == 0) |
|
|
|
accumulator[fun_top] = arg |
|
|
|
else if (accumulator[fun_top] > arg) |
|
|
|
accumulator[fun_top] = arg |
|
|
|
} else if (fun == "max") { |
|
|
|
if (n_args[fun_top] == 0) |
|
|
|
accumulator[fun_top] = arg |
|
|
|
else if (accumulator[fun_top] < arg) |
|
|
|
accumulator[fun_top] = arg |
|
|
|
} else |
|
|
|
return "internal error, unhandled operands for " fun |
|
|
|
|
|
|
|
n_args[fun_top]++ |
|
|
|
return "" |
|
|
|
} |
|
|
|
|
|
|
|
function process_end () |
|
|
|
{ |
|
|
|
if (fun_top <= 0) |
|
|
|
return "extraneous ')'" |
|
|
|
|
|
|
|
fun = funs[fun_top] |
|
|
|
if (!(fun in min_args)) |
|
|
|
return "internal error, unhandled ')' for '" fun "'" |
|
|
|
if (min_args[fun] > n_args[fun_top]) |
|
|
|
return "not enough operands for '" fun "'" |
|
|
|
|
|
|
|
# There's no 'init' function to do it in |
|
|
|
if (fun == "rand") |
|
|
|
accumulator[fun_top] = rand() |
|
|
|
else if (fun == "pi") |
|
|
|
accumulator[fun_top] = 3.141592653589793 |
|
|
|
else if (fun == "e") |
|
|
|
accumulator[fun_top] = 2.718281828459045 |
|
|
|
|
|
|
|
return process_argument(accumulator[fun_top--]) |
|
|
|
} |
|
|
|
|
|
|
|
function get_config (key) |
|
|
|
{ |
|
|
|
print "ZYKLONB get_config :" key |
|
|
|
fflush("") |
|
|
|
|
|
|
|
getline |
|
|
|
parse($0) |
|
|
|
return msg_param[0] |
|
|
|
} |
|
|
|
|
|
|
|
function parse (line, s, n, id, token) |
|
|
|
{ |
|
|
|
s = 1 |
|
|
|
id = 0 |
|
|
|
|
|
|
|
# NAWK only uses the first character of RS |
|
|
|
if (line ~ /^\n/) |
|
|
|
line = substr(line, 2) |
|
|
|
|
|
|
|
msg_prefix = "" |
|
|
|
msg_command = "" |
|
|
|
delete msg_param |
|
|
|
|
|
|
|
n = match(substr(line, s), / |$/) |
|
|
|
while (n) |
|
|
|
{ |
|
|
|
token = substr(line, s, n - 1) |
|
|
|
if (token ~ /^:/) |
|
|
|
{ |
|
|
|
if (s == 1) |
|
|
|
msg_prefix = substr(token, 2) |
|
|
|
else |
|
|
|
{ |
|
|
|
msg_param[id] = substr(line, s + 1) |
|
|
|
break |
|
|
|
} |
|
|
|
} |
|
|
|
else if (!msg_command) |
|
|
|
msg_command = toupper(token) |
|
|
|
else |
|
|
|
msg_param[id++] = token |
|
|
|
|
|
|
|
s = s + n |
|
|
|
n = index(substr(line, s), " ") |
|
|
|
|
|
|
|
if (!n) |
|
|
|
{ |
|
|
|
n = length(substr(line, s)) + 1 |
|
|
|
if (n == 1) |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|