dshellInterpreter 1.2.7.11__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.11 → dshellinterpreter-1.2.8.0}/Dshell/DISCORD_COMMANDS/dshell_ui.py +45 -24
  2. {dshellinterpreter-1.2.7.11 → dshellinterpreter-1.2.8.0}/Dshell/DshellInterpreteur/__init__.py +1 -0
  3. {dshellinterpreter-1.2.7.11 → dshellinterpreter-1.2.8.0}/Dshell/DshellInterpreteur/dshell_global_variables.py +8 -1
  4. {dshellinterpreter-1.2.7.11 → 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.11 → dshellinterpreter-1.2.8.0}/Dshell/DshellParser/ast_nodes.py +23 -1
  7. {dshellinterpreter-1.2.7.11 → dshellinterpreter-1.2.8.0}/Dshell/DshellParser/dshell_parser.py +13 -0
  8. {dshellinterpreter-1.2.7.11 → dshellinterpreter-1.2.8.0}/Dshell/DshellTokenizer/dshell_keywords.py +1 -1
  9. {dshellinterpreter-1.2.7.11 → dshellinterpreter-1.2.8.0}/Dshell/DshellTokenizer/dshell_token_type.py +3 -0
  10. {dshellinterpreter-1.2.7.11/dshellInterpreter.egg-info → dshellinterpreter-1.2.8.0}/PKG-INFO +1 -1
  11. {dshellinterpreter-1.2.7.11 → dshellinterpreter-1.2.8.0/dshellInterpreter.egg-info}/PKG-INFO +1 -1
  12. {dshellinterpreter-1.2.7.11 → dshellinterpreter-1.2.8.0}/setup.py +1 -1
  13. dshellinterpreter-1.2.7.11/Dshell/DshellInterpreteur/dshell_scope.py +0 -159
  14. {dshellinterpreter-1.2.7.11 → dshellinterpreter-1.2.8.0}/Dshell/DISCORD_COMMANDS/__init__.py +0 -0
  15. {dshellinterpreter-1.2.7.11 → dshellinterpreter-1.2.8.0}/Dshell/DISCORD_COMMANDS/dshell_channel.py +0 -0
  16. {dshellinterpreter-1.2.7.11 → dshellinterpreter-1.2.8.0}/Dshell/DISCORD_COMMANDS/dshell_embed.py +0 -0
  17. {dshellinterpreter-1.2.7.11 → dshellinterpreter-1.2.8.0}/Dshell/DISCORD_COMMANDS/dshell_file.py +0 -0
  18. {dshellinterpreter-1.2.7.11 → dshellinterpreter-1.2.8.0}/Dshell/DISCORD_COMMANDS/dshell_interaction.py +0 -0
  19. {dshellinterpreter-1.2.7.11 → dshellinterpreter-1.2.8.0}/Dshell/DISCORD_COMMANDS/dshell_member.py +0 -0
  20. {dshellinterpreter-1.2.7.11 → dshellinterpreter-1.2.8.0}/Dshell/DISCORD_COMMANDS/dshell_message.py +0 -0
  21. {dshellinterpreter-1.2.7.11 → dshellinterpreter-1.2.8.0}/Dshell/DISCORD_COMMANDS/dshell_pastbin.py +0 -0
  22. {dshellinterpreter-1.2.7.11 → dshellinterpreter-1.2.8.0}/Dshell/DISCORD_COMMANDS/dshell_role.py +0 -0
  23. {dshellinterpreter-1.2.7.11 → dshellinterpreter-1.2.8.0}/Dshell/DISCORD_COMMANDS/utils/__init__.py +0 -0
  24. {dshellinterpreter-1.2.7.11 → dshellinterpreter-1.2.8.0}/Dshell/DISCORD_COMMANDS/utils/utils_embed.py +0 -0
  25. {dshellinterpreter-1.2.7.11 → dshellinterpreter-1.2.8.0}/Dshell/DISCORD_COMMANDS/utils/utils_file.py +0 -0
  26. {dshellinterpreter-1.2.7.11 → dshellinterpreter-1.2.8.0}/Dshell/DISCORD_COMMANDS/utils/utils_global.py +0 -0
  27. {dshellinterpreter-1.2.7.11 → dshellinterpreter-1.2.8.0}/Dshell/DISCORD_COMMANDS/utils/utils_list.py +0 -0
  28. {dshellinterpreter-1.2.7.11 → dshellinterpreter-1.2.8.0}/Dshell/DISCORD_COMMANDS/utils/utils_member.py +0 -0
  29. {dshellinterpreter-1.2.7.11 → dshellinterpreter-1.2.8.0}/Dshell/DISCORD_COMMANDS/utils/utils_message.py +0 -0
  30. {dshellinterpreter-1.2.7.11 → dshellinterpreter-1.2.8.0}/Dshell/DISCORD_COMMANDS/utils/utils_numbers.py +0 -0
  31. {dshellinterpreter-1.2.7.11 → dshellinterpreter-1.2.8.0}/Dshell/DISCORD_COMMANDS/utils/utils_permissions.py +0 -0
  32. {dshellinterpreter-1.2.7.11 → dshellinterpreter-1.2.8.0}/Dshell/DISCORD_COMMANDS/utils/utils_string.py +0 -0
  33. {dshellinterpreter-1.2.7.11 → dshellinterpreter-1.2.8.0}/Dshell/DISCORD_COMMANDS/utils/utils_thread.py +0 -0
  34. {dshellinterpreter-1.2.7.11 → dshellinterpreter-1.2.8.0}/Dshell/DISCORD_COMMANDS/utils/utils_type_validation.py +0 -0
  35. {dshellinterpreter-1.2.7.11 → dshellinterpreter-1.2.8.0}/Dshell/DshellInterpreteur/cached_messages.py +0 -0
  36. {dshellinterpreter-1.2.7.11 → dshellinterpreter-1.2.8.0}/Dshell/DshellInterpreteur/dshell_arguments.py +0 -0
  37. {dshellinterpreter-1.2.7.11 → dshellinterpreter-1.2.8.0}/Dshell/DshellInterpreteur/dshell_iterators.py +0 -0
  38. {dshellinterpreter-1.2.7.11 → dshellinterpreter-1.2.8.0}/Dshell/DshellInterpreteur/errors.py +0 -0
  39. {dshellinterpreter-1.2.7.11 → dshellinterpreter-1.2.8.0}/Dshell/DshellInterpreteur/utils_interpreter.py +0 -0
  40. {dshellinterpreter-1.2.7.11 → dshellinterpreter-1.2.8.0}/Dshell/DshellParser/__init__.py +0 -0
  41. {dshellinterpreter-1.2.7.11 → dshellinterpreter-1.2.8.0}/Dshell/DshellParser/errors.py +0 -0
  42. {dshellinterpreter-1.2.7.11 → dshellinterpreter-1.2.8.0}/Dshell/DshellPreProcess/__init__.py +0 -0
  43. {dshellinterpreter-1.2.7.11 → dshellinterpreter-1.2.8.0}/Dshell/DshellPreProcess/dshell_preprocess.py +0 -0
  44. {dshellinterpreter-1.2.7.11 → dshellinterpreter-1.2.8.0}/Dshell/DshellTokenizer/__init__.py +0 -0
  45. {dshellinterpreter-1.2.7.11 → dshellinterpreter-1.2.8.0}/Dshell/DshellTokenizer/dshell_tokenizer.py +0 -0
  46. {dshellinterpreter-1.2.7.11 → dshellinterpreter-1.2.8.0}/Dshell/__init__.py +0 -0
  47. {dshellinterpreter-1.2.7.11 → dshellinterpreter-1.2.8.0}/Dshell/full_import.py +0 -0
  48. {dshellinterpreter-1.2.7.11 → dshellinterpreter-1.2.8.0}/LICENSE +0 -0
  49. {dshellinterpreter-1.2.7.11 → dshellinterpreter-1.2.8.0}/README.md +0 -0
  50. {dshellinterpreter-1.2.7.11 → dshellinterpreter-1.2.8.0}/dshellInterpreter.egg-info/SOURCES.txt +0 -0
  51. {dshellinterpreter-1.2.7.11 → dshellinterpreter-1.2.8.0}/dshellInterpreter.egg-info/dependency_links.txt +0 -0
  52. {dshellinterpreter-1.2.7.11 → dshellinterpreter-1.2.8.0}/dshellInterpreter.egg-info/requires.txt +0 -0
  53. {dshellinterpreter-1.2.7.11 → dshellinterpreter-1.2.8.0}/dshellInterpreter.egg-info/top_level.txt +0 -0
  54. {dshellinterpreter-1.2.7.11 → 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',
@@ -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.11
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.11
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.11",
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
-