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.

1315 lines
38KB

  1. local function pack(...)
  2. return {n = select('#', ...), ...}
  3. end
  4. local array_mt = {}
  5. local function mark_as_array(tab)
  6. return setmetatable(tab, array_mt)
  7. end
  8. local function array(...)
  9. return mark_as_array({...})
  10. end
  11. local function is_array(tab)
  12. return getmetatable(tab) == array_mt
  13. end
  14. local function null()
  15. return nil
  16. end
  17. local function string_starts(String, Start)
  18. return string.sub(String, 1, string.len(Start)) == Start
  19. end
  20. local function string_ends(String, End)
  21. return End == '' or string.sub(String, -string.len(End)) == End
  22. end
  23. local function is_logic(closure, tab)
  24. local is_object = type(tab) == 'table' and not closure.opts.is_nil(tab) and not closure.opts.is_array(tab)
  25. if is_object == false then
  26. return false
  27. end
  28. local contains_one_key = false
  29. for _, v in pairs(tab) do
  30. if not contains_one_key and type(v) ~= 'function' then
  31. contains_one_key = true
  32. else
  33. return false
  34. end
  35. end
  36. return is_object and contains_one_key
  37. end
  38. local function js_to_boolean(closure, value)
  39. if value == 0 or value == '' or value == '' or closure.opts.is_nil(value) then
  40. return false
  41. end
  42. if type(value) == 'number' and value ~= value then
  43. return false
  44. end
  45. return not (not value)
  46. end
  47. local function js_reducible_array_to_string(closure, value)
  48. if closure.opts.is_array(value) then
  49. local newval = value
  50. while #newval == 1 and closure.opts.is_array(newval[1]) do
  51. -- reduce array that only contain array
  52. newval = newval[1]
  53. end
  54. if #newval == 0 then
  55. return ''
  56. elseif #newval == 1 then
  57. return tostring(newval[1])
  58. end
  59. end
  60. return value
  61. end
  62. local function js_to_number(closure, value)
  63. value = js_reducible_array_to_string(closure, value)
  64. if value == 0 or value == '' or value == '0' or value == false then
  65. return 0
  66. end
  67. if value == true then
  68. return 1
  69. end
  70. local n = tonumber(value)
  71. if type(n) ~= 'number' then
  72. return 0 / 0
  73. end
  74. return n
  75. end
  76. local function js_is_equal(closure, a, b)
  77. if type(a) == type(b) then
  78. return a == b
  79. end
  80. if closure.opts.is_nil(a) or closure.opts.is_nil(b) then
  81. return a == b
  82. end
  83. -- handle empty or single item array
  84. if closure.opts.is_array(a) or closure.opts.is_array(b) then
  85. local a_ar = js_reducible_array_to_string(closure, a)
  86. local b_ar = js_reducible_array_to_string(closure, b)
  87. if type(a_ar) == 'string' and type(b_ar) == 'string' then
  88. return a_ar == b_ar
  89. end
  90. end
  91. -- convert to number
  92. local a_num = js_to_number(closure, a)
  93. local b_num = js_to_number(closure, b)
  94. return a_num == b_num
  95. end
  96. local function js_array_to_string(closure, a)
  97. local res = ''
  98. local stack = {}
  99. local local_closure = {
  100. table = a,
  101. index = 1
  102. }
  103. local first = true
  104. while local_closure ~= nil do
  105. local fully_iterated = true
  106. for i = local_closure.index, #local_closure.table, 1 do
  107. local v = local_closure.table[i]
  108. local str
  109. if closure.opts.is_array(v) then
  110. -- prevent recursive loop
  111. local recurse = false
  112. for _, saved in pairs(stack) do
  113. if saved.table == v then
  114. recurse = true
  115. break
  116. end
  117. end
  118. if recurse then
  119. break
  120. end
  121. --
  122. -- add to stack
  123. local_closure.index = i + 1
  124. table.insert(stack, local_closure)
  125. local_closure = {table = v, index = 1}
  126. fully_iterated = false
  127. --
  128. break
  129. elseif type(v) == 'table' then
  130. str = '[object Object]'
  131. else
  132. str = tostring(v)
  133. end
  134. -- add comma between array items
  135. if first then
  136. first = false
  137. else
  138. res = res .. ','
  139. end
  140. --
  141. res = res .. str
  142. end
  143. if fully_iterated then
  144. local_closure = table.remove(stack)
  145. end
  146. end
  147. return res
  148. end
  149. local function js_to_string(closure, a)
  150. if closure.opts.is_array(a) then
  151. return js_array_to_string(closure, a)
  152. elseif type(a) == 'table' then
  153. -- object
  154. return '[object Object]'
  155. end
  156. -- others
  157. return tostring(a)
  158. end
  159. local operations = {}
  160. operations.__index = operations
  161. operations['!!'] = function(closure, a)
  162. return js_to_boolean(closure, a)
  163. end
  164. operations['!'] = function(closure, a)
  165. return not js_to_boolean(closure, a)
  166. end
  167. operations['=='] = function(closure, a, b)
  168. return js_is_equal(closure, a, b)
  169. end
  170. operations['==='] = function(_, a, b)
  171. return a == b
  172. end
  173. operations['!='] = function(closure, a, b)
  174. return not js_is_equal(closure, a, b)
  175. end
  176. operations['!=='] = function(_, a, b)
  177. return a ~= b
  178. end
  179. operations['>'] = function(closure, a, b)
  180. a = js_to_number(closure, a)
  181. b = js_to_number(closure, b)
  182. return a == a and b == b and a > b
  183. end
  184. operations['>='] = function(closure, a, b)
  185. a = js_to_number(closure, a)
  186. b = js_to_number(closure, b)
  187. return a == a and b == b and a >= b
  188. end
  189. operations['<'] = function(closure, a, b, c)
  190. a = js_to_number(closure, a)
  191. b = js_to_number(closure, b)
  192. if closure.opts.is_nil(c) then
  193. return a == a and b == b and a < b
  194. else
  195. c = js_to_number(closure, c)
  196. return a == a and b == b and c == c and a < b and b < c
  197. end
  198. end
  199. operations['<='] = function(closure, a, b, c)
  200. a = js_to_number(closure, a)
  201. b = js_to_number(closure, b)
  202. if closure.opts.is_nil(c) then
  203. return a == a and b == b and a <= b
  204. else
  205. c = js_to_number(closure, c)
  206. return a == a and b == b and c == c and a <= b and b <= c
  207. end
  208. end
  209. operations['+'] = function(closure, ...)
  210. local arg = pack(...)
  211. local a = 0
  212. for i = 1, arg.n do
  213. local v = arg[i]
  214. if i == 1 and type(v) == 'string' and arg.n > 1 then
  215. a = ''
  216. end
  217. if type(a) == 'string' then
  218. a = a .. js_to_string(closure, v)
  219. else
  220. local n = js_to_number(closure, v)
  221. if n == n then
  222. a = a + n
  223. else
  224. a = js_to_string(closure, a) .. js_to_string(closure, v)
  225. end
  226. end
  227. end
  228. return a
  229. end
  230. operations['*'] = function(closure, ...)
  231. local arg = pack(...)
  232. local a = 1
  233. for i = 1, arg.n do
  234. local v = arg[i]
  235. a = a * js_to_number(closure, v)
  236. end
  237. return a
  238. end
  239. operations['-'] = function(closure, a, b)
  240. a = js_to_number(closure, a)
  241. if closure.opts.is_nil(b) then
  242. return -a
  243. end
  244. b = js_to_number(closure, b)
  245. return a - b
  246. end
  247. operations['/'] = function(closure, a, b)
  248. a = js_to_number(closure, a)
  249. b = js_to_number(closure, b)
  250. return a / b
  251. end
  252. operations['%'] = function(closure, a, b)
  253. a = js_to_number(closure, a)
  254. b = js_to_number(closure, b)
  255. return a % b
  256. end
  257. operations['min'] = function(closure, ...)
  258. local arg = pack(...)
  259. for i = 1, arg.n do
  260. local v = arg[i]
  261. v = js_to_number(closure, v)
  262. if v ~= v then
  263. return v
  264. end
  265. arg[i] = v
  266. end
  267. return math.min(unpack(arg))
  268. end
  269. operations['max'] = function(closure, ...)
  270. local arg = pack(...)
  271. for i = 1, arg.n do
  272. local v = arg[i]
  273. v = js_to_number(closure, v)
  274. if v ~= v then
  275. return v
  276. end
  277. arg[i] = v
  278. end
  279. return math.max(unpack(arg))
  280. end
  281. operations['log'] = function(_, a)
  282. print(a)
  283. return a
  284. end
  285. operations['in'] = function(closure, a, b)
  286. if closure.opts.is_array(b) then
  287. for _, v in ipairs(b) do
  288. if v == a then
  289. return true
  290. end
  291. end
  292. elseif type(b) == 'table' then
  293. for _, v in pairs(b) do
  294. if v == a then
  295. return true
  296. end
  297. end
  298. elseif type(b) == 'string' then
  299. local i = string.find(b, tostring(a))
  300. return i ~= nil
  301. end
  302. return false
  303. end
  304. operations['cat'] = function(closure, ...)
  305. local arg = pack(...)
  306. local res = ''
  307. for i = 1, arg.n do
  308. local v = arg[i]
  309. res = res .. js_to_string(closure, v)
  310. end
  311. return res
  312. end
  313. operations['substr'] = function(closure, source, st, en)
  314. if closure.opts.is_nil(st) then
  315. return source
  316. end
  317. if st >= 0 then
  318. st = st + 1
  319. end
  320. if closure.opts.is_nil(en) then
  321. return string.sub(source, st)
  322. end
  323. if en >= 0 then
  324. en = st + en - 1
  325. else
  326. en = string.len(source) + en
  327. end
  328. return string.sub(source, st, en)
  329. end
  330. operations['merge'] = function(closure, ...)
  331. local arg = pack(...)
  332. if arg.n < 1 then
  333. return closure.opts.array()
  334. end
  335. local res = closure.opts.array()
  336. for i = 1, arg.n do
  337. local v = arg[i]
  338. if not closure.opts.is_array(v) then
  339. table.insert(res, v)
  340. else
  341. for _, sv in ipairs(v) do
  342. table.insert(res, sv)
  343. end
  344. end
  345. end
  346. return res
  347. end
  348. operations['var'] = function(closure, attr, default)
  349. local data = closure.data
  350. if closure.opts.is_nil(attr) or attr == '' then
  351. return data
  352. end
  353. if closure.opts.is_nil(data) or type(data) ~= 'table' then
  354. return data
  355. end
  356. if type(attr) == 'number' then
  357. local val = data[attr + 1]
  358. if closure.opts.is_nil(val) then
  359. return default
  360. end
  361. return val
  362. end
  363. if type(attr) ~= 'string' then
  364. return closure.opts.null()
  365. end
  366. if (string_starts(attr, '.') or string_ends(attr, '.')) then
  367. return closure.opts.null()
  368. end
  369. for sub in attr:gmatch('([^\\.]+)') do
  370. data = data[sub]
  371. if closure.opts.is_nil(data) then
  372. return default
  373. end
  374. end
  375. return data
  376. end
  377. operations['missing'] = function(closure, ...)
  378. local arg = pack(...)
  379. local missing = closure.opts.array()
  380. local keys = arg
  381. if closure.opts.is_array(keys[1]) then
  382. keys = keys[1]
  383. end
  384. for i = 1, keys.n or #keys do
  385. local attr = keys[i]
  386. local val = operations.var(closure, attr)
  387. if closure.opts.is_nil(val) or val == '' then
  388. table.insert(missing, attr)
  389. end
  390. end
  391. return missing
  392. end
  393. operations['missingSome'] = function(closure, minimum, keys)
  394. local missing = operations.missing(closure, unpack(keys))
  395. if #keys - #missing >= minimum then
  396. return closure.opts.array()
  397. else
  398. return missing
  399. end
  400. end
  401. operations['method'] = function(closure, obj, method, ...)
  402. if obj ~= nil and method ~= nil then
  403. return obj[method](obj, ...)
  404. end
  405. return closure.opts.null()
  406. end
  407. operations['join'] = function(closure, separator, items)
  408. if not closure.opts.is_array(items) then
  409. return closure.opts.null()
  410. end
  411. if not js_to_boolean(closure, separator) then
  412. return js_to_string(closure, items)
  413. end
  414. local res = ''
  415. for i, v in ipairs(items) do
  416. if i > 1 then
  417. res = res .. js_to_string(closure, separator)
  418. end
  419. res = res .. js_to_string(closure, v)
  420. end
  421. return res
  422. end
  423. operations['length'] = function(_, obj)
  424. if type(obj) == 'string' then
  425. return string.len(obj)
  426. end
  427. if type(obj) == 'table' then
  428. return #obj
  429. end
  430. return 0
  431. end
  432. operations['typeof'] = function(closure, v)
  433. if closure.opts.null() == v then
  434. return 'object'
  435. end
  436. local t = type(v)
  437. if t == 'nil' then
  438. return 'undefined'
  439. end
  440. if t == 'table' or t == 'userdata' or t == 'thread' then
  441. return 'object'
  442. end
  443. return t
  444. end
  445. operations['isArray'] = function(closure, v)
  446. return closure.opts.is_array(v)
  447. end
  448. operations['toUpperCase'] = function(_, v)
  449. if type(v) ~= "string" then
  450. return nil
  451. end
  452. return string.upper(v)
  453. end
  454. operations['toLowerCase'] = function(_, v)
  455. if type(v) ~= "string" then
  456. return nil
  457. end
  458. return string.lower(v)
  459. end
  460. -- snake-case alias to be compatible with original json logic
  461. operations['missing_some'] = operations['missingSome']
  462. operations['is_array'] = operations['isArray']
  463. local function get_operator(tab)
  464. for k, _ in pairs(tab) do
  465. return k
  466. end
  467. return nil
  468. end
  469. local function table_copy_fill(source, opts)
  470. local target = {}
  471. for i, _ in pairs(source) do
  472. target[i] = opts.null() or true
  473. end
  474. if opts.is_array(source) then
  475. opts.mark_as_array(target)
  476. end
  477. return target
  478. end
  479. function recurse_array(stack, closure, last_child_result)
  480. -- zero length
  481. if #closure.logic == 0 then
  482. return table.remove(stack), closure.opts.array()
  483. end
  484. -- state initialization
  485. closure.state.length = closure.state.length or #closure.logic
  486. closure.state.index = closure.state.index or 1
  487. closure.state.recursed = closure.state.recursed or {}
  488. closure.state.normalized = closure.state.normalized or table_copy_fill(closure.logic, closure.opts)
  489. --
  490. -- recurse if necessary
  491. if not closure.state.recursed[closure.state.index] then
  492. closure.state.recursed[closure.state.index] = true
  493. table.insert(stack, closure)
  494. closure = {
  495. logic = closure.logic[closure.state.index],
  496. data = closure.data,
  497. state = {},
  498. opts = closure.opts
  499. }
  500. return closure, last_child_result
  501. end
  502. closure.state.normalized[closure.state.index] = last_child_result
  503. --
  504. -- process next item if available
  505. if closure.state.index < closure.state.length then
  506. closure.state.index = closure.state.index + 1
  507. return closure, last_child_result
  508. end
  509. return table.remove(stack), closure.state.normalized
  510. end
  511. local recurser = {}
  512. -- 'if' should be called with a odd number of parameters, 3 or greater
  513. -- This works on the pattern:
  514. -- if( 0 ){ 1 }else{ 2 };
  515. -- if( 0 ){ 1 }else if( 2 ){ 3 }else{ 4 };
  516. -- if( 0 ){ 1 }else if( 2 ){ 3 }else if( 4 ){ 5 }else{ 6 };
  517. -- The implementation is:
  518. -- For pairs of values (0,1 then 2,3 then 4,5 etc)
  519. -- If the first evaluates truthy, evaluate and return the second
  520. -- If the first evaluates falsy, jump to the next pair (e.g, 0,1 to 2,3)
  521. -- given one parameter, evaluate and return it. (it's an Else and all the If/ElseIf were false)
  522. -- given 0 parameters, return NULL (not great practice, but there was no Else)
  523. recurser['if'] = function(stack, closure, last_child_result)
  524. local op = get_operator(closure.logic)
  525. -- zero or one length
  526. if #closure.logic[op] <= 1 then
  527. return table.remove(stack), nil
  528. end
  529. -- state initialization
  530. closure.state.length = closure.state.length or #closure.logic[op]
  531. closure.state.index = closure.state.index or 1
  532. closure.state.recursed = closure.state.recursed or {}
  533. closure.state.normalized[op] = closure.state.normalized[op] or closure.opts.array()
  534. --
  535. -- recurse if haven't
  536. if not closure.state.recursed[closure.state.index] then
  537. closure.state.recursed[closure.state.index] = true
  538. table.insert(stack, closure)
  539. closure = {
  540. logic = closure.logic[op][closure.state.index],
  541. data = closure.data,
  542. state = {},
  543. opts = closure.opts
  544. }
  545. return closure, last_child_result
  546. end
  547. closure.state.normalized[op][closure.state.index] = last_child_result
  548. --
  549. if closure.state.index % 2 == 1 and closure.state.index < closure.state.length then
  550. if js_to_boolean(closure, closure.state.normalized[op][closure.state.index]) then
  551. -- closure conditions is true
  552. closure.state.index = closure.state.index + 1
  553. else
  554. -- closure conditions is false
  555. closure.state.index = closure.state.index + 2
  556. end
  557. else
  558. last_child_result = closure.state.normalized[op][closure.state.index]
  559. closure = table.remove(stack)
  560. end
  561. return closure, last_child_result
  562. end
  563. recurser['?:'] = recurser['if']
  564. recurser['and'] = function(stack, closure, last_child_result)
  565. local op = get_operator(closure.logic)
  566. -- zero length
  567. if #closure.logic[op] == 0 then
  568. return table.remove(stack), nil
  569. end
  570. -- state initialization
  571. closure.state.length = closure.state.length or #closure.logic[op]
  572. closure.state.index = closure.state.index or 1
  573. closure.state.recursed = closure.state.recursed or {}
  574. closure.state.normalized[op] = closure.state.normalized[op] or closure.opts.array()
  575. --
  576. -- recurse if haven't
  577. if not closure.state.recursed[closure.state.index] then
  578. closure.state.recursed[closure.state.index] = true
  579. table.insert(stack, closure)
  580. closure = {
  581. logic = closure.logic[op][closure.state.index],
  582. data = closure.data,
  583. state = {},
  584. opts = closure.opts
  585. }
  586. return closure, last_child_result
  587. end
  588. closure.state.normalized[op][closure.state.index] = last_child_result
  589. --
  590. if
  591. js_to_boolean(closure, closure.state.normalized[op][closure.state.index]) and
  592. closure.state.index < closure.state.length
  593. then
  594. -- closure condition is true
  595. closure.state.index = closure.state.index + 1
  596. else
  597. -- closure condition is false or the last element
  598. last_child_result = closure.state.normalized[op][closure.state.index]
  599. closure = table.remove(stack)
  600. end
  601. return closure, last_child_result
  602. end
  603. recurser['or'] = function(stack, closure, last_child_result)
  604. local op = get_operator(closure.logic)
  605. -- zero length
  606. if #closure.logic[op] == 0 then
  607. closure = table.remove(stack)
  608. return closure, nil
  609. end
  610. -- state initialization
  611. closure.state.length = closure.state.length or #closure.logic[op]
  612. closure.state.index = closure.state.index or 1
  613. closure.state.recursed = closure.state.recursed or {}
  614. closure.state.normalized[op] = closure.state.normalized[op] or closure.opts.array()
  615. --
  616. -- recurse if necessary
  617. if not closure.state.recursed[closure.state.index] then
  618. closure.state.recursed[closure.state.index] = true
  619. table.insert(stack, closure)
  620. closure = {
  621. logic = closure.logic[op][closure.state.index],
  622. data = closure.data,
  623. state = {},
  624. opts = closure.opts
  625. }
  626. return closure, last_child_result
  627. end
  628. closure.state.normalized[op][closure.state.index] = last_child_result
  629. --
  630. if
  631. not js_to_boolean(closure, closure.state.normalized[op][closure.state.index]) and
  632. closure.state.index < closure.state.length
  633. then
  634. -- if true then continue next
  635. closure.state.index = closure.state.index + 1
  636. else
  637. -- false or the last element
  638. last_child_result = closure.state.normalized[op][closure.state.index]
  639. closure = table.remove(stack)
  640. end
  641. return closure, last_child_result
  642. end
  643. recurser['filter'] = function(stack, closure, last_child_result)
  644. local op = get_operator(closure.logic)
  645. -- zero length
  646. if #closure.logic[op] == 0 then
  647. return table.remove(stack), nil
  648. end
  649. -- state initialization
  650. closure.state.index = closure.state.index or 1
  651. closure.state.recursed = closure.state.recursed or false
  652. closure.state.scoped = closure.state.scoped or false
  653. closure.state.filtered = closure.state.filtered or {}
  654. closure.state.result = closure.state.result or closure.opts.array()
  655. closure.state.normalized[op] = closure.state.normalized[op] or closure.opts.array()
  656. --
  657. -- recurse scoped_data if necessary
  658. if not closure.state.recursed then
  659. closure.state.recursed = true
  660. table.insert(stack, closure)
  661. closure = {
  662. logic = closure.logic[op][1],
  663. data = closure.data,
  664. state = {},
  665. opts = closure.opts
  666. }
  667. return closure, last_child_result
  668. end
  669. if not closure.state.scoped then
  670. if type(last_child_result) ~= 'table' then
  671. return table.remove(stack), nil
  672. end
  673. closure.state.scoped = true
  674. closure.state.normalized[op][1] = last_child_result
  675. closure.state.length = #closure.state.normalized[op][1]
  676. end
  677. --
  678. -- filter and recurse if necessary
  679. local scoped_data = closure.state.normalized[op][1]
  680. if not closure.state.filtered[closure.state.index] then
  681. closure.state.filtered[closure.state.index] = true
  682. table.insert(stack, closure)
  683. closure = {
  684. logic = closure.logic[op][2],
  685. data = scoped_data[closure.state.index],
  686. state = {},
  687. opts = closure.opts
  688. }
  689. return closure, last_child_result
  690. end
  691. if js_to_boolean(closure, last_child_result) then
  692. table.insert(closure.state.result, scoped_data[closure.state.index])
  693. end
  694. --
  695. if closure.state.index < closure.state.length then
  696. closure.state.index = closure.state.index + 1
  697. else
  698. last_child_result = closure.state.result
  699. closure = table.remove(stack)
  700. end
  701. return closure, last_child_result
  702. end
  703. recurser['map'] = function(stack, closure, last_child_result)
  704. local op = get_operator(closure.logic)
  705. -- zero length
  706. if #closure.logic[op] == 0 then
  707. return table.remove(stack), nil
  708. end
  709. -- state initialization
  710. closure.state.index = closure.state.index or 1
  711. closure.state.recursed = closure.state.recursed or false
  712. closure.state.scoped = closure.state.scoped or false
  713. closure.state.mapped = closure.state.mapped or {}
  714. closure.state.result = closure.state.result or table_copy_fill(closure.logic[op], closure.opts)
  715. closure.state.normalized[op] = closure.state.normalized[op] or closure.opts.array()
  716. --
  717. -- recurse scoped_data if necessary
  718. if not closure.state.recursed then
  719. closure.state.recursed = true
  720. table.insert(stack, closure)
  721. closure = {
  722. logic = closure.logic[op][1],
  723. data = closure.data,
  724. state = {},
  725. opts = closure.opts
  726. }
  727. return closure, last_child_result
  728. end
  729. if not closure.state.scoped then
  730. if type(last_child_result) ~= 'table' then
  731. return table.remove(stack), nil
  732. end
  733. closure.state.scoped = true
  734. closure.state.normalized[op][1] = last_child_result
  735. closure.state.length = #closure.state.normalized[op][1]
  736. end
  737. --
  738. -- map and recurse if necessary
  739. local scoped_data = closure.state.normalized[op][1]
  740. if closure.state.length < 1 then
  741. return table.remove(stack), closure.opts.array()
  742. end
  743. if not closure.state.mapped[closure.state.index] then
  744. closure.state.mapped[closure.state.index] = true
  745. table.insert(stack, closure)
  746. closure = {
  747. logic = closure.logic[op][2],
  748. data = scoped_data[closure.state.index],
  749. state = {},
  750. opts = closure.opts
  751. }
  752. return closure, last_child_result
  753. end
  754. closure.state.result[closure.state.index] = last_child_result
  755. --
  756. if closure.state.index < closure.state.length then
  757. closure.state.index = closure.state.index + 1
  758. else
  759. last_child_result = closure.state.result
  760. closure = table.remove(stack)
  761. end
  762. return closure, last_child_result
  763. end
  764. recurser['reduce'] = function(stack, closure, last_child_result)
  765. local op = get_operator(closure.logic)
  766. -- zero length
  767. if #closure.logic[op] == 0 then
  768. closure = table.remove(stack)
  769. return closure, nil
  770. end
  771. -- state initialization
  772. closure.state.index = closure.state.index or 1
  773. closure.state.recursed = closure.state.recursed or false
  774. closure.state.scoped = closure.state.scoped or false
  775. closure.state.reduced = closure.state.reduced or {}
  776. closure.state.result = closure.state.result or closure.logic[op][3]
  777. closure.state.normalized[op] = closure.state.normalized[op] or closure.opts.array()
  778. --
  779. -- recurse scoped_data if necessary
  780. if not closure.state.recursed then
  781. closure.state.recursed = true
  782. table.insert(stack, closure)
  783. closure = {
  784. logic = closure.logic[op][1],
  785. data = closure.data,
  786. state = {},
  787. opts = closure.opts
  788. }
  789. return closure, last_child_result
  790. end
  791. if not closure.state.scoped then
  792. if type(last_child_result) ~= 'table' then
  793. return table.remove(stack), closure.state.result
  794. end
  795. closure.state.scoped = true
  796. closure.state.normalized[op][1] = last_child_result
  797. closure.state.length = #closure.state.normalized[op][1]
  798. end
  799. --
  800. -- reduce and recurse if necessary
  801. local scoped_data = closure.state.normalized[op][1]
  802. if closure.state.length < 1 then
  803. return table.remove(stack), closure.state.result
  804. end
  805. if not closure.state.reduced[closure.state.index] then
  806. closure.state.reduced[closure.state.index] = true
  807. table.insert(stack, closure)
  808. closure = {
  809. logic = closure.logic[op][2],
  810. data = {
  811. current = scoped_data[closure.state.index],
  812. accumulator = closure.state.result
  813. },
  814. state = {},
  815. opts = closure.opts
  816. }
  817. return closure, last_child_result
  818. end
  819. closure.state.result = last_child_result
  820. --
  821. if closure.state.index < closure.state.length then
  822. closure.state.index = closure.state.index + 1
  823. else
  824. last_child_result = closure.state.result
  825. closure = table.remove(stack)
  826. end
  827. return closure, last_child_result
  828. end
  829. recurser['all'] = function(stack, closure, last_child_result)
  830. local op = get_operator(closure.logic)
  831. -- zero length
  832. if #closure.logic[op] == 0 then
  833. return table.remove(stack), nil
  834. end
  835. -- state initialization
  836. closure.state.index = closure.state.index or 1
  837. closure.state.recursed = closure.state.recursed or false
  838. closure.state.scoped = closure.state.scoped or false
  839. closure.state.checked = closure.state.checked or {}
  840. closure.state.normalized[op] = closure.state.normalized[op] or closure.opts.array()
  841. --
  842. -- recurse scoped_data if necessary
  843. if not closure.state.recursed then
  844. closure.state.recursed = true
  845. table.insert(stack, closure)
  846. closure = {
  847. logic = closure.logic[op][1],
  848. data = closure.data,
  849. state = {},
  850. opts = closure.opts
  851. }
  852. return closure, last_child_result
  853. end
  854. if not closure.state.scoped then
  855. if type(last_child_result) ~= 'table' then
  856. return table.remove(stack), nil
  857. end
  858. closure.state.scoped = true
  859. closure.state.normalized[op][1] = last_child_result
  860. closure.state.length = #closure.state.normalized[op][1]
  861. end
  862. --
  863. -- filter and recurse if necessary
  864. local scoped_data = closure.state.normalized[op][1]
  865. if closure.state.length < 1 then
  866. closure = table.remove(stack)
  867. return closure, nil
  868. end
  869. if not closure.state.checked[closure.state.index] then
  870. closure.state.checked[closure.state.index] = true
  871. table.insert(stack, closure)
  872. closure = {
  873. logic = closure.logic[op][2],
  874. data = scoped_data[closure.state.index],
  875. state = {},
  876. opts = closure.opts
  877. }
  878. return closure, last_child_result
  879. end
  880. --
  881. if js_to_boolean(closure, last_child_result) and closure.state.index < closure.state.length then
  882. closure.state.index = closure.state.index + 1
  883. else
  884. last_child_result = js_to_boolean(closure, last_child_result)
  885. closure = table.remove(stack)
  886. end
  887. return closure, last_child_result
  888. end
  889. recurser['some'] = function(stack, closure, last_child_result)
  890. local op = get_operator(closure.logic)
  891. -- zero length
  892. if #closure.logic[op] == 0 then
  893. return table.remove(stack), nil
  894. end
  895. -- state initialization
  896. closure.state.index = closure.state.index or 1
  897. closure.state.recursed = closure.state.recursed or false
  898. closure.state.scoped = closure.state.scoped or false
  899. closure.state.checked = closure.state.checked or {}
  900. closure.state.normalized[op] = closure.state.normalized[op] or closure.opts.array()
  901. --
  902. -- recurse scoped_data if necessary
  903. if not closure.state.recursed then
  904. closure.state.recursed = true
  905. table.insert(stack, closure)
  906. closure = {
  907. logic = closure.logic[op][1],
  908. data = closure.data,
  909. state = {},
  910. opts = closure.opts
  911. }
  912. return closure, last_child_result
  913. end
  914. if not closure.state.scoped then
  915. if type(last_child_result) ~= 'table' then
  916. return table.remove(stack), nil
  917. end
  918. closure.state.scoped = true
  919. closure.state.normalized[op][1] = last_child_result
  920. closure.state.length = #closure.state.normalized[op][1]
  921. end
  922. --
  923. -- filter and recurse if necessary
  924. local scoped_data = closure.state.normalized[op][1]
  925. if closure.state.length < 1 then
  926. return table.remove(stack), nil
  927. end
  928. if not closure.state.checked[closure.state.index] then
  929. closure.state.checked[closure.state.index] = true
  930. table.insert(stack, closure)
  931. closure = {
  932. logic = closure.logic[op][2],
  933. data = scoped_data[closure.state.index],
  934. state = {},
  935. opts = closure.opts
  936. }
  937. return closure, last_child_result
  938. end
  939. --
  940. if not js_to_boolean(closure, last_child_result) and closure.state.index < closure.state.length then
  941. closure.state.index = closure.state.index + 1
  942. else
  943. last_child_result = js_to_boolean(closure, last_child_result)
  944. closure = table.remove(stack)
  945. end
  946. return closure, last_child_result
  947. end
  948. recurser['none'] = function(stack, closure, last_child_result)
  949. local op = get_operator(closure.logic)
  950. -- zero length
  951. if #closure.logic[op] == 0 then
  952. closure = table.remove(stack)
  953. return closure, nil
  954. end
  955. -- state initialization
  956. closure.state.index = closure.state.index or 1
  957. closure.state.recursed = closure.state.recursed or false
  958. closure.state.scoped = closure.state.scoped or false
  959. closure.state.checked = closure.state.checked or {}
  960. closure.state.normalized[op] = closure.state.normalized[op] or closure.opts.array()
  961. --
  962. -- recurse scoped_data if necessary
  963. if not closure.state.recursed then
  964. closure.state.recursed = true
  965. table.insert(stack, closure)
  966. closure = {
  967. logic = closure.logic[op][1],
  968. data = closure.data,
  969. state = {},
  970. opts = closure.opts
  971. }
  972. return closure, last_child_result
  973. end
  974. if not closure.state.scoped then
  975. if type(last_child_result) ~= 'table' then
  976. return table.remove(stack), nil
  977. end
  978. closure.state.scoped = true
  979. closure.state.normalized[op][1] = last_child_result
  980. closure.state.length = #closure.state.normalized[op][1]
  981. end
  982. --
  983. -- filter and recurse if necessary
  984. local scoped_data = closure.state.normalized[op][1]
  985. if closure.state.length < 1 then
  986. return table.remove(stack), nil
  987. end
  988. if not closure.state.checked[closure.state.index] then
  989. closure.state.checked[closure.state.index] = true
  990. table.insert(stack, closure)
  991. closure = {
  992. logic = closure.logic[op][2],
  993. data = scoped_data[closure.state.index],
  994. state = {},
  995. opts = closure.opts
  996. }
  997. return closure, last_child_result
  998. end
  999. --
  1000. if not js_to_boolean(closure, last_child_result) and closure.state.index < closure.state.length then
  1001. closure.state.index = closure.state.index + 1
  1002. else
  1003. last_child_result = not js_to_boolean(closure, last_child_result)
  1004. closure = table.remove(stack)
  1005. end
  1006. return closure, last_child_result
  1007. end
  1008. local function is_sub_operation(op)
  1009. return type(op) == 'string' and string.find(op, '.', 1, true) and not string_starts(op, '.') and
  1010. not string_ends(op, '.')
  1011. end
  1012. local function get_operation(op_string, available_operations)
  1013. if type(available_operations[op_string]) == 'function' then
  1014. return available_operations[op_string]
  1015. elseif not is_sub_operation(op_string) then
  1016. return nil
  1017. end
  1018. -- op string contain "."
  1019. -- WARN: untested
  1020. local new_op = available_operations
  1021. for sub_op_string in op_string:gmatch('([^\\.]+)') do
  1022. new_op = new_op[sub_op_string]
  1023. if new_op == nil then
  1024. return nil
  1025. end
  1026. end
  1027. if type(new_op) ~= 'function' then
  1028. return nil
  1029. end
  1030. return new_op
  1031. --
  1032. end
  1033. local function recurse_others(stack, closure, last_child_result)
  1034. local err = nil
  1035. local operation_name = get_operator(closure.logic)
  1036. local available_operations
  1037. if type(closure.opts.custom_operations) == 'table' then
  1038. available_operations = setmetatable(closure.opts.custom_operations, operations)
  1039. else
  1040. available_operations = operations
  1041. end
  1042. local operation = get_operation(operation_name, available_operations)
  1043. if operation == nil then
  1044. return table.remove(stack), closure.logic, 'invalid operations'
  1045. end
  1046. -- recurse if haven't
  1047. if not closure.state.recursed then
  1048. closure.state.recursed = {}
  1049. table.insert(stack, closure)
  1050. closure = {
  1051. logic = closure.logic[operation_name],
  1052. data = closure.data,
  1053. state = {},
  1054. opts = closure.opts
  1055. }
  1056. return closure, last_child_result
  1057. end
  1058. --
  1059. if not closure.opts.is_array(last_child_result) then
  1060. last_child_result = closure.opts.mark_as_array({last_child_result})
  1061. end
  1062. closure.state.normalized[operation_name] = last_child_result
  1063. last_child_result = operation(closure, unpack(closure.state.normalized[operation_name]))
  1064. return table.remove(stack), last_child_result, err
  1065. end
  1066. local JsonLogic = {}
  1067. --- function sum description.
  1068. -- apply the json-logic with the given data
  1069. -- some behavior of json-logic can be influenced by 'opts' table
  1070. -- 'opts' can include the following attribute:
  1071. -- is_array = (function), determine wether a table is an array or not
  1072. -- mark_as_array = (function), mark a lua table as an array
  1073. -- null = (function), return value that represent json nill value
  1074. -- custom_operations = (table), a table contains custom operations
  1075. -- blacklist = (table), an array contains list of operations to be blacklisted.
  1076. -- whitelist = (table), an array contains list of operations to be whitelisted.
  1077. -- @tparam table logic description
  1078. -- @tparam table data description
  1079. -- @tparam table opts is a table containing keys:
  1080. -- @return value result of the logic.
  1081. -- @author
  1082. function JsonLogic.apply(logic, data, opts)
  1083. local stack = {}
  1084. local closure = {
  1085. logic = logic,
  1086. data = data,
  1087. state = {
  1088. normalized = nil
  1089. },
  1090. opts = nil
  1091. }
  1092. opts = opts or nil
  1093. if type(opts.is_array) ~= 'function' then
  1094. opts.is_array = is_array
  1095. end
  1096. if type(opts.mark_as_array) ~= 'function' then
  1097. opts.mark_as_array = mark_as_array
  1098. end
  1099. opts.array = function(...)
  1100. return opts.mark_as_array({...})
  1101. end
  1102. if type(opts.null) ~= 'function' then
  1103. opts.null = null
  1104. end
  1105. opts.is_nil = function(v)
  1106. return v == opts.null() or v == nil
  1107. end
  1108. closure.opts = opts
  1109. local last_child_result = opts.null()
  1110. local err = nil
  1111. -- since lua does not have "continue" like statement, we use two loops
  1112. while closure do
  1113. while closure do
  1114. -- recurse array
  1115. if closure.opts.is_array(closure.logic) then
  1116. closure, last_child_result = recurse_array(stack, closure, last_child_result)
  1117. break
  1118. end
  1119. -- You've recursed to a primitive, stop!
  1120. if not is_logic(closure, closure.logic) then
  1121. last_child_result = closure.logic
  1122. closure = table.remove(stack)
  1123. break
  1124. end
  1125. --
  1126. -- literal operator
  1127. local op = get_operator(closure.logic)
  1128. if op == '_' then
  1129. last_child_result = closure.logic[op]
  1130. closure = table.remove(stack)
  1131. break
  1132. end
  1133. -- check for blacklist or non-whitelisted operations
  1134. if type(closure.opts.blacklist) == 'table' and closure.opts.blacklist[op] then
  1135. return closure.logic, 'blacklisted operations'
  1136. elseif type(closure.opts.whitelist) == 'table' and not closure.opts.whitelist[op] then
  1137. return closure.logic, 'non-whitelisted operations'
  1138. end
  1139. --
  1140. closure.data = closure.data or {}
  1141. closure.state.normalized = closure.state.normalized or {}
  1142. if type(recurser[op]) == 'function' then
  1143. closure, last_child_result, err = recurser[op](stack, closure, last_child_result)
  1144. else
  1145. closure, last_child_result, err = recurse_others(stack, closure, last_child_result)
  1146. end
  1147. -- if the result is nil then return the specified null value
  1148. if last_child_result == nil then
  1149. last_child_result = opts.null()
  1150. end
  1151. end
  1152. end
  1153. return last_child_result, err
  1154. end
  1155. function JsonLogic.new_logic(operation, params)
  1156. local lgc = {}
  1157. if operation ~= nil then
  1158. lgc[operation] = params
  1159. end
  1160. return lgc
  1161. end
  1162. JsonLogic.is_array = is_array
  1163. JsonLogic.mark_as_array = mark_as_array
  1164. JsonLogic.array = array
  1165. return JsonLogic