No Description
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.

logic.lua 37KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286
  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['missing_some'] = 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. local function get_operator(tab)
  433. for k, _ in pairs(tab) do
  434. return k
  435. end
  436. return nil
  437. end
  438. local function table_copy_fill(source, opts)
  439. local target = {}
  440. for i, _ in pairs(source) do
  441. target[i] = opts.null() or true
  442. end
  443. if opts.is_array(source) then
  444. opts.mark_as_array(target)
  445. end
  446. return target
  447. end
  448. function recurse_array(stack, closure, last_child_result)
  449. -- zero length
  450. if #closure.logic == 0 then
  451. return table.remove(stack), closure.opts.array()
  452. end
  453. -- state initialization
  454. closure.state.length = closure.state.length or #closure.logic
  455. closure.state.index = closure.state.index or 1
  456. closure.state.recursed = closure.state.recursed or {}
  457. closure.state.normalized = closure.state.normalized or table_copy_fill(closure.logic, closure.opts)
  458. --
  459. -- recurse if necessary
  460. if not closure.state.recursed[closure.state.index] then
  461. closure.state.recursed[closure.state.index] = true
  462. table.insert(stack, closure)
  463. closure = {
  464. logic = closure.logic[closure.state.index],
  465. data = closure.data,
  466. state = {},
  467. opts = closure.opts
  468. }
  469. return closure, last_child_result
  470. end
  471. closure.state.normalized[closure.state.index] = last_child_result
  472. --
  473. -- process next item if available
  474. if closure.state.index < closure.state.length then
  475. closure.state.index = closure.state.index + 1
  476. return closure, last_child_result
  477. end
  478. return table.remove(stack), closure.state.normalized
  479. end
  480. local recurser = {}
  481. -- 'if' should be called with a odd number of parameters, 3 or greater
  482. -- This works on the pattern:
  483. -- if( 0 ){ 1 }else{ 2 };
  484. -- if( 0 ){ 1 }else if( 2 ){ 3 }else{ 4 };
  485. -- if( 0 ){ 1 }else if( 2 ){ 3 }else if( 4 ){ 5 }else{ 6 };
  486. -- The implementation is:
  487. -- For pairs of values (0,1 then 2,3 then 4,5 etc)
  488. -- If the first evaluates truthy, evaluate and return the second
  489. -- If the first evaluates falsy, jump to the next pair (e.g, 0,1 to 2,3)
  490. -- given one parameter, evaluate and return it. (it's an Else and all the If/ElseIf were false)
  491. -- given 0 parameters, return NULL (not great practice, but there was no Else)
  492. recurser['if'] =
  493. function(stack, closure, last_child_result)
  494. local op = get_operator(closure.logic)
  495. -- zero or one length
  496. if #closure.logic[op] <= 1 then
  497. return table.remove(stack), nil
  498. end
  499. -- state initialization
  500. closure.state.length = closure.state.length or #closure.logic[op]
  501. closure.state.index = closure.state.index or 1
  502. closure.state.recursed = closure.state.recursed or {}
  503. closure.state.normalized[op] = closure.state.normalized[op] or closure.opts.array()
  504. --
  505. -- recurse if haven't
  506. if not closure.state.recursed[closure.state.index] then
  507. closure.state.recursed[closure.state.index] = true
  508. table.insert(stack, closure)
  509. closure = {
  510. logic = closure.logic[op][closure.state.index],
  511. data = closure.data,
  512. state = {},
  513. opts = closure.opts
  514. }
  515. return closure, last_child_result
  516. end
  517. closure.state.normalized[op][closure.state.index] = last_child_result
  518. --
  519. if closure.state.index % 2 == 1 and closure.state.index < closure.state.length then
  520. if js_to_boolean(closure, closure.state.normalized[op][closure.state.index]) then
  521. -- closure conditions is true
  522. closure.state.index = closure.state.index + 1
  523. else
  524. -- closure conditions is false
  525. closure.state.index = closure.state.index + 2
  526. end
  527. else
  528. last_child_result = closure.state.normalized[op][closure.state.index]
  529. closure = table.remove(stack)
  530. end
  531. return closure, last_child_result
  532. end
  533. recurser['?:'] = recurser['if']
  534. recurser['and'] =
  535. function(stack, closure, last_child_result)
  536. local op = get_operator(closure.logic)
  537. -- zero length
  538. if #closure.logic[op] == 0 then
  539. return table.remove(stack), nil
  540. end
  541. -- state initialization
  542. closure.state.length = closure.state.length or #closure.logic[op]
  543. closure.state.index = closure.state.index or 1
  544. closure.state.recursed = closure.state.recursed or {}
  545. closure.state.normalized[op] = closure.state.normalized[op] or closure.opts.array()
  546. --
  547. -- recurse if haven't
  548. if not closure.state.recursed[closure.state.index] then
  549. closure.state.recursed[closure.state.index] = true
  550. table.insert(stack, closure)
  551. closure = {
  552. logic = closure.logic[op][closure.state.index],
  553. data = closure.data,
  554. state = {},
  555. opts = closure.opts
  556. }
  557. return closure, last_child_result
  558. end
  559. closure.state.normalized[op][closure.state.index] = last_child_result
  560. --
  561. if
  562. js_to_boolean(closure, closure.state.normalized[op][closure.state.index]) and
  563. closure.state.index < closure.state.length
  564. then
  565. -- closure condition is true
  566. closure.state.index = closure.state.index + 1
  567. else
  568. -- closure condition is false or the last element
  569. last_child_result = closure.state.normalized[op][closure.state.index]
  570. closure = table.remove(stack)
  571. end
  572. return closure, last_child_result
  573. end
  574. recurser['or'] =
  575. function(stack, closure, last_child_result)
  576. local op = get_operator(closure.logic)
  577. -- zero length
  578. if #closure.logic[op] == 0 then
  579. closure = table.remove(stack)
  580. return closure, nil
  581. end
  582. -- state initialization
  583. closure.state.length = closure.state.length or #closure.logic[op]
  584. closure.state.index = closure.state.index or 1
  585. closure.state.recursed = closure.state.recursed or {}
  586. closure.state.normalized[op] = closure.state.normalized[op] or closure.opts.array()
  587. --
  588. -- recurse if necessary
  589. if not closure.state.recursed[closure.state.index] then
  590. closure.state.recursed[closure.state.index] = true
  591. table.insert(stack, closure)
  592. closure = {
  593. logic = closure.logic[op][closure.state.index],
  594. data = closure.data,
  595. state = {},
  596. opts = closure.opts
  597. }
  598. return closure, last_child_result
  599. end
  600. closure.state.normalized[op][closure.state.index] = last_child_result
  601. --
  602. if
  603. not js_to_boolean(closure, closure.state.normalized[op][closure.state.index]) and
  604. closure.state.index < closure.state.length
  605. then
  606. -- if true then continue next
  607. closure.state.index = closure.state.index + 1
  608. else
  609. -- false or the last element
  610. last_child_result = closure.state.normalized[op][closure.state.index]
  611. closure = table.remove(stack)
  612. end
  613. return closure, last_child_result
  614. end
  615. recurser['filter'] =
  616. function(stack, closure, last_child_result)
  617. local op = get_operator(closure.logic)
  618. -- zero length
  619. if #closure.logic[op] == 0 then
  620. return table.remove(stack), nil
  621. end
  622. -- state initialization
  623. closure.state.index = closure.state.index or 1
  624. closure.state.recursed = closure.state.recursed or false
  625. closure.state.scoped = closure.state.scoped or false
  626. closure.state.filtered = closure.state.filtered or {}
  627. closure.state.result = closure.state.result or closure.opts.array()
  628. closure.state.normalized[op] = closure.state.normalized[op] or closure.opts.array()
  629. --
  630. -- recurse scoped_data if necessary
  631. if not closure.state.recursed then
  632. closure.state.recursed = true
  633. table.insert(stack, closure)
  634. closure = {
  635. logic = closure.logic[op][1],
  636. data = closure.data,
  637. state = {},
  638. opts = closure.opts
  639. }
  640. return closure, last_child_result
  641. end
  642. if not closure.state.scoped then
  643. if type(last_child_result) ~= 'table' then
  644. return table.remove(stack), nil
  645. end
  646. closure.state.scoped = true
  647. closure.state.normalized[op][1] = last_child_result
  648. closure.state.length = #closure.state.normalized[op][1]
  649. end
  650. --
  651. -- filter and recurse if necessary
  652. local scoped_data = closure.state.normalized[op][1]
  653. if not closure.state.filtered[closure.state.index] then
  654. closure.state.filtered[closure.state.index] = true
  655. table.insert(stack, closure)
  656. closure = {
  657. logic = closure.logic[op][2],
  658. data = scoped_data[closure.state.index],
  659. state = {},
  660. opts = closure.opts
  661. }
  662. return closure, last_child_result
  663. end
  664. if js_to_boolean(closure, last_child_result) then
  665. table.insert(closure.state.result, scoped_data[closure.state.index])
  666. end
  667. --
  668. if closure.state.index < closure.state.length then
  669. closure.state.index = closure.state.index + 1
  670. else
  671. last_child_result = closure.state.result
  672. closure = table.remove(stack)
  673. end
  674. return closure, last_child_result
  675. end
  676. recurser['map'] =
  677. function(stack, closure, last_child_result)
  678. local op = get_operator(closure.logic)
  679. -- zero length
  680. if #closure.logic[op] == 0 then
  681. return table.remove(stack), nil
  682. end
  683. -- state initialization
  684. closure.state.index = closure.state.index or 1
  685. closure.state.recursed = closure.state.recursed or false
  686. closure.state.scoped = closure.state.scoped or false
  687. closure.state.mapped = closure.state.mapped or {}
  688. closure.state.result = closure.state.result or table_copy_fill(closure.logic[op], closure.opts)
  689. closure.state.normalized[op] = closure.state.normalized[op] or closure.opts.array()
  690. --
  691. -- recurse scoped_data if necessary
  692. if not closure.state.recursed then
  693. closure.state.recursed = true
  694. table.insert(stack, closure)
  695. closure = {
  696. logic = closure.logic[op][1],
  697. data = closure.data,
  698. state = {},
  699. opts = closure.opts
  700. }
  701. return closure, last_child_result
  702. end
  703. if not closure.state.scoped then
  704. if type(last_child_result) ~= 'table' then
  705. return table.remove(stack), nil
  706. end
  707. closure.state.scoped = true
  708. closure.state.normalized[op][1] = last_child_result
  709. closure.state.length = #closure.state.normalized[op][1]
  710. end
  711. --
  712. -- map and recurse if necessary
  713. local scoped_data = closure.state.normalized[op][1]
  714. if closure.state.length < 1 then
  715. return table.remove(stack), closure.opts.array()
  716. end
  717. if not closure.state.mapped[closure.state.index] then
  718. closure.state.mapped[closure.state.index] = true
  719. table.insert(stack, closure)
  720. closure = {
  721. logic = closure.logic[op][2],
  722. data = scoped_data[closure.state.index],
  723. state = {},
  724. opts = closure.opts
  725. }
  726. return closure, last_child_result
  727. end
  728. closure.state.result[closure.state.index] = last_child_result
  729. --
  730. if closure.state.index < closure.state.length then
  731. closure.state.index = closure.state.index + 1
  732. else
  733. last_child_result = closure.state.result
  734. closure = table.remove(stack)
  735. end
  736. return closure, last_child_result
  737. end
  738. recurser['reduce'] =
  739. function(stack, closure, last_child_result)
  740. local op = get_operator(closure.logic)
  741. -- zero length
  742. if #closure.logic[op] == 0 then
  743. closure = table.remove(stack)
  744. return closure, nil
  745. end
  746. -- state initialization
  747. closure.state.index = closure.state.index or 1
  748. closure.state.recursed = closure.state.recursed or false
  749. closure.state.scoped = closure.state.scoped or false
  750. closure.state.reduced = closure.state.reduced or {}
  751. closure.state.result = closure.state.result or closure.logic[op][3]
  752. closure.state.normalized[op] = closure.state.normalized[op] or closure.opts.array()
  753. --
  754. -- recurse scoped_data if necessary
  755. if not closure.state.recursed then
  756. closure.state.recursed = true
  757. table.insert(stack, closure)
  758. closure = {
  759. logic = closure.logic[op][1],
  760. data = closure.data,
  761. state = {},
  762. opts = closure.opts
  763. }
  764. return closure, last_child_result
  765. end
  766. if not closure.state.scoped then
  767. if type(last_child_result) ~= 'table' then
  768. return table.remove(stack), closure.state.result
  769. end
  770. closure.state.scoped = true
  771. closure.state.normalized[op][1] = last_child_result
  772. closure.state.length = #closure.state.normalized[op][1]
  773. end
  774. --
  775. -- reduce and recurse if necessary
  776. local scoped_data = closure.state.normalized[op][1]
  777. if closure.state.length < 1 then
  778. return table.remove(stack), closure.state.result
  779. end
  780. if not closure.state.reduced[closure.state.index] then
  781. closure.state.reduced[closure.state.index] = true
  782. table.insert(stack, closure)
  783. closure = {
  784. logic = closure.logic[op][2],
  785. data = {
  786. current = scoped_data[closure.state.index],
  787. accumulator = closure.state.result
  788. },
  789. state = {},
  790. opts = closure.opts
  791. }
  792. return closure, last_child_result
  793. end
  794. closure.state.result = last_child_result
  795. --
  796. if closure.state.index < closure.state.length then
  797. closure.state.index = closure.state.index + 1
  798. else
  799. last_child_result = closure.state.result
  800. closure = table.remove(stack)
  801. end
  802. return closure, last_child_result
  803. end
  804. recurser['all'] =
  805. function(stack, closure, last_child_result)
  806. local op = get_operator(closure.logic)
  807. -- zero length
  808. if #closure.logic[op] == 0 then
  809. return table.remove(stack), nil
  810. end
  811. -- state initialization
  812. closure.state.index = closure.state.index or 1
  813. closure.state.recursed = closure.state.recursed or false
  814. closure.state.scoped = closure.state.scoped or false
  815. closure.state.checked = closure.state.checked or {}
  816. closure.state.normalized[op] = closure.state.normalized[op] or closure.opts.array()
  817. --
  818. -- recurse scoped_data if necessary
  819. if not closure.state.recursed then
  820. closure.state.recursed = true
  821. table.insert(stack, closure)
  822. closure = {
  823. logic = closure.logic[op][1],
  824. data = closure.data,
  825. state = {},
  826. opts = closure.opts
  827. }
  828. return closure, last_child_result
  829. end
  830. if not closure.state.scoped then
  831. if type(last_child_result) ~= 'table' then
  832. return table.remove(stack), nil
  833. end
  834. closure.state.scoped = true
  835. closure.state.normalized[op][1] = last_child_result
  836. closure.state.length = #closure.state.normalized[op][1]
  837. end
  838. --
  839. -- filter and recurse if necessary
  840. local scoped_data = closure.state.normalized[op][1]
  841. if closure.state.length < 1 then
  842. closure = table.remove(stack)
  843. return closure, nil
  844. end
  845. if not closure.state.checked[closure.state.index] then
  846. closure.state.checked[closure.state.index] = true
  847. table.insert(stack, closure)
  848. closure = {
  849. logic = closure.logic[op][2],
  850. data = scoped_data[closure.state.index],
  851. state = {},
  852. opts = closure.opts
  853. }
  854. return closure, last_child_result
  855. end
  856. --
  857. if js_to_boolean(closure, last_child_result) and closure.state.index < closure.state.length then
  858. closure.state.index = closure.state.index + 1
  859. else
  860. last_child_result = js_to_boolean(closure, last_child_result)
  861. closure = table.remove(stack)
  862. end
  863. return closure, last_child_result
  864. end
  865. recurser['some'] =
  866. function(stack, closure, last_child_result)
  867. local op = get_operator(closure.logic)
  868. -- zero length
  869. if #closure.logic[op] == 0 then
  870. return table.remove(stack), nil
  871. end
  872. -- state initialization
  873. closure.state.index = closure.state.index or 1
  874. closure.state.recursed = closure.state.recursed or false
  875. closure.state.scoped = closure.state.scoped or false
  876. closure.state.checked = closure.state.checked or {}
  877. closure.state.normalized[op] = closure.state.normalized[op] or closure.opts.array()
  878. --
  879. -- recurse scoped_data if necessary
  880. if not closure.state.recursed then
  881. closure.state.recursed = true
  882. table.insert(stack, closure)
  883. closure = {
  884. logic = closure.logic[op][1],
  885. data = closure.data,
  886. state = {},
  887. opts = closure.opts
  888. }
  889. return closure, last_child_result
  890. end
  891. if not closure.state.scoped then
  892. if type(last_child_result) ~= 'table' then
  893. return table.remove(stack), nil
  894. end
  895. closure.state.scoped = true
  896. closure.state.normalized[op][1] = last_child_result
  897. closure.state.length = #closure.state.normalized[op][1]
  898. end
  899. --
  900. -- filter and recurse if necessary
  901. local scoped_data = closure.state.normalized[op][1]
  902. if closure.state.length < 1 then
  903. return table.remove(stack), nil
  904. end
  905. if not closure.state.checked[closure.state.index] then
  906. closure.state.checked[closure.state.index] = true
  907. table.insert(stack, closure)
  908. closure = {
  909. logic = closure.logic[op][2],
  910. data = scoped_data[closure.state.index],
  911. state = {},
  912. opts = closure.opts
  913. }
  914. return closure, last_child_result
  915. end
  916. --
  917. if not js_to_boolean(closure, last_child_result) and closure.state.index < closure.state.length then
  918. closure.state.index = closure.state.index + 1
  919. else
  920. last_child_result = js_to_boolean(closure, last_child_result)
  921. closure = table.remove(stack)
  922. end
  923. return closure, last_child_result
  924. end
  925. recurser['none'] =
  926. function(stack, closure, last_child_result)
  927. local op = get_operator(closure.logic)
  928. -- zero length
  929. if #closure.logic[op] == 0 then
  930. closure = table.remove(stack)
  931. return closure, nil
  932. end
  933. -- state initialization
  934. closure.state.index = closure.state.index or 1
  935. closure.state.recursed = closure.state.recursed or false
  936. closure.state.scoped = closure.state.scoped or false
  937. closure.state.checked = closure.state.checked or {}
  938. closure.state.normalized[op] = closure.state.normalized[op] or closure.opts.array()
  939. --
  940. -- recurse scoped_data if necessary
  941. if not closure.state.recursed then
  942. closure.state.recursed = true
  943. table.insert(stack, closure)
  944. closure = {
  945. logic = closure.logic[op][1],
  946. data = closure.data,
  947. state = {},
  948. opts = closure.opts
  949. }
  950. return closure, last_child_result
  951. end
  952. if not closure.state.scoped then
  953. if type(last_child_result) ~= 'table' then
  954. return table.remove(stack), nil
  955. end
  956. closure.state.scoped = true
  957. closure.state.normalized[op][1] = last_child_result
  958. closure.state.length = #closure.state.normalized[op][1]
  959. end
  960. --
  961. -- filter and recurse if necessary
  962. local scoped_data = closure.state.normalized[op][1]
  963. if closure.state.length < 1 then
  964. return table.remove(stack), nil
  965. end
  966. if not closure.state.checked[closure.state.index] then
  967. closure.state.checked[closure.state.index] = true
  968. table.insert(stack, closure)
  969. closure = {
  970. logic = closure.logic[op][2],
  971. data = scoped_data[closure.state.index],
  972. state = {},
  973. opts = closure.opts
  974. }
  975. return closure, last_child_result
  976. end
  977. --
  978. if not js_to_boolean(closure, last_child_result) and closure.state.index < closure.state.length then
  979. closure.state.index = closure.state.index + 1
  980. else
  981. last_child_result = not js_to_boolean(closure, last_child_result)
  982. closure = table.remove(stack)
  983. end
  984. return closure, last_child_result
  985. end
  986. local function is_sub_operation(op)
  987. return type(op) == 'string' and string.find(op, '.', 1, true) and not string_starts(op, '.') and
  988. not string_ends(op, '.')
  989. end
  990. local function get_operation(op_string, available_operations)
  991. if type(available_operations[op_string]) == 'function' then
  992. return available_operations[op_string]
  993. elseif not is_sub_operation(op_string) then
  994. return nil
  995. end
  996. -- op string contain "."
  997. -- WARN: untested
  998. local new_op = available_operations
  999. for sub_op_string in op_string:gmatch('([^\\.]+)') do
  1000. new_op = new_op[sub_op_string]
  1001. if new_op == nil then
  1002. return nil
  1003. end
  1004. end
  1005. if type(new_op) ~= 'function' then
  1006. return nil
  1007. end
  1008. return new_op
  1009. --
  1010. end
  1011. local function recurse_others(stack, closure, last_child_result)
  1012. local err = nil
  1013. local operation_name = get_operator(closure.logic)
  1014. local available_operations
  1015. if type(closure.opts.custom_operations) == 'table' then
  1016. available_operations = setmetatable(closure.opts.custom_operations, operations)
  1017. else
  1018. available_operations = operations
  1019. end
  1020. local operation = get_operation(operation_name, available_operations)
  1021. if operation == nil then
  1022. return table.remove(stack), closure.logic, 'invalid operations'
  1023. end
  1024. -- recurse if haven't
  1025. if not closure.state.recursed then
  1026. closure.state.recursed = {}
  1027. table.insert(stack, closure)
  1028. closure = {
  1029. logic = closure.logic[operation_name],
  1030. data = closure.data,
  1031. state = {},
  1032. opts = closure.opts
  1033. }
  1034. return closure, last_child_result
  1035. end
  1036. --
  1037. if not closure.opts.is_array(last_child_result) then
  1038. last_child_result = closure.opts.mark_as_array({last_child_result})
  1039. end
  1040. closure.state.normalized[operation_name] = last_child_result
  1041. last_child_result = operation(closure, unpack(closure.state.normalized[operation_name]))
  1042. return table.remove(stack), last_child_result, err
  1043. end
  1044. local JsonLogic = {}
  1045. --- function sum description.
  1046. -- apply the json-logic with the given data
  1047. -- some behavior of json-logic can be influenced by 'opts' table
  1048. -- 'opts' can include the following attribute:
  1049. -- is_array = (function), determine wether a table is an array or not
  1050. -- mark_as_array = (function), mark a lua table as an array
  1051. -- null = (function), return value that represent json nill value
  1052. -- custom_operations = (table), a table contains custom operations
  1053. -- blacklist = (table), an array contains list of operations to be blacklisted.
  1054. -- whitelist = (table), an array contains list of operations to be whitelisted.
  1055. -- @tparam table logic description
  1056. -- @tparam table data description
  1057. -- @tparam table opts is a table containing keys:
  1058. -- @return value result of the logic.
  1059. -- @author
  1060. function JsonLogic.apply(logic, data, opts)
  1061. local stack = {}
  1062. local closure = {
  1063. logic = logic,
  1064. data = data,
  1065. state = {
  1066. normalized = nil
  1067. },
  1068. opts = nil
  1069. }
  1070. opts = opts or nil
  1071. if type(opts.is_array) ~= 'function' then
  1072. opts.is_array = is_array
  1073. end
  1074. if type(opts.mark_as_array) ~= 'function' then
  1075. opts.mark_as_array = mark_as_array
  1076. end
  1077. opts.array = function(...)
  1078. return opts.mark_as_array({...})
  1079. end
  1080. if type(opts.null) ~= 'function' then
  1081. opts.null = null
  1082. end
  1083. opts.is_nil = function(v)
  1084. return v == opts.null() or v == nil
  1085. end
  1086. closure.opts = opts
  1087. local last_child_result = opts.null()
  1088. local err = nil
  1089. -- since lua does not have "continue" like statement, we use two loops
  1090. while closure do
  1091. while closure do
  1092. -- recurse array
  1093. if closure.opts.is_array(closure.logic) then
  1094. closure, last_child_result = recurse_array(stack, closure, last_child_result)
  1095. break
  1096. end
  1097. -- You've recursed to a primitive, stop!
  1098. if not is_logic(closure, closure.logic) then
  1099. last_child_result = closure.logic
  1100. closure = table.remove(stack)
  1101. break
  1102. end
  1103. --
  1104. -- literal operator
  1105. local op = get_operator(closure.logic)
  1106. if op == '_' then
  1107. last_child_result = closure.logic[op]
  1108. closure = table.remove(stack)
  1109. break
  1110. end
  1111. -- check for blacklist or non-whitelisted operations
  1112. if type(closure.opts.blacklist) == 'table' and closure.opts.blacklist[op] then
  1113. return closure.logic, 'blacklisted operations'
  1114. elseif type(closure.opts.whitelist) == 'table' and not closure.opts.whitelist[op] then
  1115. return closure.logic, 'non-whitelisted operations'
  1116. end
  1117. --
  1118. closure.data = closure.data or {}
  1119. closure.state.normalized = closure.state.normalized or {}
  1120. if type(recurser[op]) == 'function' then
  1121. closure, last_child_result, err = recurser[op](stack, closure, last_child_result)
  1122. else
  1123. closure, last_child_result, err = recurse_others(stack, closure, last_child_result)
  1124. end
  1125. -- if the result is nil then return the specified null value
  1126. if last_child_result == nil then
  1127. last_child_result = opts.null()
  1128. end
  1129. end
  1130. end
  1131. return last_child_result, err
  1132. end
  1133. function JsonLogic.new_logic(operation, params)
  1134. local lgc = {}
  1135. if operation ~= nil then
  1136. lgc[operation] = params
  1137. end
  1138. return lgc
  1139. end
  1140. JsonLogic.is_array = is_array
  1141. JsonLogic.mark_as_array = mark_as_array
  1142. JsonLogic.array = array
  1143. return JsonLogic