runtime-tools.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. 'use strict';
  2. function Lookup (scope) {
  3. return (string => scope.lookup(string))
  4. }
  5. function FormatString (string, id_ref) {
  6. check(FormatString, arguments, { string: Str, id_ref: Fun })
  7. // TODO: need enhancement
  8. return string.replace(/\${([^}]+)}/g, (_, arg) => id_ref(arg))
  9. }
  10. function Abstract (checker, name) {
  11. check(Abstract, arguments, {
  12. checker: FunctionalObject, name: Optional(Str)
  13. })
  14. return ConceptObject(name || '{Temp}', checker)
  15. }
  16. function Structure (hash_object) {
  17. check(Structure, arguments, { hash_object: HashObject })
  18. let err = ErrorProducer(InvalidDefinition, 'runtime::structure')
  19. err.if_failed(need(map_lazy(
  20. hash_object.data,
  21. (key, value) => suppose(
  22. is(value, ConceptObject), `${key} is not Concept`
  23. )
  24. )))
  25. let converted = mapval(
  26. hash_object.data,
  27. concept_object => $(object => concept_object.checker(object))
  28. )
  29. let struct = Struct(converted)
  30. let struct_object = $n( HashObject, $(x => is(x.data, struct)) )
  31. let key_list = join(map(converted, key => key), ', ')
  32. return PortConcept(struct_object, `struct {${key_list}}`)
  33. }
  34. function FiniteSet (list) {
  35. check(FiniteSet, arguments, { list: ListObject })
  36. return ConceptObject('{Finite}', function (object) {
  37. return exists(list.data, e => e === object)
  38. })
  39. }
  40. function Lambda (context, parameter_names, f) {
  41. check(Lambda, arguments, {
  42. context: Scope, parameter_names: ListOf(Str), f: Fun
  43. })
  44. // if no parameter provided by user, assume a parameter called "__"
  45. let normalized = (parameter_names.length > 0)? parameter_names: ['__']
  46. let parameters = map(normalized, name => ({
  47. name: name,
  48. pass_policy: 'natural',
  49. constraint: K.Any
  50. }))
  51. let proto = {
  52. effect_range: 'local',
  53. parameters: parameters,
  54. value_constraint: K.Any
  55. }
  56. return FunctionObject('<Lambda>', context, proto, f)
  57. }
  58. function FunInst (context, effect, parameters, target, f, name = '[Anonymous]') {
  59. // create a function instance
  60. let normalized = map(parameters, array => ({
  61. name: array[0],
  62. constraint: array[1],
  63. pass_policy: array[2]
  64. }))
  65. let proto = {
  66. effect_range: effect,
  67. parameters: normalized,
  68. value_constraint: target
  69. }
  70. return FunctionObject(name, context, proto, f)
  71. }
  72. function define (scope, name, effect, parameters, target, f) {
  73. let create_at = (
  74. scope => FunInst(scope, effect, parameters, target, f, name)
  75. )
  76. let existing = scope.try_to_lookup(name)
  77. if ( is(existing, OverloadObject) ) {
  78. // the new function should have access to the overridden old function
  79. let wrapper_scope = Scope(scope, effect)
  80. wrapper_scope.set(name, existing)
  81. scope.set(name, existing.added(create_at(wrapper_scope)))
  82. } else {
  83. scope.emplace(name, OverloadObject(name, [create_at(scope)]))
  84. }
  85. }
  86. function apply (functional) {
  87. assert(is(functional, FunctionalObject))
  88. return function(...args) {
  89. return functional.apply.apply(functional, args)
  90. }
  91. }
  92. function call (functional, argument) {
  93. let e = ErrorProducer(InvalidOperation, 'runtime::call')
  94. assert(is(argument, Hash))
  95. if ( is(functional, Fun) ) {
  96. e.assert(
  97. forall(Object.keys(argument), k => is(k, NumStr)),
  98. 'cannot pass named argument to raw function'
  99. )
  100. let pairs = map(argument, (k,v) => ({ key: k, value: v }))
  101. pairs.sort((x,y) => Number(x.k) - Number(y.k))
  102. return functional.apply(null, map(pairs, p => K.raw.apply(p.value)))
  103. } else {
  104. e.assert(is(functional, FunctionalObject), 'calling non-functional')
  105. return functional.call(argument)
  106. }
  107. }
  108. function assert_key (name, err) {
  109. err.assert(is(name, Str), 'hash key must be string')
  110. return true
  111. }
  112. function assert_index (name, err) {
  113. err.assert(is(name, UnsignedInt), 'list index must be non-negative integer')
  114. return true
  115. }
  116. function get (object, name) {
  117. let e = ErrorProducer(InvalidOperation, 'runtime::get')
  118. return transform(object, [
  119. { when_it_is: HashObject,
  120. use: h => assert_key(name, e) && K.get.apply(object, name) },
  121. { when_it_is: ListObject,
  122. use: l => assert_index(name, e) && K.at.apply(object, name) },
  123. { when_it_is: RawHashObject,
  124. use: h => assert_key(name, e) && K.get.apply(object, name) },
  125. { when_it_is: RawListObject,
  126. use: l => assert_index(name, e) && K.at.apply(object, name) },
  127. { when_it_is: Otherwise,
  128. use: x => e.throw(`except Hash or List: ${GetType(object)} given`) }
  129. ])
  130. }
  131. function set (object, name, value) {
  132. let e = ErrorProducer(InvalidOperation, 'runtime::set')
  133. let msg = 'changing element value of immutable compound object'
  134. e.if(is(object, ImmutableObject), msg)
  135. transform(object, [
  136. { when_it_is: HashObject,
  137. use: h => assert_key(name, e) && K.set.apply(h, name, value) },
  138. { when_it_is: ListObject,
  139. use: l => assert_index(name, e) && K.change.apply(l, name, value) },
  140. { when_it_is: RawHashObject,
  141. use: h => assert_key(name, e) && K.set.apply(h, name, value) },
  142. { when_it_is: RawListObject,
  143. use: l => assert_index(name, e) && K.change.apply(l, name, value) },
  144. { when_it_is: Otherwise,
  145. use: x => e.throw(`except Hash or List: ${GetType(object)} given`) }
  146. ])
  147. }
  148. function access_raw (object, name) {
  149. let find_function = function (js_object, name) {
  150. if(typeof js_object[name] == 'function') {
  151. return js_object[name]
  152. } else {
  153. return NotFound
  154. }
  155. }
  156. let proto = object.__proto__
  157. let method = find_function(object, name)
  158. // make method have higer priority than data member
  159. try {
  160. method = (method == NotFound)? find_function(proto, name): method
  161. } catch (err) {
  162. // TypeError: 'Illegal invocation' may occur
  163. if (!(err instanceof TypeError)) { throw err }
  164. }
  165. if (method != NotFound) {
  166. return function () {
  167. return method.apply(object, map(arguments, x => x))
  168. }
  169. } else {
  170. return object[name]
  171. }
  172. }
  173. function access (object, name, scope) {
  174. if (is(object, RawHashObject)) {
  175. return access_raw(object, name)
  176. }
  177. function wrap (f) {
  178. check(wrap, arguments, { f: FunctionObject })
  179. return OverloadObject(f.name, [f])
  180. }
  181. function find_on (overload) {
  182. check(find_on, arguments, { overload: OverloadObject })
  183. return overload.find_method_for(object)
  184. }
  185. let maybe_method = scope.try_to_lookup(name)
  186. let method = transform(maybe_method, [
  187. { when_it_is: FunctionObject, use: f => find_on(wrap(f)) },
  188. { when_it_is: OverloadObject, use: o => find_on(o) },
  189. { when_it_is: Otherwise, use: x => NotFound }
  190. ])
  191. if (method != NotFound) {
  192. return method
  193. } else if (is(object, HashObject)) {
  194. return get(object, name)
  195. } else {
  196. let err = ErrorProducer(ObjectNotFound, 'runtime::access')
  197. let repr = ObjectObject.represent(object)
  198. err.throw(`unable to find a method called ${name} for ${repr}`)
  199. }
  200. }
  201. function assign_outer (scope, key, value) {
  202. let err = ErrorProducer(InvalidAssignment, 'runtime::assign_outer')
  203. let result = scope.find_name(key)
  204. err.if(result.scope === scope, `${key} is not an outer variable`)
  205. err.if(result.scope === K, `global scope is read-only`)
  206. err.if(is(result, NotFound), `variable ${key} not declared`)
  207. err.if(result.is_immutable, `the scope containing ${key} is immutable`)
  208. result.scope.replace(key, value)
  209. }
  210. function assert_bool (value) {
  211. assert(typeof value == 'boolean')
  212. return value
  213. }
  214. function add_module (name) {
  215. check(add_module, arguments, { name: Str })
  216. let err = ErrorProducer(NameConflict, 'runtime::add_module')
  217. let table = KumaExport.ModuleExport
  218. err.if(has(table, name), `module name ${name} already used`)
  219. table[name] = {}
  220. return table[name]
  221. }
  222. function use_module (scope, name, alias) {
  223. check(use_module, arguments, { scope: Scope, name: Str, alias: Str })
  224. let err = ErrorProducer(ObjectNotFound, 'runtime::use_module')
  225. let table = KumaExport.ModuleExport
  226. err.assert(has(table, name), `there is no module named ${name}`)
  227. scope.emplace(alias, ImRef(HashObject(table[name])))
  228. }
  229. function use_modules (scope, module_list) {
  230. map(module_list, item => use_module(scope, item.name, item.alias))
  231. }
  232. function import_module (scope, name) {
  233. check(import_module, arguments, { scope: Scope, name: Str })
  234. let err = ErrorProducer(ObjectNotFound, 'runtime::import_module')
  235. let table = KumaExport.ModuleExport
  236. err.assert(has(table, name), `there is no module named ${name}`)
  237. map(table[name], function (key, value) {
  238. let s = scope.data
  239. let s_is_overload = has(s, key) && is(s[key], OverloadObject)
  240. let v_is_overload = is(value, OverloadObject)
  241. if ( s_is_overload && v_is_overload ) {
  242. s[key] = s[key].concated(value)
  243. } else {
  244. s[key] = value
  245. }
  246. })
  247. }
  248. function import_modules (scope, names) {
  249. map(names, name => import_module(scope, name))
  250. }
  251. function export_name (export_object, scope, name, alias) {
  252. check(export_name, arguments, {
  253. export_object: Hash,
  254. scope: Scope,
  255. name: Str,
  256. alias: Str
  257. })
  258. let f_name = 'runtime::export_name'
  259. let err = ErrorProducer(ObjectNotFound, f_name)
  260. err.assert(has(scope.data, name), `variable ${name} not found in scope`)
  261. err = ErrorProducer(NameConflict, f_name)
  262. err.if(has(export_object, alias), `name ${alias} already exported`)
  263. err = ErrorProducer(InvalidOperation, f_name)
  264. err.if(is(scope.data[name], RawObject), `cannot export raw object ${name}`)
  265. export_object[alias] = ImRef(scope.data[name])
  266. }
  267. function export_names (export_object, scope, name_list) {
  268. map(name_list, x => export_name(export_object, scope, x.name, x.alias))
  269. }
  270. let global = (typeof window == 'undefined')? global: window
  271. global.KumaExport = {
  272. G: G,
  273. K: K,
  274. Scope: Scope,
  275. HashObject: HashObject,
  276. ListObject: ListObject,
  277. Lookup: Lookup,
  278. FormatString: FormatString,
  279. Abstract: Abstract,
  280. Structure: Structure,
  281. FiniteSet: FiniteSet,
  282. Lambda: Lambda,
  283. FunInst: FunInst,
  284. define: define,
  285. apply: apply,
  286. call: call,
  287. access: access,
  288. assign_outer: assign_outer,
  289. assert_bool: assert_bool,
  290. ModuleExport: {},
  291. add_module: add_module,
  292. use_modules: use_modules,
  293. import_modules: import_modules,
  294. export_names: export_names
  295. }