Skip to content

Commit fb9156e

Browse files
committed
Adding Codebase classes
1 parent 8d393d7 commit fb9156e

File tree

5 files changed

+181
-12
lines changed

5 files changed

+181
-12
lines changed

pylasu/codebase/__init__.py

Whitespace-only changes.

pylasu/codebase/lionweb.py

+147
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
import base64
2+
import re
3+
from typing import Optional
4+
5+
from lionwebpython.language import Language, Concept, Property, Containment
6+
from lionwebpython.language.classifier import Classifier
7+
from lionwebpython.language.lioncore_builtins import LionCoreBuiltins
8+
from lionwebpython.model.classifier_instance_utils import ClassifierInstanceUtils
9+
from lionwebpython.model.impl.dynamic_node import DynamicNode
10+
from lionwebpython.model.node import Node
11+
from lionwebpython.lionweb_version import LionWebVersion
12+
from lionwebpython.serialization.json_serialization import JsonSerialization
13+
14+
LW_REFERENCE_VERSION = LionWebVersion.V2023_1
15+
16+
CODEBASE_LANGUAGE = Language(lion_web_version=LW_REFERENCE_VERSION, name="Codebase",
17+
id="strumenta-codebase", version="1", key="strumenta-codebase")
18+
19+
CODEBASE_FILE = Concept(lion_web_version=LW_REFERENCE_VERSION, name="CodebaseFile",
20+
id="strumenta-codebase-file", key="strumenta-codebase-file")
21+
CODEBASE_LANGUAGE.add_element(CODEBASE_FILE)
22+
23+
CODEBASE_FILE_LANGUAGE_NAME = Property(lion_web_version=LW_REFERENCE_VERSION, name="language_name",
24+
id="strumenta-codebase-file-language-name")
25+
CODEBASE_FILE.add_feature(CODEBASE_FILE_LANGUAGE_NAME)
26+
CODEBASE_FILE_LANGUAGE_NAME.set_key("strumenta-codebase-file-language-name")
27+
CODEBASE_FILE_LANGUAGE_NAME.set_optional(False)
28+
CODEBASE_FILE_LANGUAGE_NAME.set_type(LionCoreBuiltins.get_string(LW_REFERENCE_VERSION))
29+
30+
CODEBASE_FILE_RELATIVE_PATH = Property(lion_web_version=LW_REFERENCE_VERSION, name="relative_path",
31+
id="strumenta-codebase-file-relative-path")
32+
CODEBASE_FILE.add_feature(CODEBASE_FILE_RELATIVE_PATH)
33+
CODEBASE_FILE_RELATIVE_PATH.set_key("strumenta-codebase-file-relative-path")
34+
CODEBASE_FILE_RELATIVE_PATH.set_optional(False)
35+
CODEBASE_FILE_RELATIVE_PATH.set_type(LionCoreBuiltins.get_string(LW_REFERENCE_VERSION))
36+
37+
CODEBASE_FILE_CODE = Property(lion_web_version=LW_REFERENCE_VERSION, name="code",
38+
id="strumenta-codebase-file-code")
39+
CODEBASE_FILE.add_feature(CODEBASE_FILE_CODE)
40+
CODEBASE_FILE_CODE.set_key("strumenta-codebase-file-code")
41+
CODEBASE_FILE_CODE.set_optional(False)
42+
CODEBASE_FILE_CODE.set_type(LionCoreBuiltins.get_string(LW_REFERENCE_VERSION))
43+
44+
CODEBASE_FILE_AST = Containment(lion_web_version=LW_REFERENCE_VERSION, name="ast",
45+
id="strumenta-codebase-file-ast")
46+
CODEBASE_FILE.add_feature(CODEBASE_FILE_AST)
47+
CODEBASE_FILE_AST.set_key("strumenta-codebase-file-ast")
48+
CODEBASE_FILE_AST.set_optional(True)
49+
CODEBASE_FILE_AST.set_multiple(False)
50+
CODEBASE_FILE_AST.set_type(LionCoreBuiltins.get_node(LW_REFERENCE_VERSION))
51+
52+
53+
def to_lionweb_identifier(s: str) -> str:
54+
"""
55+
Converts a given string into a valid LionWeb identifier.
56+
57+
Rules:
58+
- Starts with a letter (a-z, A-Z) or underscore (_)
59+
- Only contains letters, digits (0-9), and underscores (_)
60+
- Replaces invalid characters with underscores (_)
61+
- Ensures the identifier does not start with a digit
62+
63+
Args:
64+
s (str): Input string to be converted.
65+
66+
Returns:
67+
str: Valid LionWeb identifier.
68+
"""
69+
# Replace invalid characters with underscores
70+
s = re.sub(r'[^a-zA-Z0-9_]', '_', s)
71+
72+
# Ensure it does not start with a digit by prefixing with "_"
73+
if s and s[0].isdigit():
74+
s = "_" + s
75+
76+
# Ensure the identifier is not empty
77+
return s if s else "_"
78+
79+
class CodebaseFile(DynamicNode):
80+
language_name: str
81+
relative_path: str
82+
code: str
83+
ast: Optional[Node]
84+
85+
@property
86+
def language_name(self):
87+
return ClassifierInstanceUtils.get_property_value_by_name(self, 'language_name')
88+
89+
@language_name.setter
90+
def language_name(self, value):
91+
ClassifierInstanceUtils.set_property_value_by_name(self, 'language_name', value)
92+
93+
@property
94+
def relative_path(self):
95+
return ClassifierInstanceUtils.get_property_value_by_name(self, 'relative_path')
96+
97+
@relative_path.setter
98+
def relative_path(self, value):
99+
ClassifierInstanceUtils.set_property_value_by_name(self, 'relative_path', value)
100+
101+
@property
102+
def code(self):
103+
return ClassifierInstanceUtils.get_property_value_by_name(self, 'code')
104+
105+
@code.setter
106+
def code(self, value):
107+
ClassifierInstanceUtils.set_property_value_by_name(self, 'code', value)
108+
109+
@property
110+
def ast(self):
111+
containment = self.get_classifier().get_containment_by_name('ast')
112+
children = self.get_children(containment=containment)
113+
if len(children) == 0:
114+
return None
115+
else:
116+
return children[0]
117+
118+
@ast.setter
119+
def ast(self, value):
120+
containment = self.get_classifier().get_containment_by_name('ast')
121+
children = self.get_children(containment=containment)
122+
if value is None:
123+
if len(children) != 0:
124+
self.remove_child(child=children[0])
125+
else:
126+
if len(children) != 0:
127+
self.remove_child(child=children[0])
128+
self.add_child(containment=containment, child=value)
129+
130+
def __init__(self, language_name: str, relative_path: str, code: str, ast: Optional[Node] = None, id: Optional[str] = None):
131+
super().__init__(id or f"codebase_file_{to_lionweb_identifier(relative_path)}", CODEBASE_FILE)
132+
self.language_name = language_name
133+
self.relative_path = relative_path
134+
self.code = code
135+
self.ast = ast
136+
137+
138+
def codebase_deserializer(classifier, serialized_instance,
139+
deserialized_instances_by_id, properties_values) -> CodebaseFile:
140+
language_name = properties_values[classifier.get_property_by_name('language_name')]
141+
relative_path = properties_values[classifier.get_property_by_name('relative_path')]
142+
code = properties_values[classifier.get_property_by_name('code')]
143+
return CodebaseFile(language_name=language_name, relative_path=relative_path, code=code, id=serialized_instance.id)
144+
145+
146+
def register_codebase_deserializers(jsonser: JsonSerialization):
147+
jsonser.instantiator.register_custom_deserializer(CODEBASE_FILE.get_id(), codebase_deserializer)

