woods.py 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. # Model transformation 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.plantuml.make_url import make_url as make_plantuml_url
  16. from concrete_syntax.textual_od import parser, renderer
  17. def main():
  18. state = DevState()
  19. root = state.read_root() # id: 0
  20. # Meta-meta-model: a class diagram that describes the language of class diagrams
  21. scd_mmm_id = bootstrap_scd(state)
  22. int_mm_id = UUID(state.read_value(state.read_dict(state.read_root(), "Integer")))
  23. string_mm_id = UUID(state.read_value(state.read_dict(state.read_root(), "String")))
  24. # conf = Conformance(state, scd_mmm_id, scd_mmm_id)
  25. # print("Conformance SCD_MM -> SCD_MM?", conf.check_nominal(log=True))
  26. # print("--------------------------------------")
  27. # print(renderer.render_od(state, scd_mmm_id, scd_mmm_id, hide_names=True))
  28. # print("--------------------------------------")
  29. # Create DSL MM with parser
  30. dsl_mm_cs = """
  31. # Integer:ModelRef
  32. Bear:Class
  33. Animal:Class {
  34. abstract = True;
  35. }
  36. Man:Class {
  37. lower_cardinality = 1;
  38. upper_cardinality = 2;
  39. constraint = ```
  40. get_value(get_slot(this, "weight")) > 20
  41. ```;
  42. }
  43. Man_weight:AttributeLink (Man -> Integer) {
  44. name = "weight";
  45. optional = False;
  46. constraint = ```
  47. # this is the same constraint as above, but this time, part of the attributelink itself (and thus shorter)
  48. tgt = get_target(this)
  49. tgt_type = get_type_name(tgt)
  50. get_value(tgt) > 20
  51. ```;
  52. }
  53. afraidOf:Association (Man -> Animal) {
  54. target_lower_cardinality = 1;
  55. }
  56. :Inheritance (Man -> Animal)
  57. :Inheritance (Bear -> Animal)
  58. not_too_fat:GlobalConstraint {
  59. constraint = ```
  60. # total weight of all men low enough
  61. total_weight = 0
  62. for man_name, man_id in get_all_instances("Man"):
  63. total_weight += get_value(get_slot(man_id, "weight"))
  64. total_weight < 85
  65. ```;
  66. }
  67. """
  68. dsl_mm_id = parser.parse_od(state, dsl_mm_cs, mm=scd_mmm_id)
  69. # Create DSL M with parser
  70. dsl_m_cs = """
  71. george:Man {
  72. weight = 80;
  73. }
  74. bear1:Bear
  75. bear2:Bear
  76. :afraidOf (george -> bear1)
  77. :afraidOf (george -> bear2)
  78. """
  79. dsl_m_id = parser.parse_od(state, dsl_m_cs, mm=dsl_mm_id)
  80. # print("DSL MM:")
  81. # print("--------------------------------------")
  82. # print(renderer.render_od(state, dsl_mm_id, scd_mmm_id, hide_names=True))
  83. # print("--------------------------------------")
  84. conf = Conformance(state, dsl_mm_id, scd_mmm_id)
  85. print("Conformance DSL_MM -> SCD_MM?", conf.check_nominal(log=True))
  86. # print("DSL M:")
  87. # print("--------------------------------------")
  88. # print(renderer.render_od(state, dsl_m_id, dsl_mm_id, hide_names=True))
  89. # print("--------------------------------------")
  90. conf = Conformance(state, dsl_m_id, dsl_mm_id)
  91. print("Conformance DSL_M -> DSL_MM?", conf.check_nominal(log=True))
  92. # RAMify MM
  93. prefix = "RAM_" # all ramified types can be prefixed to distinguish them a bit more
  94. ramified_mm_id = ramify(state, dsl_mm_id, prefix)
  95. ramified_int_mm_id = ramify(state, int_mm_id, prefix)
  96. # LHS - pattern to match
  97. # TODO: enable more powerful constraints
  98. lhs_cs = f"""
  99. # object to match
  100. man:{prefix}Man {{
  101. # match only men heavy enough
  102. {prefix}weight = ```
  103. get_value(this) > 60
  104. ```;
  105. }}
  106. # object to delete
  107. scaryAnimal:{prefix}Animal
  108. # link to delete
  109. manAfraidOfAnimal:{prefix}afraidOf (man -> scaryAnimal)
  110. """
  111. lhs_id = parser.parse_od(state, lhs_cs, mm=ramified_mm_id)
  112. conf = Conformance(state, lhs_id, ramified_mm_id)
  113. print("Conformance LHS_M -> RAM_DSL_MM?", conf.check_nominal(log=True))
  114. # RHS of our rule
  115. # TODO: enable more powerful actions
  116. rhs_cs = f"""
  117. # matched object
  118. man:{prefix}Man {{
  119. # man gains weight
  120. {prefix}weight = `get_value(this) + 5`;
  121. }}
  122. # object to create
  123. bill:{prefix}Man {{
  124. {prefix}weight = `100`;
  125. }}
  126. # link to create
  127. billAfraidOfMan:{prefix}afraidOf (bill -> man)
  128. """
  129. rhs_id = parser.parse_od(state, rhs_cs, mm=ramified_mm_id)
  130. conf = Conformance(state, rhs_id, ramified_mm_id)
  131. print("Conformance RHS_M -> RAM_DSL_MM?", conf.check_nominal(log=True))
  132. def render_ramification():
  133. uml = (""
  134. # Render original and RAMified meta-models
  135. + plantuml.render_package("DSL Meta-Model", plantuml.render_class_diagram(state, dsl_mm_id))
  136. + plantuml.render_package("Int Meta-Model", plantuml.render_class_diagram(state, int_mm_id))
  137. + plantuml.render_package("RAMified DSL Meta-Model", plantuml.render_class_diagram(state, ramified_mm_id))
  138. + plantuml.render_package("RAMified Int Meta-Model", plantuml.render_class_diagram(state, ramified_int_mm_id))
  139. # Render RAMification traceability links
  140. + plantuml.render_trace_ramifies(state, dsl_mm_id, ramified_mm_id)
  141. + plantuml.render_trace_ramifies(state, int_mm_id, ramified_int_mm_id)
  142. )
  143. # Render pattern
  144. uml += plantuml.render_package("LHS", plantuml.render_object_diagram(state, lhs_id, ramified_mm_id))
  145. uml += plantuml.render_trace_conformance(state, lhs_id, ramified_mm_id)
  146. # Render pattern
  147. uml += plantuml.render_package("RHS", plantuml.render_object_diagram(state, rhs_id, ramified_mm_id))
  148. uml += plantuml.render_trace_conformance(state, rhs_id, ramified_mm_id)
  149. return uml
  150. def render_all_matches():
  151. uml = render_ramification()
  152. # Render host graph (before rewriting)
  153. uml += plantuml.render_package("Model (before rewrite)", plantuml.render_object_diagram(state, dsl_m_id, dsl_mm_id))
  154. # Render conformance
  155. uml += plantuml.render_trace_conformance(state, dsl_m_id, dsl_mm_id)
  156. print("matching...")
  157. generator = mvs_adapter.match_od(state, dsl_m_id, dsl_mm_id, lhs_id, ramified_mm_id)
  158. for match, color in zip(generator, ["red", "orange"]):
  159. print("\nMATCH:\n", match)
  160. # Render every match
  161. uml += plantuml.render_trace_match(state, match, lhs_id, dsl_m_id, color)
  162. print("DONE")
  163. return uml
  164. def render_rewrite():
  165. uml = render_ramification()
  166. # Render host graph (before rewriting)
  167. uml += plantuml.render_package("Model (before rewrite)", plantuml.render_object_diagram(state, dsl_m_id, dsl_mm_id))
  168. # Render conformance
  169. uml += plantuml.render_trace_conformance(state, dsl_m_id, dsl_mm_id)
  170. generator = mvs_adapter.match_od(state, dsl_m_id, dsl_mm_id, lhs_id, ramified_mm_id)
  171. for i, (match, color) in enumerate(zip(generator, ["red", "orange"])):
  172. uml += plantuml.render_trace_match(state, match, lhs_id, dsl_m_id, color)
  173. # rewrite happens in-place (which sucks), so we will only modify a clone:
  174. snapshot_dsl_m_id = clone_od(state, dsl_m_id, dsl_mm_id)
  175. rewriter.rewrite(state, lhs_id, rhs_id, ramified_mm_id, match, snapshot_dsl_m_id, dsl_mm_id)
  176. conf = Conformance(state, snapshot_dsl_m_id, dsl_mm_id)
  177. print(f"Conformance DSL_M (after rewrite {i}) -> DSL_MM?", conf.check_nominal(log=True))
  178. # Render host graph (after rewriting)
  179. uml += plantuml.render_package(f"Model (after rewrite {i})", plantuml.render_object_diagram(state, snapshot_dsl_m_id, dsl_mm_id))
  180. # Render match
  181. uml += plantuml.render_trace_match(state, match, rhs_id, snapshot_dsl_m_id, color)
  182. # Render conformance
  183. uml += plantuml.render_trace_conformance(state, snapshot_dsl_m_id, dsl_mm_id)
  184. return make_plantuml_url(uml)
  185. # plantuml_str = render_all_matches()
  186. plantuml_str = render_rewrite()
  187. print()
  188. print("==============================================")
  189. print("BEGIN PLANTUML")
  190. print("==============================================")
  191. print(plantuml_str)
  192. print("==============================================")
  193. print("END PLANTUML")
  194. print("==============================================")
  195. if __name__ == "__main__":
  196. main()