1
- from pyecore .ecore import EAttribute , EObject , EPackage , EReference , MetaEClass , EString , EInt
1
+ import typing
2
+ from dataclasses import is_dataclass , fields
3
+ from enum import Enum , EnumMeta
4
+ from types import resolve_bases
5
+
6
+ from pyecore .ecore import EAttribute , ECollection , EObject , EPackage , EReference , MetaEClass , EBoolean , EString , EInt , EEnum
2
7
from pyecore .resources import Resource
3
8
4
9
from pylasu import StrumentaLanguageSupport as starlasu
5
10
from pylasu .StrumentaLanguageSupport import ASTNode
11
+ from pylasu .emf .model import find_eclassifier
6
12
from pylasu .model import Node
13
+ from pylasu .model .model import InternalField
7
14
8
15
9
16
class MetamodelBuilder :
10
17
def __init__ (self , package_name : str , ns_uri : str , ns_prefix : str = None , resource : Resource = None ):
11
18
self .package = EPackage (package_name , ns_uri , ns_prefix )
12
19
if resource :
13
20
resource .append (self .package )
21
+ self .data_types = {
22
+ bool : EBoolean ,
23
+ int : EInt ,
24
+ str : EString ,
25
+ }
26
+ self .forward_references = []
14
27
15
28
def can_provide_class (self , cls : type ):
16
29
return cls .__module__ == self .package .name
17
30
18
31
def provide_class (self , cls : type ):
32
+ if cls == Node :
33
+ return ASTNode
19
34
if not self .can_provide_class (cls ):
35
+ if self .package .eResource :
36
+ eclass = find_eclassifier (self .package .eResource , cls )
37
+ if eclass :
38
+ return eclass
20
39
raise Exception (self .package .name + " cannot provide class " + str (cls ))
21
40
eclass = self .package .getEClassifier (cls .__name__ )
22
41
if not eclass :
23
42
anns = getannotations (cls )
24
43
nmspc = {
25
44
"position" : EReference ("position" , starlasu .Position , containment = True )
26
45
}
27
- for attr in anns :
28
- if anns [attr ] == str :
29
- nmspc [attr ] = EAttribute (attr , EString )
30
- elif anns [attr ] == int :
31
- nmspc [attr ] = EAttribute (attr , EInt )
46
+ for attr in anns if anns else []:
47
+ if is_dataclass (cls ):
48
+ field = next ((f for f in fields (cls ) if f .name == attr ), None )
49
+ if isinstance (field , InternalField ):
50
+ continue
51
+ attr_type = anns [attr ]
52
+ nmspc [attr ] = self .to_structural_feature (attr , attr_type )
32
53
bases = []
33
54
for c in cls .__mro__ [1 :]:
34
55
if c == Node :
35
56
bases .append (ASTNode )
36
57
elif self .can_provide_class (c ):
37
58
bases .append (self .provide_class (c ))
59
+ elif self .package .eResource :
60
+ esuperclass = find_eclassifier (self .package .eResource , c )
61
+ if esuperclass :
62
+ bases .append (esuperclass )
38
63
bases .append (EObject )
39
- eclass = MetaEClass (cls .__name__ , tuple (bases ), nmspc )
64
+ eclass = MetaEClass (cls .__name__ , resolve_bases ( tuple (bases ) ), nmspc )
40
65
eclass .eClass .ePackage = self .package
66
+ for (type_name , ref ) in self .forward_references :
67
+ if type_name == cls .__name__ :
68
+ ref .eType = eclass
69
+ self .forward_references = [(t , r ) for t , r in self .forward_references if not r .eType ]
41
70
return eclass
42
71
72
+ def to_structural_feature (self , attr , attr_type ):
73
+ if isinstance (attr_type , str ):
74
+ resolved = self .package .getEClassifier (attr_type )
75
+ if resolved :
76
+ return EReference (attr , resolved , containment = True )
77
+ else :
78
+ forward_reference = EReference (attr , containment = True )
79
+ self .forward_references .append ((attr_type , forward_reference ))
80
+ return forward_reference
81
+ elif attr_type in self .data_types :
82
+ return EAttribute (attr , self .data_types [attr_type ])
83
+ elif attr_type == object :
84
+ return EAttribute (attr )
85
+ elif isinstance (attr_type , type ) and issubclass (attr_type , Node ):
86
+ return EReference (attr , self .provide_class (attr_type ), containment = True )
87
+ elif typing .get_origin (attr_type ) == list :
88
+ type_args = typing .get_args (attr_type )
89
+ if type_args and len (type_args ) == 1 :
90
+ ft = self .to_structural_feature (attr , type_args [0 ])
91
+ ft .upperBound = - 1
92
+ return ft
93
+ elif isinstance (attr_type , EnumMeta ) and issubclass (attr_type , Enum ):
94
+ tp = EEnum (name = attr_type .__name__ , literals = attr_type .__members__ )
95
+ tp .ePackage = self .package
96
+ self .data_types [attr_type ] = tp
97
+ return EAttribute (attr , tp )
98
+ else :
99
+ raise Exception ("Unsupported type " + str (attr_type ) + " for attribute " + attr )
100
+
43
101
def generate (self ):
102
+ if self .forward_references :
103
+ raise Exception ("The following classes are missing from " + self .package .name + ": " +
104
+ ", " .join (n for n , _ in self .forward_references ))
44
105
return self .package
45
106
46
107
@@ -53,3 +114,15 @@ def getannotations(cls):
53
114
return cls .__dict__ .get ('__annotations__' , None )
54
115
else :
55
116
return getattr (cls , '__annotations__' , None )
117
+
118
+
119
+ # Monkey patch until fix
120
+ update_opposite = ECollection ._update_opposite
121
+
122
+
123
+ def update_opposite_if_not_none (self , owner , new_value , remove = False ):
124
+ if owner :
125
+ update_opposite (self , owner , new_value , remove )
126
+
127
+
128
+ ECollection ._update_opposite = update_opposite_if_not_none
0 commit comments