pylasu/lionweb/ast_generation.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ def _generate_constructor(concept: Concept) -> ast.FunctionDef:
134134
],
135135
kwonlyargs=[], kw_defaults=[],
136136
defaults = [
137-
ast.Constant(value=to_snake_case(concept.get_name()).upper())
137+
ast.Name(id=to_snake_case(concept.get_name()).upper())
138138
]
139139
),
140140
body=[
@@ -149,7 +149,7 @@ def _generate_constructor(concept: Concept) -> ast.FunctionDef:
149149
keywords=[
150150
ast.keyword(arg='id', value=ast.Name(id='id', ctx=ast.Load())),
151151
ast.keyword(arg='position', value=ast.Name(id='position', ctx=ast.Load())),
152-
ast.keyword(arg='concept', value=ast.Name(id=ast.Name(id=to_snake_case(concept.get_name()).upper(), ctx=ast.Load()))),
152+
ast.keyword(arg='concept', value=ast.Name(id='concept', ctx=ast.Load())),
153153
]
154154
)),
155155
# self.set_id(id)
@@ -194,7 +194,7 @@ def _generate_constructor(concept: Concept) -> ast.FunctionDef:
194194
def _generate_from_concept(classifier: Concept) -> ClassDef:
195195
bases = []
196196
if classifier.get_extended_concept().id == StarLasuBaseLanguage.get_astnode(LionWebVersion.V2023_1).id:
197-
if len(classifier.get_implemented()) == 0:
197+
if len([i for i in classifier.get_implemented() if i.get_language().get_name() != 'com.strumenta.StarLasu']) == 0:
198198
bases.append('ASTNode')
199199
else:
200200
bases.append(classifier.get_extended_concept().get_name())

