parser.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. # Parser for Object Diagrams textual concrete syntax
  2. from lark import Lark, logger
  3. from lark.indenter import Indenter
  4. from api.od import ODAPI
  5. from services.scd import SCD
  6. from concrete_syntax.common import _Code, TBase
  7. from uuid import UUID
  8. grammar = r"""
  9. %import common.WS
  10. %ignore WS
  11. %ignore COMMENT
  12. ?start: object*
  13. IDENTIFIER: /[A-Za-z_][A-Za-z_0-9]*/
  14. COMMENT: /#[^\n]*/
  15. literal: INT
  16. | STR
  17. | BOOL
  18. | CODE
  19. | INDENTED_CODE
  20. INT: /[0-9]+/
  21. STR: /"[^"]*"/
  22. | /'[^']*'/
  23. BOOL: "True" | "False"
  24. CODE: /`[^`]*`/
  25. INDENTED_CODE: /```[^`]*```/
  26. type_name: IDENTIFIER
  27. # name (optional) type
  28. object: [IDENTIFIER] ":" type_name [link_spec | rev_link_spec] ["{" slot* "}"]
  29. link_spec: "(" IDENTIFIER "->" IDENTIFIER ")"
  30. rev_link_spec: "(" IDENTIFIER "<-" IDENTIFIER ")"
  31. slot: IDENTIFIER "=" literal ";"
  32. """
  33. parser = Lark(grammar, parser='lalr')
  34. # given a concrete syntax text string, and a meta-model, parses the CS
  35. # Parameter 'type_transform' is useful for adding prefixes to the type names, when parsing a model and pretending it is an instance of a prefixed meta-model.
  36. def parse_od(state, m_text, mm, type_transform=lambda type_name: type_name):
  37. tree = parser.parse(m_text)
  38. m = state.create_node()
  39. od = ODAPI(state, m, mm)
  40. primitive_types = {
  41. type_name : UUID(state.read_value(state.read_dict(state.read_root(), type_name)))
  42. for type_name in ["Integer", "String", "Boolean", "ActionCode"]
  43. }
  44. class T(TBase):
  45. def __init__(self, visit_tokens):
  46. super().__init__(visit_tokens)
  47. self.obj_counter = 0 # used for generating unique names for anonymous objects
  48. def link_spec(self, el):
  49. [src, tgt] = el
  50. return (src, tgt)
  51. def rev_link_spec(self, el):
  52. [tgt, src] = el # <-- reversed :)
  53. return (src, tgt)
  54. def type_name(self, el):
  55. type_name = el[0]
  56. if type_name in primitive_types:
  57. return type_name
  58. else:
  59. return type_transform(el[0])
  60. def slot(self, el):
  61. [attr_name, value] = el
  62. return (attr_name, value)
  63. def object(self, el):
  64. [obj_name, type_name, link] = el[0:3]
  65. slots = el[3:]
  66. if state.read_dict(m, obj_name) != None:
  67. msg = f"Element '{obj_name}:{type_name}': name '{obj_name}' already in use."
  68. # raise Exception(msg + " Names must be unique")
  69. print(msg + " Ignoring.")
  70. return
  71. if obj_name == None:
  72. # object/link names are optional
  73. # generate a unique name if no name given
  74. obj_name = f"__{type_name}_{self.obj_counter}"
  75. self.obj_counter += 1
  76. if link == None:
  77. obj_node = od.create_object(obj_name, type_name)
  78. else:
  79. src, tgt = link
  80. if tgt in primitive_types:
  81. if state.read_dict(m, tgt) == None:
  82. scd = SCD(m, state)
  83. scd.create_model_ref(tgt, primitive_types[tgt])
  84. src_obj = od.get(src)
  85. tgt_obj = od.get(tgt)
  86. obj_node = od.create_link(obj_name, type_name, src_obj, tgt_obj)
  87. # Create slots
  88. for attr_name, value in slots:
  89. if isinstance(value, _Code):
  90. od.set_slot_value(obj_node, attr_name, value.code, is_code=True)
  91. else:
  92. od.set_slot_value(obj_node, attr_name, value)
  93. return obj_name
  94. t = T(visit_tokens=True).transform(tree)
  95. return m