dshellInterpreter 1.2.7.10__tar.gz → 1.2.8.0__tar.gz

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.
Files changed (54) hide show
  1. {dshellinterpreter-1.2.7.10 → dshellinterpreter-1.2.8.0}/Dshell/DISCORD_COMMANDS/dshell_ui.py +45 -24
  2. {dshellinterpreter-1.2.7.10 → dshellinterpreter-1.2.8.0}/Dshell/DshellInterpreteur/__init__.py +1 -0
  3. {dshellinterpreter-1.2.7.10 → dshellinterpreter-1.2.8.0}/Dshell/DshellInterpreteur/dshell_global_variables.py +8 -1
  4. {dshellinterpreter-1.2.7.10 → dshellinterpreter-1.2.8.0}/Dshell/DshellInterpreteur/dshell_interpreter.py +6 -12
  5. dshellinterpreter-1.2.8.0/Dshell/DshellInterpreteur/dshell_scope.py +95 -0
  6. {dshellinterpreter-1.2.7.10 → dshellinterpreter-1.2.8.0}/Dshell/DshellParser/ast_nodes.py +53 -31
  7. {dshellinterpreter-1.2.7.10 → dshellinterpreter-1.2.8.0}/Dshell/DshellParser/dshell_parser.py +13 -0
  8. {dshellinterpreter-1.2.7.10 → dshellinterpreter-1.2.8.0}/Dshell/DshellTokenizer/dshell_keywords.py +1 -1
  9. {dshellinterpreter-1.2.7.10 → dshellinterpreter-1.2.8.0}/Dshell/DshellTokenizer/dshell_token_type.py +3 -0
  10. {dshellinterpreter-1.2.7.10/dshellInterpreter.egg-info → dshellinterpreter-1.2.8.0}/PKG-INFO +1 -1
  11. {dshellinterpreter-1.2.7.10 → dshellinterpreter-1.2.8.0/dshellInterpreter.egg-info}/PKG-INFO +1 -1
  12. {dshellinterpreter-1.2.7.10 → dshellinterpreter-1.2.8.0}/setup.py +1 -1
  13. dshellinterpreter-1.2.7.10/Dshell/DshellInterpreteur/dshell_scope.py +0 -159
  14. {dshellinterpreter-1.2.7.10 → dshellinterpreter-1.2.8.0}/Dshell/DISCORD_COMMANDS/__init__.py +0 -0
  15. {dshellinterpreter-1.2.7.10 → dshellinterpreter-1.2.8.0}/Dshell/DISCORD_COMMANDS/dshell_channel.py +0 -0
  16. {dshellinterpreter-1.2.7.10 → dshellinterpreter-1.2.8.0}/Dshell/DISCORD_COMMANDS/dshell_embed.py +0 -0
  17. {dshellinterpreter-1.2.7.10 → dshellinterpreter-1.2.8.0}/Dshell/DISCORD_COMMANDS/dshell_file.py +0 -0
  18. {dshellinterpreter-1.2.7.10 → dshellinterpreter-1.2.8.0}/Dshell/DISCORD_COMMANDS/dshell_interaction.py +0 -0
  19. {dshellinterpreter-1.2.7.10 → dshellinterpreter-1.2.8.0}/Dshell/DISCORD_COMMANDS/dshell_member.py +0 -0
  20. {dshellinterpreter-1.2.7.10 → dshellinterpreter-1.2.8.0}/Dshell/DISCORD_COMMANDS/dshell_message.py +0 -0
  21. {dshellinterpreter-1.2.7.10 → dshellinterpreter-1.2.8.0}/Dshell/DISCORD_COMMANDS/dshell_pastbin.py +0 -0
  22. {dshellinterpreter-1.2.7.10 → dshellinterpreter-1.2.8.0}/Dshell/DISCORD_COMMANDS/dshell_role.py +0 -0
  23. {dshellinterpreter-1.2.7.10 → dshellinterpreter-1.2.8.0}/Dshell/DISCORD_COMMANDS/utils/__init__.py +0 -0
  24. {dshellinterpreter-1.2.7.10 → dshellinterpreter-1.2.8.0}/Dshell/DISCORD_COMMANDS/utils/utils_embed.py +0 -0
  25. {dshellinterpreter-1.2.7.10 → dshellinterpreter-1.2.8.0}/Dshell/DISCORD_COMMANDS/utils/utils_file.py +0 -0
  26. {dshellinterpreter-1.2.7.10 → dshellinterpreter-1.2.8.0}/Dshell/DISCORD_COMMANDS/utils/utils_global.py +0 -0
  27. {dshellinterpreter-1.2.7.10 → dshellinterpreter-1.2.8.0}/Dshell/DISCORD_COMMANDS/utils/utils_list.py +0 -0
  28. {dshellinterpreter-1.2.7.10 → dshellinterpreter-1.2.8.0}/Dshell/DISCORD_COMMANDS/utils/utils_member.py +0 -0
  29. {dshellinterpreter-1.2.7.10 → dshellinterpreter-1.2.8.0}/Dshell/DISCORD_COMMANDS/utils/utils_message.py +0 -0
  30. {dshellinterpreter-1.2.7.10 → dshellinterpreter-1.2.8.0}/Dshell/DISCORD_COMMANDS/utils/utils_numbers.py +0 -0
  31. {dshellinterpreter-1.2.7.10 → dshellinterpreter-1.2.8.0}/Dshell/DISCORD_COMMANDS/utils/utils_permissions.py +0 -0
  32. {dshellinterpreter-1.2.7.10 → dshellinterpreter-1.2.8.0}/Dshell/DISCORD_COMMANDS/utils/utils_string.py +0 -0
  33. {dshellinterpreter-1.2.7.10 → dshellinterpreter-1.2.8.0}/Dshell/DISCORD_COMMANDS/utils/utils_thread.py +0 -0
  34. {dshellinterpreter-1.2.7.10 → dshellinterpreter-1.2.8.0}/Dshell/DISCORD_COMMANDS/utils/utils_type_validation.py +0 -0
  35. {dshellinterpreter-1.2.7.10 → dshellinterpreter-1.2.8.0}/Dshell/DshellInterpreteur/cached_messages.py +0 -0
  36. {dshellinterpreter-1.2.7.10 → dshellinterpreter-1.2.8.0}/Dshell/DshellInterpreteur/dshell_arguments.py +0 -0
  37. {dshellinterpreter-1.2.7.10 → dshellinterpreter-1.2.8.0}/Dshell/DshellInterpreteur/dshell_iterators.py +0 -0
  38. {dshellinterpreter-1.2.7.10 → dshellinterpreter-1.2.8.0}/Dshell/DshellInterpreteur/errors.py +0 -0
  39. {dshellinterpreter-1.2.7.10 → dshellinterpreter-1.2.8.0}/Dshell/DshellInterpreteur/utils_interpreter.py +0 -0
  40. {dshellinterpreter-1.2.7.10 → dshellinterpreter-1.2.8.0}/Dshell/DshellParser/__init__.py +0 -0
  41. {dshellinterpreter-1.2.7.10 → dshellinterpreter-1.2.8.0}/Dshell/DshellParser/errors.py +0 -0
  42. {dshellinterpreter-1.2.7.10 → dshellinterpreter-1.2.8.0}/Dshell/DshellPreProcess/__init__.py +0 -0
  43. {dshellinterpreter-1.2.7.10 → dshellinterpreter-1.2.8.0}/Dshell/DshellPreProcess/dshell_preprocess.py +0 -0
  44. {dshellinterpreter-1.2.7.10 → dshellinterpreter-1.2.8.0}/Dshell/DshellTokenizer/__init__.py +0 -0
  45. {dshellinterpreter-1.2.7.10 → dshellinterpreter-1.2.8.0}/Dshell/DshellTokenizer/dshell_tokenizer.py +0 -0
  46. {dshellinterpreter-1.2.7.10 → dshellinterpreter-1.2.8.0}/Dshell/__init__.py +0 -0
  47. {dshellinterpreter-1.2.7.10 → dshellinterpreter-1.2.8.0}/Dshell/full_import.py +0 -0
  48. {dshellinterpreter-1.2.7.10 → dshellinterpreter-1.2.8.0}/LICENSE +0 -0
  49. {dshellinterpreter-1.2.7.10 → dshellinterpreter-1.2.8.0}/README.md +0 -0
  50. {dshellinterpreter-1.2.7.10 → dshellinterpreter-1.2.8.0}/dshellInterpreter.egg-info/SOURCES.txt +0 -0
  51. {dshellinterpreter-1.2.7.10 → dshellinterpreter-1.2.8.0}/dshellInterpreter.egg-info/dependency_links.txt +0 -0
  52. {dshellinterpreter-1.2.7.10 → dshellinterpreter-1.2.8.0}/dshellInterpreter.egg-info/requires.txt +0 -0
  53. {dshellinterpreter-1.2.7.10 → dshellinterpreter-1.2.8.0}/dshellInterpreter.egg-info/top_level.txt +0 -0
  54. {dshellinterpreter-1.2.7.10 → dshellinterpreter-1.2.8.0}/setup.cfg +0 -0
