Experimental IRC client, daemon and bot
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

313 lines
6.3KB

  1. #!/usr/bin/awk -f
  2. #
  3. # ZyklonB eval plugin, LISP-like expression evaluator
  4. #
  5. # Copyright 2013, 2014 Přemysl Janouch. All rights reserved.
  6. # See the file LICENSE for licensing information.
  7. #
  8. BEGIN \
  9. {
  10. RS = "\r"
  11. ORS = "\r\n"
  12. IGNORECASE = 1
  13. srand()
  14. prefix = get_config("prefix")
  15. print "ZYKLONB register"
  16. fflush("")
  17. # All functions have to be in this particular array
  18. min_args["int"] = 1
  19. min_args["+"] = 1
  20. min_args["-"] = 1
  21. min_args["*"] = 1
  22. min_args["/"] = 1
  23. min_args["%"] = 1
  24. min_args["^"] = 1
  25. min_args["**"] = 1
  26. min_args["exp"] = 1
  27. min_args["sin"] = 1
  28. min_args["cos"] = 1
  29. min_args["atan2"] = 2
  30. min_args["log"] = 1
  31. min_args["rand"] = 0
  32. min_args["sqrt"] = 1
  33. min_args["pi"] = 0
  34. min_args["e"] = 0
  35. min_args["min"] = 1
  36. min_args["max"] = 1
  37. # Whereas here their presence is only optional
  38. max_args["int"] = 1
  39. max_args["sin"] = 1
  40. max_args["cos"] = 1
  41. max_args["atan2"] = 2
  42. max_args["log"] = 1
  43. max_args["rand"] = 0
  44. max_args["sqrt"] = 1
  45. max_args["pi"] = 0
  46. max_args["e"] = 0
  47. }
  48. {
  49. parse($0)
  50. }
  51. msg_command == "PRIVMSG" \
  52. {
  53. # Context = either channel or user nickname
  54. match(msg_prefix, /^[^!]+/)
  55. ctx = substr(msg_prefix, RSTART, RLENGTH)
  56. if (msg_param[0] ~ /^[#&!+]/)
  57. {
  58. ctxquote = ctx ": "
  59. ctx = msg_param[0]
  60. }
  61. else
  62. ctxquote = ""
  63. if (substr(msg_param[1], 1, length(prefix)) == prefix)
  64. {
  65. keyword = "eval"
  66. text = substr(msg_param[1], 1 + length(prefix))
  67. if (match(text, "^" keyword "([^A-Za-z0-9].*|$)"))
  68. process_request(substr(text, 1 + length(keyword)))
  69. }
  70. }
  71. {
  72. fflush("")
  73. }
  74. function pmrespond (text)
  75. {
  76. print "PRIVMSG " ctx " :" ctxquote text
  77. }
  78. function process_request (input, res, x)
  79. {
  80. delete funs
  81. delete accumulator
  82. delete n_args
  83. res = ""
  84. fun_top = 0
  85. funs[0] = ""
  86. accumulator[0] = 0
  87. n_args[0] = 0
  88. if (match(input, "^[ \t]*"))
  89. input = substr(input, RLENGTH + 1)
  90. if (input == "")
  91. res = "expression missing"
  92. while (res == "" && input != "") {
  93. if (match(input, "^-?[0-9]+\\.?[0-9]*")) {
  94. x = substr(input, RSTART, RLENGTH)
  95. input = substr(input, RLENGTH + 1)
  96. match(input, "^ *")
  97. input = substr(input, RLENGTH + 1)
  98. res = process_argument(x)
  99. } else if (match(input, "^[(]([^ ()]+)")) {
  100. x = substr(input, RSTART + 1, RLENGTH - 1)
  101. input = substr(input, RLENGTH + 1)
  102. match(input, "^ *")
  103. input = substr(input, RLENGTH + 1)
  104. if (!(x in min_args)) {
  105. res = "undefined function '" x "'"
  106. } else {
  107. fun_top++
  108. funs[fun_top] = x
  109. accumulator[fun_top] = 636363
  110. n_args[fun_top] = 0
  111. }
  112. } else if (match(input, "^[)] *")) {
  113. input = substr(input, RLENGTH + 1)
  114. res = process_end()
  115. } else
  116. res = "invalid input at '" substr(input, 1, 10) "...'"
  117. }
  118. if (res == "") {
  119. if (fun_top != 0)
  120. res = "unclosed '" funs[fun_top] "'"
  121. else if (n_args[0] != 1)
  122. res = "internal error, expected one result" \
  123. ", got " n_args[0] " instead"
  124. }
  125. if (res == "")
  126. pmrespond(accumulator[0])
  127. else
  128. pmrespond(res)
  129. }
  130. function process_argument (arg)
  131. {
  132. if (fun_top == 0) {
  133. if (n_args[0]++ != 0)
  134. return "too many results, I only expect one"
  135. accumulator[0] = arg
  136. return ""
  137. }
  138. fun = funs[fun_top]
  139. if (fun in max_args && max_args[fun] <= n_args[fun_top])
  140. return "too many operands for " fun
  141. if (fun == "int") {
  142. accumulator[fun_top] = int(arg)
  143. } else if (fun == "+") {
  144. if (n_args[fun_top] == 0)
  145. accumulator[fun_top] = arg
  146. else
  147. accumulator[fun_top] += arg
  148. } else if (fun == "-") {
  149. if (n_args[fun_top] == 0)
  150. accumulator[fun_top] = arg
  151. else
  152. accumulator[fun_top] -= arg
  153. } else if (fun == "*") {
  154. if (n_args[fun_top] == 0)
  155. accumulator[fun_top] = arg
  156. else
  157. accumulator[fun_top] *= arg
  158. } else if (fun == "/") {
  159. if (n_args[fun_top] == 0)
  160. accumulator[fun_top] = arg
  161. else if (arg == 0)
  162. return "division by zero"
  163. else
  164. accumulator[fun_top] /= arg
  165. } else if (fun == "%") {
  166. if (n_args[fun_top] == 0)
  167. accumulator[fun_top] = arg
  168. else if (arg == 0)
  169. return "division by zero"
  170. else
  171. accumulator[fun_top] %= arg
  172. } else if (fun == "^" || fun == "**" || fun == "exp") {
  173. if (n_args[fun_top] == 0)
  174. accumulator[fun_top] = arg
  175. else
  176. accumulator[fun_top] ^= arg
  177. } else if (fun == "sin") {
  178. accumulator[fun_top] = sin(arg)
  179. } else if (fun == "cos") {
  180. accumulator[fun_top] = cos(arg)
  181. } else if (fun == "atan2") {
  182. if (n_args[fun_top] == 0)
  183. accumulator[fun_top] = arg
  184. else
  185. accumulator[fun_top] = atan2(accumulator[fun_top], arg)
  186. } else if (fun == "log") {
  187. accumulator[fun_top] = log(arg)
  188. } else if (fun == "rand") {
  189. # Just for completeness, execution never gets here
  190. } else if (fun == "sqrt") {
  191. accumulator[fun_top] = sqrt(arg)
  192. } else if (fun == "min") {
  193. if (n_args[fun_top] == 0)
  194. accumulator[fun_top] = arg
  195. else if (accumulator[fun_top] > arg)
  196. accumulator[fun_top] = arg
  197. } else if (fun == "max") {
  198. if (n_args[fun_top] == 0)
  199. accumulator[fun_top] = arg
  200. else if (accumulator[fun_top] < arg)
  201. accumulator[fun_top] = arg
  202. } else
  203. return "internal error, unhandled operands for " fun
  204. n_args[fun_top]++
  205. return ""
  206. }
  207. function process_end ()
  208. {
  209. if (fun_top <= 0)
  210. return "extraneous ')'"
  211. fun = funs[fun_top]
  212. if (!(fun in min_args))
  213. return "internal error, unhandled ')' for '" fun "'"
  214. if (min_args[fun] > n_args[fun_top])
  215. return "not enough operands for '" fun "'"
  216. # There's no 'init' function to do it in
  217. if (fun == "rand")
  218. accumulator[fun_top] = rand()
  219. else if (fun == "pi")
  220. accumulator[fun_top] = 3.141592653589793
  221. else if (fun == "e")
  222. accumulator[fun_top] = 2.718281828459045
  223. return process_argument(accumulator[fun_top--])
  224. }
  225. function get_config (key)
  226. {
  227. print "ZYKLONB get_config :" key
  228. fflush("")
  229. getline
  230. parse($0)
  231. return msg_param[0]
  232. }
  233. function parse (line, s, n, id, token)
  234. {
  235. s = 1
  236. id = 0
  237. # NAWK only uses the first character of RS
  238. if (line ~ /^\n/)
  239. line = substr(line, 2)
  240. msg_prefix = ""
  241. msg_command = ""
  242. delete msg_param
  243. n = match(substr(line, s), / |$/)
  244. while (n)
  245. {
  246. token = substr(line, s, n - 1)
  247. if (token ~ /^:/)
  248. {
  249. if (s == 1)
  250. msg_prefix = substr(token, 2)
  251. else
  252. {
  253. msg_param[id] = substr(line, s + 1)
  254. break
  255. }
  256. }
  257. else if (!msg_command)
  258. msg_command = toupper(token)
  259. else
  260. msg_param[id++] = token
  261. s = s + n
  262. n = index(substr(line, s), " ")
  263. if (!n)
  264. {
  265. n = length(substr(line, s)) + 1
  266. if (n == 1)
  267. break;
  268. }
  269. }
  270. }