parser.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  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
  9. %ignore WS
  10. %ignore COMMENT
  11. ?start: object*
  12. IDENTIFIER: /[A-Za-z_][A-Za-z_0-9]*/
  13. COMMENT: /#[^\n]*\n/
  14. literal: INT
  15. | STR
  16. | BOOL
  17. | CODE
  18. | INDENTED_CODE
  19. INT: /[0-9]+/
  20. STR: /"[^"]*"/
  21. | /'[^']*'/
  22. BOOL: "True" | "False"
  23. CODE: /`[^`]*`/
  24. INDENTED_CODE: /```[^`]*```/
  25. # name (optional) type
  26. object: [IDENTIFIER] ":" IDENTIFIER [link_spec] ["{" slot* "}"]
  27. link_spec: "(" IDENTIFIER "->" IDENTIFIER ")"
  28. slot: IDENTIFIER "=" literal ";"
  29. """
  30. parser = Lark(grammar, parser='lalr')
  31. # internal use only
  32. # just a dumb wrapper to distinguish between code and string
  33. class _Code:
  34. def __init__(self, code):
  35. self.code = code
  36. # given a concrete syntax text string, and a meta-model, parses the CS
  37. def parse_od(state, m_text, mm):
  38. tree = parser.parse(m_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 "" or ''
  54. def CODE(self, token):
  55. return _Code(str(token[1:-1])) # strip the ``
  56. def INDENTED_CODE(self, token):
  57. skip = 4 # strip the ``` and the following newline character
  58. space_count = 0
  59. while token[skip+space_count] == " ":
  60. space_count += 1
  61. lines = token.split('\n')[1:-1]
  62. for line in lines:
  63. if len(line) >= space_count and line[0:space_count] != ' '*space_count:
  64. raise Exception("wrong indentation of INDENTED_CODE")
  65. unindented_lines = [l[space_count:] for l in lines]
  66. return _Code('\n'.join(unindented_lines))
  67. def literal(self, el):
  68. return el[0]
  69. def link_spec(self, el):
  70. [src, tgt] = el
  71. return (src, tgt)
  72. def slot(self, el):
  73. [attr_name, value] = el
  74. return (attr_name, value)
  75. def object(self, el):
  76. [obj_name, type_name, link] = el[0:3]
  77. if obj_name == None:
  78. # object/link names are optional
  79. # generate a unique name if no name given
  80. obj_name = f"__o{self.obj_counter}"
  81. self.obj_counter += 1
  82. if link == None:
  83. obj_node = od.create_object(obj_name, type_name)
  84. else:
  85. src, tgt = link
  86. if tgt == "Integer":
  87. if state.read_dict(m, "Integer") == None:
  88. scd = SCD(m, state)
  89. scd.create_model_ref("Integer", int_mm_id)
  90. od.create_link(obj_name, type_name, src, tgt)
  91. # Create slots
  92. slots = el[3:]
  93. for attr_name, value in slots:
  94. value_name = f"{obj_name}.{attr_name}"
  95. # watch out: in Python, 'bool' is subtype of 'int'
  96. # so we must check for 'bool' first
  97. if isinstance(value, bool):
  98. tgt = od.create_boolean_value(value_name, value)
  99. elif isinstance(value, int):
  100. tgt = od.create_integer_value(value_name, value)
  101. elif isinstance(value, str):
  102. tgt = od.create_string_value(value_name, value)
  103. elif isinstance(value, _Code):
  104. tgt = od.create_actioncode_value(value_name, value.code)
  105. else:
  106. raise Exception("Unimplemented type "+value)
  107. od.create_slot(attr_name, obj_name, tgt)
  108. return obj_name
  109. t = T(visit_tokens=True).transform(tree)
  110. return m