313 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			Awk
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			313 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			Awk
		
	
	
		
			Executable File
		
	
	
	
	
#!/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] ~ /^[#&!+]/)
 | 
						|
	{
 | 
						|
		ctx_quote = ctx ": "
 | 
						|
		ctx = msg_param[0]
 | 
						|
	}
 | 
						|
	else
 | 
						|
		ctx_quote = ""
 | 
						|
 | 
						|
 | 
						|
	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 " :" ctx_quote 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;
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 |