wao 0.25.3 → 0.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,2563 +0,0 @@
1
-
2
- local function load_json()
3
- local json = {_version = "0.2.0"}
4
-
5
- -------------------------------------------------------------------------------
6
- -- Encode
7
- -------------------------------------------------------------------------------
8
-
9
- local encode
10
-
11
- local escape_char_map = {
12
- ["\\"] = "\\",
13
- ["\""] = "\"",
14
- ["\b"] = "b",
15
- ["\f"] = "f",
16
- ["\n"] = "n",
17
- ["\r"] = "r",
18
- ["\t"] = "t"
19
- }
20
-
21
- local escape_char_map_inv = {["/"] = "/"}
22
- for k, v in pairs(escape_char_map) do escape_char_map_inv[v] = k end
23
-
24
- local function escape_char(c)
25
- return "\\" .. (escape_char_map[c] or string.format("u%04x", c:byte()))
26
- end
27
-
28
- local function encode_nil(val) return "null" end
29
-
30
- local function encode_table(val, stack)
31
- local res = {}
32
- stack = stack or {}
33
-
34
- -- Circular reference?
35
- if stack[val] then error("circular reference") end
36
-
37
- stack[val] = true
38
-
39
- if rawget(val, 1) ~= nil or next(val) == nil then
40
- -- Treat as array -- check keys are valid and it is not sparse
41
- local n = 0
42
- for k in pairs(val) do
43
- if type(k) ~= "number" then
44
- error("invalid table: mixed or invalid key types")
45
- end
46
- n = n + 1
47
- end
48
- if n ~= #val then error("invalid table: sparse array") end
49
- -- Encode
50
- for i = 1, #val do res[i] = encode(val[i], stack) end
51
- stack[val] = nil
52
- return "[" .. table.concat(res, ",") .. "]"
53
- else
54
- -- Treat as an object
55
- local i = 1
56
- for k, v in pairs(val) do
57
- if type(k) ~= "string" then
58
- error("invalid table: mixed or invalid key types")
59
- end
60
- if type(v) ~= "function" then
61
- res[i] = encode(k, stack) .. ":" .. encode(v, stack)
62
- i = i + 1
63
- end
64
- end
65
- stack[val] = nil
66
- return "{" .. table.concat(res, ",") .. "}"
67
- end
68
- end
69
-
70
- local function encode_string(val)
71
- return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"'
72
- end
73
-
74
- local function encode_number(val)
75
- -- Check for NaN, -inf and inf
76
- if val ~= val or val <= -math.huge or val >= math.huge then
77
- error("unexpected number value '" .. tostring(val) .. "'")
78
- end
79
- return string.format("%.14g", val)
80
- end
81
-
82
- local type_func_map = {
83
- ["nil"] = encode_nil,
84
- ["table"] = encode_table,
85
- ["string"] = encode_string,
86
- ["number"] = encode_number,
87
- ["boolean"] = tostring
88
- }
89
-
90
- encode = function(val, stack)
91
- local t = type(val)
92
- local f = type_func_map[t]
93
- if f then return f(val, stack) end
94
- error("unexpected type '" .. t .. "'")
95
- end
96
-
97
- function json.encode(val) return (encode(val)) end
98
-
99
- -------------------------------------------------------------------------------
100
- -- Decode
101
- -------------------------------------------------------------------------------
102
-
103
- local parse
104
-
105
- local function create_set(...)
106
- local res = {}
107
- for i = 1, select("#", ...) do res[select(i, ...)] = true end
108
- return res
109
- end
110
-
111
- local space_chars = create_set(" ", "\t", "\r", "\n")
112
- local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",")
113
- local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u")
114
- local literals = create_set("true", "false", "null")
115
-
116
- local literal_map = {["true"] = true, ["false"] = false, ["null"] = nil}
117
-
118
- local function next_char(str, idx, set, negate)
119
- for i = idx, #str do if set[str:sub(i, i)] ~= negate then return i end end
120
- return #str + 1
121
- end
122
-
123
- local function decode_error(str, idx, msg)
124
- local line_count = 1
125
- local col_count = 1
126
- for i = 1, idx - 1 do
127
- col_count = col_count + 1
128
- if str:sub(i, i) == "\n" then
129
- line_count = line_count + 1
130
- col_count = 1
131
- end
132
- end
133
- error(string.format("%s at line %d col %d", msg, line_count, col_count))
134
- end
135
-
136
- local function codepoint_to_utf8(n)
137
- local f = math.floor
138
- if n <= 0x7f then
139
- return string.char(n)
140
- elseif n <= 0x7ff then
141
- return string.char(f(n / 64) + 192, n % 64 + 128)
142
- elseif n <= 0xffff then
143
- return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128,
144
- n % 64 + 128)
145
- elseif n <= 0x10ffff then
146
- return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128,
147
- f(n % 4096 / 64) + 128, n % 64 + 128)
148
- end
149
- error(string.format("invalid unicode codepoint '%x'", n))
150
- end
151
-
152
- local function parse_unicode_escape(s)
153
- local n1 = tonumber(s:sub(1, 4), 16)
154
- local n2 = tonumber(s:sub(7, 10), 16)
155
- if n2 then
156
- return
157
- codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000)
158
- else
159
- return codepoint_to_utf8(n1)
160
- end
161
- end
162
-
163
- local function parse_string(str, i)
164
- local res = {}
165
- local j = i + 1
166
- local k = j
167
-
168
- while j <= #str do
169
- local x = str:byte(j)
170
-
171
- if x < 32 then
172
- decode_error(str, j, "control character in string")
173
- elseif x == 92 then -- `\`: Escape
174
- res[#res + 1] = str:sub(k, j - 1)
175
- j = j + 1
176
- local c = str:sub(j, j)
177
- if c == "u" then
178
- local hex = str:match("^[dD][89aAbB]%x%x\\u%x%x%x%x", j + 1) or
179
- str:match("^%x%x%x%x", j + 1) or
180
- decode_error(str, j - 1,
181
- "invalid unicode escape in string")
182
- res[#res + 1] = parse_unicode_escape(hex)
183
- j = j + #hex
184
- else
185
- if not escape_chars[c] then
186
- decode_error(str, j - 1,
187
- "invalid escape char '" .. c .. "' in string")
188
- end
189
- res[#res + 1] = escape_char_map_inv[c]
190
- end
191
- k = j + 1
192
- elseif x == 34 then -- `"`: End of string
193
- res[#res + 1] = str:sub(k, j - 1)
194
- return table.concat(res), j + 1
195
- end
196
- j = j + 1
197
- end
198
-
199
- decode_error(str, i, "expected closing quote for string")
200
- end
201
-
202
- local function parse_number(str, i)
203
- local x = next_char(str, i, delim_chars)
204
- local s = str:sub(i, x - 1)
205
- local n = tonumber(s)
206
- if not n then decode_error(str, i, "invalid number '" .. s .. "'") end
207
- return n, x
208
- end
209
-
210
- local function parse_literal(str, i)
211
- local x = next_char(str, i, delim_chars)
212
- local word = str:sub(i, x - 1)
213
- if not literals[word] then
214
- decode_error(str, i, "invalid literal '" .. word .. "'")
215
- end
216
- return literal_map[word], x
217
- end
218
-
219
- local function parse_array(str, i)
220
- local res = {}
221
- local n = 1
222
- i = i + 1
223
- while true do
224
- local x
225
- i = next_char(str, i, space_chars, true)
226
- if str:sub(i, i) == "]" then
227
- i = i + 1
228
- break
229
- end
230
- x, i = parse(str, i)
231
- res[n] = x
232
- n = n + 1
233
- i = next_char(str, i, space_chars, true)
234
- local chr = str:sub(i, i)
235
- i = i + 1
236
- if chr == "]" then break end
237
- if chr ~= "," then decode_error(str, i, "expected ']' or ','") end
238
- end
239
- return res, i
240
- end
241
-
242
- local function parse_object(str, i)
243
- local res = {}
244
- i = i + 1
245
- while true do
246
- local key, val
247
- i = next_char(str, i, space_chars, true)
248
- if str:sub(i, i) == "}" then
249
- i = i + 1
250
- break
251
- end
252
- if str:sub(i, i) ~= '"' then
253
- decode_error(str, i, "expected string for key")
254
- end
255
- key, i = parse(str, i)
256
- i = next_char(str, i, space_chars, true)
257
- if str:sub(i, i) ~= ":" then
258
- decode_error(str, i, "expected ':' after key")
259
- end
260
- i = next_char(str, i + 1, space_chars, true)
261
- val, i = parse(str, i)
262
- res[key] = val
263
- i = next_char(str, i, space_chars, true)
264
- local chr = str:sub(i, i)
265
- i = i + 1
266
- if chr == "}" then break end
267
- if chr ~= "," then decode_error(str, i, "expected '}' or ','") end
268
- end
269
- return res, i
270
- end
271
-
272
- local char_func_map = {
273
- ['"'] = parse_string,
274
- ["0"] = parse_number,
275
- ["1"] = parse_number,
276
- ["2"] = parse_number,
277
- ["3"] = parse_number,
278
- ["4"] = parse_number,
279
- ["5"] = parse_number,
280
- ["6"] = parse_number,
281
- ["7"] = parse_number,
282
- ["8"] = parse_number,
283
- ["9"] = parse_number,
284
- ["-"] = parse_number,
285
- ["t"] = parse_literal,
286
- ["f"] = parse_literal,
287
- ["n"] = parse_literal,
288
- ["["] = parse_array,
289
- ["{"] = parse_object
290
- }
291
-
292
- parse = function(str, idx)
293
- local chr = str:sub(idx, idx)
294
- local f = char_func_map[chr]
295
- if f then return f(str, idx) end
296
- decode_error(str, idx, "unexpected character '" .. chr .. "'")
297
- end
298
-
299
- function json.decode(str)
300
- if type(str) ~= "string" then
301
- error("expected argument of type string, got " .. type(str))
302
- end
303
- local res, idx = parse(str, next_char(str, 1, space_chars, true))
304
- idx = next_char(str, idx, space_chars, true)
305
- if idx <= #str then decode_error(str, idx, "trailing garbage") end
306
- return res
307
- end
308
-
309
- return json
310
-
311
- end
312
- _G.package.loaded[".json"] = load_json()
313
- print("loaded json")
314
-
315
-
316
-
317
- local function load_stringify()
318
- --- The Stringify module provides utilities for formatting and displaying Lua tables in a more readable manner. Returns the stringify table.
319
- -- @module stringify
320
-
321
- --- The stringify table
322
- -- @table stringify
323
- -- @field _version The version number of the stringify module
324
- -- @field isSimpleArray The isSimpleArray function
325
- -- @field format The format function
326
- local stringify = { _version = "0.0.1" }
327
-
328
- -- ANSI color codes
329
- local colors = {
330
- red = "\27[31m",
331
- green = "\27[32m",
332
- blue = "\27[34m",
333
- reset = "\27[0m"
334
- }
335
-
336
- --- Checks if a table is a simple array (i.e., an array with consecutive numeric keys starting from 1).
337
- -- @function isSimpleArray
338
- -- @tparam {table} tbl The table to check
339
- -- @treturn {boolean} Whether the table is a simple array
340
- function stringify.isSimpleArray(tbl)
341
- local arrayIndex = 1
342
- for k, v in pairs(tbl) do
343
- if k ~= arrayIndex or (type(v) ~= "number" and type(v) ~= "string") then
344
- return false
345
- end
346
- arrayIndex = arrayIndex + 1
347
- end
348
- return true
349
- end
350
-
351
- --- Formats a table for display, handling circular references and formatting strings and tables recursively.
352
- -- @function format
353
- -- @tparam {table} tbl The table to format
354
- -- @tparam {number} indent The indentation level (default is 0)
355
- -- @tparam {table} visited A table to track visited tables and detect circular references (optional)
356
- -- @treturn {string} A string representation of the table
357
- function stringify.format(tbl, indent, visited)
358
- indent = indent or 0
359
- local toIndent = string.rep(" ", indent)
360
- local toIndentChild = string.rep(" ", indent + 2)
361
-
362
- local result = {}
363
- local isArray = true
364
- local arrayIndex = 1
365
-
366
- if stringify.isSimpleArray(tbl) then
367
- for _, v in ipairs(tbl) do
368
- if type(v) == "string" then
369
- v = colors.green .. '"' .. v .. '"' .. colors.reset
370
- else
371
- v = colors.blue .. tostring(v) .. colors.reset
372
- end
373
- table.insert(result, v)
374
- end
375
- return "{ " .. table.concat(result, ", ") .. " }"
376
- end
377
-
378
- for k, v in pairs(tbl) do
379
- if isArray then
380
- if k == arrayIndex then
381
- arrayIndex = arrayIndex + 1
382
- if type(v) == "table" then
383
- v = stringify.format(v, indent + 2)
384
- elseif type(v) == "string" then
385
- v = colors.green .. '"' .. v .. '"' .. colors.reset
386
- else
387
- v = colors.blue .. tostring(v) .. colors.reset
388
- end
389
- table.insert(result, toIndentChild .. v)
390
- else
391
- isArray = false
392
- result = {}
393
- end
394
- end
395
- if not isArray then
396
- if type(v) == "table" then
397
- visited = visited or {}
398
- if visited[v] then
399
- return "<circular reference>"
400
- end
401
- visited[v] = true
402
-
403
- v = stringify.format(v, indent + 2, visited)
404
- elseif type(v) == "string" then
405
- v = colors.green .. '"' .. v .. '"' .. colors.reset
406
- else
407
- v = colors.blue .. tostring(v) .. colors.reset
408
- end
409
- k = colors.red .. k .. colors.reset
410
- table.insert(result, toIndentChild .. k .. " = " .. v)
411
- end
412
- end
413
-
414
- local prefix = isArray and "{\n" or "{\n "
415
- local suffix = isArray and "\n" .. toIndent .. " }" or "\n" .. toIndent .. "}"
416
- local separator = isArray and ",\n" or ",\n "
417
- return prefix .. table.concat(result, separator) .. suffix
418
- end
419
-
420
- return stringify
421
-
422
- end
423
- _G.package.loaded[".stringify"] = load_stringify()
424
- print("loaded stringify")
425
-
426
-
427
-
428
- local function load_eval()
429
- --- The Eval module provides a handler for evaluating Lua expressions. Returns the eval function.
430
- -- @module eval
431
-
432
- local stringify = require(".stringify")
433
- local json = require('.json')
434
- --- The eval function.
435
- -- Handler for executing and evaluating Lua expressions.
436
- -- After execution, the result is stringified and placed in ao.outbox.Output.
437
- -- @function eval
438
- -- @tparam {table} ao The ao environment object
439
- -- @treturn {function} The handler function, which takes a message as an argument.
440
- -- @see stringify
441
- return function (ao)
442
- return function (req)
443
- local msg = req.body
444
- -- exec expression
445
- local expr = msg.body and msg.body.body or msg.data or ""
446
- local func, err = load("return " .. expr, 'aos', 't', _G)
447
- local output = ""
448
- local e = nil
449
- if err then
450
- func, err = load(expr, 'aos', 't', _G)
451
- end
452
- if func then
453
- output, e = func()
454
- else
455
- ao.outbox.Error = err
456
- return
457
- end
458
- if e then
459
- ao.outbox.Error = e
460
- return
461
- end
462
- if HandlerPrintLogs and output then
463
- table.insert(HandlerPrintLogs,
464
- type(output) == "table"
465
- and stringify.format(output)
466
- or tostring(output)
467
- )
468
- -- print(stringify.format(HandlerPrintLogs))
469
- -- else
470
- -- -- set result in outbox.Output (Left for backwards compatibility)
471
- -- ao.outbox.Output = {
472
- -- data = type(output) == "table"
473
- -- and stringify.format(output) or tostring(output),
474
- -- prompt = Prompt()
475
- -- }
476
- --
477
- end
478
- end
479
- end
480
-
481
- end
482
- _G.package.loaded[".eval"] = load_eval()
483
- print("loaded eval")
484
-
485
-
486
-
487
- local function load_utils()
488
- --- The Utils module provides a collection of utility functions for functional programming in Lua. It includes functions for array manipulation such as concatenation, mapping, reduction, filtering, and finding elements, as well as a property equality checker.
489
- -- @module utils
490
-
491
- --- The utils table
492
- -- @table utils
493
- -- @field _version The version number of the utils module
494
- -- @field matchesPattern The matchesPattern function
495
- -- @field matchesSpec The matchesSpec function
496
- -- @field curry The curry function
497
- -- @field concat The concat function
498
- -- @field reduce The reduce function
499
- -- @field map The map function
500
- -- @field filter The filter function
501
- -- @field find The find function
502
- -- @field propEq The propEq function
503
- -- @field reverse The reverse function
504
- -- @field compose The compose function
505
- -- @field prop The prop function
506
- -- @field includes The includes function
507
- -- @field keys The keys function
508
- -- @field values The values function
509
- local utils = { _version = "0.0.5" }
510
-
511
- --- Given a pattern, a value, and a message, returns whether there is a pattern match.
512
- -- @usage utils.matchesPattern(pattern, value, msg)
513
- -- @param pattern The pattern to match
514
- -- @param value The value to check for in the pattern
515
- -- @param msg The message to check for the pattern
516
- -- @treturn {boolean} Whether there is a pattern match
517
- function utils.matchesPattern(pattern, value, msg)
518
- -- If the key is not in the message, then it does not match
519
- if (not pattern) then
520
- return false
521
- end
522
- -- if the patternMatchSpec is a wildcard, then it always matches
523
- if pattern == '_' then
524
- return true
525
- end
526
- -- if the patternMatchSpec is a function, then it is executed on the tag value
527
- if type(pattern) == "function" then
528
- if pattern(value, msg) then
529
- return true
530
- else
531
- return false
532
- end
533
- end
534
- -- if the patternMatchSpec is a string, check it for special symbols (less `-` alone)
535
- -- and exact string match mode
536
- if (type(pattern) == 'string') then
537
- if string.match(pattern, "[%^%$%(%)%%%.%[%]%*%+%?]") then
538
- if string.match(value, pattern) then
539
- return true
540
- end
541
- else
542
- if value == pattern then
543
- return true
544
- end
545
- end
546
- end
547
-
548
- -- if the pattern is a table, recursively check if any of its sub-patterns match
549
- if type(pattern) == 'table' then
550
- for _, subPattern in pairs(pattern) do
551
- if utils.matchesPattern(subPattern, value, msg) then
552
- return true
553
- end
554
- end
555
- end
556
-
557
- return false
558
- end
559
-
560
- --- Given a message and a spec, returns whether there is a spec match.
561
- -- @usage utils.matchesSpec(msg, spec)
562
- -- @param msg The message to check for the spec
563
- -- @param spec The spec to check for in the message
564
- -- @treturn {boolean} Whether there is a spec match
565
- function utils.matchesSpec(msg, spec)
566
- if type(spec) == 'function' then
567
- return spec(msg)
568
- -- If the spec is a table, step through every key/value pair in the pattern and check if the msg matches
569
- -- Supported pattern types:
570
- -- - Exact string match
571
- -- - Lua gmatch string
572
- -- - '_' (wildcard: Message has tag, but can be any value)
573
- -- - Function execution on the tag, optionally using the msg as the second argument
574
- -- - Table of patterns, where ANY of the sub-patterns matching the tag will result in a match
575
- end
576
- if type(spec) == 'table' then
577
- for key, pattern in pairs(spec) do
578
- -- The key can either be in the top level of the 'msg' object
579
- -- or in the body table of the msg
580
- local msgValue = msg[key] or msg.body[key]
581
- if not msgValue then
582
- return false
583
- end
584
- local matchesMsgValue = utils.matchesPattern(pattern, msgValue, msg)
585
- if not matchesMsgValue then
586
- return false
587
- end
588
-
589
- end
590
- return true
591
- end
592
-
593
- if type(spec) == 'string' and msg.action and msg.action == spec then
594
- return true
595
- end
596
- if type(spec) == 'string' and msg.body.action and msg.body.action == spec then
597
- return true
598
- end
599
- return false
600
- end
601
-
602
- --- Given a table, returns whether it is an array.
603
- -- An 'array' is defined as a table with integer keys starting from 1 and
604
- -- having no gaps between the keys.
605
- -- @lfunction isArray
606
- -- @param table The table to check
607
- -- @treturn {boolean} Whether the table is an array
608
- local function isArray(table)
609
- if type(table) == "table" then
610
- local maxIndex = 0
611
- for k, v in pairs(table) do
612
- if type(k) ~= "number" or k < 1 or math.floor(k) ~= k then
613
- return false -- If there's a non-integer key, it's not an array
614
- end
615
- maxIndex = math.max(maxIndex, k)
616
- end
617
- -- If the highest numeric index is equal to the number of elements, it's an array
618
- return maxIndex == #table
619
- end
620
- return false
621
- end
622
-
623
- --- Curries a function.
624
- -- @tparam {function} fn The function to curry
625
- -- @tparam {number} arity The arity of the function
626
- -- @treturn {function} The curried function
627
- utils.curry = function (fn, arity)
628
- assert(type(fn) == "function", "function is required as first argument")
629
- arity = arity or debug.getinfo(fn, "u").nparams
630
- if arity < 2 then return fn end
631
-
632
- return function (...)
633
- local args = {...}
634
-
635
- if #args >= arity then
636
- return fn(table.unpack(args))
637
- else
638
- return utils.curry(function (...)
639
- return fn(table.unpack(args), ...)
640
- end, arity - #args)
641
- end
642
- end
643
- end
644
-
645
- --- Concat two Array Tables
646
- -- @function concat
647
- -- @usage utils.concat(a)(b)
648
- -- @usage utils.concat({1, 2})({3, 4}) --> {1, 2, 3, 4}
649
- -- @tparam {table<Array>} a The first array
650
- -- @tparam {table<Array>} b The second array
651
- -- @treturn {table<Array>} The concatenated array
652
- utils.concat = utils.curry(function (a, b)
653
- assert(type(a) == "table", "first argument should be a table that is an array")
654
- assert(type(b) == "table", "second argument should be a table that is an array")
655
- assert(isArray(a), "first argument should be a table")
656
- assert(isArray(b), "second argument should be a table")
657
-
658
- local result = {}
659
- for i = 1, #a do
660
- result[#result + 1] = a[i]
661
- end
662
- for i = 1, #b do
663
- result[#result + 1] = b[i]
664
- end
665
- return result
666
- end, 2)
667
-
668
- --- Applies a function to each element of a table, reducing it to a single value.
669
- -- @function utils.reduce
670
- -- @usage utils.reduce(fn)(initial)(t)
671
- -- @usage utils.reduce(function(acc, x) return acc + x end)(0)({1, 2, 3}) --> 6
672
- -- @tparam {function} fn The function to apply
673
- -- @param initial The initial value
674
- -- @tparam {table<Array>} t The table to reduce
675
- -- @return The reduced value
676
- utils.reduce = utils.curry(function (fn, initial, t)
677
- assert(type(fn) == "function", "first argument should be a function that accepts (result, value, key)")
678
- assert(type(t) == "table" and isArray(t), "third argument should be a table that is an array")
679
- local result = initial
680
- for k, v in pairs(t) do
681
- if result == nil then
682
- result = v
683
- else
684
- result = fn(result, v, k)
685
- end
686
- end
687
- return result
688
- end, 3)
689
-
690
- --- Applies a function to each element of an array table, mapping it to a new value.
691
- -- @function utils.map
692
- -- @usage utils.map(fn)(t)
693
- -- @usage utils.map(function(x) return x * 2 end)({1, 2, 3}) --> {2, 4, 6}
694
- -- @tparam {function} fn The function to apply to each element
695
- -- @tparam {table<Array>} data The table to map over
696
- -- @treturn {table<Array>} The mapped table
697
- utils.map = utils.curry(function (fn, data)
698
- assert(type(fn) == "function", "first argument should be a unary function")
699
- assert(type(data) == "table" and isArray(data), "second argument should be an Array")
700
-
701
- local function map (result, v, k)
702
- result[k] = fn(v, k)
703
- return result
704
- end
705
-
706
- return utils.reduce(map, {}, data)
707
- end, 2)
708
-
709
- --- Filters an array table based on a predicate function.
710
- -- @function utils.filter
711
- -- @usage utils.filter(fn)(t)
712
- -- @usage utils.filter(function(x) return x > 1 end)({1, 2, 3}) --> {2,3}
713
- -- @tparam {function} fn The predicate function to determine if an element should be included.
714
- -- @tparam {table<Array>} data The array to filter
715
- -- @treturn {table<Array>} The filtered table
716
- utils.filter = utils.curry(function (fn, data)
717
- assert(type(fn) == "function", "first argument should be a unary function")
718
- assert(type(data) == "table" and isArray(data), "second argument should be an Array")
719
-
720
- local function filter (result, v, _k)
721
- if fn(v) then
722
- table.insert(result, v)
723
- end
724
- return result
725
- end
726
-
727
- return utils.reduce(filter,{}, data)
728
- end, 2)
729
-
730
- --- Finds the first element in an array table that satisfies a predicate function.
731
- -- @function utils.find
732
- -- @usage utils.find(fn)(t)
733
- -- @usage utils.find(function(x) return x > 1 end)({1, 2, 3}) --> 2
734
- -- @tparam {function} fn The predicate function to determine if an element should be included.
735
- -- @tparam {table<Array>} t The array table to search
736
- -- @treturn The first element that satisfies the predicate function
737
- utils.find = utils.curry(function (fn, t)
738
- assert(type(fn) == "function", "first argument should be a unary function")
739
- assert(type(t) == "table", "second argument should be a table that is an array")
740
- for _, v in pairs(t) do
741
- if fn(v) then
742
- return v
743
- end
744
- end
745
- end, 2)
746
-
747
- --- Checks if a property of an object is equal to a value.
748
- -- @function utils.propEq
749
- -- @usage utils.propEq(propName)(value)(object)
750
- -- @usage utils.propEq("name")("Lua")({name = "Lua"}) --> true
751
- -- @tparam {string} propName The property name to check
752
- -- @tparam {string} value The value to check against
753
- -- @tparam {table} object The object to check
754
- -- @treturn {boolean} Whether the property is equal to the value
755
- utils.propEq = utils.curry(function (propName, value, object)
756
- assert(type(propName) == "string", "first argument should be a string")
757
- assert(type(value) == "string", "second argument should be a string")
758
- assert(type(object) == "table", "third argument should be a table<object>")
759
-
760
- return object[propName] == value
761
- end, 3)
762
-
763
- --- Reverses an array table.
764
- -- @function utils.reverse
765
- -- @usage utils.reverse(data)
766
- -- @usage utils.reverse({1, 2, 3}) --> {3, 2, 1}
767
- -- @tparam {table<Array>} data The array table to reverse
768
- -- @treturn {table<Array>} The reversed array table
769
- utils.reverse = function (data)
770
- assert(type(data) == "table", "argument needs to be a table that is an array")
771
- return utils.reduce(
772
- function (result, v, i)
773
- result[#data - i + 1] = v
774
- return result
775
- end,
776
- {},
777
- data
778
- )
779
- end
780
-
781
- --- Composes a series of functions into a single function.
782
- -- @function utils.compose
783
- -- @usage utils.compose(fn1)(fn2)(fn3)(v)
784
- -- @usage utils.compose(function(x) return x + 1 end)(function(x) return x * 2 end)(3) --> 7
785
- -- @tparam {function} ... The functions to compose
786
- -- @treturn {function} The composed function
787
- utils.compose = utils.curry(function (...)
788
- local mutations = utils.reverse({...})
789
-
790
- return function (v)
791
- local result = v
792
- for _, fn in pairs(mutations) do
793
- assert(type(fn) == "function", "each argument needs to be a function")
794
- result = fn(result)
795
- end
796
- return result
797
- end
798
- end, 2)
799
-
800
- --- Returns the value of a property of an object.
801
- -- @function utils.prop
802
- -- @usage utils.prop(propName)(object)
803
- -- @usage utils.prop("name")({name = "Lua"}) --> "Lua"
804
- -- @tparam {string} propName The property name to get
805
- -- @tparam {table} object The object to get the property from
806
- -- @treturn The value of the property
807
- utils.prop = utils.curry(function (propName, object)
808
- return object[propName]
809
- end, 2)
810
-
811
- --- Checks if an array table includes a value.
812
- -- @function utils.includes
813
- -- @usage utils.includes(val)(t)
814
- -- @usage utils.includes(2)({1, 2, 3}) --> true
815
- -- @param val The value to check for
816
- -- @tparam {table<Array>} t The array table to check
817
- -- @treturn {boolean} Whether the value is in the array table
818
- utils.includes = utils.curry(function (val, t)
819
- assert(type(t) == "table", "argument needs to be a table")
820
- assert(isArray(t), "argument should be a table that is an array")
821
- return utils.find(function (v) return v == val end, t) ~= nil
822
- end, 2)
823
-
824
- --- Returns the keys of a table.
825
- -- @usage utils.keys(t)
826
- -- @usage utils.keys({name = "Lua", age = 25}) --> {"name", "age"}
827
- -- @tparam {table} t The table to get the keys from
828
- -- @treturn {table<Array>} The keys of the table
829
- utils.keys = function (t)
830
- assert(type(t) == "table", "argument needs to be a table")
831
- local keys = {}
832
- for key in pairs(t) do
833
- table.insert(keys, key)
834
- end
835
- return keys
836
- end
837
-
838
- --- Returns the values of a table.
839
- -- @usage utils.values(t)
840
- -- @usage utils.values({name = "Lua", age = 25}) --> {"Lua", 25}
841
- -- @tparam {table} t The table to get the values from
842
- -- @treturn {table<Array>} The values of the table
843
- utils.values = function (t)
844
- assert(type(t) == "table", "argument needs to be a table")
845
- local values = {}
846
- for _, value in pairs(t) do
847
- table.insert(values, value)
848
- end
849
- return values
850
- end
851
-
852
- --- Convert a message's tags to a table of key-value pairs
853
- -- @function Tab
854
- -- @tparam {table} msg The message containing tags
855
- -- @treturn {table} A table with tag names as keys and their values
856
- function utils.Tab(msg)
857
- local inputs = {}
858
- for _, o in ipairs(msg.Tags) do
859
- if not inputs[o.name] then
860
- inputs[o.name] = o.value
861
- end
862
- end
863
- return inputs
864
- end
865
-
866
-
867
- return utils
868
-
869
- end
870
- _G.package.loaded[".utils"] = load_utils()
871
- print("loaded utils")
872
-
873
-
874
-
875
- local function load_handlers_utils()
876
- --- The Handler Utils module is a lightweight Lua utility library designed to provide common functionalities for handling and processing messages within the AOS computer system. It offers a set of functions to check message attributes and send replies, simplifying the development of more complex scripts and modules. This document will guide you through the module's functionalities, installation, and usage. Returns the _utils table.
877
- -- @module handlers-utils
878
-
879
- --- The _utils table
880
- -- @table _utils
881
- -- @field _version The version number of the _utils module
882
- -- @field hasMatchingTag The hasMatchingTag function
883
- -- @field hasMatchingTagOf The hasMatchingTagOf function
884
- -- @field hasMatchingData The hasMatchingData function
885
- -- @field reply The reply function
886
- -- @field continue The continue function
887
- local _utils = { _version = "0.0.2" }
888
-
889
- local _ = require('.utils')
890
-
891
- --- Checks if a given message has a tag that matches the specified name and value.
892
- -- @function hasMatchingTag
893
- -- @tparam {string} name The tag name to check
894
- -- @tparam {string} value The value to match for in the tag
895
- -- @treturn {function} A function that takes a message and returns whether there is a tag match (-1 if matches, 0 otherwise)
896
- function _utils.hasMatchingTag(name, value)
897
- assert(type(name) == 'string' and type(value) == 'string', 'invalid arguments: (name : string, value : string)')
898
-
899
- return function (msg)
900
- return msg.Tags[name] == value
901
- end
902
- end
903
-
904
- --- Checks if a given message has a tag that matches the specified name and one of the specified values.
905
- -- @function hasMatchingTagOf
906
- -- @tparam {string} name The tag name to check
907
- -- @tparam {string[]} values The list of values of which one should match
908
- -- @treturn {function} A function that takes a message and returns whether there is a tag match (-1 if matches, 0 otherwise)
909
- function _utils.hasMatchingTagOf(name, values)
910
- assert(type(name) == 'string' and type(values) == 'table', 'invalid arguments: (name : string, values : string[])')
911
- return function (msg)
912
- for _, value in ipairs(values) do
913
- local patternResult = Handlers.utils.hasMatchingTag(name, value)(msg)
914
-
915
- if patternResult ~= 0 and patternResult ~= false and patternResult ~= "skip" then
916
- return patternResult
917
- end
918
- end
919
-
920
- return 0
921
- end
922
- end
923
-
924
- --- Checks if a given message has data that matches the specified value.
925
- -- @function hasMatchingData
926
- -- @tparam {string} value The value to match against the message data
927
- -- @treturn {function} A function that takes a message and returns whether the data matches the value (-1 if matches, 0 otherwise)
928
- function _utils.hasMatchingData(value)
929
- assert(type(value) == 'string', 'invalid arguments: (value : string)')
930
- return function (msg)
931
- return msg.Data == value
932
- end
933
- end
934
-
935
- --- Given an input, returns a function that takes a message and replies to it.
936
- -- @function reply
937
- -- @tparam {table | string} input The content to send back. If a string, it sends it as data. If a table, it assumes a structure with `Tags`.
938
- -- @treturn {function} A function that takes a message and replies to it
939
- function _utils.reply(input)
940
- assert(type(input) == 'table' or type(input) == 'string', 'invalid arguments: (input : table or string)')
941
- return function (msg)
942
- if type(input) == 'string' then
943
- msg.reply({ Data = input })
944
- return
945
- end
946
- msg.reply(input)
947
- end
948
- end
949
-
950
- --- Inverts the provided pattern's result if it matches, so that it continues execution with the next matching handler.
951
- -- @function continue
952
- -- @tparam {table | function} pattern The pattern to check for in the message
953
- -- @treturn {function} Function that executes the pattern matching function and returns `1` (continue), so that the execution of handlers continues.
954
- function _utils.continue(pattern)
955
- return function (msg)
956
- local match = _.matchesSpec(msg, pattern)
957
-
958
- if not match or match == 0 or match == "skip" then
959
- return match
960
- end
961
- return 1
962
- end
963
- end
964
-
965
- return _utils
966
-
967
- end
968
- _G.package.loaded[".handlers-utils"] = load_handlers_utils()
969
- print("loaded handlers-utils")
970
-
971
-
972
-
973
- local function load_handlers()
974
- --- The Handlers library provides a flexible way to manage and execute a series of handlers based on patterns. Each handler consists of a pattern function, a handle function, and a name. This library is suitable for scenarios where different actions need to be taken based on varying input criteria. Returns the handlers table.
975
- -- @module handlers
976
-
977
- --- The handlers table
978
- -- @table handlers
979
- -- @field _version The version number of the handlers module
980
- -- @field list The list of handlers
981
- -- @field onceNonce The nonce for the once handlers
982
- -- @field utils The handlers-utils module
983
- -- @field generateResolver The generateResolver function
984
- -- @field receive The receive function
985
- -- @field once The once function
986
- -- @field add The add function
987
- -- @field append The append function
988
- -- @field prepend The prepend function
989
- -- @field remove The remove function
990
- -- @field evaluate The evaluate function
991
- local handlers = { _version = "0.0.5" }
992
- local utils = require('.utils')
993
-
994
- handlers.utils = require('.handlers-utils')
995
- -- if update we need to keep defined handlers
996
- if Handlers then
997
- handlers.list = Handlers.list or {}
998
- else
999
- handlers.list = {}
1000
- end
1001
- handlers.onceNonce = 0
1002
-
1003
- --- Given an array, a property name, and a value, returns the index of the object in the array that has the property with the value.
1004
- -- @lfunction findIndexByProp
1005
- -- @tparam {table[]} array The array to search through
1006
- -- @tparam {string} prop The property name to check
1007
- -- @tparam {any} value The value to check for in the property
1008
- -- @treturn {number | nil} The index of the object in the array that has the property with the value, or nil if no such object is found
1009
- local function findIndexByProp(array, prop, value)
1010
- for index, object in ipairs(array) do
1011
- if object[prop] == value then
1012
- return index
1013
- end
1014
- end
1015
- return nil
1016
- end
1017
-
1018
- --- Given a name, a pattern, and a handle, asserts that the arguments are valid.
1019
- -- @lfunction assertAddArgs
1020
- -- @tparam {string} name The name of the handler
1021
- -- @tparam {table | function | string} pattern The pattern to check for in the message
1022
- -- @tparam {function} handle The function to call if the pattern matches
1023
- -- @tparam {number | string | nil} maxRuns The maximum number of times the handler should run, or nil if there is no limit
1024
- local function assertAddArgs(name, pattern, handle, maxRuns)
1025
- assert(
1026
- type(name) == 'string' and
1027
- (type(pattern) == 'function' or type(pattern) == 'table' or type(pattern) == 'string'),
1028
- 'Invalid arguments given. Expected: \n' ..
1029
- '\tname : string, ' ..
1030
- '\tpattern : action : string | MsgMatch : table,\n' ..
1031
- '\t\tfunction(msg: Message) : {-1 = break, 0 = skip, 1 = continue},\n' ..
1032
- '\thandle(msg : Message) : void) | Resolver,\n' ..
1033
- '\tMaxRuns? : number | "inf" | nil')
1034
- end
1035
-
1036
- --- Given a resolver specification, returns a resolver function.
1037
- -- @function generateResolver
1038
- -- @tparam {table | function} resolveSpec The resolver specification
1039
- -- @treturn {function} A resolver function
1040
- function handlers.generateResolver(resolveSpec)
1041
- return function(msg)
1042
- -- If the resolver is a single function, call it.
1043
- -- Else, find the first matching pattern (by its matchSpec), and exec.
1044
- if type(resolveSpec) == "function" then
1045
- return resolveSpec(msg)
1046
- else
1047
- for matchSpec, func in pairs(resolveSpec) do
1048
- if utils.matchesSpec(msg, matchSpec) then
1049
- return func(msg)
1050
- end
1051
- end
1052
- end
1053
- end
1054
- end
1055
-
1056
- --- Given a pattern, returns the next message that matches the pattern.
1057
- -- This function uses Lua's coroutines under-the-hood to add a handler, pause,
1058
- -- and then resume the current coroutine. This allows us to effectively block
1059
- -- processing of one message until another is received that matches the pattern.
1060
- -- @function receive
1061
- -- @tparam {table | function} pattern The pattern to check for in the message
1062
- function handlers.receive(pattern)
1063
- return 'not implemented'
1064
- end
1065
-
1066
- --- Given a name, a pattern, and a handle, adds a handler to the list.
1067
- -- If name is not provided, "_once_" prefix plus onceNonce will be used as the name.
1068
- -- Adds handler with maxRuns of 1 such that it will only be called once then removed from the list.
1069
- -- @function once
1070
- -- @tparam {string} name The name of the handler
1071
- -- @tparam {table | function | string} pattern The pattern to check for in the message
1072
- -- @tparam {function} handle The function to call if the pattern matches
1073
- function handlers.once(...)
1074
- local name, pattern, handle
1075
- if select("#", ...) == 3 then
1076
- name = select(1, ...)
1077
- pattern = select(2, ...)
1078
- handle = select(3, ...)
1079
- else
1080
- name = "_once_" .. tostring(handlers.onceNonce)
1081
- handlers.onceNonce = handlers.onceNonce + 1
1082
- pattern = select(1, ...)
1083
- handle = select(2, ...)
1084
- end
1085
- handlers.prepend(name, pattern, handle, 1)
1086
- end
1087
-
1088
- --- Given a name, a pattern, and a handle, adds a handler to the list.
1089
- -- @function add
1090
- -- @tparam {string} name The name of the handler
1091
- -- @tparam {table | function | string} pattern The pattern to check for in the message
1092
- -- @tparam {function} handle The function to call if the pattern matches
1093
- -- @tparam {number | string | nil} maxRuns The maximum number of times the handler should run, or nil if there is no limit
1094
- function handlers.add(...)
1095
- local name, pattern, handle, maxRuns
1096
- local args = select("#", ...)
1097
- if args == 2 then
1098
- name = select(1, ...)
1099
- pattern = select(1, ...)
1100
- handle = select(2, ...)
1101
- maxRuns = nil
1102
- elseif args == 3 then
1103
- name = select(1, ...)
1104
- pattern = select(2, ...)
1105
- handle = select(3, ...)
1106
- maxRuns = nil
1107
- else
1108
- name = select(1, ...)
1109
- pattern = select(2, ...)
1110
- handle = select(3, ...)
1111
- maxRuns = select(4, ...)
1112
- end
1113
- assertAddArgs(name, pattern, handle, maxRuns)
1114
-
1115
- handle = handlers.generateResolver(handle)
1116
-
1117
- -- update existing handler by name
1118
- local idx = findIndexByProp(handlers.list, "name", name)
1119
- if idx ~= nil and idx > 0 then
1120
- -- found update
1121
- handlers.list[idx].pattern = pattern
1122
- handlers.list[idx].handle = handle
1123
- handlers.list[idx].maxRuns = maxRuns
1124
- else
1125
- -- not found then add
1126
- table.insert(handlers.list, { pattern = pattern, handle = handle, name = name, maxRuns = maxRuns })
1127
-
1128
- end
1129
- return #handlers.list
1130
- end
1131
-
1132
- --- Appends a new handler to the end of the handlers list.
1133
- -- @function append
1134
- -- @tparam {string} name The name of the handler
1135
- -- @tparam {table | function | string} pattern The pattern to check for in the message
1136
- -- @tparam {function} handle The function to call if the pattern matches
1137
- -- @tparam {number | string | nil} maxRuns The maximum number of times the handler should run, or nil if there is no limit
1138
- function handlers.append(...)
1139
- local name, pattern, handle, maxRuns
1140
- local args = select("#", ...)
1141
- if args == 2 then
1142
- name = select(1, ...)
1143
- pattern = select(1, ...)
1144
- handle = select(2, ...)
1145
- maxRuns = nil
1146
- elseif args == 3 then
1147
- name = select(1, ...)
1148
- pattern = select(2, ...)
1149
- handle = select(3, ...)
1150
- maxRuns = nil
1151
- else
1152
- name = select(1, ...)
1153
- pattern = select(2, ...)
1154
- handle = select(3, ...)
1155
- maxRuns = select(4, ...)
1156
- end
1157
- assertAddArgs(name, pattern, handle, maxRuns)
1158
-
1159
- handle = handlers.generateResolver(handle)
1160
- -- update existing handler by name
1161
- local idx = findIndexByProp(handlers.list, "name", name)
1162
- if idx ~= nil and idx > 0 then
1163
- -- found update
1164
- handlers.list[idx].pattern = pattern
1165
- handlers.list[idx].handle = handle
1166
- handlers.list[idx].maxRuns = maxRuns
1167
- else
1168
- table.insert(handlers.list, { pattern = pattern, handle = handle, name = name, maxRuns = maxRuns })
1169
- end
1170
- end
1171
-
1172
- --- Prepends a new handler to the beginning of the handlers list.
1173
- -- @function prepend
1174
- -- @tparam {string} name The name of the handler
1175
- -- @tparam {table | function | string} pattern The pattern to check for in the message
1176
- -- @tparam {function} handle The function to call if the pattern matches
1177
- -- @tparam {number | string | nil} maxRuns The maximum number of times the handler should run, or nil if there is no limit
1178
- function handlers.prepend(...)
1179
- local name, pattern, handle, maxRuns
1180
- local args = select("#", ...)
1181
- if args == 2 then
1182
- name = select(1, ...)
1183
- pattern = select(1, ...)
1184
- handle = select(2, ...)
1185
- maxRuns = nil
1186
- elseif args == 3 then
1187
- name = select(1, ...)
1188
- pattern = select(2, ...)
1189
- handle = select(3, ...)
1190
- maxRuns = nil
1191
- else
1192
- name = select(1, ...)
1193
- pattern = select(2, ...)
1194
- handle = select(3, ...)
1195
- maxRuns = select(4, ...)
1196
- end
1197
- assertAddArgs(name, pattern, handle, maxRuns)
1198
-
1199
- handle = handlers.generateResolver(handle)
1200
-
1201
- -- update existing handler by name
1202
- local idx = findIndexByProp(handlers.list, "name", name)
1203
- if idx ~= nil and idx > 0 then
1204
- -- found update
1205
- handlers.list[idx].pattern = pattern
1206
- handlers.list[idx].handle = handle
1207
- handlers.list[idx].maxRuns = maxRuns
1208
- else
1209
- table.insert(handlers.list, 1, { pattern = pattern, handle = handle, name = name, maxRuns = maxRuns })
1210
- end
1211
- end
1212
-
1213
- --- Returns an object that allows adding a new handler before a specified handler.
1214
- -- @function before
1215
- -- @tparam {string} handleName The name of the handler before which the new handler will be added
1216
- -- @treturn {table} An object with an `add` method to insert the new handler
1217
- function handlers.before(handleName)
1218
- assert(type(handleName) == 'string', 'Handler name MUST be a string')
1219
-
1220
- local idx = findIndexByProp(handlers.list, "name", handleName)
1221
- return {
1222
- add = function (name, pattern, handle, maxRuns)
1223
- assertAddArgs(name, pattern, handle, maxRuns)
1224
- handle = handlers.generateResolver(handle)
1225
- if idx then
1226
- table.insert(handlers.list, idx, { pattern = pattern, handle = handle, name = name, maxRuns = maxRuns })
1227
- end
1228
- end
1229
- }
1230
- end
1231
-
1232
- --- Returns an object that allows adding a new handler after a specified handler.
1233
- -- @function after
1234
- -- @tparam {string} handleName The name of the handler after which the new handler will be added
1235
- -- @treturn {table} An object with an `add` method to insert the new handler
1236
- function handlers.after(handleName)
1237
- assert(type(handleName) == 'string', 'Handler name MUST be a string')
1238
- local idx = findIndexByProp(handlers.list, "name", handleName)
1239
- return {
1240
- add = function (name, pattern, handle, maxRuns)
1241
- assertAddArgs(name, pattern, handle, maxRuns)
1242
- handle = handlers.generateResolver(handle)
1243
- if idx then
1244
- table.insert(handlers.list, idx + 1, { pattern = pattern, handle = handle, name = name, maxRuns = maxRuns })
1245
- end
1246
- end
1247
- }
1248
-
1249
- end
1250
-
1251
- --- Removes a handler from the handlers list by name.
1252
- -- @function remove
1253
- -- @tparam {string} name The name of the handler to be removed
1254
- function handlers.remove(name)
1255
- assert(type(name) == 'string', 'name MUST be string')
1256
- if #handlers.list == 1 and handlers.list[1].name == name then
1257
- handlers.list = {}
1258
- end
1259
-
1260
- local idx = findIndexByProp(handlers.list, "name", name)
1261
- if idx ~= nil and idx > 0 then
1262
- table.remove(handlers.list, idx)
1263
- end
1264
- end
1265
-
1266
- --- Evaluates each handler against a given message and environment. Handlers are called in the order they appear in the handlers list.
1267
- -- Return 0 to not call handler, -1 to break after handler is called, 1 to continue
1268
- -- @function evaluate
1269
- -- @tparam {table} msg The message to be processed by the handlers.
1270
- -- @tparam {table} env The environment in which the handlers are executed.
1271
- -- @treturn The response from the handler(s). Returns a default message if no handler matches.
1272
- function handlers.evaluate(msg, env)
1273
- local handled = false
1274
- assert(type(msg) == 'table', 'msg is not valid')
1275
- assert(type(env) == 'table', 'env is not valid')
1276
- for _, o in ipairs(handlers.list) do
1277
- if o.name ~= "_default" then
1278
- local match = utils.matchesSpec(msg, o.pattern)
1279
- if not (type(match) == 'number' or type(match) == 'string' or type(match) == 'boolean') then
1280
- error("Pattern result is not valid, it MUST be string, number, or boolean")
1281
- end
1282
- -- handle boolean returns
1283
- if type(match) == "boolean" and match == true then
1284
- match = -1
1285
- elseif type(match) == "boolean" and match == false then
1286
- match = 0
1287
- end
1288
-
1289
- -- handle string returns
1290
- if type(match) == "string" then
1291
- if match == "continue" then
1292
- match = 1
1293
- elseif match == "break" then
1294
- match = -1
1295
- else
1296
- match = 0
1297
- end
1298
- end
1299
-
1300
- if match ~= 0 then
1301
- if match < 0 then
1302
- handled = true
1303
- end
1304
- -- each handle function can accept, the msg, env
1305
- local status, err = pcall(o.handle, msg, env)
1306
- if not status then
1307
- error(err)
1308
- end
1309
- -- remove handler if maxRuns is reached. maxRuns can be either a number or "inf"
1310
- if o.maxRuns ~= nil and o.maxRuns ~= "inf" then
1311
- o.maxRuns = o.maxRuns - 1
1312
- if o.maxRuns == 0 then
1313
- handlers.remove(o.name)
1314
- end
1315
- end
1316
- end
1317
- if match < 0 then
1318
- return handled
1319
- end
1320
- end
1321
- end
1322
- -- do default
1323
- if not handled then
1324
- local idx = findIndexByProp(handlers.list, "name", "_default")
1325
- handlers.list[idx].handle(msg,env)
1326
- end
1327
- end
1328
-
1329
- return handlers
1330
-
1331
- end
1332
- _G.package.loaded[".handlers"] = load_handlers()
1333
- print("loaded handlers")
1334
-
1335
-
1336
-
1337
- local function load_dump()
1338
- --
1339
- -- Copyright (C) 2018 Masatoshi Teruya
1340
- --
1341
- -- Permission is hereby granted, free of charge, to any person obtaining a copy
1342
- -- of this software and associated documentation files (the "Software"), to deal
1343
- -- in the Software without restriction, including without limitation the rights
1344
- -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1345
- -- copies of the Software, and to permit persons to whom the Software is
1346
- -- furnished to do so, subject to the following conditions:
1347
- --
1348
- -- The above copyright notice and this permission notice shall be included in
1349
- -- all copies or substantial portions of the Software.
1350
- --
1351
- -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1352
- -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1353
- -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1354
- -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1355
- -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1356
- -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
1357
- -- THE SOFTWARE.
1358
- --
1359
- -- dump.lua
1360
- -- lua-dump
1361
- -- Created by Masatoshi Teruya on 18/04/22.
1362
- --
1363
- --- file-scope variables
1364
- local type = type
1365
- local floor = math.floor
1366
- local tostring = tostring
1367
- local tblsort = table.sort
1368
- local tblconcat = table.concat
1369
- local strmatch = string.match
1370
- local strformat = string.format
1371
- --- constants
1372
- local INFINITE_POS = math.huge
1373
- local LUA_FIELDNAME_PAT = '^[a-zA-Z_][a-zA-Z0-9_]*$'
1374
- local FOR_KEY = 'key'
1375
- local FOR_VAL = 'val'
1376
- local FOR_CIRCULAR = 'circular'
1377
- local RESERVED_WORD = {
1378
- -- primitive data
1379
- ['nil'] = true,
1380
- ['true'] = true,
1381
- ['false'] = true,
1382
- -- declaraton
1383
- ['local'] = true,
1384
- ['function'] = true,
1385
- -- boolean logic
1386
- ['and'] = true,
1387
- ['or'] = true,
1388
- ['not'] = true,
1389
- -- conditional statement
1390
- ['if'] = true,
1391
- ['elseif'] = true,
1392
- ['else'] = true,
1393
- -- iteration statement
1394
- ['for'] = true,
1395
- ['in'] = true,
1396
- ['while'] = true,
1397
- ['until'] = true,
1398
- ['repeat'] = true,
1399
- -- jump statement
1400
- ['break'] = true,
1401
- ['goto'] = true,
1402
- ['return'] = true,
1403
- -- block scope statement
1404
- ['then'] = true,
1405
- ['do'] = true,
1406
- ['end'] = true,
1407
- }
1408
- local DEFAULT_INDENT = 4
1409
-
1410
- --- filter function for dump
1411
- --- @param val any
1412
- --- @param depth integer
1413
- --- @param vtype string
1414
- --- @param use string
1415
- --- @param key any
1416
- --- @param udata any
1417
- --- @return any val
1418
- --- @return boolean nodump
1419
- local function DEFAULT_FILTER(val)
1420
- return val
1421
- end
1422
-
1423
- --- sort_index
1424
- --- @param a table
1425
- --- @param b table
1426
- local function sort_index(a, b)
1427
- if a.typ == b.typ then
1428
- if a.typ == 'boolean' then
1429
- return b.key
1430
- end
1431
-
1432
- return a.key < b.key
1433
- end
1434
-
1435
- return a.typ == 'number'
1436
- end
1437
-
1438
- --- dumptbl
1439
- --- @param tbl table
1440
- --- @param depth integer
1441
- --- @param indent string
1442
- --- @param nestIndent string
1443
- --- @param ctx table
1444
- --- @return string
1445
- local function dumptbl(tbl, depth, indent, nestIndent, ctx)
1446
- local ref = tostring(tbl)
1447
-
1448
- -- circular reference
1449
- if ctx.circular[ref] then
1450
- local val, nodump = ctx.filter(tbl, depth, type(tbl), FOR_CIRCULAR, tbl,
1451
- ctx.udata)
1452
-
1453
- if val ~= nil and val ~= tbl then
1454
- local t = type(val)
1455
-
1456
- if t == 'table' then
1457
- -- dump table value
1458
- if not nodump then
1459
- return dumptbl(val, depth + 1, indent, nestIndent, ctx)
1460
- end
1461
- return tostring(val)
1462
- elseif t == 'string' then
1463
- return strformat('%q', val)
1464
- elseif t == 'number' or t == 'boolean' then
1465
- return tostring(val)
1466
- end
1467
-
1468
- return strformat('%q', tostring(val))
1469
- end
1470
-
1471
- return '"<Circular ' .. ref .. '>"'
1472
- end
1473
-
1474
- local res = {}
1475
- local arr = {}
1476
- local narr = 0
1477
- local fieldIndent = indent .. nestIndent
1478
-
1479
- -- save reference
1480
- ctx.circular[ref] = true
1481
-
1482
- for k, v in pairs(tbl) do
1483
- -- check key
1484
- local key, nokdump = ctx.filter(k, depth, type(k), FOR_KEY, nil,
1485
- ctx.udata)
1486
-
1487
- if key ~= nil then
1488
- -- check val
1489
- local val, novdump = ctx.filter(v, depth, type(v), FOR_VAL, key,
1490
- ctx.udata)
1491
- local kv
1492
-
1493
- if val ~= nil then
1494
- local kt = type(key)
1495
- local vt = type(val)
1496
-
1497
- -- convert key to suitable to be safely read back
1498
- -- by the Lua interpreter
1499
- if kt == 'number' or kt == 'boolean' then
1500
- k = key
1501
- key = '[' .. tostring(key) .. ']'
1502
- -- dump table value
1503
- elseif kt == 'table' and not nokdump then
1504
- key = '[' ..
1505
- dumptbl(key, depth + 1, fieldIndent, nestIndent,
1506
- ctx) .. ']'
1507
- k = key
1508
- kt = 'string'
1509
- elseif kt ~= 'string' or RESERVED_WORD[key] or
1510
- not strmatch(key, LUA_FIELDNAME_PAT) then
1511
- key = strformat("[%q]", tostring(key), v)
1512
- k = key
1513
- kt = 'string'
1514
- end
1515
-
1516
- -- convert key-val pair to suitable to be safely read back
1517
- -- by the Lua interpreter
1518
- if vt == 'number' or vt == 'boolean' then
1519
- kv = strformat('%s%s = %s', fieldIndent, key, tostring(val))
1520
- elseif vt == 'string' then
1521
- -- dump a string-value
1522
- if not novdump then
1523
- kv = strformat('%s%s = %q', fieldIndent, key, val)
1524
- else
1525
- kv = strformat('%s%s = %s', fieldIndent, key, val)
1526
- end
1527
- elseif vt == 'table' and not novdump then
1528
- kv = strformat('%s%s = %s', fieldIndent, key, dumptbl(val,
1529
- depth +
1530
- 1,
1531
- fieldIndent,
1532
- nestIndent,
1533
- ctx))
1534
- else
1535
- kv = strformat('%s%s = %q', fieldIndent, key, tostring(val))
1536
- end
1537
-
1538
- -- add to array
1539
- narr = narr + 1
1540
- arr[narr] = {
1541
- typ = kt,
1542
- key = k,
1543
- val = kv,
1544
- }
1545
- end
1546
- end
1547
- end
1548
-
1549
- -- remove reference
1550
- ctx.circular[ref] = nil
1551
- -- concat result
1552
- if narr > 0 then
1553
- tblsort(arr, sort_index)
1554
-
1555
- for i = 1, narr do
1556
- res[i] = arr[i].val
1557
- end
1558
- res[1] = '{' .. ctx.LF .. res[1]
1559
- res = tblconcat(res, ',' .. ctx.LF) .. ctx.LF .. indent .. '}'
1560
- else
1561
- res = '{}'
1562
- end
1563
-
1564
- return res
1565
- end
1566
-
1567
- --- is_uint
1568
- --- @param v any
1569
- --- @return boolean ok
1570
- local function is_uint(v)
1571
- return type(v) == 'number' and v < INFINITE_POS and v >= 0 and floor(v) == v
1572
- end
1573
-
1574
- --- dump
1575
- --- @param val any
1576
- --- @param indent integer
1577
- --- @param padding integer
1578
- --- @param filter function
1579
- --- @param udata
1580
- --- @return string
1581
- local function dump(val, indent, padding, filter, udata)
1582
- local t = type(val)
1583
-
1584
- -- check indent
1585
- if indent == nil then
1586
- indent = DEFAULT_INDENT
1587
- elseif not is_uint(indent) then
1588
- error('indent must be unsigned integer', 2)
1589
- end
1590
-
1591
- -- check padding
1592
- if padding == nil then
1593
- padding = 0
1594
- elseif not is_uint(padding) then
1595
- error('padding must be unsigned integer', 2)
1596
- end
1597
-
1598
- -- check filter
1599
- if filter == nil then
1600
- filter = DEFAULT_FILTER
1601
- elseif type(filter) ~= 'function' then
1602
- error('filter must be function', 2)
1603
- end
1604
-
1605
- -- dump table
1606
- if t == 'table' then
1607
- local ispace = ''
1608
- local pspace = ''
1609
-
1610
- if indent > 0 then
1611
- ispace = strformat('%' .. tostring(indent) .. 's', '')
1612
- end
1613
-
1614
- if padding > 0 then
1615
- pspace = strformat('%' .. tostring(padding) .. 's', '')
1616
- end
1617
-
1618
- return dumptbl(val, 1, pspace, ispace, {
1619
- LF = ispace == '' and ' ' or '\n',
1620
- circular = {},
1621
- filter = filter,
1622
- udata = udata,
1623
- })
1624
- end
1625
-
1626
- -- dump value
1627
- local v, nodump = filter(val, 0, t, FOR_VAL, nil, udata)
1628
- if nodump == true then
1629
- return tostring(v)
1630
- end
1631
- return strformat('%q', tostring(v))
1632
- end
1633
-
1634
- return dump
1635
- end
1636
- _G.package.loaded[".dump"] = load_dump()
1637
- print("loaded dump")
1638
-
1639
-
1640
-
1641
- local function load_pretty()
1642
- local pretty = { _version = "0.0.1" }
1643
-
1644
- pretty.tprint = function (tbl, indent)
1645
- if not indent then indent = 0 end
1646
- local output = ""
1647
- for k, v in pairs(tbl) do
1648
- local formatting = string.rep(" ", indent) .. k .. ": "
1649
- if type(v) == "table" then
1650
- output = output .. formatting .. "\n"
1651
- output = output .. pretty.tprint(v, indent+1)
1652
- elseif type(v) == 'boolean' then
1653
- output = output .. formatting .. tostring(v) .. "\n"
1654
- else
1655
- output = output .. formatting .. v .. "\n"
1656
- end
1657
- end
1658
- return output
1659
- end
1660
-
1661
- return pretty
1662
-
1663
- end
1664
- _G.package.loaded[".pretty"] = load_pretty()
1665
- print("loaded pretty")
1666
-
1667
-
1668
-
1669
- local function load_chance()
1670
- --- The Chance module provides utilities for generating random numbers and values. Returns the chance table.
1671
- -- @module chance
1672
-
1673
- local N = 624
1674
- local M = 397
1675
- local MATRIX_A = 0x9908b0df
1676
- local UPPER_MASK = 0x80000000
1677
- local LOWER_MASK = 0x7fffffff
1678
-
1679
- --- Initializes mt[N] with a seed
1680
- -- @lfunction init_genrand
1681
- -- @tparam {table} o The table to initialize
1682
- -- @tparam {number} s The seed
1683
- local function init_genrand(o, s)
1684
- o.mt[0] = s & 0xffffffff
1685
- for i = 1, N - 1 do
1686
- o.mt[i] = (1812433253 * (o.mt[i - 1] ~ (o.mt[i - 1] >> 30))) + i
1687
- -- See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier.
1688
- -- In the previous versions, MSBs of the seed affect
1689
- -- only MSBs of the array mt[].
1690
- -- 2002/01/09 modified by Makoto Matsumoto
1691
- o.mt[i] = o.mt[i] & 0xffffffff
1692
- -- for >32 bit machines
1693
- end
1694
- o.mti = N
1695
- end
1696
-
1697
- --- Generates a random number on [0,0xffffffff]-interval
1698
- -- @lfunction genrand_int32
1699
- -- @tparam {table} o The table to generate the random number from
1700
- -- @treturn {number} The random number
1701
- local function genrand_int32(o)
1702
- local y
1703
- local mag01 = {} -- mag01[x] = x * MATRIX_A for x=0,1
1704
- mag01[0] = 0x0
1705
- mag01[1] = MATRIX_A
1706
- if o.mti >= N then -- generate N words at one time
1707
- if o.mti == N + 1 then -- if init_genrand() has not been called,
1708
- init_genrand(o, 5489) -- a default initial seed is used
1709
- end
1710
- for kk = 0, N - M - 1 do
1711
- y = (o.mt[kk] & UPPER_MASK) | (o.mt[kk + 1] & LOWER_MASK)
1712
- o.mt[kk] = o.mt[kk + M] ~ (y >> 1) ~ mag01[y & 0x1]
1713
- end
1714
- for kk = N - M, N - 2 do
1715
- y = (o.mt[kk] & UPPER_MASK) | (o.mt[kk + 1] & LOWER_MASK)
1716
- o.mt[kk] = o.mt[kk + (M - N)] ~ (y >> 1) ~ mag01[y & 0x1]
1717
- end
1718
- y = (o.mt[N - 1] & UPPER_MASK) | (o.mt[0] & LOWER_MASK)
1719
- o.mt[N - 1] = o.mt[M - 1] ~ (y >> 1) ~ mag01[y & 0x1]
1720
-
1721
- o.mti = 0
1722
- end
1723
-
1724
- y = o.mt[o.mti]
1725
- o.mti = o.mti + 1
1726
-
1727
- -- Tempering
1728
- y = y ~ (y >> 11)
1729
- y = y ~ ((y << 7) & 0x9d2c5680)
1730
- y = y ~ ((y << 15) & 0xefc60000)
1731
- y = y ~ (y >> 18)
1732
-
1733
- return y
1734
- end
1735
-
1736
- local MersenneTwister = {}
1737
- MersenneTwister.mt = {}
1738
- MersenneTwister.mti = N + 1
1739
-
1740
-
1741
- --- The Random table
1742
- -- @table Random
1743
- -- @field seed The seed function
1744
- -- @field random The random function
1745
- -- @field integer The integer function
1746
- local Random = {}
1747
-
1748
- --- Sets a new random table given a seed.
1749
- -- @function seed
1750
- -- @tparam {number} seed The seed
1751
- function Random.seed(seed)
1752
- init_genrand(MersenneTwister, seed)
1753
- end
1754
-
1755
- --- Generates a random number on [0,1)-real-interval.
1756
- -- @function random
1757
- -- @treturn {number} The random number
1758
- function Random.random()
1759
- return genrand_int32(MersenneTwister) * (1.0 / 4294967296.0)
1760
- end
1761
-
1762
- --- Returns a random integer. The min and max are INCLUDED in the range.
1763
- -- The max integer in lua is math.maxinteger
1764
- -- The min is math.mininteger
1765
- -- @function Random.integer
1766
- -- @tparam {number} min The minimum value
1767
- -- @tparam {number} max The maximum value
1768
- -- @treturn {number} The random integer
1769
- function Random.integer(min, max)
1770
- assert(max >= min, "max must bigger than min")
1771
- return math.floor(Random.random() * (max - min + 1) + min)
1772
- end
1773
-
1774
- return Random
1775
- end
1776
- _G.package.loaded[".chance"] = load_chance()
1777
- print("loaded chance")
1778
-
1779
-
1780
-
1781
- local function load_boot()
1782
- --- The Boot module provides functionality for booting the process. Returns the boot function.
1783
- -- @module boot
1784
-
1785
- -- This is for aop6 Boot Loader
1786
- -- See: https://github.com/permaweb/aos/issues/342
1787
- -- For the Process as the first Message, if On-Boot
1788
- -- has the value 'data' then evaluate the data
1789
- -- if it is a tx id, then download and evaluate the tx
1790
-
1791
- local drive = { _version = "0.0.1" }
1792
-
1793
- function drive.getData(txId)
1794
- local file = io.open('/data/' .. txId)
1795
- if not file then
1796
- return nil, "File not found!"
1797
- end
1798
- local contents = file:read(
1799
- file:seek('end')
1800
- )
1801
- file:close()
1802
- return contents
1803
- end
1804
-
1805
- --- The boot function.
1806
- -- If the message has no On-Boot tag, do nothing.
1807
- -- If the message has an On-Boot tag with the value 'Data', then evaluate the message.
1808
- -- If the message has an On-Boot tag with a tx id, then download and evaluate the tx data.
1809
- -- @function boot
1810
- -- @param ao The ao environment object
1811
- -- @see eval
1812
- return function (ao)
1813
- local eval = require(".eval")(ao)
1814
- return function (msg)
1815
- if #Inbox == 0 then
1816
- table.insert(Inbox, msg)
1817
- end
1818
- if msg.Tags['On-Boot'] == nil then
1819
- return
1820
- end
1821
- if msg.Tags['On-Boot'] == 'Data' then
1822
- eval(msg)
1823
- else
1824
- local loadedVal = drive.getData(msg.Tags['On-Boot'])
1825
- eval({ Data = loadedVal })
1826
- end
1827
- end
1828
- end
1829
- end
1830
- _G.package.loaded[".boot"] = load_boot()
1831
- print("loaded boot")
1832
-
1833
-
1834
-
1835
- local function load_default()
1836
- local json = require('.json')
1837
- -- default handler for aos
1838
- return function (insertInbox)
1839
- return function (msg)
1840
- -- Add Message to Inbox
1841
- insertInbox(msg)
1842
-
1843
- -- local txt = Colors.gray .. "New Message From " .. Colors.green ..
1844
- -- (msg.From and (msg.From:sub(1,3) .. "..." .. msg.From:sub(-3)) or "unknown") .. Colors.gray .. ": "
1845
- -- if msg.Action then
1846
- -- txt = txt .. Colors.gray .. (msg.Action and ("Action = " .. Colors.blue .. msg.Action:sub(1,20)) or "") .. Colors.reset
1847
- -- else
1848
- -- local data = msg.Data
1849
- -- if type(data) == 'table' then
1850
- -- data = json.encode(data)
1851
- -- end
1852
- -- txt = txt .. Colors.gray .. "Data = " .. Colors.blue .. (data and data:sub(1,20) or "") .. Colors.reset
1853
- -- end
1854
- -- Print to Output
1855
- -- print(txt)
1856
- print("New Message")
1857
- end
1858
-
1859
- end
1860
-
1861
- end
1862
- _G.package.loaded[".default"] = load_default()
1863
- print("loaded default")
1864
-
1865
-
1866
-
1867
- local function load_ao()
1868
- Handlers = Handlers or require('.handlers')
1869
-
1870
- local oldao = ao or {}
1871
-
1872
- local utils = require('.utils')
1873
-
1874
- local ao = {
1875
- _version = "0.0.6",
1876
- id = oldao.id or "",
1877
- _module = oldao._module or "",
1878
- authorities = oldao.authorities or {},
1879
- reference = oldao.reference or 0,
1880
- outbox = oldao.outbox or
1881
- {Output = {}, Messages = {}, Spawns = {}, Assignments = {}},
1882
- nonExtractableTags = {
1883
- 'data-protocol', 'variant', 'from-process', 'from-module', 'type',
1884
- 'from', 'owner', 'anchor', 'target', 'data', 'tags', 'read-only'
1885
- },
1886
- nonForwardableTags = {
1887
- 'data-protocol', 'variant', 'from-process', 'from-module', 'type',
1888
- 'from', 'owner', 'anchor', 'target', 'tags', 'tagArray', 'hash-chain',
1889
- 'timestamp', 'nonce', 'slot', 'epoch', 'signature', 'forwarded-by',
1890
- 'pushed-for', 'read-only', 'cron', 'block-height', 'reference', 'id',
1891
- 'reply-to'
1892
- },
1893
- Nonce = nil
1894
- }
1895
-
1896
- function ao.clearOutbox()
1897
- ao.outbox = { Output = {}, Messages = {}, Spawns = {}, Assignments = {}}
1898
- end
1899
-
1900
- local function getId(m)
1901
- local id = ""
1902
- utils.map(function (k)
1903
- local c = m.commitments[k]
1904
- if c.alg == "rsa-pss-sha512" then
1905
- id = k
1906
- elseif c.alg == "signed" and c['commitment-device'] == "ans104" then
1907
- id = k
1908
- end
1909
- end, utils.keys(m.commitments)
1910
- )
1911
- return id
1912
- end
1913
-
1914
- function ao.init(env)
1915
- if ao.id == "" then ao.id = getId(env.process) end
1916
-
1917
- -- if ao._module == "" then
1918
- -- ao._module = env.Module.Id
1919
- -- end
1920
- -- TODO: need to deal with assignables
1921
- if #ao.authorities < 1 then
1922
- if type(env.process.authority) == 'string' then
1923
- ao.authorities = { env.process.authority }
1924
- else
1925
- ao.authorities = env.process.authority
1926
- end
1927
- end
1928
-
1929
- ao.outbox = {Output = {}, Messages = {}, Spawns = {}, Assignments = {}}
1930
- ao.env = env
1931
-
1932
- end
1933
-
1934
- function ao.send(msg)
1935
- assert(type(msg) == 'table', 'msg should be a table')
1936
-
1937
- ao.reference = ao.reference + 1
1938
- local referenceString = tostring(ao.reference)
1939
- -- set kv
1940
- msg.reference = referenceString
1941
-
1942
- -- clone message info and add to outbox
1943
- table.insert(ao.outbox.Messages, utils.reduce(
1944
- function (acc, key)
1945
- acc[key] = msg[key]
1946
- return acc
1947
- end,
1948
- {},
1949
- utils.keys(msg)
1950
- ))
1951
-
1952
- if msg.target then
1953
- msg.onReply = function(...)
1954
- local from, resolver
1955
- if select("#", ...) == 2 then
1956
- from = select(1, ...)
1957
- resolver = select(2, ...)
1958
- else
1959
- from = msg.target
1960
- resolver = select(1, ...)
1961
- end
1962
- Handlers.once({
1963
- from = from,
1964
- ["x-reference"] = referenceString
1965
- }, resolver)
1966
- end
1967
- end
1968
- return msg
1969
- end
1970
-
1971
- function ao.spawn(module, msg)
1972
- assert(type(module) == "string", "Module source id is required!")
1973
- assert(type(msg) == "table", "Message must be a table.")
1974
-
1975
- ao.reference = ao.reference + 1
1976
-
1977
- local spawnRef = tostring(ao.reference)
1978
-
1979
- msg["reference"] = spawnRef
1980
-
1981
- -- clone message info and add to outbox
1982
- table.insert(ao.outbox.Spawns, utils.reduce(
1983
- function (acc, key)
1984
- acc[key] = msg[key]
1985
- return acc
1986
- end,
1987
- {},
1988
- utils.keys(msg)
1989
- ))
1990
-
1991
- msg.onReply = function(cb)
1992
- Handlers.once({
1993
- action = "Spawned",
1994
- from = ao.id,
1995
- ["x-reference"] = spawnRef
1996
- }, cb)
1997
- end
1998
-
1999
- return msg
2000
-
2001
- end
2002
-
2003
- function ao.result(result)
2004
- if ao.outbox.Error or result.Error then
2005
- return { Error = result.Error or ao.outbox.Error }
2006
- end
2007
- return {
2008
- Output = result.Output or ao.output.Output,
2009
- Messages = ao.outbox.Messages,
2010
- Spawns = ao.outbox.Spawns,
2011
- Assignments = ao.outbox.Assignments
2012
- }
2013
- end
2014
-
2015
- -- set global Send and Spawn
2016
- Send = Send or ao.send
2017
- Spawn = Spawn or ao.spawn
2018
-
2019
- return ao
2020
-
2021
- end
2022
- _G.package.loaded[".ao"] = load_ao()
2023
- print("loaded ao")
2024
-
2025
-
2026
-
2027
- local function load_base64()
2028
- --[[
2029
-
2030
- base64 -- v1.5.3 public domain Lua base64 encoder/decoder
2031
- no warranty implied; use at your own risk
2032
-
2033
- Needs bit32.extract function. If not present it's implemented using BitOp
2034
- or Lua 5.3 native bit operators. For Lua 5.1 fallbacks to pure Lua
2035
- implementation inspired by Rici Lake's post:
2036
- http://ricilake.blogspot.co.uk/2007/10/iterating-bits-in-lua.html
2037
-
2038
- author: Ilya Kolbin (iskolbin@gmail.com)
2039
- url: github.com/iskolbin/lbase64
2040
-
2041
- COMPATIBILITY
2042
-
2043
- Lua 5.1+, LuaJIT
2044
-
2045
- LICENSE
2046
-
2047
- See end of file for license information.
2048
-
2049
- --]]
2050
-
2051
-
2052
- local base64 = {}
2053
-
2054
- local extract = _G.bit32 and _G.bit32.extract -- Lua 5.2/Lua 5.3 in compatibility mode
2055
- if not extract then
2056
- if _G.bit then -- LuaJIT
2057
- local shl, shr, band = _G.bit.lshift, _G.bit.rshift, _G.bit.band
2058
- extract = function( v, from, width )
2059
- return band( shr( v, from ), shl( 1, width ) - 1 )
2060
- end
2061
- elseif _G._VERSION == "Lua 5.1" then
2062
- extract = function( v, from, width )
2063
- local w = 0
2064
- local flag = 2^from
2065
- for i = 0, width-1 do
2066
- local flag2 = flag + flag
2067
- if v % flag2 >= flag then
2068
- w = w + 2^i
2069
- end
2070
- flag = flag2
2071
- end
2072
- return w
2073
- end
2074
- else -- Lua 5.3+
2075
- extract = load[[return function( v, from, width )
2076
- return ( v >> from ) & ((1 << width) - 1)
2077
- end]]()
2078
- end
2079
- end
2080
-
2081
-
2082
- function base64.makeencoder( s62, s63, spad )
2083
- local encoder = {}
2084
- for b64code, char in pairs{[0]='A','B','C','D','E','F','G','H','I','J',
2085
- 'K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y',
2086
- 'Z','a','b','c','d','e','f','g','h','i','j','k','l','m','n',
2087
- 'o','p','q','r','s','t','u','v','w','x','y','z','0','1','2',
2088
- '3','4','5','6','7','8','9',s62 or '+',s63 or'/',spad or'='} do
2089
- encoder[b64code] = char:byte()
2090
- end
2091
- return encoder
2092
- end
2093
-
2094
- function base64.makedecoder( s62, s63, spad )
2095
- local decoder = {}
2096
- for b64code, charcode in pairs( base64.makeencoder( s62, s63, spad )) do
2097
- decoder[charcode] = b64code
2098
- end
2099
- return decoder
2100
- end
2101
-
2102
- local DEFAULT_ENCODER = base64.makeencoder()
2103
- local DEFAULT_DECODER = base64.makedecoder()
2104
-
2105
- local char, concat = string.char, table.concat
2106
-
2107
- function base64.encode( str, encoder, usecaching )
2108
- encoder = encoder or DEFAULT_ENCODER
2109
- local t, k, n = {}, 1, #str
2110
- local lastn = n % 3
2111
- local cache = {}
2112
- for i = 1, n-lastn, 3 do
2113
- local a, b, c = str:byte( i, i+2 )
2114
- local v = a*0x10000 + b*0x100 + c
2115
- local s
2116
- if usecaching then
2117
- s = cache[v]
2118
- if not s then
2119
- s = char(encoder[extract(v,18,6)], encoder[extract(v,12,6)], encoder[extract(v,6,6)], encoder[extract(v,0,6)])
2120
- cache[v] = s
2121
- end
2122
- else
2123
- s = char(encoder[extract(v,18,6)], encoder[extract(v,12,6)], encoder[extract(v,6,6)], encoder[extract(v,0,6)])
2124
- end
2125
- t[k] = s
2126
- k = k + 1
2127
- end
2128
- if lastn == 2 then
2129
- local a, b = str:byte( n-1, n )
2130
- local v = a*0x10000 + b*0x100
2131
- t[k] = char(encoder[extract(v,18,6)], encoder[extract(v,12,6)], encoder[extract(v,6,6)], encoder[64])
2132
- elseif lastn == 1 then
2133
- local v = str:byte( n )*0x10000
2134
- t[k] = char(encoder[extract(v,18,6)], encoder[extract(v,12,6)], encoder[64], encoder[64])
2135
- end
2136
- return concat( t )
2137
- end
2138
-
2139
- function base64.decode( b64, decoder, usecaching )
2140
- decoder = decoder or DEFAULT_DECODER
2141
- local pattern = '[^%w%+%/%=]'
2142
- if decoder then
2143
- local s62, s63
2144
- for charcode, b64code in pairs( decoder ) do
2145
- if b64code == 62 then s62 = charcode
2146
- elseif b64code == 63 then s63 = charcode
2147
- end
2148
- end
2149
- pattern = ('[^%%w%%%s%%%s%%=]'):format( char(s62), char(s63) )
2150
- end
2151
- b64 = b64:gsub( pattern, '' )
2152
- local cache = usecaching and {}
2153
- local t, k = {}, 1
2154
- local n = #b64
2155
- local padding = b64:sub(-2) == '==' and 2 or b64:sub(-1) == '=' and 1 or 0
2156
- for i = 1, padding > 0 and n-4 or n, 4 do
2157
- local a, b, c, d = b64:byte( i, i+3 )
2158
- local s
2159
- if usecaching then
2160
- local v0 = a*0x1000000 + b*0x10000 + c*0x100 + d
2161
- s = cache[v0]
2162
- if not s then
2163
- local v = decoder[a]*0x40000 + decoder[b]*0x1000 + decoder[c]*0x40 + decoder[d]
2164
- s = char( extract(v,16,8), extract(v,8,8), extract(v,0,8))
2165
- cache[v0] = s
2166
- end
2167
- else
2168
- local v = decoder[a]*0x40000 + decoder[b]*0x1000 + decoder[c]*0x40 + decoder[d]
2169
- s = char( extract(v,16,8), extract(v,8,8), extract(v,0,8))
2170
- end
2171
- t[k] = s
2172
- k = k + 1
2173
- end
2174
- if padding == 1 then
2175
- local a, b, c = b64:byte( n-3, n-1 )
2176
- local v = decoder[a]*0x40000 + decoder[b]*0x1000 + decoder[c]*0x40
2177
- t[k] = char( extract(v,16,8), extract(v,8,8))
2178
- elseif padding == 2 then
2179
- local a, b = b64:byte( n-3, n-2 )
2180
- local v = decoder[a]*0x40000 + decoder[b]*0x1000
2181
- t[k] = char( extract(v,16,8))
2182
- end
2183
- return concat( t )
2184
- end
2185
-
2186
- return base64
2187
-
2188
- --[[
2189
- ------------------------------------------------------------------------------
2190
- This software is available under 2 licenses -- choose whichever you prefer.
2191
- ------------------------------------------------------------------------------
2192
- ALTERNATIVE A - MIT License
2193
- Copyright (c) 2018 Ilya Kolbin
2194
- Permission is hereby granted, free of charge, to any person obtaining a copy of
2195
- this software and associated documentation files (the "Software"), to deal in
2196
- the Software without restriction, including without limitation the rights to
2197
- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
2198
- of the Software, and to permit persons to whom the Software is furnished to do
2199
- so, subject to the following conditions:
2200
- The above copyright notice and this permission notice shall be included in all
2201
- copies or substantial portions of the Software.
2202
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2203
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2204
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
2205
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2206
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2207
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2208
- SOFTWARE.
2209
- ------------------------------------------------------------------------------
2210
- ALTERNATIVE B - Public Domain (www.unlicense.org)
2211
- This is free and unencumbered software released into the public domain.
2212
- Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
2213
- software, either in source code form or as a compiled binary, for any purpose,
2214
- commercial or non-commercial, and by any means.
2215
- In jurisdictions that recognize copyright laws, the author or authors of this
2216
- software dedicate any and all copyright interest in the software to the public
2217
- domain. We make this dedication for the benefit of the public at large and to
2218
- the detriment of our heirs and successors. We intend this dedication to be an
2219
- overt act of relinquishment in perpetuity of all present and future rights to
2220
- this software under copyright law.
2221
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2222
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2223
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
2224
- AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
2225
- ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
2226
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2227
- ------------------------------------------------------------------------------
2228
- --]]
2229
- end
2230
- _G.package.loaded[".base64"] = load_base64()
2231
- print("loaded base64")
2232
-
2233
-
2234
-
2235
- local function load_state()
2236
- ao = ao or require('.ao')
2237
- local state = {}
2238
- local stringify = require('.stringify')
2239
- local utils = require('.utils')
2240
-
2241
- Colors = { red = "\27[31m", green = "\27[32m",
2242
- blue = "\27[34m", reset = "\27[0m", gray = "\27[90m"
2243
- }
2244
- Bell = "\x07"
2245
-
2246
- Initialized = Initialized or false
2247
- Name = Name or "aos"
2248
-
2249
- Owner = Owner or ""
2250
- Inbox = Inbox or {}
2251
-
2252
- -- global prompt function
2253
- function Prompt()
2254
- return "aos> "
2255
- end
2256
-
2257
- local maxInboxCount = 10000
2258
-
2259
- function state.insertInbox(msg)
2260
- table.insert(Inbox, msg)
2261
- local overflow = #Inbox - maxInboxCount
2262
- for i = 1,overflow do
2263
- table.remove(Inbox,1)
2264
- end
2265
- end
2266
- local function getOwnerAddress(m)
2267
- local _owner = nil
2268
- utils.map(function (k)
2269
- local c = m.commitments[k]
2270
- if c.alg == "rsa-pss-sha512" then
2271
- _owner = c.committer
2272
- elseif c.alg == "signed" and c['commitment-device'] == "ans104" then
2273
- _owner = c.commiter
2274
- end
2275
- end, utils.keys(m.commitments))
2276
- return _owner
2277
- end
2278
-
2279
- local function isFromOwner(m)
2280
- local _owner = getOwnerAddress(m)
2281
- local _fromProcess = m['from-process'] or _owner
2282
- return _owner ~= nil and _fromProcess == _owner
2283
- end
2284
-
2285
- local function getOwner(m)
2286
- local id = ""
2287
- if m['from-process'] then
2288
- return m['from-process']
2289
- end
2290
-
2291
- utils.map(function (k)
2292
- local c = m.commitments[k]
2293
- if c.alg == "rsa-pss-sha512" then
2294
- id = c.committer
2295
- elseif c.alg == "signed" and c['commitment-device'] == "ans104" then
2296
- id = c.committer
2297
- end
2298
- end, utils.keys(m.commitments)
2299
- )
2300
- return id
2301
- end
2302
-
2303
- function state.init(req, base)
2304
- if not Initialized then
2305
- Owner = getOwner(base.process)
2306
- -- if process id is equal to message id then set Owner
2307
- -- TODO: need additional check, like msg.Slot == 1
2308
- -- if env.Process.Id == msg.Id and Owner ~= msg.Id then
2309
- -- Owner = env.Process['From-Process'] or msg.From
2310
- -- end
2311
- -- if env.Process.Name then
2312
- -- Name = Name == "aos" and env.Process.Name
2313
- -- end
2314
- -- global print function
2315
- function print(a)
2316
- if type(a) == "table" then
2317
- a = stringify.format(a)
2318
- end
2319
-
2320
- if type(a) == "boolean" then
2321
- a = Colors.blue .. tostring(a) .. Colors.reset
2322
- end
2323
- if type(a) == "nil" then
2324
- a = Colors.red .. tostring(a) .. Colors.reset
2325
- end
2326
- if type(a) == "number" then
2327
- a = Colors.green .. tostring(a) .. Colors.reset
2328
- end
2329
-
2330
- if HandlerPrintLogs then
2331
- table.insert(HandlerPrintLogs, a)
2332
- return nil
2333
- end
2334
-
2335
- return tostring(a)
2336
- end
2337
-
2338
- Initialized = true
2339
- end
2340
- end
2341
-
2342
- function state.getFrom(req)
2343
- return getOwner(req.body)
2344
- end
2345
-
2346
- function state.isTrusted(req)
2347
- if isFromOwner(req.body) then
2348
- return true
2349
- end
2350
- local _trusted = false
2351
-
2352
- if req.body['from-process'] then
2353
- _trusted = utils.includes(
2354
- req.body['from-process'],
2355
- ao.authorities
2356
- )
2357
- end
2358
-
2359
- if not _trusted then
2360
- _trusted = utils.includes(
2361
- getOwner(req.body), ao.authorities
2362
- )
2363
- end
2364
- return _trusted
2365
- end
2366
-
2367
- function state.checkSlot(req, ao)
2368
- -- slot check
2369
- if not ao.slot then
2370
- ao.slot = tonumber(req.slot)
2371
- else
2372
- if tonumber(req.slot) ~= (ao.slot + 1) then
2373
- print(table.concat({
2374
- Colors.red,
2375
- "WARNING: Slot did not match, may be due to an error generated by process",
2376
- Colors.reset
2377
- }))
2378
- print("")
2379
- end
2380
- end
2381
- end
2382
-
2383
- function state.reset(tbl)
2384
- tbl = nil
2385
- collectgarbage()
2386
- return {}
2387
- end
2388
-
2389
- return state
2390
-
2391
- end
2392
- _G.package.loaded[".state"] = load_state()
2393
- print("loaded state")
2394
-
2395
-
2396
-
2397
- local function load_process()
2398
- ao = ao or require('.ao')
2399
- Handlers = require('.handlers')
2400
- Utils = require('.utils')
2401
- Dump = require('.dump')
2402
-
2403
- local process = { _version = "2.0.7" }
2404
- local state = require('.state')
2405
- local eval = require('.eval')
2406
- local default = require('.default')
2407
- local json = require('.json')
2408
-
2409
- function Prompt()
2410
- return "aos> "
2411
- end
2412
-
2413
- function process.handle(req, base)
2414
- HandlerPrintLogs = state.reset(HandlerPrintLogs)
2415
- os.time = function () return tonumber(req['block-timestamp']) end
2416
-
2417
- ao.init(base)
2418
- -- initialize state
2419
- state.init(req, base)
2420
-
2421
-
2422
- -- magic table
2423
- req.body.data = req.body['Content-Type'] == 'application/json'
2424
- and json.decode(req.body.data or "{}")
2425
- or req.body.data
2426
-
2427
- Errors = Errors or {}
2428
- -- clear outbox
2429
- ao.clearOutbox()
2430
-
2431
- if not state.isTrusted(req) then
2432
- return ao.result({
2433
- Output = {
2434
- data = "Message is not trusted."
2435
- }
2436
- })
2437
- end
2438
-
2439
- req.reply = function (_reply)
2440
- local _from = state.getFrom(req)
2441
- _reply.target = _reply.target and _reply.target or _from
2442
- _reply['x-reference'] = req.body.reference or nil
2443
- _reply['x-origin'] = req.body['x-origin'] or nil
2444
- return ao.send(_reply)
2445
- end
2446
-
2447
-
2448
- -- state.checkSlot(msg, ao)
2449
- Handlers.add("_eval", function (_req)
2450
- local function getMsgFrom(m)
2451
- local from = ""
2452
- Utils.map(
2453
- function (k)
2454
- local c = m.commitments[k]
2455
- if c.alg == "rsa-pss-sha512" then
2456
- from = c.committer
2457
- end
2458
- end,
2459
- Utils.keys(m.commitments)
2460
- )
2461
- return from
2462
- end
2463
- return _req.body.action == "Eval" and Owner == getMsgFrom(_req.body)
2464
- end, eval(ao))
2465
-
2466
- Handlers.add("_default",
2467
- function () return true end,
2468
- default(state.insertInbox)
2469
- )
2470
-
2471
- local status, error = pcall(Handlers.evaluate, req, base)
2472
-
2473
- -- cleanup handlers so that they are always at the end of the pipeline
2474
- Handlers.remove("_eval")
2475
- Handlers.remove("_default")
2476
-
2477
- local printData = table.concat(HandlerPrintLogs, "\n")
2478
- if not status then
2479
- if req.body.action == "Eval" then
2480
- return {
2481
- Error = table.concat({
2482
- printData,
2483
- "\n",
2484
- Colors.red,
2485
- "error: " .. error,
2486
- Colors.reset,
2487
- })
2488
- }
2489
- end
2490
- print(Colors.red .. "Error" .. Colors.gray .. " handling message " .. Colors.reset)
2491
- print(Colors.green .. error .. Colors.reset)
2492
- -- print("\n" .. Colors.gray .. debug.traceback() .. Colors.reset)
2493
- return ao.result({
2494
- Output = {
2495
- data = printData .. '\n\n' .. Colors.red .. 'error:\n' .. Colors.reset .. error
2496
- },
2497
- Messages = {},
2498
- Spawns = {},
2499
- Assignments = {}
2500
- })
2501
- end
2502
-
2503
- local response = {}
2504
-
2505
- if req.body.action == "Eval" then
2506
- response = ao.result({
2507
- Output = {
2508
- data = printData,
2509
- prompt = Prompt()
2510
- }
2511
- })
2512
- else
2513
- response = ao.result({
2514
- Output = {
2515
- data = printData,
2516
- prompt = Prompt(),
2517
- print = true
2518
- }
2519
- })
2520
- end
2521
-
2522
- HandlerPrintLogs = state.reset(HandlerPrintLogs) -- clear logs
2523
- -- ao.Slot = msg.Slot
2524
- return response
2525
- end
2526
-
2527
- function Version()
2528
- print("version: " .. process._version)
2529
- end
2530
-
2531
- return process
2532
-
2533
- end
2534
- _G.package.loaded[".process"] = load_process()
2535
- print("loaded process")
2536
-
2537
-
2538
- ---@diagnostic disable lowercase-global
2539
- function compute(base, req, opts)
2540
- -- local _ao = require('.ao')
2541
- local _process = require('.process')
2542
-
2543
- ao.event(base.process)
2544
- ao.event(req.body)
2545
- local _results = _process.handle(req, base)
2546
- base.results = {
2547
- info = "hyper-aos",
2548
- outbox = {},
2549
- output = _results.Output
2550
- }
2551
- for i=1,#_results.Messages do
2552
- base.results.outbox[tostring(i)] = _results.Messages[i]
2553
- end
2554
- return base
2555
- end
2556
-
2557
-
2558
- print [[ _ ___ ____
2559
- / \ / _ \/ ___|
2560
- / _ \| | | \___ \
2561
- / ___ \ |_| |___) |
2562
- /_/ \_\___/|____/
2563
- ]]