logic.lua 36KB

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