muMLE/concrete_syntax/textual_cd/parser.py

150 lines
4.7 KiB
Python

from lark import Lark, logger
from concrete_syntax.common import _Code, TBase
from uuid import UUID
from services.scd import SCD
from services.od import OD
grammar = r"""
%import common.WS
%ignore WS
%ignore COMMENT
?start: (class_ | association | global_constraint)*
IDENTIFIER: /[A-Za-z_][A-Za-z_0-9]*/
COMMENT: /#[^\n]*\n/
literal: INT
| STR
| BOOL
| CODE
| INDENTED_CODE
INT: /[0-9]+/
STR: /"[^"]*"/
| /'[^']*'/
BOOL: "True" | "False"
CODE: /`[^`]*`/
INDENTED_CODE: /```[^`]*```/
INT_OR_INF: INT | "*"
multiplicity: "[" INT ".." INT_OR_INF "]"
ABSTRACT: "abstract"
superclasses: IDENTIFIER ("," IDENTIFIER)*
attrs: attr*
constraint: CODE | INDENTED_CODE
class_: [ABSTRACT] "class" IDENTIFIER [multiplicity] ["(" superclasses ")"] ["{" attrs [constraint ";"] "}"]
association: "association" IDENTIFIER [multiplicity] IDENTIFIER "->" IDENTIFIER [multiplicity] ["{" attrs [constraint] "}"]
OPTIONAL: "optional"
attr: [OPTIONAL] IDENTIFIER IDENTIFIER [constraint] ";"
global_constraint: "global" IDENTIFIER constraint
"""
parser = Lark(grammar, parser='lalr')
def _handle_missing_multiplicity(multiplicity):
if multiplicity != None:
return multiplicity
else:
return (None, None)
def parse_cd(state, m_text):
type_model_id = state.read_dict(state.read_root(), "SCD")
scd_mmm = UUID(state.read_value(type_model_id))
m = state.create_node()
cd = SCD(m, state)
od = OD(scd_mmm, m, state)
def _add_constraint_to_obj(obj_name, constraint):
constraint_name = f"{obj_name}.constraint"
od.create_actioncode_value(constraint_name, constraint.code)
od.create_slot("constraint", obj_name, constraint_name)
primitive_types = {
type_name : UUID(state.read_value(state.read_dict(state.read_root(), type_name)))
for type_name in ["Integer", "String", "Boolean", "ActionCode"]
}
class T(TBase):
def __init__(self, visit_tokens):
super().__init__(visit_tokens)
self.obj_counter = 0
def ABSTRACT(self, el):
return True
def INT_OR_INF(self, el):
# infinity only used for upper cardinality,
# where the default value (None) represents infinity
# cannot use `float('inf')` because then it violates the constraint of type 'Integer'
return None if el == "*" else int(el)
def multiplicity(self, el):
[lower, upper] = el
return (lower, upper)
def superclasses(self, el):
return list(el)
def attrs(self, el):
return list(el)
def constraint(self, el):
return el[0]
def attr(self, el):
[optional, attr_type, attr_name, constraint] = el
return (optional == "optional", attr_type, attr_name, constraint)
def global_constraint(self, el):
[name, constraint] = el
od.create_object(name, "GlobalConstraint")
_add_constraint_to_obj(name, constraint)
def process_attrs(self, attrs, class_name):
if attrs != None:
for attr in attrs:
(optional, attr_type, attr_name, constraint) = attr
if state.read_dict(m, attr_type) == None:
cd.create_model_ref(attr_type, primitive_types[attr_type])
cd.create_attribute_link(class_name, attr_type, attr_name, optional)
if constraint != None:
_add_constraint_to_obj(f"{class_name}_{attr_name}", constraint)
def class_(self, el):
[abstract, class_name, multiplicity, super_classes, attrs, constraint] = el
(lower, upper) = _handle_missing_multiplicity(multiplicity)
cd.create_class(class_name, abstract, lower, upper)
if super_classes != None:
for super_class in super_classes:
cd.create_inheritance(class_name, super_class)
if constraint != None:
_add_constraint_to_obj(class_name, constraint)
self.process_attrs(attrs, class_name)
def association(self, el):
[assoc_name, src_multiplicity, src_name, tgt_name, tgt_multiplicity, attrs, constraint] = el
(src_lower, src_upper) = _handle_missing_multiplicity(src_multiplicity)
(tgt_lower, tgt_upper) = _handle_missing_multiplicity(tgt_multiplicity)
cd.create_association(assoc_name, src_name, tgt_name, src_lower, src_upper, tgt_lower, tgt_upper)
if constraint != None:
_add_constraint_to_obj(assoc_name, constraint)
self.process_attrs(attrs, assoc_name)
tree = parser.parse(m_text)
t = T(visit_tokens=True).transform(tree)
return m