No Description

logic.lua 37KB

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