exp_scd.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. # Simple Class Diagram experiment
  2. from state.devstate import DevState
  3. from bootstrap.scd import bootstrap_scd
  4. from uuid import UUID
  5. from services.scd import SCD
  6. from framework.conformance import Conformance
  7. from services.od import OD
  8. from transformation.matcher import mvs_adapter
  9. from transformation.ramify import ramify
  10. from transformation.cloner import clone_od
  11. from transformation import rewriter
  12. from services.bottom.V0 import Bottom
  13. from services.primitives.integer_type import Integer
  14. from concrete_syntax.plantuml import renderer as plantuml
  15. from concrete_syntax.textual_od import parser, renderer
  16. import sys
  17. def create_integer_node(state, i: int):
  18. node = state.create_node()
  19. integer_t = Integer(node, state)
  20. integer_t.create(i)
  21. return node
  22. def main():
  23. state = DevState()
  24. root = state.read_root() # id: 0
  25. scd_mm_id = bootstrap_scd(state)
  26. int_mm_id = UUID(state.read_value(state.read_dict(state.read_root(), "Integer")))
  27. string_mm_id = UUID(state.read_value(state.read_dict(state.read_root(), "String")))
  28. # conf = Conformance(state, scd_mm_id, scd_mm_id)
  29. # print("Conformance SCD_MM -> SCD_MM?", conf.check_nominal(log=True))
  30. # print("--------------------------------------")
  31. # print(renderer.render_od(state, scd_mm_id, scd_mm_id, hide_names=True))
  32. # print("--------------------------------------")
  33. def create_dsl_mm_api():
  34. # Create DSL MM with SCD API
  35. dsl_mm_id = state.create_node()
  36. dsl_mm_scd = SCD(dsl_mm_id, state)
  37. dsl_mm_scd.create_class("Animal", abstract=True)
  38. dsl_mm_scd.create_class("Man", min_c=1, max_c=2)
  39. dsl_mm_scd.create_inheritance("Man", "Animal")
  40. dsl_mm_scd.create_model_ref("Integer", int_mm_id)
  41. dsl_mm_scd.create_attribute_link("Man", "Integer", "weight", optional=False)
  42. dsl_mm_scd.create_class("Bear")
  43. dsl_mm_scd.create_inheritance("Bear", "Animal")
  44. dsl_mm_scd.create_association("afraidOf", "Man", "Animal",
  45. # Every Man afraid of at least one Animal:
  46. src_min_c=0,
  47. src_max_c=None,
  48. tgt_min_c=1,
  49. tgt_max_c=None,
  50. )
  51. dsl_mm_scd.add_constraint("Man", "read_value(element) < 100")
  52. return dsl_mm_id
  53. def create_dsl_mm_parser():
  54. # Create DSL MM with parser
  55. dsl_mm_cs = """
  56. # Integer:ModelRef
  57. Bear:Class
  58. Animal:Class {
  59. abstract = True;
  60. }
  61. Man:Class {
  62. lower_cardinality = 1;
  63. upper_cardinality = 2;
  64. constraint = `get_value(get_slot(element, "weight")) > 20`;
  65. }
  66. Man_weight:AttributeLink (Man -> Integer) {
  67. name = "weight";
  68. optional = False;
  69. constraint = ```
  70. # this is the same constraint as above, but this time, part of the attributelink itself (and thus shorter)
  71. node = get_target(element)
  72. get_value(node) > 20
  73. ```;
  74. }
  75. afraidOf:Association (Man -> Animal) {
  76. target_lower_cardinality = 1;
  77. }
  78. :Inheritance (Man -> Animal)
  79. :Inheritance (Bear -> Animal)
  80. not_too_fat:GlobalConstraint {
  81. constraint = ```
  82. # total weight of all men low enough
  83. total_weight = 0
  84. for man_name, man_id in get_all_instances("Man"):
  85. total_weight += get_value(get_slot(man_id, "weight"))
  86. total_weight < 85
  87. ```;
  88. }
  89. """
  90. dsl_mm_id = parser.parse_od(state, dsl_mm_cs, mm=scd_mm_id)
  91. return dsl_mm_id
  92. def create_dsl_m_api():
  93. # Create DSL M with OD API
  94. dsl_m_id = state.create_node()
  95. dsl_m_od = OD(dsl_mm_id, dsl_m_id, state)
  96. dsl_m_od.create_object("george", "Man")
  97. dsl_m_od.create_slot("weight", "george",
  98. dsl_m_od.create_integer_value("george.weight", 80))
  99. dsl_m_od.create_object("bear1", "Bear")
  100. dsl_m_od.create_object("bear2", "Bear")
  101. dsl_m_od.create_link("georgeAfraidOfBear1", "afraidOf", "george", "bear1")
  102. dsl_m_od.create_link("georgeAfraidOfBear2", "afraidOf", "george", "bear2")
  103. return dsl_m_id
  104. def create_dsl_m_parser():
  105. # Create DSL M with parser
  106. dsl_m_cs = """
  107. george:Man {
  108. weight = 80;
  109. }
  110. bear1:Bear
  111. bear2:Bear
  112. :afraidOf (george -> bear1)
  113. :afraidOf (george -> bear2)
  114. """
  115. dsl_m_id = parser.parse_od(state, dsl_m_cs, mm=dsl_mm_id)
  116. return dsl_m_id
  117. # dsl_mm_id = create_dsl_mm_api()
  118. dsl_mm_id = create_dsl_mm_parser()
  119. # print("DSL MM:")
  120. # print("--------------------------------------")
  121. # print(renderer.render_od(state, dsl_mm_id, scd_mm_id, hide_names=True))
  122. # print("--------------------------------------")
  123. conf = Conformance(state, dsl_mm_id, scd_mm_id)
  124. print("Conformance DSL_MM -> SCD_MM?", conf.check_nominal(log=True))
  125. # dsl_m_id = create_dsl_m_api()
  126. dsl_m_id = create_dsl_m_parser()
  127. # print("DSL M:")
  128. # print("--------------------------------------")
  129. # print(renderer.render_od(state, dsl_m_id, dsl_mm_id, hide_names=True))
  130. # print("--------------------------------------")
  131. conf = Conformance(state, dsl_m_id, dsl_mm_id)
  132. print("Conformance DSL_M -> DSL_MM?", conf.check_nominal(log=True))
  133. # RAMify MM
  134. prefix = "RAM_" # all ramified types can be prefixed to distinguish them a bit more
  135. ramified_mm_id = ramify(state, dsl_mm_id, prefix)
  136. ramified_int_mm_id = ramify(state, int_mm_id, prefix)
  137. # LHS - pattern to match
  138. # TODO: enable more powerful constraints
  139. lhs_cs = f"""
  140. # object to match
  141. man:{prefix}Man {{
  142. # match only men heavy enough
  143. {prefix}weight = `v > 60`;
  144. }}
  145. # object to delete
  146. scaryAnimal:{prefix}Animal
  147. # link to delete
  148. manAfraidOfAnimal:{prefix}afraidOf (man -> scaryAnimal)
  149. """
  150. lhs_id = parser.parse_od(state, lhs_cs, mm=ramified_mm_id)
  151. conf = Conformance(state, lhs_id, ramified_mm_id)
  152. print("Conformance LHS_M -> RAM_DSL_MM?", conf.check_nominal(log=True))
  153. # RHS of our rule
  154. # TODO: enable more powerful actions
  155. rhs_cs = f"""
  156. # matched object
  157. man:{prefix}Man {{
  158. # man gains weight
  159. {prefix}weight = `v + 5`;
  160. }}
  161. # object to create
  162. bill:{prefix}Man {{
  163. {prefix}weight = `100`;
  164. }}
  165. # link to create
  166. billAfraidOfMan:{prefix}afraidOf (bill -> man)
  167. """
  168. rhs_id = parser.parse_od(state, rhs_cs, mm=ramified_mm_id)
  169. conf = Conformance(state, rhs_id, ramified_mm_id)
  170. print("Conformance RHS_M -> RAM_DSL_MM?", conf.check_nominal(log=True))
  171. def render_ramification():
  172. uml = (""
  173. # Render original and RAMified meta-models
  174. + plantuml.render_package("DSL Meta-Model", plantuml.render_class_diagram(state, dsl_mm_id))
  175. + plantuml.render_package("Int Meta-Model", plantuml.render_class_diagram(state, int_mm_id))
  176. + plantuml.render_package("RAMified DSL Meta-Model", plantuml.render_class_diagram(state, ramified_mm_id))
  177. + plantuml.render_package("RAMified Int Meta-Model", plantuml.render_class_diagram(state, ramified_int_mm_id))
  178. # Render RAMification traceability links
  179. + plantuml.render_trace_ramifies(state, dsl_mm_id, ramified_mm_id)
  180. + plantuml.render_trace_ramifies(state, int_mm_id, ramified_int_mm_id)
  181. )
  182. # Render pattern
  183. uml += plantuml.render_package("LHS", plantuml.render_object_diagram(state, lhs_id, ramified_mm_id))
  184. uml += plantuml.render_trace_conformance(state, lhs_id, ramified_mm_id)
  185. # Render pattern
  186. uml += plantuml.render_package("RHS", plantuml.render_object_diagram(state, rhs_id, ramified_mm_id))
  187. uml += plantuml.render_trace_conformance(state, rhs_id, ramified_mm_id)
  188. return uml
  189. def render_all_matches():
  190. uml = render_ramification()
  191. # Render host graph (before rewriting)
  192. uml += plantuml.render_package("Model (before rewrite)", plantuml.render_object_diagram(state, dsl_m_id, dsl_mm_id))
  193. # Render conformance
  194. uml += plantuml.render_trace_conformance(state, dsl_m_id, dsl_mm_id)
  195. print("matching...")
  196. generator = mvs_adapter.match_od(state, dsl_m_id, dsl_mm_id, lhs_id, ramified_mm_id)
  197. for match, color in zip(generator, ["red", "orange"]):
  198. print("\nMATCH:\n", match)
  199. # Render every match
  200. uml += plantuml.render_trace_match(state, match, lhs_id, dsl_m_id, color)
  201. print("DONE")
  202. return uml
  203. def render_rewrite():
  204. uml = render_ramification()
  205. # Render host graph (before rewriting)
  206. uml += plantuml.render_package("Model (before rewrite)", plantuml.render_object_diagram(state, dsl_m_id, dsl_mm_id))
  207. # Render conformance
  208. uml += plantuml.render_trace_conformance(state, dsl_m_id, dsl_mm_id)
  209. generator = mvs_adapter.match_od(state, dsl_m_id, dsl_mm_id, lhs_id, ramified_mm_id)
  210. for i, (match, color) in enumerate(zip(generator, ["red", "orange"])):
  211. uml += plantuml.render_trace_match(state, match, lhs_id, dsl_m_id, color)
  212. # rewrite happens in-place (which sucks), so we will only modify a clone:
  213. snapshot_dsl_m_id = clone_od(state, dsl_m_id, dsl_mm_id)
  214. rewriter.rewrite(state, lhs_id, rhs_id, ramified_mm_id, match, snapshot_dsl_m_id, dsl_mm_id)
  215. conf = Conformance(state, snapshot_dsl_m_id, dsl_mm_id)
  216. print(f"Conformance DSL_M (after rewrite {i}) -> DSL_MM?", conf.check_nominal(log=True))
  217. # Render host graph (after rewriting)
  218. uml += plantuml.render_package(f"Model (after rewrite {i})", plantuml.render_object_diagram(state, snapshot_dsl_m_id, dsl_mm_id))
  219. # Render match
  220. uml += plantuml.render_trace_match(state, match, rhs_id, snapshot_dsl_m_id, color)
  221. # Render conformance
  222. uml += plantuml.render_trace_conformance(state, snapshot_dsl_m_id, dsl_mm_id)
  223. return uml
  224. # plantuml_str = render_all_matches()
  225. plantuml_str = render_rewrite()
  226. print()
  227. print("==============================================")
  228. print("BEGIN PLANTUML")
  229. print("==============================================")
  230. print(plantuml_str)
  231. print("==============================================")
  232. print("END PLANTUML")
  233. print("==============================================")
  234. if __name__ == "__main__":
  235. main()