pylasu/lionweb/deserializer_generation.py

+16-9
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,22 @@ def make_cond(enumeration_name: str, member_name: str):
1414
left=ast.Name(id="serialized", ctx=ast.Load()),
1515
ops=[ast.Eq()],
1616
comparators=[
17-
ast.Attribute(
18-
value=ast.Attribute(
19-
value=ast.Name(id=enumeration_name, ctx=ast.Load()),
20-
attr=member_name,
21-
ctx=ast.Load()
22-
),
23-
attr="value",
24-
ctx=ast.Load()
25-
)
17+
ast.JoinedStr(values=[
18+
ast.Constant(value=f"{enumeration_name}-"),
19+
ast.FormattedValue(
20+
value=ast.Attribute(
21+
value=ast.Attribute(
22+
value=ast.Name(id=enumeration_name, ctx=ast.Load()),
23+
attr=member_name,
24+
ctx=ast.Load()
25+
),
26+
attr="value",
27+
ctx=ast.Load()
28+
),
29+
conversion=-1,
30+
format_spec=None
31+
)
32+
])
2633
]
2734
)
2835

pylasu/lionweb/language_generation.py

+15
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import astor
55
from lionwebpython.language import Language, Concept
6+
from lionwebpython.language.primitive_type import PrimitiveType
67

78
from pylasu.lionweb.utils import to_snake_case
89

@@ -335,6 +336,20 @@ def language_generation(click, language: Language, output, language_name: str):
335336
)
336337
)
337338
module.body.append(assign)
339+
if isinstance(element, PrimitiveType):
340+
assign = ast.Assign(
341+
targets=[ast.Name(id=to_snake_case(element.get_name()).upper(), ctx=ast.Store())],
342+
value=ast.Call(
343+
func=ast.Attribute(
344+
value=ast.Name(id="LANGUAGE", ctx=ast.Load()),
345+
attr="get_primitive_type_by_name",
346+
ctx=ast.Load()
347+
),
348+
args=[ast.Constant(value=element.get_name())],
349+
keywords=[]
350+
)
351+
)
352+
module.body.append(assign)
338353

339354
generated_code = astor.to_source(module)
340355
output_path = Path(output)

0 commit comments

Comments
 (0)