parser.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. # Parser for Object Diagrams textual concrete syntax
  2. from lark import Lark, logger, Transformer
  3. from lark.indenter import Indenter
  4. from services.od import OD
  5. from services.scd import SCD
  6. from uuid import UUID
  7. grammar = r"""
  8. %import common.WS_INLINE
  9. %ignore WS_INLINE
  10. %ignore COMMENT
  11. %declare _INDENT _DEDENT
  12. ?start: (_NL | object )*
  13. IDENTIFIER: /[A-Za-z_][A-Za-z_0-9]*/
  14. COMMENT: /#.*/
  15. # newline
  16. _NL: /(\r?\n[\t ]*)+/
  17. literal: INT
  18. | STR
  19. | BOOL
  20. INT: /[0-9]+/
  21. STR: /"[^"]*"/
  22. | /'[^']*'/
  23. BOOL: "True" | "False"
  24. object: [IDENTIFIER] ":" IDENTIFIER [link] _NL [_INDENT slot+ _DEDENT]
  25. link: "(" IDENTIFIER "->" IDENTIFIER ")"
  26. slot: IDENTIFIER "=" literal _NL
  27. """
  28. class TreeIndenter(Indenter):
  29. NL_type = '_NL'
  30. OPEN_PAREN_types = []
  31. CLOSE_PAREN_types = []
  32. INDENT_type = '_INDENT'
  33. DEDENT_type = '_DEDENT'
  34. tab_len = 4
  35. parser = Lark(grammar, parser='lalr', postlex=TreeIndenter())
  36. # given a concrete syntax text string, and a meta-model, parses the CS
  37. def parse_od(state, cs_text, mm):
  38. tree = parser.parse(cs_text)
  39. m = state.create_node()
  40. od = OD(mm, m, state)
  41. int_mm_id = UUID(state.read_value(state.read_dict(state.read_root(), "Integer")))
  42. class T(Transformer):
  43. def __init__(self, visit_tokens):
  44. super().__init__(visit_tokens)
  45. self.obj_counter = 0
  46. def IDENTIFIER(self, token):
  47. return str(token)
  48. def INT(self, token):
  49. return int(token)
  50. def BOOL(self, token):
  51. return token == "True"
  52. def STR(self, token):
  53. return str(token[1:-1]) # strip the ""
  54. def literal(self, el):
  55. return el[0]
  56. def link(self, el):
  57. [src, tgt] = el
  58. return (src, tgt)
  59. def slot(self, el):
  60. [attr_name, value] = el
  61. return (attr_name, value)
  62. def object(self, el):
  63. [obj_name, type_name, link] = el[0:3]
  64. if obj_name == None:
  65. # object/link names are optional
  66. # generate a unique name if no name given
  67. obj_name = f"__o{self.obj_counter}"
  68. self.obj_counter += 1
  69. if link == None:
  70. obj_node = od.create_object(obj_name, type_name)
  71. else:
  72. src, tgt = link
  73. if tgt == "Integer":
  74. if state.read_dict(m, "Integer") == None:
  75. scd = SCD(m, state)
  76. scd.create_model_ref("Integer", int_mm_id)
  77. od.create_link(obj_name, type_name, src, tgt)
  78. # Create slots
  79. slots = el[3:]
  80. for attr_name, value in slots:
  81. value_name = f"{obj_name}.{attr_name}"
  82. # watch out: in Python, 'bool' is subtype of 'int'
  83. # so we must check for 'bool' first
  84. if isinstance(value, bool):
  85. tgt = od.create_boolean_value(value_name, value)
  86. elif isinstance(value, int):
  87. tgt = od.create_integer_value(value_name, value)
  88. elif isinstance(value, str):
  89. tgt = od.create_string_value(value_name, value)
  90. else:
  91. raise Exception("Unimplemented type "+value)
  92. od.create_slot(attr_name, obj_name, tgt)
  93. return obj_name
  94. t = T(visit_tokens=True).transform(tree)
  95. return m