internal.rst 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. Internal workings
  2. =================
  3. For more detailed information on the Modelverse specification, which this project is implementing, we refer to the `Modelverse Specification <http://msdl.cs.mcgill.ca/people/yentl/files/Modelverse.pdf>`_.
  4. Information on the implementation can be found below.
  5. Modelverse State
  6. ----------------
  7. The Modelverse State is basically just an implementation of a graph library.
  8. As we have a particular kind of graph, this implementation is mostly done by hand at the moment.
  9. The notable exception to this is the RDF backend, proving that other implementations can also be used.
  10. The basic implementation just stores everything as dictionaries.
  11. All operations are then defined by doing operations on these dictionaries.
  12. The most interesting operations here are dictionary operations, which need to traverse these dictionaries in complex ways.
  13. To overcome performance problems for these operations, all results are cached (and validated afterwards).
  14. RDF backend
  15. ^^^^^^^^^^^
  16. The RDF backend requires the *rdflib* module in Python.
  17. The Modelverse graph is then stored in RDF representation and all operations on it are done using SPARQL queries.
  18. Due to this level of indirection, performance is extremely slow.
  19. To increase performance, we would likely have to make more *composite* operations, or even group different requests together internally.
  20. Status codes
  21. ^^^^^^^^^^^^
  22. The MvS returns, apart from its actual return value, a status code for the request.
  23. This value is not used by the MvK at all, since sometimes a request is expected to give an error (*e.g.*, checking whether an element is present).
  24. When debugging the MvS, however, these status codes can come in handy.
  25. Modelverse Kernel
  26. -----------------
  27. Precompiled functions
  28. ^^^^^^^^^^^^^^^^^^^^^
  29. Modelverse Interface
  30. --------------------
  31. Semantics visitor
  32. ^^^^^^^^^^^^^^^^^
  33. Constructors visitor
  34. ^^^^^^^^^^^^^^^^^^^^
  35. Primitives visitor
  36. ^^^^^^^^^^^^^^^^^^
  37. Bootstrap visitor
  38. ^^^^^^^^^^^^^^^^^
  39. Model visitor
  40. ^^^^^^^^^^^^^
  41. Bootstrapping
  42. -------------
  43. To bootstrap, you just have to run the file *bootstrap.py*.
  44. Here, we explain what this file actually does...
  45. The bootstrap script primarily creates the initial graph manually.
  46. This manual creation is done by writing data to a file, which is later read by the MvS.
  47. The initial graph consists of several parts:
  48. * The Modelverse Root node;
  49. * A global definition of all primitives;
  50. * The stack frame of the initial user *user_manager*, which manages all other users;
  51. * The code for the *user_manager* user;
  52. * The code for all new users, as assigned by the *user_manager*;
  53. * Bindings in the compilation manager for bootstrap files.
  54. These are all fairly simple in themselves.
  55. For some parts, such as the code, the HUTN compiler is invoked on a temporary piece of code.
  56. All bootstrap files are also compiled and made available to the compilation manager with their MD5 hash.
  57. How to add a primitive
  58. ----------------------
  59. Probably the most important reason why you would want to know about the Modelverse internals, is if you want to add a Modelverse primitive.
  60. Primitives are functions implemented in the MvK core, and thus become hardcoded.
  61. .. warning::
  62. While these functions are hardcoded, their implementation needs to follow strict rules, such that their semantics is identical in all possible programming languages.
  63. For example, a primitive *getTime()* cannot simply be implemented as *time.time()* in Python, as this gives different results on Linux and Windows.
  64. To add a primitive, follow these steps:
  65. 1. Add the code of the primitive to the MvK implementation by adding it in the file *primitives.py*.
  66. a. Name the function identically to how the primitive will be invoked later on.
  67. b. Take a set of parameters. Parameters for primitives are always positional and start from *a* onwards.
  68. c. The final parameter should be a catch-all element, as it is possible that a high number of additional information is passed. In Python, this is done with a parameter prepended with \*\*.
  69. d. When asking the MvS for information, use yields, just like the implementation of transformation rules in the MvK.
  70. e. Instead of return, use a *raise* with the exception *PrimitiveFinished*. This exception takes one argument: the returnvalue.
  71. 2. Add the signature to *bootstrap.py* in the topmost dictionary *primitives*. The key is the name of the function, and the value is a list starting with the return type, followed by all parameter types (as string).
  72. 3. Add the declaration to *includes/primitives.alh* to make them available everywhere.
  73. 4. Add the definition to *primitives.alc* and assign the Modelverse reference *?primitives/function_name*.
  74. 5. Recreate the bootstrap file by running the script *generate_bootstrap.py*.
  75. 6. Restart the Modelverse to reload the bootstrap file.
  76. 7. From now on, all files including *primitives.alh* have access to the defined function.
  77. Adding a precompiled function
  78. -----------------------------
  79. Adding a precompiled function is way easier: only step 1 of the addition of a primitive should be done, but in the file *compiled.py* instead of *primitives.py*.
  80. All other steps are done automatically since there is an action language implementation present.
  81. The MvK will then automatically pick the precompiled function or the explicitly modelled operation, depending on preferences.
  82. A restart of the MvK is needed for Python to pick up the new functions.