core_algorithm.alc 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678
  1. include "modelling.alh"
  2. include "library.alh"
  3. include "primitives.alh"
  4. include "constructors.alh"
  5. include "object_operations.alh"
  6. Element core = ?
  7. Void function main():
  8. // Initialize the Core Formalism
  9. String core_location
  10. String core_model
  11. String admin_group
  12. String admin_user
  13. core_location = "models/CoreFormalism"
  14. // Create the Model itself and make public
  15. core = instantiate_model(import_node(core_location))
  16. // Create admin group
  17. admin_group = instantiate_node(core, "Group", "")
  18. instantiate_attribute(core, admin_group, "name", "admin")
  19. // Create admin user
  20. admin_user = instantiate_node(core, "User", "")
  21. output("Desired username for admin user?")
  22. instantiate_attribute(core, admin_user, "name", input())
  23. instantiate_attribute(core, admin_user, "admin", True)
  24. // Create link between admin user and group
  25. instantiate_link(core, "ownedBy", "", admin_group, admin_user)
  26. instantiate_link(core, "belongsTo", "", admin_user, admin_group)
  27. // Add the core formalism already
  28. core_model = instantiate_node(core, "Model", "")
  29. instantiate_attribute(core, core_model, "name", "CoreFormalism")
  30. instantiate_attribute(core, core_model, "location", core_location)
  31. instantiate_attribute(core, core_model, "permissions", "220")
  32. // Make necessary links for the formalism to the owners
  33. instantiate_link(core, "group", "", core_model, admin_group)
  34. instantiate_link(core, "owner", "", core_model, admin_user)
  35. // Switch all new users to the user_function
  36. // This accesses the bootstrap level, so do not change this unless you know what you are doing
  37. Element root
  38. root = read_root()
  39. dict_delete(root["__hierarchy"], "__IP")
  40. dict_add(root["__hierarchy"], "__IP", user_function)
  41. // Call this for ourselves as well
  42. user_function_skip_init(admin_user)
  43. // Done, so finish up
  44. // Admin user will have been deleted by the user_function as usual
  45. // Note that if there are no admin users left, it will be very difficult to manage, as nobody will have admin permissions!
  46. return !
  47. Integer function get_relation_to_model(user_id : String, model_id : String):
  48. if (set_in(allAssociationDestinations(core, model_id, "owner"), user_id)):
  49. // We are the owner
  50. return 0!
  51. else:
  52. String group_id
  53. group_id = set_pop(allAssociationDestinations(core, model_id, "group"))
  54. if (set_in(allAssociationDestinations(core, user_id, "belongsTo"), group_id)):
  55. // We are in the owning group
  56. return 1!
  57. else:
  58. // We are not related whatsoever
  59. return 2!
  60. Boolean function is_admin(user_id : String):
  61. if (read_attribute(core, user_id, "admin")):
  62. return True!
  63. else:
  64. return False!
  65. Boolean function allow_read(user_id : String, model_id : String):
  66. if (is_admin(user_id)):
  67. // Is admin, so always allow
  68. return True!
  69. else:
  70. // Check permissions
  71. String permission
  72. permission = string_get(read_attribute(core, model_id, "permissions"), get_relation_to_model(user_id, model_id))
  73. if (bool_or(permission == "1", permission == "2")):
  74. return True!
  75. else:
  76. return False!
  77. Boolean function allow_write(user_id : String, model_id : String):
  78. if (is_admin(user_id)):
  79. // Is admin, so always allow
  80. return True!
  81. else:
  82. // Check permissions
  83. String permission
  84. permission = string_get(read_attribute(core, model_id, "permissions"), get_relation_to_model(user_id, model_id))
  85. if (permission == "2"):
  86. return True!
  87. else:
  88. return False!
  89. Boolean function allow_change_metadata(user_id : String, model_id : String):
  90. if (is_admin(user_id)):
  91. // Is admin, so always allow
  92. return True!
  93. else:
  94. if (get_relation_to_model(user_id, model_id) == "0"):
  95. // Only owner can chmod
  96. return True!
  97. else:
  98. return False!
  99. Void function check_login(user_id : String):
  100. // TODO
  101. return !
  102. Element function user_function():
  103. String username
  104. String user_id
  105. output("Log on as which user?")
  106. output("non-existing user will be created")
  107. username = input()
  108. user_id = get_user_id(username)
  109. if (user_id == ""):
  110. // New user
  111. // Add user to Core Formalism
  112. user_id = instantiate_node(core, "User", "")
  113. instantiate_attribute(core, user_id, "name", username)
  114. instantiate_attribute(core, user_id, "admin", False)
  115. // Now call with user created
  116. user_function_skip_init(user_id)
  117. else:
  118. if (check_login(user_id)):
  119. user_function_skip_init(user_id)
  120. // User destroyed already, so just stop execution
  121. return !
  122. String function get_model_id(name : String):
  123. Element models
  124. String model
  125. models = allInstances(core, "Model")
  126. while (read_nr_out(models) > 0):
  127. model = set_pop(models)
  128. if (read_attribute(core, model, "name") == name):
  129. return model!
  130. return ""!
  131. Void function user_function_skip_init(user_id : String):
  132. Boolean do_continue
  133. String cmd
  134. do_continue = True
  135. output("Welcome to the Model Management Interface v2.0!")
  136. output("Use the 'help' command for a list of possible commands")
  137. while (do_continue):
  138. output("Ready for command...")
  139. cmd = input()
  140. if (cmd == "help"):
  141. output("Model operations")
  142. output(" model_add -- Add a new model")
  143. output(" model_modify -- Modify an existing model")
  144. output(" model_delete -- [TODO] Delete a model and all related transformations")
  145. output(" model_list -- List all models")
  146. output(" model_list_full -- List all models with full info")
  147. output("")
  148. output("Transformation-specific operations")
  149. output(" transformation_add_MT_language -- TODO")
  150. output(" transformation_add_MT -- TODO")
  151. output(" transformation_add_AL -- TODO")
  152. output(" transformation_source_add -- TODO")
  153. output(" transformation_source_delete -- TODO")
  154. output(" transformation_target_add -- TODO")
  155. output(" transformation_target_delete -- TODO")
  156. output(" transformation_execute -- TODO")
  157. output("")
  158. output("Model permission operations")
  159. output(" permission_modify -- Change model permissions")
  160. output(" permission_owner -- Change model owner")
  161. output(" permission_group -- Change model group")
  162. output("")
  163. output("Group operations")
  164. output(" group_create -- Create a group")
  165. output(" group_delete -- Delete a group")
  166. output(" group_owner_add -- Add group owner")
  167. output(" group_owner_delete -- Remove group owner")
  168. output(" group_join -- Add someone to your group")
  169. output(" group_kick -- Kick someone from your group")
  170. output(" group_list -- List all groups you are a member of")
  171. output("")
  172. output("Admin operations")
  173. output(" admin_promote -- Promote a user to admin status")
  174. output(" admin_demote -- Demote a user to normal status")
  175. output("")
  176. output("General operations")
  177. output(" account_delete -- Remove current user and revoke all permissions ")
  178. elif (cmd == "model_add"):
  179. // Model addition operation, which uses model upload commands of the compiler
  180. String name
  181. String type
  182. String location
  183. Element new_model
  184. output("Creating new model!")
  185. output("Model type?")
  186. type_id = get_model_id(input())
  187. if (type_id != ""):
  188. // Type exists
  189. if (allow_read(user_id, type_id)):
  190. // And is readable
  191. output("Model name?")
  192. name = input()
  193. if (get_model_id(name) == ""):
  194. // Model doesn't exist yet
  195. output("Waiting for model constructors...")
  196. // TODO update for access control
  197. new_model = construct_model(read_attribute(core, type_id, "location"))
  198. output("Model upload success!")
  199. location = "/models/" + cast_id2s(new_model)
  200. export_node(new_model, location)
  201. // Manage meta-info
  202. new_model_id = instantiate_node(core, "Model", "")
  203. instantiate_attribute(core, new_model_id, "name", name)
  204. instantiate_attribute(core, new_model_id, "location", location)
  205. instantiate_attribute(core, new_model_id, "permissions", "200")
  206. instantiate_link(core, "owner", "", new_model_id, user_id)
  207. instantiate_link(core, "instanceOf", "", new_model_id, type_id)
  208. output("Meta-info correctly set!")
  209. else:
  210. output("Model with that name already exists!")
  211. else:
  212. output("You are not allowed to read this type model!")
  213. else:
  214. output("Could not find type model!")
  215. elif (cmd == "model_modify"):
  216. // Model modify operation, which uses the mini_modify.alc operations, though with extensions for access control
  217. String model_id
  218. output("Which model do you want to modify?")
  219. model_id = get_model_id(input())
  220. if (model_id != ""):
  221. if (allow_read(user_id, model_id)):
  222. type_id = set_pop(allAssociationDestinations(core, model_id, "instanceOf"))
  223. if (allow_read(user_id, type_id)):
  224. mini_modify(import_node(read_attribute(core, model_id, "location")), allow_write(user_id, model_id))
  225. else:
  226. output("You are not allowed to read the type model!")
  227. else:
  228. output("You are not allowed to read this model!")
  229. else:
  230. output("Could not find model!")
  231. elif (cmd == "model_delete"):
  232. // Delete a model and all of its related transformations
  233. String model_id
  234. output("=================================================")
  235. output("WARNING: Deletion is a very destructive operation")
  236. output(" as it also deletes all transformations ")
  237. output(" defined which make use of this model! ")
  238. output("=================================================")
  239. output("")
  240. output("Currently not supported!")
  241. elif (cmd == "model_list"):
  242. // List all models
  243. Element models
  244. String m
  245. models = allInstances(core, "Model")
  246. while (read_nr_out(models) > 0):
  247. m = set_pop(models)
  248. output((" " + (read_attribute(core, m, "name")) + " : ") + read_attribute(core, set_pop(allAssociationDestinations(core, m, "instanceOf")), "name"))
  249. elif (cmd == "model_list_full"):
  250. // List all models with full info
  251. Element models
  252. String m
  253. String permissions
  254. String owner
  255. String group
  256. String name
  257. String type
  258. models = allInstances(core, "Model")
  259. while (read_nr_out(models) > 0):
  260. m = set_pop(models)
  261. permissions = read_attribute(core, m, "permissions")
  262. owner = read_attribute(core, set_pop(allAssociationDestinations(core, m, "owner")), "name")
  263. group = read_attribute(core, set_pop(allAssociationDestinations(core, m, "group")), "name")
  264. name = read_attribute(core, m, "name")
  265. size = read_nr_out(dict_read(import_node(read_attribute(core, m, "location")), "model"))
  266. type = read_attribute(core, set_pop(allAssociationDestinations(core, m, "instanceOf")), "name")
  267. output(((((((((((" " + permissions) + " ") + owner) + " ") + group) + " ") + size) + " ") + name) + " : ") + type)
  268. elif (cmd == "transformation_add_MT_language"):
  269. // Create a model transformation language from a set of input and output formalisms
  270. String name
  271. String model_id
  272. Element source
  273. Element target
  274. Element all_formalisms
  275. Element merged_formalism
  276. String old_type_id
  277. String type_id
  278. source = create_node()
  279. target = create_node()
  280. old_type_id = ""
  281. output("Source formalisms (terminate with empty string)?")
  282. name = input()
  283. while (name != ""):
  284. model_id = get_model_id(name)
  285. if (model_id != ""):
  286. if (allow_read(user_id, model_id)):
  287. type_id = set_pop(allAssociationDestinations(core, model_id, "instanceOf"))
  288. if (bool_or(old_type_id == "", type_id == old_type_id)):
  289. set_add(source, model_id)
  290. set_add(all_formalisms, create_tuple(name, model_id))
  291. old_type_id = type_id
  292. elif (old_type_id != type_id):
  293. // Already have a previous type_id and now another: CLASH
  294. output("Cannot add model as types not compatible with previous models; try again")
  295. else:
  296. output("Model not readable; try again")
  297. name = input()
  298. else:
  299. output("No such model; try again")
  300. output("Target formalisms (terminate with empty string)?")
  301. name = input()
  302. while (name != ""):
  303. model_id = get_model_id(name)
  304. if (model_id != ""):
  305. if (allow_read(user_id, model_id)):
  306. type_id = set_pop(allAssociationDestinations(core, model_id, "instanceOf"))
  307. if (bool_or(old_type_id == "", type_id == old_type_id)):
  308. set_add(target, model_id)
  309. if (bool_not(set_in(source, model_id))):
  310. set_add(all_formalisms, create_tuple(name, model_id))
  311. old_type_id = type_id
  312. elif (old_type_id != type_id):
  313. // Already have a previous type_id and now another: CLASH
  314. output("Cannot add model as types not compatible with previous models; try again")
  315. else:
  316. output("Model not readable; try again")
  317. name = input()
  318. else:
  319. output("No such model; try again")
  320. if (read_nr_out(source) > 0):
  321. if (read_nr_out(target) > 0):
  322. output("Name of the new tranformation?")
  323. name = input()
  324. if (get_model_id(name) == ""):
  325. // New location is available, so write
  326. merged_formalism = model_fuse(all_formalisms)
  327. location = "/models/" + cast_id2s(merged_formalism)
  328. export_node(merged_formalism, location)
  329. // Manage meta-info
  330. new_model_id = instantiate_node(core, "Model", "")
  331. instantiate_attribute(core, new_model_id, "name", name)
  332. instantiate_attribute(core, new_model_id, "location", location)
  333. instantiate_attribute(core, new_model_id, "permissions", "200")
  334. instantiate_link(core, "owner", "", new_model_id, user_id)
  335. instantiate_link(core, "instanceOf", "", new_model_id, type_id)
  336. else:
  337. output("Model already exists!")
  338. else:
  339. output("At least one target formalism is required")
  340. else:
  341. output("At least one source formalism is required")
  342. elif (cmd == "permission_modify"):
  343. String permissions
  344. String model_id
  345. output("Which model do you want to change permissions of?")
  346. model_id = get_model_id(input())
  347. if (model_id != ""):
  348. if (get_relation_to_model(user_id, model_id) == 0):
  349. output("New permissions?")
  350. permissions = input()
  351. Boolean fail
  352. Integer i
  353. i = 0
  354. if (string_length(permissions) != 3):
  355. fail = True
  356. while (bool_and(bool_not(fail), i < 3)):
  357. permission = cast_s2i(string_read(permissions, i))
  358. if (bool_or(permission < 0, permission > 2)):
  359. fail = True
  360. if (bool_not(fail)):
  361. unset_attribute(core, model_id, "permissions")
  362. instantiate_attribute(core, model_id, "permissions", permissions)
  363. else:
  364. output("Permissions must be a string of three characters with each character being a digit between 0 and 2")
  365. else:
  366. output("Permission denied!")
  367. else:
  368. output("No such model!")
  369. elif (cmd == "permission_owner"):
  370. String permissions
  371. String model_id
  372. String user_id
  373. output("Which model do you want to change owner of?")
  374. model_id = get_model_id(input())
  375. if (model_id != ""):
  376. if (get_relation_to_model(user_id, model_id) == 0):
  377. output("New owner?")
  378. user_id = get_user_id(input())
  379. if (user_id != ""):
  380. model_delete_element(set_pop(allOutgoingAssociationInstances(core, model_id, "owner")))
  381. instantiate_link(core, "owner", "", model_id, user_id)
  382. else:
  383. output("No such user!")
  384. else:
  385. output("Permission denied!")
  386. else:
  387. output("No such model!")
  388. elif (cmd == "permission_group"):
  389. String permissions
  390. String model_id
  391. String group_id
  392. output("Which model do you want to change permissions of?")
  393. model_id = get_model_id(input())
  394. if (model_id != ""):
  395. if (get_relation_to_model(user_id, model_id) == 0):
  396. output("New group?")
  397. group_id = get_group_id(input())
  398. if (group_id != ""):
  399. model_delete_element(set_pop(allOutgoingAssociationInstances(core, model_id, "group")))
  400. instantiate_link(core, "group", "", model_id, group_id)
  401. else:
  402. output("No such group!")
  403. else:
  404. output("Permission denied!")
  405. else:
  406. output("No such model!")
  407. elif (cmd == "group_create"):
  408. // Create a new group and become its owner
  409. String group_id
  410. String name
  411. output("Which group do you want to create?")
  412. name = input()
  413. group_id = get_group_id(name)
  414. if (group_id == ""):
  415. group_id = instantiate_node(core, "Group", "")
  416. instantiate_attribute(core, group_id, "name", name)
  417. instantiate_link(core, "belongsTo", "", user_id, group_id)
  418. isntantiate_link(core, "owner", "", group_id, user_id)
  419. output("Group created!")
  420. else:
  421. output("Group already exists")
  422. elif (cmd == "group_delete"):
  423. // Delete an existing group
  424. String group_id
  425. String name
  426. output("Which group do you want to delete?")
  427. name = input()
  428. group_id = get_group_id(name)
  429. if (group_id != ""):
  430. if (allow_group_modify(user_id, group_id)):
  431. model_delete_element(core, group_id)
  432. else:
  433. output("Permission denied")
  434. else:
  435. output("No such group")
  436. elif (cmd == "group_owner_add"):
  437. // Add an owner to your group
  438. String group_id
  439. output("Which group do you want to add an owner to?")
  440. group_id = get_group_id(input())
  441. if (group_id != ""):
  442. if (allow_group_modify(user_id, group_id)):
  443. output("Which user do you want to make an owner?")
  444. other_user_id = get_user_id(input())
  445. if (other_user_id != ""):
  446. Element overlap
  447. overlap = set_overlap(allIncomingAssociationInstances(core, user_id, "owner"), allOutgoingAssociationInstances(core, group_id, "owner"))
  448. if (read_nr_out(overlap) == 0):
  449. instantiate_link(core, "owner", "", group_id, user_id)
  450. overlap = set_overlap(allOutgoingAssociationInstances(core, user_id, "belongsTo"), allIncomingAssociationInstances(core, group_id, "belongsTo"))
  451. if (read_nr_out(overlap) == 0):
  452. instantiate_link(core, "belongsTo", "", user_id, group_id)
  453. output("New owner added to group!")
  454. else:
  455. output("User is already an owner!")
  456. else:
  457. output("No such user")
  458. else:
  459. output("Permission denied!")
  460. else:
  461. output("No such group")
  462. elif (cmd == "group_owner_delete"):
  463. // Remove an owner from your group
  464. String group_id
  465. output("Which group do you want to disown someone from?")
  466. group_id = get_group_id(input())
  467. if (group_id != ""):
  468. if (allow_group_modify(user_id, group_id)):
  469. output("Which user do you want to disown?")
  470. other_user_id = get_user_id(input())
  471. if (other_user_id != ""):
  472. Element overlap
  473. overlap = set_overlap(allOutgoingAssociationInstances(core, user_id, "belongsTo"), allIncomingAssociationInstances(core, group_id, "belongsTo"))
  474. if (read_nr_out(overlap) > 0):
  475. overlap = set_overlap(allIncomingAssociationInstances(core, user_id, "owner"), allOutgoingAssociationInstances(core, group_id, "owner"))
  476. if (read_nr_out(overlap) > 0):
  477. model_delete_element(core, set_pop(overlap))
  478. output("Disowned group from user!")
  479. else:
  480. output("User is not even an owner of the group!")
  481. else:
  482. output("User is not even a member of the group!")
  483. else:
  484. output("No such user")
  485. else:
  486. output("Permission denied!")
  487. else:
  488. output("No such group")
  489. elif (cmd == "group_join"):
  490. // Add someone to your group
  491. String group_id
  492. output("Which group do you want to add someone to?")
  493. group_id = get_group_id(input())
  494. if (group_id != ""):
  495. if (allow_group_modify(user_id, group_id)):
  496. output("Which user do you want to add?")
  497. other_user_id = get_user_id(input())
  498. if (other_user_id != ""):
  499. Element overlap
  500. overlap = set_overlap(allOutgoingAssociationInstances(core, user_id, "belongsTo"), allIncomingAssociationInstances(core, group_id, "belongsTo"))
  501. if (read_nr_out(overlap) == 0):
  502. instantiate_link(core, "belongsTo", "", user_id, group_id)
  503. output("User added to the group!")
  504. else:
  505. output("User is already a member of the group!")
  506. else:
  507. output("No such user")
  508. else:
  509. output("Permission denied!")
  510. else:
  511. output("No such group")
  512. elif (cmd == "group_kick"):
  513. // Remove someone from your group
  514. String group_id
  515. output("Which group do you want to kick someone from?")
  516. group_id = get_group_id(input())
  517. if (group_id != ""):
  518. if (allow_group_modify(user_id, group_id)):
  519. output("Which user do you want to kick?")
  520. other_user_id = get_user_id(input())
  521. if (other_user_id != ""):
  522. Element overlap
  523. overlap = set_overlap(allOutgoingAssociationInstances(core, user_id, "belongsTo"), allIncomingAssociationInstances(core, group_id, "belongsTo"))
  524. if (read_nr_out(overlap) > 0):
  525. model_delete_element(core, set_pop(overlap))
  526. // Check if user was an owner as well
  527. overlap = set_overlap(allIncomingAssociationInstances(core, user_id, "owner"), allOutgoingAssociationInstances(core, group_id, "owner"))
  528. if (read_nr_out(overlap) > 0):
  529. model_delete_element(core, set_pop(overlap))
  530. output("User kicked!")
  531. else:
  532. output("User is not even a member of the group!")
  533. else:
  534. output("No such user")
  535. else:
  536. output("Permission denied!")
  537. else:
  538. output("No such group")
  539. elif (cmd == "group_list"):
  540. // List all groups you are a member of (and whether you are admin or not!)
  541. Element groups
  542. String group_id
  543. String admin
  544. groups = allAssociationDestinations(core, user_id, "belongsTo")
  545. while (True):
  546. group_id = set_pop(groups)
  547. if (set_in(allAssociation(core, group_id, "owner"), user_id)):
  548. admin = " A "
  549. else:
  550. admin = " "
  551. output(admin + read_attribute(core, group_id, "name"))
  552. elif (cmd == "admin_promote"):
  553. // Promote a user to admin status
  554. if (is_admin(user_id)):
  555. String other_user_id
  556. output("Which user do you want to promote?")
  557. other_user_id = get_user_id(input())
  558. if (other_user_id != ""):
  559. unset_attribute(core, other_user_id, "admin")
  560. instantiate_attribute(core, other_user_id, "admin", True)
  561. output("Permissions granted!")
  562. else:
  563. output("No such user!")
  564. else:
  565. output("Permission denied!")
  566. elif (cmd == "admin_demote"):
  567. // Demote a user to normal status
  568. if (is_admin(user_id)):
  569. String other_user_id
  570. output("Which user do you want to demote?")
  571. other_user_id = get_user_id(input())
  572. if (other_user_id != ""):
  573. unset_attribute(core, other_user_id, "admin")
  574. instantiate_attribute(core, other_user_id, "admin", False)
  575. output("Permissions revoked!")
  576. else:
  577. output("No such user!")
  578. else:
  579. output("Permission denied!")
  580. elif (cmd == "exit"):
  581. // Exit by actually removing the user and decoupling it from all of its models
  582. // Restarting with the same user name will NOT grant you access to anything of the previous user with that same name
  583. do_continue = False
  584. // Delete user from Core Formalism
  585. model_delete_element(core, user_id)
  586. output("Goodbye!")
  587. return create_node()!