woods.py 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. from state.devstate import DevState
  2. from bootstrap.scd import bootstrap_scd
  3. from framework.conformance import Conformance, render_conformance_check_result
  4. from concrete_syntax.textual_od import parser, renderer
  5. from concrete_syntax.common import indent
  6. from concrete_syntax.plantuml import renderer as plantuml
  7. from util.prompt import yes_no, pause
  8. state = DevState()
  9. print("Loading meta-meta-model...")
  10. scd_mmm = bootstrap_scd(state)
  11. print("OK")
  12. print("Is our meta-meta-model a valid class diagram?")
  13. conf = Conformance(state, scd_mmm, scd_mmm)
  14. print(render_conformance_check_result(conf.check_nominal()))
  15. # If you are curious, you can serialize the meta-meta-model:
  16. # print("--------------")
  17. # print(indent(
  18. # renderer.render_od(state,
  19. # m_id=scd_mmm,
  20. # mm_id=scd_mmm),
  21. # 4))
  22. # print("--------------")
  23. # Change this:
  24. woods_mm_cs = """
  25. Animal:Class {
  26. # The class Animal is an abstract class:
  27. abstract = True;
  28. }
  29. # A class without attributes
  30. # The `abstract` attribute shown above is optional (default: False)
  31. Bear:Class
  32. # Inheritance between two Classes is expressed as follows:
  33. :Inheritance (Bear -> Animal) # meaning: Bear is an Animal
  34. Man:Class {
  35. # We can define lower and upper cardinalities on Classes
  36. # (if unspecified, the lower-card is 0, and upper-card is infinity)
  37. lower_cardinality = 1; # there must be at least one Man in every model
  38. upper_cardinality = 2; # there must be at most two Men in every model
  39. constraint = ```
  40. # Python code
  41. # the last statement must be a boolean expression
  42. # When conformance checking, this code will be run for every Man-object.
  43. # The variable 'this' refers to the current Man-object.
  44. # Every man weighs at least '20'
  45. # (the attribute 'weight' is added further down)
  46. get_value(get_slot(this, "weight")) > 20
  47. ```;
  48. }
  49. # Note that we can only declare the inheritance link after having declared both Man and Animal: We can only refer to earlier objects
  50. :Inheritance (Man -> Animal) # Man is also an Animal
  51. # BTW, we could also give the Inheritance-link a name, for instance:
  52. # man_is_animal:Inheritance (Man -> Animal)
  53. #
  54. # Likewise, Classes, Associations, ... can also be nameless, for instance:
  55. # :Class { ... }
  56. # :Association (Man -> Man) { ... }
  57. # However, we typically want to give names to classes and associations, because we want to refer to them later.
  58. # We now add an attribute to 'Man'
  59. # Attributes are not that different from Associations: both are represented by links
  60. Man_weight:AttributeLink (Man -> Integer) {
  61. name = "weight"; # mandatory!
  62. optional = False; # <- meaning: every Man *must* have a weight
  63. # We can also define constraints on attributes
  64. constraint = ```
  65. # Python code
  66. # Here, 'this' refers to the LINK that connects a Man-object to an Integer
  67. tgt = get_target(this) # <- we get the target of the LINK (an Integer-object)
  68. weight = get_value(tgt) # <- get the Integer-value (e.g., 80)
  69. weight > 20
  70. ```;
  71. }
  72. # Create an Association from Man to Animal
  73. afraidOf:Association (Man -> Animal) {
  74. # An association has the following (optional) attributes:
  75. # - source_lower_cardinality (default: 0)
  76. # - source_upper_cardinality (default: infinity)
  77. # - target_lower_cardinality (default: 0)
  78. # - target_upper_cardinality (default: infinity)
  79. # Every Man is afraid of at least one Animal:
  80. target_lower_cardinality = 1;
  81. # No more than 6 Men are afraid of the same Animal:
  82. source_upper_cardinality = 6;
  83. }
  84. # Create a GlobalConstraint
  85. total_weight_small_enough:GlobalConstraint {
  86. # Note: for GlobalConstraints, there is no 'this'-variable
  87. constraint = ```
  88. # Python code
  89. # compute sum of all weights
  90. total_weight = 0
  91. for man_name, man_id in get_all_instances("Man"):
  92. total_weight += get_value(get_slot(man_id, "weight"))
  93. # as usual, the last statement is a boolean expression that we think should be satisfied
  94. total_weight < 85
  95. ```;
  96. }
  97. """
  98. print()
  99. print("Parsing 'woods' meta-model...")
  100. woods_mm = parser.parse_od(
  101. state,
  102. m_text=woods_mm_cs, # the string of text to parse
  103. mm=scd_mmm, # the meta-model of class diagrams (= our meta-meta-model)
  104. )
  105. print("OK")
  106. # As a double-check, you can serialize the parsed model:
  107. # print("--------------")
  108. # print(indent(
  109. # renderer.render_od(state,
  110. # m_id=woods_mm,
  111. # mm_id=scd_mmm),
  112. # 4))
  113. # print("--------------")
  114. print("Is our 'woods' meta-model a valid class diagram?")
  115. conf = Conformance(state, woods_mm, scd_mmm)
  116. print(render_conformance_check_result(conf.check_nominal()))
  117. # Change this:
  118. woods_m_cs = """
  119. george:Man {
  120. weight = 15;
  121. }
  122. billy:Man {
  123. weight = 100;
  124. }
  125. bear1:Bear
  126. bear2:Bear
  127. :afraidOf (george -> bear1)
  128. :afraidOf (george -> bear2)
  129. """
  130. print()
  131. print("Parsing 'woods' model...")
  132. woods_m = parser.parse_od(
  133. state,
  134. m_text=woods_m_cs,
  135. mm=woods_mm, # this time, the meta-model is the previous model we parsed
  136. )
  137. print("OK")
  138. # As a double-check, you can serialize the parsed model:
  139. # print("--------------")
  140. # print(indent(
  141. # renderer.render_od(state,
  142. # m_id=woods_m,
  143. # mm_id=woods_mm),
  144. # 4))
  145. # print("--------------")
  146. print("Is our model a valid woods-diagram?")
  147. conf = Conformance(state, woods_m, woods_mm)
  148. print(render_conformance_check_result(conf.check_nominal()))
  149. print()
  150. print("==================================")
  151. if yes_no("Print PlantUML?"):
  152. print_mm = yes_no(" ▸ Print meta-model?")
  153. print_m = yes_no(" ▸ Print model?")
  154. print_conf = print_mm and print_m and yes_no(" ▸ Print conformance links?")
  155. uml = ""
  156. if print_mm:
  157. uml += plantuml.render_package("Meta-model", plantuml.render_class_diagram(state, woods_mm))
  158. if print_m:
  159. uml += plantuml.render_package("Model", plantuml.render_object_diagram(state, woods_m, woods_mm))
  160. if print_conf:
  161. uml += plantuml.render_trace_conformance(state, woods_m, woods_mm)
  162. print("==================================")
  163. print(uml)
  164. print("==================================")
  165. print("Go to either:")
  166. print(" ▸ https://www.plantuml.com/plantuml/uml")
  167. print(" ▸ https://mstro.duckdns.org/plantuml/uml")
  168. print("and paste the above string.")