toolkit.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640
  1. 'use strict';
  2. const TAB = '\t'
  3. const CR = '\r'
  4. const LF = '\n'
  5. function assert (bool) {
  6. if(!bool) {
  7. throw Error('Assertion Error')
  8. }
  9. return true
  10. }
  11. class Concept {
  12. constructor (contains) {
  13. assert(typeof contains == 'function')
  14. this.contains = contains
  15. }
  16. static intersect (...concepts) {
  17. return new Concept(function (x) {
  18. var result = true
  19. for(let concept of concepts) {
  20. if (!concept.contains(x)) {
  21. result = false
  22. return result
  23. }
  24. }
  25. return result
  26. })
  27. }
  28. static union (...concepts) {
  29. return new Concept(function (x) {
  30. var result = false
  31. for(let concept of concepts) {
  32. if (concept.contains(x)) {
  33. result = true
  34. return result
  35. }
  36. }
  37. return result
  38. })
  39. }
  40. static complement (A) {
  41. return new Concept(x => !A.contains(x))
  42. }
  43. static contains (x) {
  44. return (typeof x.contains == 'function')
  45. }
  46. }
  47. const $ = f => new Concept(f)
  48. const $n = Concept.intersect
  49. const $u = Concept.union
  50. const $_ = Concept.complement
  51. const $d = (x, y) => $n(x, $_(y))
  52. const $1 = y => $(x => x === y)
  53. const $f = (...elements) => $(x => exists(elements, e => e === x))
  54. const Any = $(any => true)
  55. const Void = $(any => false)
  56. const Otherwise = Any // for transform()
  57. const NoValue = $(x => typeof x == 'undefined')
  58. const Num = $(x => typeof x == 'number')
  59. const Int = $n( Num, $(x => Number.isInteger(x)) )
  60. const UnsignedInt = $n( Int, $(x => x >= 0) )
  61. const Str = $(x => typeof x == 'string')
  62. const Bool = $(x => typeof x == 'boolean')
  63. const JsObject = $(x => x instanceof Object)
  64. const Fun = $(object => object instanceof Function)
  65. const List = $(object => object instanceof Array)
  66. const Hash = $n( $(x => x instanceof Object), $_(List) )
  67. const StrictHash = $n( $(x => x instanceof Object), $_($u(List, Fun)) )
  68. const Optional = concept => $u(NoValue, concept)
  69. const SetEquivalent = (target, concept) => target.contains = concept.contains
  70. const MadeBy = (maker) => $n(JsObject, $(x => x.maker === maker))
  71. const SetMakerConcept = (maker) => maker.contains = MadeBy(maker).contains
  72. const NumStr = $n(Str, $(x => !Number.isNaN(parseInt(x))) )
  73. const Regex = regex => $n( Str, $(s => s.match(regex)) )
  74. const SingletonContains = function(x) { return x === this }
  75. const Break = { contains: SingletonContains, _name: 'Break' } // for fold()
  76. function BreakWith (value) {
  77. return { value: value, maker: BreakWith }
  78. }
  79. SetMakerConcept(BreakWith)
  80. // special value for find()
  81. const NotFound = { contains: SingletonContains, _name: 'NotFound' }
  82. // special value for insert() and added()
  83. const Nothing = { contains: SingletonContains, _name: 'Nothing' }
  84. const Iterable = $(
  85. x => typeof x != 'undefined' && typeof x[Symbol.iterator] == 'function'
  86. )
  87. const ListOf = (
  88. concept => $(
  89. array => List.contains(array) && forall(array, x=>concept.contains(x))
  90. )
  91. )
  92. const HashOf = (
  93. concept => $(
  94. hash => Hash.contains(hash)
  95. && forall(Object.keys(hash), key=>concept.contains(hash[key]))
  96. )
  97. )
  98. function Enum (...str_list) {
  99. assert( ListOf(Str).contains(str_list) )
  100. var set = new Set(str_list)
  101. return $(function (item) {
  102. return Str.contains(item) && set.has(item)
  103. })
  104. }
  105. const one_of = Enum
  106. function Struct (concept_of) {
  107. check(Struct, arguments, { concept_of: HashOf(Concept) })
  108. return $(
  109. x => forall(
  110. Object.keys(concept_of),
  111. key => has(x, key)
  112. && concept_of[key].contains(x[key])
  113. )
  114. )
  115. }
  116. function is (object, concept) {
  117. return concept.contains(object)
  118. }
  119. function is_not (object, concept) {
  120. return !is(object, concept)
  121. }
  122. const once = (function () {
  123. let cache = {}
  124. function once (caller, value) {
  125. check(once, arguments, { caller: Fun, value: Any })
  126. if ( has(cache, caller.name) ) {
  127. return cache[caller.name]
  128. } else {
  129. cache[caller.name] = value
  130. return value
  131. }
  132. }
  133. return once
  134. })()
  135. function Failed (message) {
  136. return {
  137. message: message,
  138. maker: Failed
  139. }
  140. }
  141. SetMakerConcept(Failed)
  142. const OK = { name: 'OK', contains: x => x === OK }
  143. const Result = $u(OK, Failed)
  144. function suppose (bool, message) {
  145. check(suppose, arguments, { bool: Bool, message: Str })
  146. if (bool) {
  147. return OK
  148. } else {
  149. return Failed(message)
  150. }
  151. }
  152. function need (items) {
  153. assert(Iterable.contains(items))
  154. for ( let item of items ) {
  155. assert( $u(Result, Fun).contains(item) )
  156. let result = (Result.contains(item))? item: item()
  157. if ( Failed.contains(result) ) {
  158. return result
  159. } else {
  160. continue
  161. }
  162. }
  163. return OK
  164. }
  165. function check(callee, args, concept_table) {
  166. assert(Fun.contains(callee))
  167. assert(Hash.contains(concept_table))
  168. var parameters = get_function_parameters(callee)
  169. var parameter_to_index = {}
  170. var index = 0
  171. for ( let parameter of parameters ) {
  172. parameter_to_index[parameter] = index
  173. index++
  174. }
  175. for ( let parameter of Object.keys(concept_table) ) {
  176. var argument = args[parameter_to_index[parameter]]
  177. var concept = concept_table[parameter]
  178. if (!concept.contains(argument)) {
  179. throw Error(
  180. `Invalid argument '${parameter}' in function ${callee.name}()`
  181. )
  182. }
  183. /*
  184. if (NoValue.contains(argument) && has(concept, 'defval')) {
  185. args[parameter_to_index[parameter]] = concept.defval
  186. }
  187. */
  188. }
  189. }
  190. function check_hash(callee, argument, constraint) {
  191. check(
  192. check_hash, arguments,
  193. { callee: Fun, argument: Hash, constraint: Hash }
  194. )
  195. for ( let name of Object.keys(constraint) ) {
  196. if (!constraint[name].contains(argument[name])) {
  197. throw Error(`Invalid argument ${name} in function ${callee.name}`)
  198. }
  199. if (NoValue.contains(argument[name]) && has(constraint[name], 'defval')) {
  200. argument[name] = constraint[name].defval
  201. }
  202. }
  203. }
  204. function* map_lazy (to_be_mapped, f) {
  205. check(map_lazy, arguments, { to_be_mapped: $u(JsObject,Str), f: Fun })
  206. if( Iterable.contains(to_be_mapped) ) {
  207. let iterable = to_be_mapped
  208. let index = 0
  209. for ( let I of iterable ) {
  210. yield f(I, index)
  211. index += 1
  212. }
  213. } else {
  214. let hash = to_be_mapped
  215. for ( let key of Object.keys(hash) ) {
  216. yield f(key, hash[key])
  217. }
  218. }
  219. }
  220. function map (to_be_mapped, f) {
  221. check(map, arguments, { to_be_mapped: $u(JsObject,Str), f: Fun })
  222. return list(map_lazy(to_be_mapped, f))
  223. }
  224. function mapkey (hash, f) {
  225. check(mapkey, arguments, { hash: Hash, f: Fun })
  226. var result = {}
  227. for ( let key of Object.keys(hash) ) {
  228. let new_key = f(key, hash[key])
  229. result[new_key] = hash[key]
  230. /*
  231. if ( !has(result, new_key) ) {
  232. result[new_key] = hash[key]
  233. } else {
  234. throw Error('mapkey(): Key conflict detected')
  235. }
  236. */
  237. }
  238. return result
  239. }
  240. function mapval (hash, f) {
  241. check(mapval, arguments, { hash: Hash, f: Fun })
  242. var result = {}
  243. for ( let key of Object.keys(hash) ) {
  244. let value = hash[key]
  245. result[key] = f(value, key)
  246. }
  247. return result
  248. }
  249. function *rev (array) {
  250. check(rev, arguments, { array: List })
  251. for ( let i=array.length-1; i>=0; i-- ) {
  252. yield array[i]
  253. }
  254. }
  255. function *cat (...iterables) {
  256. for( let iterable of iterables ) {
  257. assert(Iterable.contains(iterable))
  258. for ( let element of iterable ) {
  259. yield element
  260. }
  261. }
  262. }
  263. function concat (iterable_of_iterable) {
  264. check(concat, arguments, { iterable_of_iterable: Iterable })
  265. return cat.apply({}, iterable_of_iterable)
  266. }
  267. function *lazy (f) {
  268. assert(Fun.contains(f))
  269. let iterable = f()
  270. assert(Iterable.contains(iterable))
  271. for ( let I of iterable ) {
  272. yield I
  273. }
  274. }
  275. function* take_while (iterable, f) {
  276. let index = 0
  277. for ( let I of iterable ) {
  278. if (f(I, index)) {
  279. yield I
  280. } else {
  281. break
  282. }
  283. index++
  284. }
  285. }
  286. function* map_while (iterable, condition, f) {
  287. take_while(map_lazy(iterable, f), condition)
  288. }
  289. function take_at (iterable, index, default_value) {
  290. check(take_at, arguments, { iterable: Iterable })
  291. let count = 0;
  292. for (let I of iterable) {
  293. if (count == index) {
  294. return I
  295. }
  296. count++
  297. }
  298. return default_value
  299. }
  300. function list (iterable) {
  301. check(list, arguments, { iterable: Iterable })
  302. var result = []
  303. for ( let element of iterable ) {
  304. result.push(element)
  305. }
  306. return result
  307. }
  308. function join (iterable, separator) {
  309. check(join, arguments, { iterable: Iterable, separator: Str })
  310. return list(iterable).join(separator)
  311. }
  312. function join_lines (...lines) {
  313. return join(lines, LF);
  314. }
  315. function* filter_lazy (iterable, f) {
  316. check(filter_lazy, arguments, {
  317. iterable: Iterable,
  318. f: Fun
  319. })
  320. let index = 0
  321. let count = 0
  322. for ( let element of iterable ) {
  323. if ( f(element, index, count) ) {
  324. yield element
  325. count++
  326. }
  327. index++
  328. }
  329. }
  330. function filter (to_be_filtered, f) {
  331. check(filter, arguments, {
  332. to_be_filtered: JsObject,
  333. f: Fun
  334. })
  335. if (Iterable.contains(to_be_filtered)) {
  336. let iterable = to_be_filtered
  337. return list(filter_lazy(iterable, f))
  338. } else {
  339. let hash = to_be_filtered
  340. let result = {}
  341. for ( let key of Object.keys(hash) ) {
  342. if ( f(key, hash[key]) ) {
  343. result[key] = hash[key]
  344. }
  345. }
  346. return result
  347. }
  348. }
  349. function pour (target, source) {
  350. check(pour, arguments, { target: Hash, source: Hash })
  351. for ( let key of Object.keys(source) ) {
  352. if (key != 'contains') {
  353. target[key] = source[key]
  354. }
  355. }
  356. if ( source.__proto__ !== Object.prototype ) {
  357. target.__proto__ = source.__proto__
  358. }
  359. return target
  360. }
  361. function fold (iterable, initial, f) {
  362. check(
  363. fold, arguments,
  364. { iterable: Iterable, initial: Any, f: Fun }
  365. )
  366. var value = initial
  367. var index = 0
  368. for ( let element of iterable ) {
  369. let new_value = f(element, value, index)
  370. assert(new_value !== undefined)
  371. if (Break.contains(new_value)) {
  372. break
  373. } else if (BreakWith.contains(new_value)) {
  374. value = new_value.value
  375. break
  376. } else {
  377. value = new_value
  378. index++
  379. }
  380. }
  381. return value
  382. }
  383. function chain (...functions) {
  384. return ( x => fold(functions, x, (f,v) => f(v)) )
  385. }
  386. function transform (object, situations) {
  387. check(transform, arguments, {
  388. object: Any,
  389. situations: ListOf(Struct({
  390. when_it_is: Concept,
  391. use: Fun
  392. }))
  393. })
  394. for (let s of situations) {
  395. let concept = s.when_it_is
  396. let f = s.use
  397. if ( concept.contains(object) ) {
  398. return f(object)
  399. }
  400. }
  401. throw Error('transform(): cannot find proper transformation')
  402. }
  403. function* iterate (initial, next_of, terminate_when) {
  404. check(iterate, arguments, {
  405. initial: Any,
  406. next_of: Fun,
  407. terminate_when: Concept
  408. })
  409. let current = initial
  410. while ( !terminate_when.contains(current) ) {
  411. yield current
  412. current = next_of(current)
  413. }
  414. }
  415. function forall (to_be_checked, f) {
  416. check(forall, arguments, { to_be_checked: JsObject, f: Fun })
  417. if (Iterable.contains(to_be_checked)) {
  418. let iterable = to_be_checked
  419. return Boolean(fold(iterable, true, (e,v) => v && f(e)))
  420. } else {
  421. let hash = to_be_checked
  422. return Boolean(fold(Object.keys(hash), true, (k,v) => v && f(hash[k])))
  423. }
  424. }
  425. function exists (to_be_checked, f) {
  426. check(exists, arguments, { to_be_checked: JsObject, f: Fun })
  427. return !forall(to_be_checked, x => !f(x))
  428. }
  429. function find (container, f) {
  430. check(find, arguments, { container: JsObject, f: Fun })
  431. if (Iterable.contains(container)) {
  432. let iterable = container
  433. for ( let I of iterable ) {
  434. if (f(I)) {
  435. return I
  436. break
  437. }
  438. }
  439. } else {
  440. let hash = container
  441. for ( let key of Object.keys(hash) ) {
  442. if (f(hash[key], key)) {
  443. return { key: key, value: hash[key] }
  444. break
  445. }
  446. }
  447. }
  448. return NotFound
  449. }
  450. function* lookahead (iterable, empty_value) {
  451. assert(Iterable.contains(iterable))
  452. let it = iterable[Symbol.iterator]()
  453. let current = it.next()
  454. let next = it.next()
  455. let third = it.next()
  456. while ( !current.done ) {
  457. yield {
  458. current: current.value,
  459. next: next.done? empty_value: next.value,
  460. third: third.done? empty_value: third.value
  461. }
  462. current = next
  463. next = third
  464. third = it.next()
  465. }
  466. }
  467. function* lookaside (iterable, empty_value) {
  468. assert(Iterable.contains(iterable))
  469. let it = iterable[Symbol.iterator]()
  470. let left = { value: empty_value, done: false }
  471. let current = it.next()
  472. let right = it.next()
  473. while ( !current.done ) {
  474. yield {
  475. left: left.value,
  476. current: current.value,
  477. right: right.done? empty_value: right.value
  478. }
  479. left = current
  480. current = right
  481. right = it.next()
  482. }
  483. }
  484. function* insert (iterable, empty_value, f) {
  485. assert(Iterable.contains(iterable))
  486. assert(Fun.contains(f))
  487. for( let look of lookahead(iterable, empty_value)) {
  488. yield look.current
  489. let add = f(look.current, look.next)
  490. if (add != Nothing) {
  491. yield add
  492. }
  493. }
  494. }
  495. function* count (n) {
  496. let i = 0
  497. while (i < n) {
  498. yield i
  499. i += 1
  500. }
  501. }
  502. function apply_on (object, f) {
  503. return f(object)
  504. }
  505. function has (hash, key) {
  506. return Object.prototype.hasOwnProperty.call(hash, key)
  507. }
  508. function added (iterable, element) {
  509. check(added, arguments, { iterable: Iterable, element: Any })
  510. let r = list(iterable)
  511. if (element != Nothing) {
  512. r.push(element)
  513. }
  514. return r
  515. }
  516. function added_front (iterable, element) {
  517. check(added_front, arguments, { iterable: Iterable, element: Any })
  518. if (element != Nothing) {
  519. return list(cat([element], iterable))
  520. } else {
  521. return list(iterable)
  522. }
  523. }
  524. function get_function_parameters (f) {
  525. /* https://stackoverflow.com/questions/1007981/how-to-get-thistion-parameter-names-values-dynamically */
  526. var STRIP_COMMENTS = /(\/\/.*$)|(\/\*[\s\S]*?\*\/)|(\s*=[^,\)]*(('(?:\\'|[^'\r\n])*')|("(?:\\"|[^"\r\n])*"))|(\s*=[^,\)]*))/mg;
  527. var ARGUMENT_NAMES = /([^\s,]+)/g;
  528. var str = f.toString().replace(STRIP_COMMENTS, '')
  529. return (
  530. str
  531. .slice(str.indexOf('(')+1, str.indexOf(')'))
  532. .match(ARGUMENT_NAMES)
  533. ) || []
  534. }