dfpyre 0.4.2__py3-none-any.whl → 0.10.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of dfpyre might be problematic. Click here for more details.
- dfpyre/__init__.py +3 -1
- dfpyre/core/actiondump.py +277 -0
- dfpyre/core/codeblock.py +207 -0
- dfpyre/core/items.py +580 -0
- dfpyre/core/template.py +220 -0
- dfpyre/data/actiondump_min.json +1 -0
- dfpyre/data/deprecated_actions.json +172 -0
- dfpyre/data/method_templates/action.txt +5 -0
- dfpyre/data/method_templates/conditional.txt +7 -0
- dfpyre/data/method_templates/event.txt +6 -0
- dfpyre/data/method_templates/notarget_action.txt +5 -0
- dfpyre/data/method_templates/notarget_conditional.txt +6 -0
- dfpyre/data/method_templates/repeat.txt +5 -0
- dfpyre/data/method_templates/repeat_while.txt +9 -0
- dfpyre/data/method_templates/select_obj_subaction.txt +8 -0
- dfpyre/export/action_classes.py +10891 -0
- dfpyre/export/block_functions.py +90 -0
- dfpyre/gen/action_class_data.py +203 -0
- dfpyre/gen/action_literals.py +20 -0
- dfpyre/scripts/action_gen.py +222 -0
- dfpyre/scripts/action_literal_gen.py +43 -0
- dfpyre/tool/scriptgen.py +274 -0
- dfpyre/tool/slice.py +199 -0
- dfpyre/util/style.py +23 -0
- dfpyre/util/util.py +65 -0
- dfpyre-0.10.5.dist-info/METADATA +64 -0
- dfpyre-0.10.5.dist-info/RECORD +29 -0
- {dfpyre-0.4.2.dist-info → dfpyre-0.10.5.dist-info}/WHEEL +1 -2
- {dfpyre-0.4.2.dist-info → dfpyre-0.10.5.dist-info/licenses}/LICENSE +21 -21
- dfpyre/data/data.json +0 -1
- dfpyre/items.py +0 -244
- dfpyre/pyre.py +0 -407
- dfpyre/style.py +0 -21
- dfpyre-0.4.2.dist-info/METADATA +0 -11
- dfpyre-0.4.2.dist-info/RECORD +0 -10
- dfpyre-0.4.2.dist-info/top_level.txt +0 -1
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Generates Codeblock classes with static methods for each action.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from dfpyre.core.actiondump import ACTIONDUMP, ActionArgument, ActionTag, TagOption
|
|
7
|
+
from dfpyre.util.util import flatten
|
|
8
|
+
from dfpyre.gen.action_class_data import (
|
|
9
|
+
INDENT, CODEBLOCK_LOOKUP, PARAM_NAME_REPLACEMENTS, PARAM_TYPE_LOOKUP, TEMPLATE_OVERRIDES, OUTPUT_PATH, IMPORTS, CLASS_ALIASES,
|
|
10
|
+
get_method_name_and_aliases, to_valid_identifier
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class ParameterData:
|
|
16
|
+
name: str
|
|
17
|
+
types: str
|
|
18
|
+
description: str
|
|
19
|
+
notes: str
|
|
20
|
+
is_optional: bool
|
|
21
|
+
has_none: bool
|
|
22
|
+
|
|
23
|
+
def get_param_string(self) -> str:
|
|
24
|
+
param_str = f'{self.name}: {self.types}'
|
|
25
|
+
if self.has_none or self.is_optional:
|
|
26
|
+
param_str += '=None'
|
|
27
|
+
return param_str
|
|
28
|
+
|
|
29
|
+
def get_docstring(self) -> str:
|
|
30
|
+
docstring = f':param {self.types} {self.name}: {self.description}'
|
|
31
|
+
if self.is_optional:
|
|
32
|
+
docstring += ' (optional)'
|
|
33
|
+
if self.notes:
|
|
34
|
+
docstring += f' ({self.notes})'
|
|
35
|
+
return docstring
|
|
36
|
+
|
|
37
|
+
def __eq__(self, value):
|
|
38
|
+
return self is value
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def parse_parameters(action_arguments: list[tuple[ActionArgument, ...]]) -> list[ParameterData]:
|
|
42
|
+
parameter_list: list[ParameterData] = []
|
|
43
|
+
prev_optional_exists = False
|
|
44
|
+
|
|
45
|
+
for arg_union in action_arguments:
|
|
46
|
+
if len(arg_union) == 1:
|
|
47
|
+
param_name = arg_union[0].type.lower()
|
|
48
|
+
if param_name in PARAM_NAME_REPLACEMENTS:
|
|
49
|
+
param_name = PARAM_NAME_REPLACEMENTS[param_name]
|
|
50
|
+
else:
|
|
51
|
+
param_name = f'arg'
|
|
52
|
+
|
|
53
|
+
is_optional = arg_union[0].optional
|
|
54
|
+
has_none = 'NONE' in (arg.type for arg in arg_union)
|
|
55
|
+
|
|
56
|
+
param_types_list = list(flatten(PARAM_TYPE_LOOKUP[arg.type] for arg in arg_union))
|
|
57
|
+
if is_optional:
|
|
58
|
+
param_types_list.append('None')
|
|
59
|
+
prev_optional_exists = True
|
|
60
|
+
elif prev_optional_exists:
|
|
61
|
+
# If a previous optional param exists, all subsequent params must be
|
|
62
|
+
# optional as well, so we set `has_none` to True to add `=None` to future params.
|
|
63
|
+
has_none = True
|
|
64
|
+
|
|
65
|
+
param_types_dedup = list(dict.fromkeys(param_types_list))
|
|
66
|
+
param_types = ' | '.join(param_types_dedup)
|
|
67
|
+
|
|
68
|
+
if arg_union[0].plural:
|
|
69
|
+
param_name += 's'
|
|
70
|
+
plural_param_types = ' | '.join(t for t in param_types_dedup if t != 'None') # Doesn't make sense to have a list of None
|
|
71
|
+
param_types = f'list[{plural_param_types}] | {param_types}'
|
|
72
|
+
|
|
73
|
+
param_description = ' OR '.join(arg.description for arg in arg_union if arg.description)
|
|
74
|
+
param_notes = ' OR '.join(arg.notes for arg in arg_union if arg.notes)
|
|
75
|
+
|
|
76
|
+
param = ParameterData(param_name, param_types, param_description, param_notes, is_optional, has_none)
|
|
77
|
+
parameter_list.append(param)
|
|
78
|
+
|
|
79
|
+
# Add numbers to params with duplicate names
|
|
80
|
+
param_names = [p.name for p in parameter_list]
|
|
81
|
+
param_name_lookup = {p.name: [q for q in parameter_list if p.name == q.name] for p in parameter_list}
|
|
82
|
+
for param in parameter_list:
|
|
83
|
+
if param_names.count(param.name) > 1:
|
|
84
|
+
params_with_name = param_name_lookup[param.name]
|
|
85
|
+
param_index = params_with_name.index(param)
|
|
86
|
+
param.name += str(param_index + 1)
|
|
87
|
+
|
|
88
|
+
return parameter_list
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@dataclass
|
|
92
|
+
class TagData:
|
|
93
|
+
name: str
|
|
94
|
+
options: list[TagOption]
|
|
95
|
+
default: str
|
|
96
|
+
|
|
97
|
+
def get_varname(self) -> str:
|
|
98
|
+
return to_valid_identifier(self.name.lower())
|
|
99
|
+
|
|
100
|
+
def get_param_string(self) -> str:
|
|
101
|
+
options_list = ', '.join(f'"{o.name}"' for o in self.options)
|
|
102
|
+
tag_type = f'Literal[{options_list}]'
|
|
103
|
+
return f'{self.get_varname()}: {tag_type}="{self.default}"'
|
|
104
|
+
|
|
105
|
+
def get_docstring(self) -> str:
|
|
106
|
+
docstring_lines = [f':param str {self.get_varname()}: {self.name}']
|
|
107
|
+
docstring_lines += [f'{INDENT*2}- {o.name}: {o.description}' for o in self.options if o.description]
|
|
108
|
+
if len(docstring_lines) > 1:
|
|
109
|
+
docstring_lines.insert(1, '')
|
|
110
|
+
return '\n'.join(docstring_lines)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def parse_tags(action_tags: list[ActionTag]) -> list[TagData]:
|
|
114
|
+
return [TagData(t.name, t.options, t.default) for t in action_tags]
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def generate_actions():
|
|
118
|
+
generated_lines: list[str] = IMPORTS.copy()
|
|
119
|
+
generated_lines += ['']
|
|
120
|
+
|
|
121
|
+
for codeblock_type, actions in ACTIONDUMP.action_data.items():
|
|
122
|
+
codeblock_data = CODEBLOCK_LOOKUP.get(codeblock_type)
|
|
123
|
+
if codeblock_data is None:
|
|
124
|
+
continue
|
|
125
|
+
|
|
126
|
+
class_name, method_template = codeblock_data
|
|
127
|
+
|
|
128
|
+
class_docstring = ACTIONDUMP.codeblock_data[codeblock_type].description
|
|
129
|
+
class_def_lines = [
|
|
130
|
+
f'class {class_name}:',
|
|
131
|
+
f'{INDENT}"""',
|
|
132
|
+
f'{INDENT}{class_docstring}',
|
|
133
|
+
f'{INDENT}"""',
|
|
134
|
+
''
|
|
135
|
+
]
|
|
136
|
+
generated_lines += class_def_lines
|
|
137
|
+
|
|
138
|
+
for action_name, action_data in actions.items():
|
|
139
|
+
if action_data.is_deprecated:
|
|
140
|
+
# Skip deprecated actions
|
|
141
|
+
continue
|
|
142
|
+
|
|
143
|
+
method_data = get_method_name_and_aliases(codeblock_type, action_name)
|
|
144
|
+
if method_data is None:
|
|
145
|
+
continue
|
|
146
|
+
method_name, method_aliases = method_data
|
|
147
|
+
|
|
148
|
+
template_overrides = TEMPLATE_OVERRIDES.get(codeblock_type)
|
|
149
|
+
if template_overrides:
|
|
150
|
+
override_template = template_overrides.get(action_name)
|
|
151
|
+
if override_template is not None:
|
|
152
|
+
method_template = override_template
|
|
153
|
+
|
|
154
|
+
action_description = action_data.description or ''
|
|
155
|
+
if action_description:
|
|
156
|
+
action_description = f'{INDENT}{action_description}\n\n'
|
|
157
|
+
|
|
158
|
+
# Get tag data
|
|
159
|
+
tags = parse_tags(action_data.tags)
|
|
160
|
+
|
|
161
|
+
tag_parameter_list = ', '.join(t.get_param_string() for t in tags)
|
|
162
|
+
if tag_parameter_list:
|
|
163
|
+
tag_parameter_list = f'{tag_parameter_list}, '
|
|
164
|
+
|
|
165
|
+
tag_values = ', '.join(f"'{t.name}': {t.get_varname()}" for t in tags)
|
|
166
|
+
|
|
167
|
+
# Get parameter data
|
|
168
|
+
parameters = parse_parameters(action_data.arguments)
|
|
169
|
+
|
|
170
|
+
parameter_list = ', '.join(p.get_param_string() for p in parameters)
|
|
171
|
+
if parameter_list:
|
|
172
|
+
parameter_list += ', '
|
|
173
|
+
|
|
174
|
+
parameter_names = ', '.join(p.name for p in parameters)
|
|
175
|
+
if parameter_names:
|
|
176
|
+
parameter_names += ','
|
|
177
|
+
|
|
178
|
+
docstring_list = [p.get_docstring() for p in parameters] + [t.get_docstring() for t in tags]
|
|
179
|
+
parameter_docstrings = '\n'.join(INDENT + s for s in docstring_list)
|
|
180
|
+
|
|
181
|
+
if parameter_docstrings:
|
|
182
|
+
parameter_docstrings += '\n'
|
|
183
|
+
|
|
184
|
+
if action_description or parameter_docstrings:
|
|
185
|
+
# Fix indentation
|
|
186
|
+
parameter_docstrings += INDENT
|
|
187
|
+
|
|
188
|
+
method_code = method_template.format(
|
|
189
|
+
method_name = method_name,
|
|
190
|
+
parameter_list = parameter_list,
|
|
191
|
+
parameter_names = parameter_names,
|
|
192
|
+
parameter_docstrings = parameter_docstrings,
|
|
193
|
+
tag_parameter_list = tag_parameter_list,
|
|
194
|
+
tag_values = tag_values,
|
|
195
|
+
codeblock_type = codeblock_type,
|
|
196
|
+
action_name = action_name,
|
|
197
|
+
action_description = action_description
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
method_lines = [f'@staticmethod']
|
|
201
|
+
method_lines += method_code.split('\n')
|
|
202
|
+
|
|
203
|
+
for alias in method_aliases:
|
|
204
|
+
method_lines += [f'{alias} = {method_name}']
|
|
205
|
+
|
|
206
|
+
method_lines = [INDENT + l for l in method_lines]
|
|
207
|
+
method_lines += ['']
|
|
208
|
+
generated_lines += method_lines
|
|
209
|
+
|
|
210
|
+
class_aliases = CLASS_ALIASES.get(codeblock_type) or []
|
|
211
|
+
for alias in class_aliases:
|
|
212
|
+
alias_def = f'{alias} = {class_name}'
|
|
213
|
+
generated_lines.append(alias_def)
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
with open(OUTPUT_PATH, 'w') as f:
|
|
217
|
+
f.write('\n'.join(generated_lines) + '\n')
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
if __name__ == '__main__':
|
|
221
|
+
generate_actions()
|
|
222
|
+
print(f'Wrote Codeblock action classes to {OUTPUT_PATH}.')
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Generates `Literal` objects containing action names for each codeblock type.
|
|
3
|
+
This allows action names to be autocompleted if the user's IDE supports it.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from datetime import datetime, timezone
|
|
7
|
+
from dfpyre.core.actiondump import ACTIONDUMP
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
OUTPUT_PATH = 'dfpyre/gen/action_literals.py'
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def generate_action_literals():
|
|
14
|
+
generated_lines: list[str] = [
|
|
15
|
+
f'# Auto generated by pyre {datetime.now(timezone.utc).isoformat()}',
|
|
16
|
+
'from typing import Literal\n'
|
|
17
|
+
]
|
|
18
|
+
for codeblock_type, actions in ACTIONDUMP.action_data.items():
|
|
19
|
+
if len(actions) == 1:
|
|
20
|
+
continue
|
|
21
|
+
|
|
22
|
+
filtered_actions = [a for a, d in actions.items() if not d.is_deprecated] # Omit deprecated actions
|
|
23
|
+
if not filtered_actions:
|
|
24
|
+
continue
|
|
25
|
+
action_list = str(filtered_actions)
|
|
26
|
+
literal_line = f'{codeblock_type.upper()}_ACTION = Literal{action_list}'
|
|
27
|
+
generated_lines.append(literal_line)
|
|
28
|
+
|
|
29
|
+
game_value_names = list(ACTIONDUMP.game_values.keys())
|
|
30
|
+
generated_lines += [
|
|
31
|
+
f'GAME_VALUE_NAME = Literal{str(game_value_names)}',
|
|
32
|
+
f'SOUND_NAME = Literal{str(ACTIONDUMP.sound_names)}',
|
|
33
|
+
f'POTION_NAME = Literal{str(ACTIONDUMP.potion_names)}',
|
|
34
|
+
'SUBACTION = IF_PLAYER_ACTION | IF_ENTITY_ACTION | IF_GAME_ACTION | IF_VAR_ACTION'
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
with open(OUTPUT_PATH, 'w') as f:
|
|
38
|
+
f.write('\n'.join(generated_lines) + '\n')
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
if __name__ == '__main__':
|
|
42
|
+
generate_action_literals()
|
|
43
|
+
print(f'Wrote codeblock action Literals to {OUTPUT_PATH}.')
|
dfpyre/tool/scriptgen.py
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
from dfpyre.util.util import is_number
|
|
3
|
+
from dfpyre.core.items import *
|
|
4
|
+
from dfpyre.core.actiondump import get_default_tags
|
|
5
|
+
from dfpyre.core.codeblock import CodeBlock, CONDITIONAL_CODEBLOCKS, TARGET_CODEBLOCKS, EVENT_CODEBLOCKS
|
|
6
|
+
from dfpyre.gen.action_class_data import get_method_name_and_aliases, to_valid_identifier
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
IMPORT_STATEMENT = 'from dfpyre import *'
|
|
10
|
+
|
|
11
|
+
CODEBLOCK_FUNCTION_LOOKUP = {
|
|
12
|
+
'event': 'PlayerEvent',
|
|
13
|
+
'entity_event': 'EntityEvent',
|
|
14
|
+
'func': 'Function',
|
|
15
|
+
'process': 'Process',
|
|
16
|
+
'call_func': 'CallFunction',
|
|
17
|
+
'start_process': 'StartProcess',
|
|
18
|
+
'player_action': 'PlayerAction',
|
|
19
|
+
'game_action': 'GameAction',
|
|
20
|
+
'entity_action': 'EntityAction',
|
|
21
|
+
'if_player': 'IfPlayer',
|
|
22
|
+
'if_var': 'IfVariable',
|
|
23
|
+
'if_game': 'IfGame',
|
|
24
|
+
'if_entity': 'IfEntity',
|
|
25
|
+
'else': 'Else',
|
|
26
|
+
'repeat': 'Repeat',
|
|
27
|
+
'control': 'Control',
|
|
28
|
+
'select_obj': 'SelectObject',
|
|
29
|
+
'set_var': 'SetVariable'
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
NO_ACTION_BLOCKS = {'func', 'process', 'call_func', 'start_process', 'else'}
|
|
33
|
+
CONTAINER_CODEBLOCKS = {'event', 'entity_event', 'func', 'process', 'if_player', 'if_entity', 'if_game', 'if_var', 'else', 'repeat'}
|
|
34
|
+
VAR_SCOPE_LOOKUP = {'unsaved': 'g', 'saved': 's', 'local': 'l', 'line': 'i'}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclasses.dataclass
|
|
38
|
+
class GeneratorFlags:
|
|
39
|
+
indent_size: int
|
|
40
|
+
literal_shorthand: bool
|
|
41
|
+
var_shorthand: bool
|
|
42
|
+
preserve_slots: bool
|
|
43
|
+
assign_variable: bool
|
|
44
|
+
include_import: bool
|
|
45
|
+
build_and_send: bool
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def item_to_string(class_name: str, i: Item, slot_argument: str):
|
|
49
|
+
i.nbt.pop('DF_NBT')
|
|
50
|
+
stripped_id = i.get_id().replace('minecraft:', '')
|
|
51
|
+
if set(i.nbt.keys()) == {'id', 'count'}:
|
|
52
|
+
if i.get_count() == 1:
|
|
53
|
+
return f"{class_name}('{stripped_id}'{slot_argument})"
|
|
54
|
+
return f"{class_name}('{stripped_id}', {i.get_count()}{slot_argument})"
|
|
55
|
+
|
|
56
|
+
snbt_string = i.get_snbt().replace('\\"', '\\\\"')
|
|
57
|
+
return f'{class_name}.from_snbt("""{snbt_string}""")'
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def escape(s: str) -> str:
|
|
61
|
+
return s.replace('\n', '\\n').replace("\'", "\\'")
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def str_literal(s: str) -> str:
|
|
65
|
+
return "'" + escape(s) + "'"
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def argument_item_to_string(flags: GeneratorFlags, arg_item: CodeItem) -> str:
|
|
69
|
+
class_name = arg_item.__class__.__name__
|
|
70
|
+
has_slot = arg_item.slot is not None and flags.preserve_slots
|
|
71
|
+
slot_argument = f', slot={arg_item.slot}' if has_slot else ''
|
|
72
|
+
|
|
73
|
+
if isinstance(arg_item, Item):
|
|
74
|
+
return item_to_string(class_name, arg_item, slot_argument)
|
|
75
|
+
|
|
76
|
+
if isinstance(arg_item, String):
|
|
77
|
+
literal = str_literal(arg_item.value)
|
|
78
|
+
if not has_slot and flags.literal_shorthand:
|
|
79
|
+
return literal
|
|
80
|
+
return f"{class_name}({literal}{slot_argument})"
|
|
81
|
+
|
|
82
|
+
if isinstance(arg_item, Text):
|
|
83
|
+
literal = str_literal(arg_item.value)
|
|
84
|
+
return f"{class_name}({literal}{slot_argument})"
|
|
85
|
+
|
|
86
|
+
if isinstance(arg_item, Number):
|
|
87
|
+
if not is_number(str(arg_item.value)): # Probably a math expression
|
|
88
|
+
return f"{class_name}({str_literal(arg_item.value)}{slot_argument})"
|
|
89
|
+
if not has_slot and flags.literal_shorthand:
|
|
90
|
+
return str(arg_item.value)
|
|
91
|
+
return f'{class_name}({arg_item.value}{slot_argument})'
|
|
92
|
+
|
|
93
|
+
if isinstance(arg_item, Location):
|
|
94
|
+
loc_components = [arg_item.x, arg_item.y, arg_item.z]
|
|
95
|
+
if arg_item.pitch != 0:
|
|
96
|
+
loc_components.append(arg_item.pitch)
|
|
97
|
+
if arg_item.yaw != 0:
|
|
98
|
+
loc_components.append(arg_item.yaw)
|
|
99
|
+
return f'{class_name}({", ".join(str(c) for c in loc_components)}{slot_argument})'
|
|
100
|
+
|
|
101
|
+
if isinstance(arg_item, Variable):
|
|
102
|
+
name = escape(arg_item.name)
|
|
103
|
+
if not has_slot and flags.var_shorthand:
|
|
104
|
+
return f"'${VAR_SCOPE_LOOKUP[arg_item.scope]} {name}'"
|
|
105
|
+
if arg_item.scope == 'unsaved':
|
|
106
|
+
return f"{class_name}('{name}'{slot_argument})"
|
|
107
|
+
return f"{class_name}('{name}', '{arg_item.scope}'{slot_argument})"
|
|
108
|
+
|
|
109
|
+
if isinstance(arg_item, Sound):
|
|
110
|
+
return f"{class_name}({str_literal(arg_item.name)}, {arg_item.pitch}, {arg_item.vol}{slot_argument})"
|
|
111
|
+
|
|
112
|
+
if isinstance(arg_item, Particle):
|
|
113
|
+
return f'{class_name}({arg_item.particle_data})'
|
|
114
|
+
|
|
115
|
+
if isinstance(arg_item, Potion):
|
|
116
|
+
return f"{class_name}({str_literal(arg_item.name)}, {arg_item.dur}, {arg_item.amp}{slot_argument})"
|
|
117
|
+
|
|
118
|
+
if isinstance(arg_item, GameValue):
|
|
119
|
+
name = str_literal(arg_item.name)
|
|
120
|
+
if arg_item.target == 'Default':
|
|
121
|
+
return f"{class_name}({name}{slot_argument})"
|
|
122
|
+
return f"{class_name}({name}, '{arg_item.target}'{slot_argument})"
|
|
123
|
+
|
|
124
|
+
if isinstance(arg_item, Parameter):
|
|
125
|
+
param_type_class_name = arg_item.param_type.__class__.__name__
|
|
126
|
+
param_args = [str_literal(arg_item.name), f'{param_type_class_name}.{arg_item.param_type.name}']
|
|
127
|
+
if arg_item.plural:
|
|
128
|
+
param_args.append('plural=True')
|
|
129
|
+
if arg_item.optional:
|
|
130
|
+
param_args.append('optional=True')
|
|
131
|
+
if arg_item.default_value is not None:
|
|
132
|
+
param_args.append(f'default_value={argument_item_to_string(flags, arg_item.default_value)}')
|
|
133
|
+
if arg_item.description:
|
|
134
|
+
param_args.append(f"description={str_literal(arg_item.description)}")
|
|
135
|
+
if arg_item.note:
|
|
136
|
+
param_args.append(f"note={str_literal(arg_item.note)}")
|
|
137
|
+
return f'{class_name}({", ".join(param_args)}{slot_argument})'
|
|
138
|
+
|
|
139
|
+
if isinstance(arg_item, Vector):
|
|
140
|
+
return f'{class_name}({arg_item.x}, {arg_item.y}, {arg_item.z}{slot_argument})'
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def string_to_python_name(string: str) -> str:
|
|
144
|
+
"""Converts `string` into a valid python identifier."""
|
|
145
|
+
string = string.strip()
|
|
146
|
+
if string[0].isnumeric():
|
|
147
|
+
string = '_' + string
|
|
148
|
+
return ''.join(c if c.isalnum() else '_' for c in string)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def add_script_line(flags: GeneratorFlags, script_lines: list[str], indent_level: int, line: str, add_comma: bool=True):
|
|
152
|
+
added_line = ' '*flags.indent_size*indent_level + line
|
|
153
|
+
if add_comma and indent_level > 0:
|
|
154
|
+
added_line += ','
|
|
155
|
+
script_lines.append(added_line)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def generate_script(codeblocks: list[CodeBlock], flags: GeneratorFlags) -> str:
|
|
159
|
+
indent_level = 0
|
|
160
|
+
script_lines = []
|
|
161
|
+
variable_assigned = False
|
|
162
|
+
|
|
163
|
+
if flags.include_import:
|
|
164
|
+
script_lines.append(IMPORT_STATEMENT + '\n')
|
|
165
|
+
|
|
166
|
+
def remove_comma_from_last_line():
|
|
167
|
+
if script_lines[-1].endswith(','):
|
|
168
|
+
script_lines[-1] = script_lines[-1][:-1]
|
|
169
|
+
|
|
170
|
+
def get_var_assignment_snippet() -> str:
|
|
171
|
+
first_block_data = codeblocks[0].data
|
|
172
|
+
if 'data' in first_block_data:
|
|
173
|
+
name = first_block_data['data']
|
|
174
|
+
var_name = name if name else 'unnamed_template'
|
|
175
|
+
else:
|
|
176
|
+
var_name = first_block_data['block'] + '_' + first_block_data['action']
|
|
177
|
+
return f'{string_to_python_name(var_name)} = '
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
for codeblock in codeblocks:
|
|
181
|
+
# Handle closing brackets
|
|
182
|
+
if codeblock.type == 'bracket':
|
|
183
|
+
if codeblock.data['direct'] == 'close':
|
|
184
|
+
remove_comma_from_last_line()
|
|
185
|
+
indent_level -= 1
|
|
186
|
+
add_script_line(flags, script_lines, indent_level, '])')
|
|
187
|
+
continue
|
|
188
|
+
|
|
189
|
+
# Get codeblock function and set the action method
|
|
190
|
+
function_name = CODEBLOCK_FUNCTION_LOOKUP[codeblock.type]
|
|
191
|
+
if codeblock.type in NO_ACTION_BLOCKS:
|
|
192
|
+
function_args = [str_literal(codeblock.action_name)]
|
|
193
|
+
else:
|
|
194
|
+
method_data = get_method_name_and_aliases(codeblock.type, codeblock.action_name)
|
|
195
|
+
if method_data is None:
|
|
196
|
+
raise PyreException(f'scriptgen: Failed to get method data of {codeblock.action_name}')
|
|
197
|
+
|
|
198
|
+
function_name += f'.{method_data[0]}'
|
|
199
|
+
function_args = []
|
|
200
|
+
|
|
201
|
+
# Add variable assignment if necessary
|
|
202
|
+
var_assignment_snippet = ''
|
|
203
|
+
if flags.assign_variable and not variable_assigned:
|
|
204
|
+
var_assignment_snippet = get_var_assignment_snippet()
|
|
205
|
+
variable_assigned = True
|
|
206
|
+
|
|
207
|
+
# Set function or process name if necessary
|
|
208
|
+
if codeblock.action_name == 'dynamic':
|
|
209
|
+
function_args[0] = str_literal(codeblock.data["data"])
|
|
210
|
+
|
|
211
|
+
# Convert argument objects to valid Python strings
|
|
212
|
+
codeblock_args = [argument_item_to_string(flags, i) for i in codeblock.args]
|
|
213
|
+
if codeblock_args:
|
|
214
|
+
function_args.extend(codeblock_args)
|
|
215
|
+
|
|
216
|
+
sub_action = codeblock.data.get('subAction')
|
|
217
|
+
|
|
218
|
+
# Add tags
|
|
219
|
+
if codeblock.tags:
|
|
220
|
+
default_tags = codeblock.tags
|
|
221
|
+
if sub_action is not None:
|
|
222
|
+
for conditional_block_type in CONDITIONAL_CODEBLOCKS:
|
|
223
|
+
default_tags = get_default_tags(conditional_block_type, sub_action)
|
|
224
|
+
if default_tags:
|
|
225
|
+
break
|
|
226
|
+
else:
|
|
227
|
+
default_tags = get_default_tags(codeblock.data.get('block'), codeblock.action_name)
|
|
228
|
+
|
|
229
|
+
for tag, option in codeblock.tags.items():
|
|
230
|
+
if default_tags[tag] != option:
|
|
231
|
+
tag_param_name = to_valid_identifier(tag.lower())
|
|
232
|
+
function_args.append(f"{tag_param_name}='{option}'")
|
|
233
|
+
|
|
234
|
+
# Add sub-action for repeat and select object
|
|
235
|
+
if sub_action is not None:
|
|
236
|
+
function_args.append(f"sub_action='{sub_action}'")
|
|
237
|
+
|
|
238
|
+
# Add target if necessary
|
|
239
|
+
if codeblock.type in TARGET_CODEBLOCKS and codeblock.target.name != 'SELECTION':
|
|
240
|
+
function_args.append(f'target=Target.{codeblock.target.name}')
|
|
241
|
+
|
|
242
|
+
codeblock_attribute = codeblock.data.get('attribute')
|
|
243
|
+
# Add inversion for NOT
|
|
244
|
+
if codeblock_attribute == 'NOT':
|
|
245
|
+
function_args.append('inverted=True')
|
|
246
|
+
|
|
247
|
+
# Add LS Cancel
|
|
248
|
+
elif codeblock_attribute == 'LS-CANCEL':
|
|
249
|
+
function_args.append('ls_cancel=True')
|
|
250
|
+
|
|
251
|
+
# Create and add the line
|
|
252
|
+
if codeblock.type in CONTAINER_CODEBLOCKS:
|
|
253
|
+
if codeblock.type == 'else':
|
|
254
|
+
line = f'{function_name}(['
|
|
255
|
+
elif codeblock.type in EVENT_CODEBLOCKS and codeblock_attribute is None:
|
|
256
|
+
line = f'{var_assignment_snippet}{function_name}([' # omit `codeblocks=` when we don't need it
|
|
257
|
+
else:
|
|
258
|
+
joined_args = ', '.join(function_args) + ', ' if function_args else ''
|
|
259
|
+
line = f'{var_assignment_snippet}{function_name}({joined_args}codeblocks=['
|
|
260
|
+
add_script_line(flags, script_lines, indent_level, line, False)
|
|
261
|
+
indent_level += 1
|
|
262
|
+
else:
|
|
263
|
+
line = f'{function_name}({", ".join(function_args)})'
|
|
264
|
+
add_script_line(flags, script_lines, indent_level, line)
|
|
265
|
+
|
|
266
|
+
remove_comma_from_last_line()
|
|
267
|
+
indent_level -= 1
|
|
268
|
+
add_script_line(flags, script_lines, indent_level, '])') # add final closing brackets
|
|
269
|
+
|
|
270
|
+
# Add `.build_and_send()` if necessary
|
|
271
|
+
if flags.build_and_send:
|
|
272
|
+
script_lines[-1] += '.build_and_send()'
|
|
273
|
+
|
|
274
|
+
return '\n'.join(script_lines)
|