Skip to content

Commit c33a6f6

Browse files
committed
Merge remote-tracking branch 'origin/feature/lionweb-integration' into feature/lionweb-integration
2 parents b0f26f3 + 2fa2185 commit c33a6f6

File tree

9 files changed

+151
-162251
lines changed

9 files changed

+151
-162251
lines changed

.github/workflows/pythonapp.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,4 @@ jobs:
3636
- name: Test with pytest
3737
run: |
3838
pip install pytest pytest-cov pyecore==0.12.2
39-
pytest --cov=pylasu --cov-fail-under=60 tests
39+
pytest --cov=pylasu --cov-fail-under=50 tests

pylasu/lionweb/ast_generation.py

Lines changed: 108 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import ast
22
import keyword
3+
from _ast import ClassDef
34
from pathlib import Path
45
from typing import List, Dict
56

67
import astor
7-
from lionwebpython.language import Language, Concept, Interface, Containment, Property
8+
from lionwebpython.language import Language, Concept, Interface, Containment, Property, Feature
89
from lionwebpython.language.classifier import Classifier
910
from lionwebpython.language.enumeration import Enumeration
1011
from lionwebpython.language.primitive_type import PrimitiveType
@@ -15,10 +16,7 @@
1516
from pylasu.lionweb.utils import calculate_field_name
1617

1718