@@ -8,13 +8,21 @@ from ..full_import import (ButtonStyle,
8
8
  SelectMenu,
9
9
  ComponentType,
10
10
  random,
11
- IntEnum)
12
-
13
- from ..DshellParser.ast_nodes import UiSelectNode, UiButtonNode, OptionUiSelectNode, ListNode, StrNode, IntNode, BoolNode
11
+ IntEnum,
12
+ deepcopy)
13
+
14
+ from ..DshellParser.ast_nodes import (UiSelectNode,
15
+ UiButtonNode,
16
+ OptionUiSelectNode,
17
+ CodeNode,
18
+ ListNode,
19
+ StrNode,
20
+ IntNode,
21
+ BoolNode)
14
22
 
15
23
  from ..DshellInterpreteur.utils_interpreter import regroupe_commandes
16
24
 
17
- from ..DshellInterpreteur.dshell_scope import new_scope, update_nbr_usage_scope
25
+ from ..DshellInterpreteur.dshell_scope import new_scope, Scope
18
26
 
19
27
  from ..full_import import Any, TYPE_CHECKING, Union, Optional
20
28
 
@@ -31,8 +39,6 @@ from .utils.utils_global import utils_refactor_emoji
31
39
  if TYPE_CHECKING:
32
40
  from ..DshellInterpreteur.dshell_interpreter import DshellInterpreteur
33
41
 
34
- scope_id = "scope_id"
35
-
36
42
  ButtonStyleValues: set = {i.name for i in ButtonStyle}
37
43
  SelectSyleValues: dict = {'string': ComponentType.string_select,
38
44
  'role': ComponentType.role_select,
@@ -56,6 +62,25 @@ def check_timeout(timeout: Union[int, IntNode]):
56
62
  f"and less or equal than[{UITimeout.max_timeout}]")
57
63
  return timeout
58
64
 
65
+
66
+ def build_ui_scope(env: Scope, code_node: CodeNode):
67
+ """
68
+ Create a new scope to store only variables used in UI.
69
+ :param code_node:
70
+ :return:
71
+ """
72
+
73
+ new_ui_scope = Scope()
74
+
75
+ if not isinstance(code_node, CodeNode) or code_node.global_env_vars is None:
76
+ return new_ui_scope
77
+
78
+ for token in code_node.global_env_vars.env_variables:
79
+
80
+ new_ui_scope.set(token.value, deepcopy(env.get(token.value, None)))
81
+
82
+ return new_ui_scope
83
+
59
84
  async def build_ui_button_parameters(ui_button_node: UiButtonNode, interpreter: "DshellInterpreteur"):
