logic.lua 36 KB

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