18-
def topological_classifiers_sort(classifiers: List[Classifier]) -> List[Classifier]:
19-
id_to_concept = {el.get_id(): el for el in classifiers}
20-
21-
# Build graph edges: child -> [parents]
19+
def _identify_topological_deps(classifiers: List[Classifier], id_to_concept) -> Dict[str, List[str]]:
2220
graph: Dict[str, List[str]] = {el.get_id(): [] for el in classifiers}
2321
for c in classifiers:
2422
if isinstance(c, Concept):
@@ -34,6 +32,14 @@ def topological_classifiers_sort(classifiers: List[Classifier]) -> List[Classifi
3432
pass
3533
else:
3634
raise ValueError()
35+
return graph
36+
37+
38+
def topological_classifiers_sort(classifiers: List[Classifier]) -> List[Classifier]:
39+
id_to_concept = {el.get_id(): el for el in classifiers}
40+
41+
# Build graph edges: child -> [parents]
42+
graph: Dict[str, List[str]] = _identify_topological_deps(classifiers, id_to_concept)
3743

3844
visited = set()
3945
sorted_list = []
@@ -53,6 +59,100 @@ def visit(name: str):
5359

5460
return sorted_list
5561

62+
63+
def _generate_from_containment(feature: Containment, classdef: ClassDef):
64+
field_name = calculate_field_name(feature)
65+
type = feature.get_type().get_name()
66+
if feature.is_multiple():
67+
type = f"List[{type}]"
68+
elif feature.is_optional():
69+
type = f"Optional[{type}]"
70+
field = ast.AnnAssign(
71+
target=ast.Name(id=field_name, ctx=ast.Store()),
72+
annotation=ast.Constant(value=type),
73+
value=None,
74+
simple=1,
75+
)
76+
if len(classdef.body) == 1 and isinstance(classdef.body[0], ast.Pass):
77+
classdef.body = []
78+
classdef.body.append(field)
79+
80+
81+
def _generate_from_feature(feature: Feature, classdef: ClassDef):
82+
if isinstance(feature, Containment):
83+
_generate_from_containment(feature, classdef)
84+
elif isinstance(feature, Reference):
85+
field_name = feature.get_name()
86+
if field_name in keyword.kwlist:
87+
field_name = f"{field_name}_"
88+
type = f"ReferenceByName[{feature.get_type().get_name()}]"
89+
if feature.is_optional():
90+
type = f"Optional[{type}]"
91+
field = ast.AnnAssign(
92+
target=ast.Name(id=field_name, ctx=ast.Store()),
93+
annotation=ast.Constant(value=type),
94+
value=None,
95+
simple=1,
96+
)
97+
if len(classdef.body) == 1 and isinstance(classdef.body[0], ast.Pass):
98+
classdef.body = []
99+
classdef.body.append(field)
100+
elif isinstance(feature, Property):
101+
field_name = feature.get_name()
102+
if field_name in keyword.kwlist:
103+
field_name = f"{field_name}_"
104+
type = feature.get_type().get_name()
105+
if feature.is_optional():
106+
type = f"Optional[{type}]"
107+
field = ast.AnnAssign(
108+
target=ast.Name(id=field_name, ctx=ast.Store()),
109+
annotation=ast.Constant(value=type),
110+
value=None,
111+
simple=1,
112+
)
113+
if len(classdef.body) == 1 and isinstance(classdef.body[0], ast.Pass):
114+
classdef.body = []
115+
classdef.body.append(field)
116+
else:
117+
raise ValueError()
118+
119+
120+
def _generate_from_concept(classifier: Concept) -> ClassDef:
121+
bases = []
122+
if classifier.get_extended_concept().id == StarLasuBaseLanguage.get_astnode(LionWebVersion.V2023_1).id:
123+
if len(classifier.get_implemented()) == 0:
124+
bases.append('Node')
125+
else:
126+
bases.append(classifier.get_extended_concept().get_name())
127+
special_interfaces = {
128+
'com-strumenta-StarLasu-Expression-id': 'StarLasuExpression',
129+
'com-strumenta-StarLasu-Statement-id': 'StarLasuStatement',
130+
'com-strumenta-StarLasu-PlaceholderElement-id': 'StarLasuPlaceholderElement',
131+
'com-strumenta-StarLasu-Parameter-id': 'StarLasuParameter',
132+
'com-strumenta-StarLasu-Documentation-id': 'StarLasuDocumentation',
133+
'com-strumenta-StarLasu-BehaviorDeclaration-id': 'StarLasuBehaviorDeclaration',
134+
'com-strumenta-StarLasu-EntityDeclaration-id': 'StarLasuEntityDeclaration',
135+
'LionCore-builtins-INamed': 'StarLasuNamed'
136+
}
137+
for i in classifier.get_implemented():
138+
if i.get_id() in special_interfaces:
139+
bases.append(special_interfaces[i.get_id()])
140+
else:
141+
bases.append(i.get_name())
142+
# if classifier.is_abstract():
143+
# bases.append('ABC')
144+
dataclass_decorator = ast.Name(id="dataclass", ctx=ast.Load())
145+
classdef = ast.ClassDef(classifier.get_name(), bases=bases,
146+
keywords=[],
147+
body=[ast.Pass()],
148+
decorator_list=[dataclass_decorator])
149+
150+
for feature in classifier.get_features():
151+
_generate_from_feature(feature, classdef)
152+
153+
return classdef
154+
155+
56156
def ast_generation(click, language: Language, output):
57157
import_abc = ast.ImportFrom(
58158
module='abc',
@@ -126,98 +226,12 @@ def ast_generation(click, language: Language, output):
126226

127227
for classifier in sorted_classifier:
128228
if isinstance(classifier, Concept):
129-
bases = []
130-
if classifier.get_extended_concept().id == StarLasuBaseLanguage.get_astnode(LionWebVersion.V2023_1).id:
131-
bases.append('Node')
132-
else:
133-
bases.append(classifier.get_extended_concept().get_name())
134-
for i in classifier.get_implemented():
135-
if i.get_id() == 'com-strumenta-StarLasu-Expression-id':
136-
bases.append('StarLasuExpression')
137-
elif i.get_id() == 'com-strumenta-StarLasu-Statement-id':
138-
bases.append('StarLasuStatement')
139-
elif i.get_id() == 'com-strumenta-StarLasu-PlaceholderElement-id':
140-
bases.append('StarLasuPlaceholderElement')
141-
elif i.get_id() == 'com-strumenta-StarLasu-Parameter-id':
142-
bases.append('StarLasuParameter')
143-
elif i.get_id() == 'com-strumenta-StarLasu-Documentation-id':
144-
bases.append('StarLasuDocumentation')
145-
elif i.get_id() == 'com-strumenta-StarLasu-TypeAnnotation-id':
146-
bases.append('StarLasuTypeAnnotation')
147-
elif i.get_id() == 'com-strumenta-StarLasu-BehaviorDeclaration-id':
148-
bases.append('StarLasuBehaviorDeclaration')
149-
elif i.get_id() == 'com-strumenta-StarLasu-EntityDeclaration-id':
150-
bases.append('StarLasuEntityDeclaration')
151-
elif i.get_id() == 'LionCore-builtins-INamed':
152-
bases.append('StarLasuNamed')
153-
else:
154-
bases.append(i.get_name())
155-
if classifier.is_abstract():
156-
bases.append('ABC')
157-
dataclass_decorator = ast.Name(id="dataclass", ctx=ast.Load())
158-
classdef = ast.ClassDef(classifier.get_name(), bases=bases,
159-
keywords=[],
160-
body=[ast.Pass()],
161-
decorator_list=[dataclass_decorator])
162-
163-
for feature in classifier.get_features():
164-
if isinstance(feature, Containment):
165-
field_name = calculate_field_name(feature)
166-
type = feature.get_type().get_name()
167-
if feature.is_multiple():
168-
type = f"List[{type}]"
169-
elif feature.is_optional():
170-
type = f"Optional[{type}]"
171-
field = ast.AnnAssign(
172-
target=ast.Name(id=field_name, ctx=ast.Store()),
173-
annotation=ast.Constant(value=type),
174-
value=None,
175-
simple=1,
176-
)
177-
if len(classdef.body) == 1 and isinstance(classdef.body[0], ast.Pass):
178-
classdef.body = []
179-
classdef.body.append(field)
180-
elif isinstance(feature, Reference):
181-
field_name = feature.get_name()
182-
if field_name in keyword.kwlist:
183-
field_name = f"{field_name}_"
184-
type = f"ReferenceByName[{feature.get_type().get_name()}]"
185-
if feature.is_optional():
186-
type = f"Optional[{type}]"
187-
field = ast.AnnAssign(
188-
target=ast.Name(id=field_name, ctx=ast.Store()),
189-
annotation=ast.Constant(value=type),
190-
value=None,
191-
simple=1,
192-
)
193-
if len(classdef.body) == 1 and isinstance(classdef.body[0], ast.Pass):
194-
classdef.body = []
195-
classdef.body.append(field)
196-
elif isinstance(feature, Property):
197-
field_name = feature.get_name()
198-
if field_name in keyword.kwlist:
199-
field_name = f"{field_name}_"
200-
type = feature.get_type().get_name()
201-
if feature.is_optional():
202-
type = f"Optional[{type}]"
203-
field = ast.AnnAssign(
204-
target=ast.Name(id=field_name, ctx=ast.Store()),
205-
annotation=ast.Constant(value=type),
206-
value=None,
207-
simple=1,
208-
)
209-
if len(classdef.body) == 1 and isinstance(classdef.body[0], ast.Pass):
210-
classdef.body = []
211-
classdef.body.append(field)
212-
else:
213-
raise ValueError()
214-
215-
module.body.append(classdef)
229+
module.body.append(_generate_from_concept(classifier))
216230
elif isinstance(classifier, Interface):
217231
bases = []
218232
if len(classifier.get_extended_interfaces()) == 0:
219233
bases.append("Node")
220-
bases.append("ABC")
234+
# bases.append("ABC")
221235

222236
classdef = ast.ClassDef(classifier.get_name(), bases=bases,
223237
keywords=[],
@@ -232,4 +246,4 @@ def ast_generation(click, language: Language, output):
232246
output_path = Path(output)
233247
output_path.mkdir(parents=True, exist_ok=True)
234248
with Path(f"{output}/ast.py").open("w", encoding="utf-8") as f:
235-
f.write(generated_code)
249+
f.write(generated_code)

pylasu/lionweb/deserializer_generation.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,11 @@
11
import ast
2-
import keyword
32
from pathlib import Path
4-
from typing import List, Dict
53

64
import astor
7-
from lionwebpython.language import Language, Concept, Interface, Containment, Property
8-
from lionwebpython.language.classifier import Classifier
5+
from lionwebpython.language import Language, Concept
96
from lionwebpython.language.enumeration import Enumeration
107
from lionwebpython.language.primitive_type import PrimitiveType
11-
from lionwebpython.language.reference import Reference
12-
from lionwebpython.lionweb_version import LionWebVersion
138

14-
from pylasu.lionweb.starlasu import StarLasuBaseLanguage
159
from pylasu.lionweb.utils import to_snake_case, calculate_field_name
1610

1711

@@ -32,6 +26,7 @@ def make_cond(enumeration_name: str, member_name: str):
3226
]
3327
)
3428

29+
3530
# The return: return AssignmentType.Add
3631
def make_return(enumeration_name: str, member_name: str):
3732
return ast.Return(
@@ -42,6 +37,7 @@ def make_return(enumeration_name: str, member_name: str):
4237
)
4338
)
4439