60
85
  """
61
86
  Builds the parameters for a UI component from the UiNode.
@@ -201,18 +226,15 @@ async def build_ui(ui_node: Union[UiButtonNode, UiSelectNode], interpreter: "Dsh
201
226
  :return:
202
227
  """
203
228
 
204
- async def ui_timeout(ctx):
205
- update_nbr_usage_scope(interpreter.scope_id, -1)
206
-
207
- view = EasyModifiedViews(timeout=600, call_on_timeout=ui_timeout, disabled_on_timeout=True)
229
+ view = EasyModifiedViews(timeout=600, disabled_on_timeout=True)
208
230
 
209
231
  if isinstance(ui_node, UiButtonNode):
210
232
  async for _, args_button, code in build_ui_button_parameters(ui_node, interpreter):
233
+ code: CodeNode
211
234
  view.timeout = args_button.pop('timeout', IntNode(UITimeout.default_timeout))
212
235
  b = ui.Button(**args_button)
213
236
  view.add_items(b)
214
- view.set_callable(b.custom_id, _callable=ui_button_callback, data={'code': code, scope_id: interpreter.scope_id})
215
- update_nbr_usage_scope(interpreter.scope_id, 1)
237
+ view.set_callable(b.custom_id, _callable=ui_button_callback, data={'code': code, 'scope': build_ui_scope(interpreter.env, code)})
216
238
 
217
239
  elif isinstance(ui_node, UiSelectNode):
218
240
  s = SelectMenu()
