runtime-detail.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479
  1. 'use strict';
  2. /**
  3. * Exceptions Definition
  4. */
  5. class RuntimeError extends Error {}
  6. class RedundantParameter extends RuntimeError {}
  7. class InvalidOperation extends RuntimeError {}
  8. class InvalidArgument extends RuntimeError {}
  9. class InvalidReturnValue extends RuntimeError {}
  10. class InvalidDefinition extends RuntimeError {}
  11. class NoMatchingPattern extends RuntimeError {}
  12. class NoMatchingCase extends RuntimeError {}
  13. class KeyError extends RuntimeError {}
  14. class IndexError extends RuntimeError {}
  15. class NameConflict extends RuntimeError {}
  16. class ObjectNotFound extends RuntimeError {}
  17. class VariableConflict extends RuntimeError {}
  18. class InvalidAssignment extends RuntimeError {}
  19. function ErrorProducer (err_class, f_name) {
  20. check(ErrorProducer, arguments, {
  21. err_class: $(x => x.prototype instanceof Error),
  22. f_name: Str
  23. })
  24. return {
  25. if: function (bool, err_msg) {
  26. check(this.if, arguments, { bool: Bool, err_msg: Str })
  27. if ( bool ) {
  28. let err_type = err_class.name.replace(
  29. /([a-z])([A-Z])/g, '$1 $2'
  30. )
  31. throw new err_class(`${f_name}(): ${err_type}: ${err_msg}`)
  32. }
  33. },
  34. assert: function (bool, err_msg) {
  35. check(this.assert, arguments, { bool: Bool, err_msg: Str })
  36. return this.if(!bool, err_msg)
  37. },
  38. throw: function (err_msg) {
  39. return this.if(true, err_msg)
  40. },
  41. if_failed: function (result) {
  42. check(this.if_failed, arguments, { result: Result })
  43. if ( is(result, Failed) ) {
  44. this.if(true, result.message)
  45. }
  46. }
  47. }
  48. }
  49. /**
  50. * Detail Functions
  51. */
  52. const Detail = {
  53. Config: {},
  54. Hash: {},
  55. List: {},
  56. Scope: {},
  57. Concept: {},
  58. Prototype: {},
  59. Argument: {},
  60. Function: {},
  61. Object: {}
  62. }
  63. Detail.Config.get_flags = function (object) {
  64. check(Detail.Config.get_flags, arguments, {
  65. object: ObjectObject
  66. })
  67. let flag = { immutable: '[Im]' }
  68. let order = ['immutable']
  69. let has = filter(order, k => object.config[k])
  70. return (has.length > 0)? flag[has[0]] : ''
  71. }
  72. Detail.Hash.Prototype = {
  73. mapper: mapval,
  74. has: function (key) {
  75. return has(this.data, key)
  76. },
  77. get: function (key) {
  78. let err = ErrorProducer(KeyError, 'Hash::get')
  79. err.assert(this.has(key), `'${key}' does not exist`)
  80. return this.data[key]
  81. },
  82. fetch: function (key) {
  83. return this.has(key) && this.data[key] || NaObject
  84. },
  85. set: function (key, value) {
  86. this.data[key] = value
  87. return VoidObject
  88. },
  89. emplace: function (key, value) {
  90. let err = ErrorProducer(KeyError, 'Hash::emplace')
  91. err.if(this.has(key), `'${key}' already exist`)
  92. this.data[key] = value
  93. return VoidObject
  94. },
  95. replace: function (key, value) {
  96. let err = ErrorProducer(KeyError, 'Hash::replace')
  97. err.assert(this.has(key), `'${key}' does not exist`)
  98. this.data[key] = value
  99. return VoidObject
  100. },
  101. take: function (key) {
  102. let err = ErrorProducer(KeyError, 'Hash::take')
  103. err.assert(this.has(key), `'${key}' does not exist`)
  104. let value = this.data[key]
  105. delete this.data[key]
  106. return value
  107. },
  108. toString: function () {
  109. let flags = Config.get_flags(this)
  110. let list = map(
  111. this.data,
  112. (k,v) => `${k}: ${ObjectObject.represent(v)}`
  113. )
  114. return `${flags}{${join(list, ', ')}}`
  115. }
  116. }
  117. Detail.List.Prototype = {
  118. mapper: map,
  119. length: function () { return this.data.length },
  120. at: function (index) {
  121. let err = ErrorProducer(IndexError, 'List::at')
  122. err.assert(index < this.data.length, `${index}`)
  123. assert(typeof this.data[index] != 'undefined')
  124. return this.data[index]
  125. },
  126. change: function (index, value) {
  127. let err = ErrorProducer(IndexError, 'List::change')
  128. err.assert(index < this.data.length, `${index}`)
  129. this.data[index] = value
  130. },
  131. append: function (element) {
  132. this.data.push(element)
  133. return VoidObject
  134. },
  135. toString: function () {
  136. let flag = ''
  137. if ( this.config.frozen ) {
  138. flag = '[Fz]'
  139. } else if ( this.config.immutable ) {
  140. flag = '[Im]'
  141. }
  142. let list = map(this.data, x => ObjectObject.represent(x))
  143. return `${flag}[${join(list, ', ')}]`
  144. }
  145. }
  146. Detail.Scope.Prototype = {
  147. has: function (key) {
  148. return has(this.data, key)
  149. },
  150. get: function (key) {
  151. assert(this.has(key))
  152. return this.data[key]
  153. },
  154. set: function (key, value) {
  155. assert(is(value, ObjectObject))
  156. this.data[key] = value
  157. },
  158. emplace: function (key, value) {
  159. let err = ErrorProducer(VariableConflict, 'Scope::Emplace')
  160. err.if(this.has(key), `variable ${key} already declared`)
  161. this.set(key, value)
  162. },
  163. replace: function (key, value) {
  164. let err = ErrorProducer(InvalidAssignment, 'Scope::Replace')
  165. err.if(!this.has(key), `variable ${key} not declared`)
  166. this.set(key, value)
  167. },
  168. upward_iterator: function () {
  169. return iterate(this, x => x.context, NullScope)
  170. },
  171. find_name: function (name) {
  172. let get_scope_chain = (() => this.upward_iterator())
  173. let range = this.range
  174. /**
  175. * upper_max indicates the most out scope
  176. * that can be modified by function whose EffectRange = 'upper'
  177. * for example:
  178. * 0 1 2
  179. * [upper, local, global] => upper_max = 1
  180. * [upper, upper, local, global] => upper_max = 2
  181. */
  182. let upper_max = fold(get_scope_chain(), 0, (scope, index) => (
  183. scope.range == 'upper' && index + 1 || Break
  184. ))
  185. let is_immutable = function (layer) {
  186. let outside_local = (range == 'local' && layer > 0)
  187. let outside_upper = (range == 'upper' && layer > upper_max)
  188. return (outside_local || outside_upper)
  189. }
  190. return apply_on(get_scope_chain(), chain(
  191. x => map_lazy(x, (scope, index) => ({
  192. is_immutable: is_immutable(index),
  193. layer: index,
  194. scope: scope,
  195. object: scope.has(name)? scope.get(name): NotFound
  196. })),
  197. x => find(x, item => item.object != NotFound)
  198. ))
  199. },
  200. lookup: function (name) {
  201. check(this.__proto__.lookup, arguments, { name: Str })
  202. let result = this.find_name(name)
  203. if (result != NotFound) {
  204. let object = result.object
  205. let immutable = result.is_immutable
  206. return (immutable)? ImRef(object): object
  207. } else {
  208. ErrorProducer(ObjectNotFound, 'Scope::Lookup').throw(
  209. `there is no object named '${name}'`
  210. )
  211. }
  212. },
  213. try_to_lookup: function (name) {
  214. try {
  215. return this.lookup(name)
  216. } catch (ObjectNotFound) {
  217. return NotFound
  218. }
  219. }
  220. }
  221. Detail.Concept.UnionAll = function (concepts) {
  222. check(Detail.Concept.UnionAll, arguments, {
  223. concepts: ListOf(ConceptObject)
  224. })
  225. let name = `(${join(map_lazy(concepts, c => c.name), ' | ')})`
  226. let f = ( x => exists(concepts, c => c.checker(x)) )
  227. return ConceptObject(name, f)
  228. }
  229. Detail.Concept.IntersectAll = function (concepts) {
  230. check(Detail.Concept.IntersectAll, arguments, {
  231. concepts: ListOf(ConceptObject)
  232. })
  233. let name = `(${join(map_lazy(concepts, c => c.name), ' & ')})`
  234. let f = ( x => forall(concepts, c => c.checker(x)) )
  235. return ConceptObject(name, f)
  236. }
  237. Detail.Concept.Union = function (concept1, concept2) {
  238. return ConceptObject.UnionAll([concept1, concept2])
  239. }
  240. Detail.Concept.Intersect = function (concept1, concept2) {
  241. return ConceptObject.IntersectAll([concept1, concept2])
  242. }
  243. Detail.Concept.Complement = function (concept) {
  244. check(Detail.Concept.Complement, arguments, {
  245. concept: ConceptObject
  246. })
  247. let name = `~${concept.name}`
  248. let f = ( x => !concept.checker(x) )
  249. return ConceptObject(name, f)
  250. }
  251. Detail.Prototype.get_param_hash = function (prototype) {
  252. return fold(prototype.parameters, {}, function (parameter, hash) {
  253. hash[parameter.name] = parameter
  254. return hash
  255. })
  256. }
  257. Detail.Prototype.check_argument = function (prototype, argument) {
  258. check( Detail.Prototype.check_argument, arguments, {
  259. prototype: Prototype, argument: Hash
  260. })
  261. let proto = prototype
  262. let parameters = proto.parameters
  263. let hash = Prototype.get_param_hash(proto)
  264. let arg_has = (key => has(argument, key))
  265. map(argument, arg => assert(ObjectObject.contains(arg)))
  266. return need (
  267. cat(
  268. map_lazy(Object.keys(argument), key => suppose(
  269. !(is(key, NumStr) && !parameters[key])
  270. && !(is_not(key, NumStr) && !hash[key]),
  271. `redundant argument ${key}`
  272. )),
  273. map_lazy(Object.keys(argument), key => suppose(
  274. !(is(key, NumStr) && arg_has(parameters[key].name)),
  275. `conflict argument ${key}`
  276. )),
  277. map_lazy(parameters, (parameter, index) => suppose(
  278. arg_has(index) || arg_has(parameter.name),
  279. `missing argument ${parameter.name}`
  280. )),
  281. lazy(function () {
  282. let arg = mapkey(
  283. argument,
  284. key => is(key, NumStr)? parameters[key].name: key
  285. )
  286. return map_lazy(hash, (key, param) => suppose(
  287. (param.constraint === K.Any
  288. && ObjectObject.contains(arg[key]))
  289. || param.constraint.checker(arg[key]),
  290. `illegal argument '${key}'`
  291. ))
  292. })
  293. )
  294. )
  295. }
  296. Detail.Prototype.normalize_argument = function (prototype, argument) {
  297. check( Detail.Prototype.normalize_argument, arguments, {
  298. prototype: Prototype, argument: Hash
  299. })
  300. return mapkey(
  301. argument,
  302. key => is(key, NumStr)? prototype.parameters[key].name: key
  303. )
  304. }
  305. Detail.Prototype.set_mutability = function (prototype, normalized) {
  306. let h = Prototype.get_param_hash(prototype)
  307. return mapval(
  308. normalized, (val, key) => PassAction[h[key].pass_policy](val)
  309. )
  310. }
  311. Detail.Prototype.check_return_value = function (prototype, value) {
  312. check( Detail.Prototype.check_return_value, arguments, {
  313. prototype: Prototype, value: Any
  314. })
  315. return suppose(
  316. prototype.value_constraint.checker(value),
  317. `invalid return value ${value}`
  318. )
  319. }
  320. Detail.Prototype.represent = function (prototype) {
  321. check(Detail.Prototype.represent, arguments, { prototype: Prototype })
  322. function repr_parameter (parameter) {
  323. check(repr_parameter, arguments, { parameter: Parameter })
  324. let type = parameter.constraint.name
  325. let flags = PassFlag[parameter.pass_policy]
  326. return `${type} ${flags}${parameter.name}`
  327. }
  328. let effect = prototype.effect_range
  329. let parameters = prototype.parameters
  330. let retval_constraint = prototype.value_constraint
  331. return effect + ' ' + '(' + join(filter([
  332. join(map(parameters,p => repr_parameter(p)), ', ')
  333. ], x => x), ', ') + ') -> ' + `${retval_constraint.name}`
  334. }
  335. Detail.Prototype.parse = function (string) {
  336. const pattern = /\((.*)\) -> (.*)/
  337. check(Detail.Prototype.parse, arguments, { string: Regex(pattern) })
  338. let str = {
  339. parameters: string.match(pattern)[1].trim(),
  340. return_value: string.match(pattern)[2].trim()
  341. }
  342. function assert_concept (string) {
  343. if ( ConceptObject.contains(K[string]) ) {
  344. return K[string]
  345. } else {
  346. throw Error('prototype parsing error: invalid constraint')
  347. }
  348. }
  349. function parse_parameter (string) {
  350. const pattern = /([^ ]*) *([\*\&]?)(.+)/
  351. check(parse_parameter, arguments, { string: Regex(pattern) })
  352. let str = {
  353. constraint: string.match(pattern)[1].trim(),
  354. pass_policy: string.match(pattern)[2].trim()
  355. }
  356. let name = string.match(pattern)[3]
  357. return pour({ name: name }, mapval(str, function (s, item) {
  358. return ({
  359. constraint: () => assert_concept(s),
  360. pass_policy: () => PassFlagValue[s]
  361. })[item]()
  362. }))
  363. }
  364. function safe_split (s) {
  365. let array = s.split(',')
  366. return (array.length == 1 && array[0].trim() == '')? []: array
  367. }
  368. let trim_all = x => map_lazy(x, y => y.trim())
  369. return {
  370. effect_range: string.split(' ')[0],
  371. parameters: map(
  372. trim_all(safe_split(str.parameters)),
  373. s => parse_parameter(s)
  374. ),
  375. value_constraint: assert_concept(str.return_value)
  376. }
  377. }
  378. Detail.Argument.represent = function (argument) {
  379. let list = map(
  380. argument,
  381. (k,v) => `${k}=${ObjectObject.represent(v)}`
  382. )
  383. return `(${join(list, ', ')})`
  384. }
  385. Detail.Function.create = function (name_and_proto, js_function) {
  386. check(Detail.Function.create, arguments, {
  387. name_and_proto: Str, js_function: Fun
  388. })
  389. let name = name_and_proto.split(' ')[1]
  390. let prototype = join(
  391. filter(name_and_proto.split(' '), (s,index) => index != 1), ' '
  392. )
  393. return FunctionObject(
  394. name, G, Prototype.parse(prototype), function (scope) {
  395. return js_function (scope.data.argument.data)
  396. }
  397. )
  398. }
  399. Detail.Function.converge = function (proto_list, f) {
  400. check(Detail.Function.converge, arguments, {
  401. proto_list: ListOf(Str),
  402. f: Fun
  403. })
  404. return map(proto_list, p => Detail.Function.create(p, f))
  405. }
  406. Detail.Object.represent = function represent_object (object) {
  407. check(Detail.Object.represent, arguments, {
  408. object: ObjectObject
  409. })
  410. if ( is(object, PrimitiveObject) ) {
  411. if ( is(object, StringObject) ) {
  412. return `"${object}"`
  413. } else {
  414. return `${object}`
  415. }
  416. } else {
  417. return `<${GetType(object)}>`
  418. }
  419. }
  420. Detail.Function.MethodFor = function (object) {
  421. return $n(FunctionObject, $(function (f) {
  422. let p = f.prototype.parameters
  423. return (p.length > 0) && (p[0].constraint.checker(object))
  424. }))
  425. }