40+
4541
def generate_register_deserializers_func(language: Language) -> ast.FunctionDef:
4642
fd = ast.FunctionDef(
4743
name="register_deserializers",
@@ -100,6 +96,7 @@ def generate_register_deserializers_func(language: Language) -> ast.FunctionDef:
10096
))
10197
return fd
10298

99+
103100
def generate_concept_deserializer(concept: Concept) -> ast.FunctionDef:
104101
constructor_assignments = []
105102
for f in concept.all_features():
@@ -132,6 +129,7 @@ def generate_concept_deserializer(concept: Concept) -> ast.FunctionDef:
132129
returns=ast.Name(id=concept.get_name(), ctx=ast.Load())
133130
)
134131

132+
135133
def deserializer_generation(click, language: Language, output):
136134
import_abc = ast.ImportFrom(
137135
module='abc',
@@ -172,13 +170,15 @@ def deserializer_generation(click, language: Language, output):
172170
level=0
173171
)
174172
import_ast = ast.ImportFrom(
175-
module='ast',
176-
names=[ast.alias(name=e.get_name(), asname=None) for e in language.get_elements() if not isinstance(e, PrimitiveType)],
173+
module='.ast',
174+
names=[ast.alias(name=e.get_name(), asname=None) for e in language.get_elements()
175+
if not isinstance(e, PrimitiveType)],
177176
level=0
178177
)
179178
import_primitives = ast.ImportFrom(
180179
module='primitive_types',
181-
names=[ast.alias(name=e.get_name(), asname=None) for e in language.get_elements() if isinstance(e, PrimitiveType)],
180+
names=[ast.alias(name=e.get_name(), asname=None) for e in language.get_elements()
181+
if isinstance(e, PrimitiveType)],
182182
level=0
183183
)
184184
import_json_serialization = ast.ImportFrom(
@@ -256,4 +256,4 @@ def deserializer_generation(click, language: Language, output):
256256
output_path.mkdir(parents=True, exist_ok=True)
257257
click.echo(f"📂 Saving deserializer to: {output}")
258258
with Path(f"{output}/deserializer.py").open("w", encoding="utf-8") as f:
259-
f.write(generated_code)
259+
f.write(generated_code)

pylasu/lionweb/generator.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,4 @@ def main(dependencies, lionweb_language, output):
3434

3535

3636
if __name__ == "__main__":
37-
main()
37+
main()

0 commit comments

Comments
 (0)