@@ -228,26 +250,25 @@ async def build_ui(ui_node: Union[UiButtonNode, UiSelectNode], interpreter: "Dsh
228
250
  for option in options:
229
251
  menu.add_option(**option)
230
252
 
231
- s.set_callable(args_select["custom_id"], _callable=ui_select_callback, data={'code': code, scope_id: interpreter.scope_id})
253
+ s.set_callable(args_select["custom_id"], _callable=ui_select_callback, data={'code': code, "scope": build_ui_scope(interpreter.env, code)})
232
254
 
233
255
  elif select_type == ComponentType.role_select:
234
256
  s.add_role_select_menu(**args_select)
235
- s.set_callable(args_select["custom_id"], _callable=ui_select_callback, data={'code': code, scope_id: interpreter.scope_id})
257
+ s.set_callable(args_select["custom_id"], _callable=ui_select_callback, data={'code': code, "scope": build_ui_scope(interpreter.env, code)})
236
258
 
237
259
  elif select_type == ComponentType.user_select:
238
260
  s.add_user_select_menu(**args_select)
239
- s.set_callable(args_select["custom_id"], _callable=ui_select_callback, data={'code': code, scope_id: interpreter.scope_id})
261
+ s.set_callable(args_select["custom_id"], _callable=ui_select_callback, data={'code': code, "scope": build_ui_scope(interpreter.env, code)})
240
262
 
241
263
  elif select_type == ComponentType.mentionable_select:
242
264
  s.add_mentionable_select_menu(**args_select)
243
- s.set_callable(args_select["custom_id"], _callable=ui_select_callback, data={'code': code, scope_id: interpreter.scope_id})
265
+ s.set_callable(args_select["custom_id"], _callable=ui_select_callback, data={'code': code, "scope": build_ui_scope(interpreter.env, code)})
244
266
 
245
267
  elif select_type == ComponentType.channel_select:
246
268
  s.add_channel_select_menu(**args_select)
247
- s.set_callable(args_select["custom_id"], _callable=ui_select_callback, data={'code': code, scope_id: interpreter.scope_id})
269
+ s.set_callable(args_select["custom_id"], _callable=ui_select_callback, data={'code': code, "scope": build_ui_scope(interpreter.env, code)})
248
270
 
249
271
  view.add_items(s)
250
- update_nbr_usage_scope(interpreter.scope_id, 1)
251
272
 
252
273
  else:
253
274
  raise TypeError(f"UI node must be UiButtonNode or UiSelectNode, not {type(ui_node).__name__} !")
@@ -276,8 +297,8 @@ async def rebuild_ui(ui_node: Union[UiButtonNode, UiSelectNode], view: EasyModif
276
297
  ui.disabled = args_button.get('disabled', ui.disabled)
277
298
  ui.url = args_button.get('url', ui.url)
278
299
  ui.row = args_button.get('row', ui.row)
279
- new_code = code if code is not None else view.get_callable_data(args_button['custom_id'])['code']
280
- view.set_callable(args_button['custom_id'], _callable=ui_button_callback, data={'code': new_code, scope_id: interpreter.scope_id})
300
+ new_code: CodeNode = code if code is not None else view.get_callable_data(args_button['custom_id'])['code']
301
+ view.set_callable(args_button['custom_id'], _callable=ui_button_callback, data={'code': new_code, "scope": build_ui_scope(interpreter.env, new_code)})
281
302
 
282
303
  elif isinstance(ui_node, UiSelectNode):
283
304
 
@@ -297,8 +318,8 @@ async def rebuild_ui(ui_node: Union[UiButtonNode, UiSelectNode], view: EasyModif
297
318
  for option in options:
298
319
  ui.add_option(**option)
299
320
 
300
- new_code = code if code is not None else view.get_callable_data(args_select['custom_id'])['code']
301
- view.set_callable(args_select['custom_id'], _callable=ui_select_callback, data={'code': new_code, scope_id: interpreter.scope_id})
321
+ new_code: CodeNode = code if code is not None else view.get_callable_data(args_select['custom_id'])['code']
322
+ view.set_callable(args_select['custom_id'], _callable=ui_select_callback, data={'code': new_code, "scope": build_ui_scope(interpreter.env, new_code)})
302
323
 
303
324
  return view
304
325
 
@@ -313,7 +334,7 @@ async def ui_button_callback(button: ui.Button, interaction: Interaction, data:
313
334
  :return:
314
335
  """
315
336
  code = data.get('code', None)
316
- scope: Optional[str] = data.get(scope_id, None)
337
+ scope: Optional[str] = data.get("scope", None)
317
338
 
318
339
  if code is not None:
319
340
  message = interaction
@@ -427,7 +448,7 @@ async def ui_select_callback(select: ui.Select, interaction: Interaction, data:
427
448
  :return:
428
449
  """
429
450
  code = data.get('code', None)
430
- scope: Optional[str] = data.get(scope_id, None)
451
+ scope: Optional[str] = data.get("scope", None)
431
452
 
432
453
  message = interaction
433
454
  if code is not None:
@@ -5,3 +5,4 @@ from .dshell_arguments import DshellArguments
5
5
  from .dshell_iterators import *
6
6
  from .dshell_scope import *
7
7
  from .dshell_global_variables import *
8
+ from .utils_interpreter import *
@@ -7,4 +7,11 @@ MAX_FILE_SIZE = 1_000_000 # bytes
7
7
  MAX_SLEEP_TIME_SECONDS = 3_600 # 1 hour
8
8
  MIN_SLEEP_TIME_SECONDS = 1
9
9
 
10
- MAX_WAIT_MESSAGE_TIMEOUT = 300
10
+ MAX_WAIT_MESSAGE_TIMEOUT = 300
11
+
12
+
13
+ class Nothing:
14
+ """
15
+ Do nothing.
16
+ Used if None is a usable value.
17
+ """
@@ -11,7 +11,7 @@ from .utils_interpreter import get_params, eval_expression, eval_expression_inli
11
11
  from ..DISCORD_COMMANDS.dshell_embed import build_embed, rebuild_embed
12
12
  from ..DISCORD_COMMANDS.dshell_ui import build_ui
13
13
  from ..DISCORD_COMMANDS.utils.utils_permissions import build_permission
14
- from .dshell_scope import Scope, new_scope, get_scope, create_scope, update_nbr_usage_scope
14
+ from .dshell_scope import Scope, new_scope
15
15
  from .dshell_global_variables import MAX_SLEEP_TIME_SECONDS, MIN_SLEEP_TIME_SECONDS
16
16
  from .dshell_iterators import DshellIterator
17
17
 
@@ -50,16 +50,7 @@ class DshellInterpreteur:
50
50
  message = ctx.message if isinstance(ctx, Interaction) else ctx
51
51
 
52
52
  # scope creation
53
- scope = get_scope(vars_env)
54
- if scope:
55
- self.env = scope
56
- self.scope_id = vars_env
57
- else:
58
- self.scope_id: str = create_scope()
59
- self.env: Optional[Scope] = get_scope(self.scope_id)
60
-
61
- if self.env is None:
62
- raise Exception(f"Scope {self.scope_id} not found in interpreter creation !")
53
+ self.env: Scope = Scope()
63
54
 
64
55
  self.env.update({
65
56
  '__ret__': None, # environment variables, '__ret__' is used to store the return value of commands
@@ -350,7 +341,10 @@ class DshellInterpreteur:
350
341
  """
351
342
  Clear the interpreter environment.
352
343
  """
353
- update_nbr_usage_scope(self.scope_id, -1)
344
+ self.env.clear()
345
+ del self.env
346
+ self.ast.clear()
347
+ del self.ast
354
348
 
355
349
 
356
350
  async def call_function(function: Callable, args: ArgsCommandNode, interpreter: DshellInterpreteur):
@@ -0,0 +1,95 @@
1
+ from contextlib import contextmanager
2
+ from typing import Any, Optional, Dict, Set
3
+ from .dshell_global_variables import Nothing
4
+
5
+
6
+ class Scope:
7
+ """
8
+ Represents a variable scope with optional parent scope for nested scoping.
9
+ """
10
+ def __init__(self, parent: Optional["Scope"] = None):
11
+ self.parent: Optional[Scope] = parent
12
+ self.vars: Dict[str, Any] = {}
13
+
14
+ def get(self, name: str, default: Any = Nothing()) -> Any:
15
+ """
16
+ Get a variable value from this scope or parent scopes.
17
+ :param name: Variable name
18
+ :param default: The default variable to return if nothing was found
19
+ :return: Variable value
20
+ :raises KeyError: If variable not found in any scope
21
+ """
22
+ if name in self.vars:
23
+ return self.vars[name]
24
+ if self.parent:
25
+ return self.parent.get(name)
26
+ if not isinstance(default, Nothing):
27
+ return default
28
+ del default
29
+ raise KeyError(name)
30
+
31
+ def set(self, name: str, value: Any) -> None:
32
+ """
33
+ Set a variable in this scope.
34
+ :param name: Variable name
35
+ :param value: Variable value
36
+ """
37
+ self.vars[name] = value
38
+
39
+ def update(self, mapping: Dict[str, Any]) -> None:
40
+ """
41
+ Update multiple variables in this scope.
42
+ :param mapping: Dictionary of variable names and values
43
+ """
44
+ self.vars.update(mapping)
45
+
46
+ def contains(self, name: str) -> bool:
47
+ """
48
+ Check if a variable exists in this scope or parent scopes.
49
+ :param name: Variable name
50
+ :return: True if variable exists, False otherwise
51
+ """
52
+ if name in self.vars:
53
+ return True
54
+ if self.parent:
55
+ return self.parent.contains(name)
56
+ return False
57
+
58
+ def keys(self) -> Set[str]:
59
+ """
60
+ Get all variable names from this scope and parent scopes.
61
+ :return: Set of variable names
62
+ """
63
+ keys = set(self.vars.keys())
64
+ if self.parent:
65
+ keys.update(self.parent.keys())
66
+ return keys
67
+
68
+ def clear(self) -> None:
69
+ """Clear all variables in this scope (does not affect parent)."""
70
+ self.vars.clear()
71
+
72
+
73
+
74
+
75
+ @contextmanager
76
+ def new_scope(interpreter, initial_vars: Optional[Dict[str, Any]] = None):
77
+ """
78
+ Context manager for creating a new scope temporarily.
79
+ :param interpreter: The interpreter instance
80
+ :param initial_vars: Optional initial variables for the new scope
81
+ """
82
+ parent = interpreter.env
83
+
84
+ new_scope: Scope = Scope(parent=parent)
85
+
86
+ interpreter.env = new_scope
87
+
88
+ if initial_vars:
89
+ interpreter.env.update(initial_vars)
90
+ try:
91
+ yield
92
+ finally:
93
+ interpreter.clear()
94
+ interpreter.env = parent
95
+
@@ -40,6 +40,7 @@ __all__ = [
40
40
  'PermissionNode',
41
41
  'EvalGroupNode',
42
42
  'ParamNode',
43
+ 'GlobalNode',
43
44
  'CodeNode',
44
45
  'EvalNode',
45
46
  'ReturnNode',
@@ -144,52 +145,52 @@ class IntNode(int, ASTNode, DATANode):
144
145
  return getsizeof(IntNode)
145
146
 
146
147
  def __add__(self, value: "IntNode", /) -> "IntNode":
147
- if not isinstance(value, IntNode):
148
+ if not isinstance(value, (IntNode, int)):
148
149
  raise Exception(f"Cannot add {type(value).__name__} type to IntNode")
149
150
  return IntNode(super().__add__(value))
150
151
 
151
152
  def __sub__(self, value: "IntNode", /) -> "IntNode":
152
- if not isinstance(value, IntNode):
153
+ if not isinstance(value, (IntNode, int)):
153
154
  raise Exception(f"Cannot sub {type(value).__name__} type to IntNode")
154
155
  return IntNode(super().__sub__(value))
155
156
 
156
157
  def __pow__(self, x: "IntNode", /) -> "IntNode":
157
- if not isinstance(x, IntNode):
158
+ if not isinstance(x, (IntNode, int)):
158
159
  raise Exception(f"Cannot pow {type(x).__name__} type to IntNode")
159
160
  return IntNode(super().__pow__(x))
160
161
 
161
162
  def __floordiv__(self, value: "IntNode", /) -> "IntNode":
162
- if not isinstance(value, IntNode):
163
+ if not isinstance(value, (IntNode, int)):
163
164
  raise Exception(f"Cannot divide {type(value).__name__} type to IntNode")
164
165
  return IntNode(super().__floordiv__(value))
165
166
 
166
167
  def __rshift__(self, value: "IntNode", /) -> "IntNode":
167
- if not isinstance(value, IntNode):
168
+ if not isinstance(value, (IntNode, int)):
168
169
  raise Exception(f"Cannot shift to the right {type(value).__name__} type to IntNode")
169
170
  return IntNode(super().__rshift__(value))
170
171
 
171
172
  def __lshift__(self, value: "IntNode", /) -> "IntNode":
172
- if not isinstance(value, IntNode):
173
+ if not isinstance(value, (IntNode, int)):
173
174
  raise Exception(f"Cannot shift to the left {type(value).__name__} type to IntNode")
174
175
  return IntNode(super().__lshift__(value))
175
176
 
176
177
  def __xor__(self, value: "IntNode", /) -> "IntNode":
177
- if not isinstance(value, IntNode):
178
+ if not isinstance(value, (IntNode, int)):
178
179
  raise Exception(f"Cannot xor {type(value).__name__} type to IntNode")
179
180
  return IntNode(super().__xor__(value))
180
181
 
181
182
  def __truediv__(self, value: "IntNode", /) -> "FloatNode":
182
- if not isinstance(value, IntNode):
183
+ if not isinstance(value, (IntNode, int)):
183
184
  raise Exception(f"Cannot divide {type(value).__name__} type to IntNode")
184
185
  return FloatNode(super().__truediv__(value))
185
186
 
186
187
  def __mul__(self, value: "IntNode", /) -> "IntNode":
187
- if not isinstance(value, IntNode):
188
+ if not isinstance(value, (IntNode, int)):
188
189
  raise Exception(f"Cannot multiply {type(value).__name__} type to IntNode")
189
190
  return IntNode(super().__mul__(value))
190
191
 
191
192
  def __mod__(self, value: "IntNode", /) -> "IntNode":
192
- if not isinstance(value, IntNode):
193
+ if not isinstance(value, (IntNode, int)):
193
194
  raise Exception(f"Cannot modulo {type(value).__name__} type to IntNode")
194
195
  return IntNode(super().__mod__(value))
195
196
 
@@ -205,52 +206,52 @@ class FloatNode(float, ASTNode, DATANode):
205
206
  return getsizeof(FloatNode)
206
207
 
207
208
  def __add__(self, value: "FloatNode", /) -> "FloatNode":
208
- if not isinstance(value, FloatNode):
209
+ if not isinstance(value, (FloatNode, float)):
209
210
  raise Exception(f"Cannot add {type(value).__name__} type to FloatNode")
210
211
  return FloatNode(super().__add__(value))
211
212
 
212
213
  def __sub__(self, value: "FloatNode", /) -> "FloatNode":
213
- if not isinstance(value, FloatNode):
214
+ if not isinstance(value, (FloatNode, float)):
214
215
  raise Exception(f"Cannot sub {type(value).__name__} type to FloatNode")
215
216
  return FloatNode(super().__sub__(value))
216
217
 
217
218
  def __pow__(self, x: "FloatNode", /) -> "FloatNode":
218
- if not isinstance(x, FloatNode):
219
+ if not isinstance(x, (FloatNode, float)):
219
220
  raise Exception(f"Cannot pow {type(x).__name__} type to FloatNode")
220
221
  return FloatNode(super().__pow__(x))
221
222
 
222
223
  def __floordiv__(self, value: "FloatNode", /) -> "FloatNode":
223
- if not isinstance(value, FloatNode):
224
+ if not isinstance(value, (FloatNode, float)):
224
225
  raise Exception(f"Cannot divide {type(value).__name__} type to FloatNode")
225
226
  return FloatNode(super().__floordiv__(value))
226
227
 
227
228
  def __rshift__(self, value: "FloatNode", /) -> "FloatNode":
228
- if not isinstance(value, FloatNode):
229
+ if not isinstance(value, (FloatNode, float)):
229
230
  raise Exception(f"Cannot shift to the right {type(value).__name__} type to FloatNode")
230
231
  return FloatNode(super().__rshift__(value))
231
232
 
232
233
  def __lshift__(self, value: "FloatNode", /) -> "FloatNode":
233
- if not isinstance(value, FloatNode):
234
+ if not isinstance(value, (FloatNode, float)):
234
235
  raise Exception(f"Cannot shift to the left {type(value).__name__} type to FloatNode")
235
236
  return FloatNode(super().__lshift__(value))
236
237
 
237
238
  def __xor__(self, value: "FloatNode", /) -> "FloatNode":
238
- if not isinstance(value, FloatNode):
239
+ if not isinstance(value, (FloatNode, float)):
239
240
  raise Exception(f"Cannot xor {type(value).__name__} type to FloatNode")
240
241
  return FloatNode(super().__xor__(value))
241
242
 
242
243
  def __truediv__(self, value: "FloatNode", /) -> "FloatNode":
243
- if not isinstance(value, FloatNode):
244
+ if not isinstance(value, (FloatNode, float)):
244
245
  raise Exception(f"Cannot divide {type(value).__name__} type to FloatNode")
245
246
  return FloatNode(super().__truediv__(value))
246
247
 
247
248
  def __mul__(self, value: "FloatNode", /) -> "FloatNode":
248
- if not isinstance(value, FloatNode):
249
+ if not isinstance(value, (FloatNode, float)):
249
250
  raise Exception(f"Cannot multiply {type(value).__name__} type to FloatNode")
250
251
  return FloatNode(super().__mul__(value))
251
252
 
252
253
  def __mod__(self, value: "FloatNode", /) -> "FloatNode":
253
- if not isinstance(value, FloatNode):
254
+ if not isinstance(value, (FloatNode, float)):
254
255
  raise Exception(f"Cannot modulo {type(value).__name__} type to FloatNode")
255
256
  return FloatNode(super().__mod__(value))
256
257
 
@@ -265,52 +266,52 @@ class BoolNode(int, ASTNode, DATANode):
265
266
  return getsizeof(BoolNode)
266
267
 
267
268
  def __add__(self, value: Union["BoolNode", "IntNode"], /) -> "BoolNode":
268
- if not isinstance(value, (BoolNode, IntNode)):
269
+ if not isinstance(value, (BoolNode, IntNode, bool)):
269
270
  raise Exception(f"Cannot add {type(value).__name__} type to IntNode")
270
271
  return BoolNode(super().__add__(value))
271
272
 
272
273
  def __sub__(self, value: Union["BoolNode", "IntNode"], /) -> "BoolNode":
273
- if not isinstance(value, (BoolNode, IntNode)):
274
+ if not isinstance(value, (BoolNode, IntNode, bool)):
274
275
  raise Exception(f"Cannot sub {type(value).__name__} type to IntNode")
275
276
  return BoolNode(super().__sub__(value))
276
277
 
277
278
  def __pow__(self, x: Union["BoolNode", "IntNode"], /) -> "BoolNode":
278
- if not isinstance(x, (BoolNode, IntNode)):
279
+ if not isinstance(x, (BoolNode, IntNode, bool)):
279
280
  raise Exception(f"Cannot pow {type(x).__name__} type to IntNode")
280
281
  return BoolNode(super().__pow__(x))
281
282
 
282
283
  def __floordiv__(self, value: Union["BoolNode", "IntNode"], /) -> "BoolNode":
283
- if not isinstance(value, (BoolNode, IntNode)):
284
+ if not isinstance(value, (BoolNode, IntNode, bool)):
284
285
  raise Exception(f"Cannot divide {type(value).__name__} type to IntNode")
285
286
  return BoolNode(super().__floordiv__(value))
286
287
 
287
288
  def __rshift__(self, value: Union["BoolNode", "IntNode"], /) -> "BoolNode":
288
- if not isinstance(value, (BoolNode, IntNode)):
289
+ if not isinstance(value, (BoolNode, IntNode, bool)):
289
290
  raise Exception(f"Cannot shift to the right {type(value).__name__} type to IntNode")
290
291
  return BoolNode(super().__rshift__(value))
291
292
 
292
293
  def __lshift__(self, value: Union["BoolNode", "IntNode"], /) -> "BoolNode":
293
- if not isinstance(value, (BoolNode, IntNode)):
294
+ if not isinstance(value, (BoolNode, IntNode, bool)):
294
295
  raise Exception(f"Cannot shift to the left {type(value).__name__} type to IntNode")
295
296
  return BoolNode(super().__lshift__(value))
296
297
 
297
298
  def __xor__(self, value: Union["BoolNode", "IntNode"], /) -> "BoolNode":
298
- if not isinstance(value, (BoolNode, IntNode)):
299
+ if not isinstance(value, (BoolNode, IntNode, bool)):
299
300
  raise Exception(f"Cannot xor {type(value).__name__} type to IntNode")
300
301
  return BoolNode(super().__xor__(value))
301
302
 
302
303
  def __truediv__(self, value: Union["BoolNode", "IntNode"], /) -> "BoolNode":
303
- if not isinstance(value, (BoolNode, IntNode)):
304
+ if not isinstance(value, (BoolNode, IntNode, bool)):
304
305
  raise Exception(f"Cannot divide {type(value).__name__} type to IntNode")
305
306
  return BoolNode(super().__truediv__(value))
306
307
 
307
308
  def __mul__(self, value: Union["BoolNode", "IntNode"], /) -> "BoolNode":
308
- if not isinstance(value, (BoolNode, IntNode)):
309
+ if not isinstance(value, (BoolNode, IntNode, bool)):
309
310
  raise Exception(f"Cannot multiply {type(value).__name__} type to IntNode")
310
311
  return BoolNode(super().__mul__(value))
311
312
 
312
313
  def __mod__(self, value: Union["BoolNode", "IntNode"], /) -> "BoolNode":
313
- if not isinstance(value, (BoolNode, IntNode)):
314
+ if not isinstance(value, (BoolNode, IntNode, bool)):
314
315
  raise Exception(f"Cannot modulo {type(value).__name__} type to IntNode")
315
316
  return BoolNode(super().__mod__(value))
316
317
 
@@ -827,16 +828,35 @@ class ParamNode(ASTNode):
827
828
  "body": [token.to_dict() for token in self.body]
828
829
  }
829
830
 
831
+
832
+ class GlobalNode(ASTNode):
833
+ """
834
+ Node representing env variables used in code node.
835
+ """
836
+ def __init__(self, line: int, *args: Token):
837
+ super().__init__(line)
838
+ self.env_variables: tuple[Token, ...] = args
839
+
840
+ def __repr__(self) -> str:
841
+ return StrNode(f"<GLOBAL - {', '.join((str(i) for i in self.env_variables))}")
842
+
843
+ def to_dict(self):
844
+ return {
845
+ "type": "GlobalNode",
846
+ "env_variables": self.env_variables
847
+ }
848
+
830
849
  class CodeNode(ASTNode):
831
850
  """
832
851
  Node representing a block of code to pass in arguments.
833
852
  """
834
- def __init__(self, body: list[ASTNode], line: int):
853
+ def __init__(self, body: list[ASTNode], line: int, global_env_vars: Optional[GlobalNode] = None):
835
854
  """
836
855
  :param body: list of Node representing the code already parsed
837
856
  """
838
857
  super().__init__(line)
839
858
  self.body = body
859
+ self.global_env_vars: Optional[GlobalNode] = global_env_vars
840
860
 
841
861
  def __repr__(self):
842
862
  return StrNode(f"<CODE> - {self.body}")
@@ -848,9 +868,11 @@ class CodeNode(ASTNode):
848
868
  """
849
869
  return {
850
870
  "type": "CodeNode",
871
+ "global_env_vars": self.global_env_vars.to_dict(),
851
872
  "body": [token.to_dict() for token in self.body]
852
873
  }
853
874
 
875
+
854
876
  class EvalNode(ASTNode):
855
877
  """
856
878
  Node representing an evaluation in the AST.
@@ -6,6 +6,7 @@ __all__ = [
6
6
  "ast_to_dict",
7
7
  ]
8
8
 
9
+ from .ast_nodes import GlobalNode
9
10
  from ..DshellTokenizer.dshell_token_type import Token
10
11
  from ..DshellTokenizer.dshell_token_type import DshellTokenType as DTT
11
12
  from ..DshellTokenizer.dshell_token_type import DTT_DATA
@@ -202,6 +203,18 @@ def parse(token_lines: list[list[Token]], start_node: ASTNode) -> tuple[list[AST
202
203
  blocks.pop()
203
204
  return blocks, pointer
204
205
 
206
+ elif first_token_line_value == 'global':
207
+ if not isinstance(last_block, CodeNode):
208
+ raise SyntaxError(f"[GLOBAL] No code open on line {first_token_line.position}")
209
+
210
+ for i in range(1, len_tokens_by_line):
211
+ ident = tokens_by_line[i]
212
+ if ident.type != DTT.IDENT:
213
+ raise ValueError(f"[GLOBAL] '{ident}' value must be a ident on line {first_token_line.position}")
214
+
215
+ global_node = GlobalNode(line, *tokens_by_line[1:])
216
+ last_block.global_env_vars = global_node
217
+
205
218
  elif first_token_line_value == 'eval':
206
219
  if len_tokens_by_line_since_command_name < 1:
207
220
  raise SyntaxError(f"[EVAL] take one or more arguments on line {first_token_line.position}")
@@ -17,7 +17,7 @@ from ..DshellInterpreteur.dshell_iterators import IntIterator
17
17
  dshell_keyword: set[str] = {
18
18
  'if', 'else', 'elif', 'loop', '#end', 'var', '#loop', '#if', 'sleep',
19
19
  'param', '#param', 'code', '#code', 'eval', 'return', 'break',
20
- 'catch', '#catch', 'raise'
20
+ 'catch', '#catch', 'raise', 'global'
21
21
  }
22
22
 
23
23
  dshell_discord_keyword: set[str] = {
@@ -61,6 +61,9 @@ class Token:
61
61
  self.value = value
62
62
  self.position = position
63
63
 
64
+ def __str__(self):
65
+ return self.__repr__()
66
+
64
67
  def __repr__(self):
65
68
  return f"<{self.type.name} '{self.value}'>"
66
69
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dshellInterpreter
3
- Version: 1.2.7.10
3
+ Version: 1.2.8.0
4
4
  Summary: A Discord bot interpreter for creating custom commands and automations.
5
5
  Home-page: https://github.com/BOXERRMD/Dshell_Interpreter
6
6
  Author: Chronos
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dshellInterpreter
3
- Version: 1.2.7.10
3
+ Version: 1.2.8.0
4
4
  Summary: A Discord bot interpreter for creating custom commands and automations.
5
5
  Home-page: https://github.com/BOXERRMD/Dshell_Interpreter
6
6
  Author: Chronos
@@ -5,7 +5,7 @@ with open("README.md", "r") as fh:
5
5
 
6
6
  setup(
7
7
  name="dshellInterpreter",
8
- version="1.2.7.10",
8
+ version="1.2.8.0",
9
9
  author="Chronos",
10
10
  author_email="vagabonwalybi@gmail.com",
11
11
  description="A Discord bot interpreter for creating custom commands and automations.",
@@ -1,159 +0,0 @@
1
- from contextlib import contextmanager
2
- from typing import Any, Optional, Dict, Set, Union
3
- from random import random
4
-
5
- # manage all scope with one unique ID by scope to separate the interpreter and scopes
6
- context_scope: dict[str, tuple[int, "Scope"]] = {}
7
-
8
- def generate_scope_id() -> str:
9
- """
10
- generate a new scope code unused
11
- :return:
12
- """
13
- while (_id := (str(random()) + str(random()))) in context_scope: pass
14
- return _id
15
-
16
-
17
- def get_scope(_id: str) -> Union["Scope", None]:
18
- """
19
- Get the current scope linked with an ID
20
- :param _id:
21
- :return: the scope link with the id, or None if the scope id is not found
22
- """
23
- x = context_scope.get(_id, None)
24
- return x[1] if x is not None else x
25
-
26
- def get_usage_scope(_id: str) -> Union[int, None]:
27
- """
28
- Get the usage number for a scope
29
- :param _id: An integer, or None if the scope id is not found
30
- :return:
31
- """
32
- x = context_scope.get(_id, None)
33
- return x[0] if x is not None else x
34
-
35
- class Scope:
36
- """
37
- Represents a variable scope with optional parent scope for nested scoping.
38
- """
39
- def __init__(self, parent: Optional[str] = None):
40
- self.parent: Optional[str] = parent
41
- self.vars: Dict[str, Any] = {}
42
-
43
- def get(self, name: str) -> Any:
44
- """
45
- Get a variable value from this scope or parent scopes.
46
- :param name: Variable name
47
- :return: Variable value
48
- :raises KeyError: If variable not found in any scope
49
- """
50
- if name in self.vars:
51
- return self.vars[name]
52
- if self.parent:
53
- if self.parent in context_scope:
54
- return context_scope[self.parent][1].get(name)
55
- raise Exception(f"Scope {self.parent} not found [get] !")
56
- raise KeyError(name)
57
-
58
- def set(self, name: str, value: Any) -> None:
59
- """
60
- Set a variable in this scope.
61
- :param name: Variable name
62
- :param value: Variable value
63
- """
64
- self.vars[name] = value
65
-
66
- def update(self, mapping: Dict[str, Any]) -> None:
67
- """
68
- Update multiple variables in this scope.
69
- :param mapping: Dictionary of variable names and values
70
- """
71
- self.vars.update(mapping)
72
-
73
- def contains(self, name: str) -> bool:
74
- """
75
- Check if a variable exists in this scope or parent scopes.
76
- :param name: Variable name
77
- :return: True if variable exists, False otherwise
78
- """
79
- if name in self.vars:
80
- return True
81
- if self.parent:
82
- if self.parent in context_scope:
83
- return context_scope[self.parent][1].contains(name)
84
- Exception(f"Scope {self.parent} not found [contains] !")
85
- return False
86
-
87
- def keys(self) -> Set[str]:
88
- """
89
- Get all variable names from this scope and parent scopes.
90
- :return: Set of variable names
91
- """
92
- keys = set(self.vars.keys())
93
- if self.parent:
94
- if self.parent in context_scope:
95
- keys.update(context_scope[self.parent][1].keys())
96
- else:
97
- Exception(f"Scope {self.parent} not found [keys] !")
98
- return keys
99
-
100
- def clear(self) -> None:
101
- """Clear all variables in this scope (does not affect parent)."""
102
- self.vars.clear()
103
-
104
-
105
- def create_scope() -> str:
106
- """
107
- Create a new scope and return the scope code
108
- :return:
109
- """
110
- _id = generate_scope_id()
111
- scope = Scope()
112
- context_scope[_id] = (1, scope)
113
- return _id
114
-
115
- def update_nbr_usage_scope(scope_id: str, nbr_usage: int):
116
- """
117
- Update the usage number for a scope
118
- :param nbr_usage:
119
- :return:
120
- """
121
- if scope_id not in context_scope:
122
- raise Exception(f"Scope {scope_id} not found [update nbr usage scope] !")
123
-
124
- new_usage = get_usage_scope(scope_id)+nbr_usage
125
-
126
- if new_usage <= 0:
127
- del context_scope[scope_id]
128
- return
129
-
130
- new = (new_usage, context_scope[scope_id][1])
131
-
132
- del context_scope[scope_id]
133
- context_scope[scope_id] = new
134
-
135
-
136
- @contextmanager
137
- def new_scope(interpreter, initial_vars: Optional[Dict[str, Any]] = None):
138
- """
139
- Context manager for creating a new scope temporarily.
140
- :param interpreter: The interpreter instance
141
- :param initial_vars: Optional initial variables for the new scope
142
- """
143
- parent = interpreter.scope_id
144
-
145
- new_scope_id: str = generate_scope_id()
146
- context_scope[new_scope_id] = (1, Scope(parent))
147
-
148
- interpreter.scope_id = new_scope_id
149
- interpreter.env = get_scope(new_scope_id)
150
-
151
- if initial_vars:
152
- interpreter.env.update(initial_vars)
153
- try:
154
- yield
155
- finally:
156
- interpreter.clear()
157
- interpreter.env = get_scope(parent)
158
- interpreter.scope_id = parent
159
-