dshellInterpreter 1.2.7.2__tar.gz → 1.2.7.4__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.2 → dshellinterpreter-1.2.7.4}/Dshell/DISCORD_COMMANDS/utils/utils_type_validation.py +1 -1
  2. {dshellinterpreter-1.2.7.2 → dshellinterpreter-1.2.7.4}/Dshell/DshellInterpreteur/dshell_interpreter.py +2 -2
  3. dshellinterpreter-1.2.7.4/Dshell/DshellPreProcess/dshell_preprocess.py +73 -0
  4. {dshellinterpreter-1.2.7.2 → dshellinterpreter-1.2.7.4}/Dshell/DshellTokenizer/dshell_keywords.py +21 -8
  5. {dshellinterpreter-1.2.7.2 → dshellinterpreter-1.2.7.4}/Dshell/DshellTokenizer/dshell_tokenizer.py +66 -87
  6. {dshellinterpreter-1.2.7.2 → dshellinterpreter-1.2.7.4}/Dshell/full_import.py +1 -0
  7. {dshellinterpreter-1.2.7.2/dshellInterpreter.egg-info → dshellinterpreter-1.2.7.4}/PKG-INFO +1 -1
  8. {dshellinterpreter-1.2.7.2 → dshellinterpreter-1.2.7.4/dshellInterpreter.egg-info}/PKG-INFO +1 -1
  9. {dshellinterpreter-1.2.7.2 → dshellinterpreter-1.2.7.4}/setup.py +1 -1
  10. dshellinterpreter-1.2.7.2/Dshell/DshellPreProcess/dshell_preprocess.py +0 -38
  11. {dshellinterpreter-1.2.7.2 → dshellinterpreter-1.2.7.4}/Dshell/DISCORD_COMMANDS/__init__.py +0 -0
  12. {dshellinterpreter-1.2.7.2 → dshellinterpreter-1.2.7.4}/Dshell/DISCORD_COMMANDS/dshell_channel.py +0 -0
  13. {dshellinterpreter-1.2.7.2 → dshellinterpreter-1.2.7.4}/Dshell/DISCORD_COMMANDS/dshell_embed.py +0 -0
  14. {dshellinterpreter-1.2.7.2 → dshellinterpreter-1.2.7.4}/Dshell/DISCORD_COMMANDS/dshell_file.py +0 -0
  15. {dshellinterpreter-1.2.7.2 → dshellinterpreter-1.2.7.4}/Dshell/DISCORD_COMMANDS/dshell_interaction.py +0 -0
  16. {dshellinterpreter-1.2.7.2 → dshellinterpreter-1.2.7.4}/Dshell/DISCORD_COMMANDS/dshell_member.py +0 -0
  17. {dshellinterpreter-1.2.7.2 → dshellinterpreter-1.2.7.4}/Dshell/DISCORD_COMMANDS/dshell_message.py +0 -0
  18. {dshellinterpreter-1.2.7.2 → dshellinterpreter-1.2.7.4}/Dshell/DISCORD_COMMANDS/dshell_pastbin.py +0 -0
  19. {dshellinterpreter-1.2.7.2 → dshellinterpreter-1.2.7.4}/Dshell/DISCORD_COMMANDS/dshell_role.py +0 -0
  20. {dshellinterpreter-1.2.7.2 → dshellinterpreter-1.2.7.4}/Dshell/DISCORD_COMMANDS/dshell_ui.py +0 -0
  21. {dshellinterpreter-1.2.7.2 → dshellinterpreter-1.2.7.4}/Dshell/DISCORD_COMMANDS/utils/__init__.py +0 -0
  22. {dshellinterpreter-1.2.7.2 → dshellinterpreter-1.2.7.4}/Dshell/DISCORD_COMMANDS/utils/utils_embed.py +0 -0
  23. {dshellinterpreter-1.2.7.2 → dshellinterpreter-1.2.7.4}/Dshell/DISCORD_COMMANDS/utils/utils_file.py +0 -0
  24. {dshellinterpreter-1.2.7.2 → dshellinterpreter-1.2.7.4}/Dshell/DISCORD_COMMANDS/utils/utils_global.py +0 -0
  25. {dshellinterpreter-1.2.7.2 → dshellinterpreter-1.2.7.4}/Dshell/DISCORD_COMMANDS/utils/utils_list.py +0 -0
  26. {dshellinterpreter-1.2.7.2 → dshellinterpreter-1.2.7.4}/Dshell/DISCORD_COMMANDS/utils/utils_member.py +0 -0
  27. {dshellinterpreter-1.2.7.2 → dshellinterpreter-1.2.7.4}/Dshell/DISCORD_COMMANDS/utils/utils_message.py +0 -0
  28. {dshellinterpreter-1.2.7.2 → dshellinterpreter-1.2.7.4}/Dshell/DISCORD_COMMANDS/utils/utils_numbers.py +0 -0
  29. {dshellinterpreter-1.2.7.2 → dshellinterpreter-1.2.7.4}/Dshell/DISCORD_COMMANDS/utils/utils_permissions.py +0 -0
  30. {dshellinterpreter-1.2.7.2 → dshellinterpreter-1.2.7.4}/Dshell/DISCORD_COMMANDS/utils/utils_string.py +0 -0
  31. {dshellinterpreter-1.2.7.2 → dshellinterpreter-1.2.7.4}/Dshell/DISCORD_COMMANDS/utils/utils_thread.py +0 -0
  32. {dshellinterpreter-1.2.7.2 → dshellinterpreter-1.2.7.4}/Dshell/DshellInterpreteur/__init__.py +0 -0
  33. {dshellinterpreter-1.2.7.2 → dshellinterpreter-1.2.7.4}/Dshell/DshellInterpreteur/cached_messages.py +0 -0
  34. {dshellinterpreter-1.2.7.2 → dshellinterpreter-1.2.7.4}/Dshell/DshellInterpreteur/dshell_arguments.py +0 -0
  35. {dshellinterpreter-1.2.7.2 → dshellinterpreter-1.2.7.4}/Dshell/DshellInterpreteur/dshell_global_variables.py +0 -0
  36. {dshellinterpreter-1.2.7.2 → dshellinterpreter-1.2.7.4}/Dshell/DshellInterpreteur/dshell_iterators.py +0 -0
  37. {dshellinterpreter-1.2.7.2 → dshellinterpreter-1.2.7.4}/Dshell/DshellInterpreteur/dshell_scope.py +0 -0
  38. {dshellinterpreter-1.2.7.2 → dshellinterpreter-1.2.7.4}/Dshell/DshellInterpreteur/errors.py +0 -0
  39. {dshellinterpreter-1.2.7.2 → dshellinterpreter-1.2.7.4}/Dshell/DshellInterpreteur/utils_interpreter.py +0 -0
  40. {dshellinterpreter-1.2.7.2 → dshellinterpreter-1.2.7.4}/Dshell/DshellParser/__init__.py +0 -0
  41. {dshellinterpreter-1.2.7.2 → dshellinterpreter-1.2.7.4}/Dshell/DshellParser/ast_nodes.py +0 -0
  42. {dshellinterpreter-1.2.7.2 → dshellinterpreter-1.2.7.4}/Dshell/DshellParser/dshell_parser.py +0 -0
  43. {dshellinterpreter-1.2.7.2 → dshellinterpreter-1.2.7.4}/Dshell/DshellParser/errors.py +0 -0
  44. {dshellinterpreter-1.2.7.2 → dshellinterpreter-1.2.7.4}/Dshell/DshellPreProcess/__init__.py +0 -0
  45. {dshellinterpreter-1.2.7.2 → dshellinterpreter-1.2.7.4}/Dshell/DshellTokenizer/__init__.py +0 -0
  46. {dshellinterpreter-1.2.7.2 → dshellinterpreter-1.2.7.4}/Dshell/DshellTokenizer/dshell_token_type.py +0 -0
  47. {dshellinterpreter-1.2.7.2 → dshellinterpreter-1.2.7.4}/Dshell/__init__.py +0 -0
  48. {dshellinterpreter-1.2.7.2 → dshellinterpreter-1.2.7.4}/LICENSE +0 -0
  49. {dshellinterpreter-1.2.7.2 → dshellinterpreter-1.2.7.4}/README.md +0 -0
  50. {dshellinterpreter-1.2.7.2 → dshellinterpreter-1.2.7.4}/dshellInterpreter.egg-info/SOURCES.txt +0 -0
  51. {dshellinterpreter-1.2.7.2 → dshellinterpreter-1.2.7.4}/dshellInterpreter.egg-info/dependency_links.txt +0 -0
  52. {dshellinterpreter-1.2.7.2 → dshellinterpreter-1.2.7.4}/dshellInterpreter.egg-info/requires.txt +0 -0
  53. {dshellinterpreter-1.2.7.2 → dshellinterpreter-1.2.7.4}/dshellInterpreter.egg-info/top_level.txt +0 -0
  54. {dshellinterpreter-1.2.7.2 → dshellinterpreter-1.2.7.4}/setup.cfg +0 -0
@@ -117,7 +117,7 @@ def _validate_optional_permission(value, param_name: StrNode, command_name: StrN
117
117
  :param command_name: The command name for error messages (optional)
118
118
  :raises TypeError: If the value is not None and not a PermissionNode
119
119
  """
120
- if value is not None and not isinstance(value, PermissionNode):
120
+ if value is not None and not isinstance(value, _MissingSentinel) and not isinstance(value, PermissionNode):
121
121
  raise TypeError(f"[{command_name}] -> {param_name} must be a PermissionNode or None, not {type(value).__name__}")
122
122
 
123
123
 
@@ -88,7 +88,7 @@ class DshellInterpreteur:
88
88
  '__message_add_reaction__': None, # Can be overwritten by add vars_env parameter to get the reaction added on message add event reaction
89
89
  '__message_remove_reaction__': None, # Can be overwritten by add vars_env parameter to get the reaction removed on message remove event reaction
90
90
  '__message_url__': StrNode(message.jump_url) if hasattr(message, 'jump_url') else None,
91
- '__last_message__': IntNode(message.channel.last_message_id),
91
+ '__last_message__': IntNode(message.channel.last_message_id or 1),
92
92
 
93
93
  '__channel__': IntNode(message.channel.id),
94
94
  '__channel_name__': StrNode(message.channel.name),
@@ -102,7 +102,7 @@ class DshellInterpreteur:
102
102
  '__guild_members__': ListNode([IntNode(member.id) for member in message.channel.guild.members], bypass_limit_elt=True, editable=False),
103
103
  '__guild_member_count__': IntNode(message.channel.guild.member_count),
104
104
  '__guild_icon__': StrNode(message.channel.guild.icon.url) if message.channel.guild.icon else None,
105
- '__guild_owner_id__': IntNode(message.channel.guild.owner_id),
105
+ '__guild_owner_id__': IntNode(message.channel.guild.owner_id or 1),
106
106
  '__guild_description__': StrNode(message.channel.guild.description),
107
107
  '__guild_roles__': ListNode([IntNode(role.id) for role in message.channel.guild.roles], bypass_limit_elt=True, editable=False),
108
108
  '__guild_roles_count__': IntNode(len(message.channel.guild.roles)),
@@ -0,0 +1,73 @@
1
+ from ..full_import import sub, MULTILINE, StrEnum, search, compile, DOTALL
2
+
3
+ class PreProcessorInstructions(StrEnum):
4
+ DEFINE = "define"
5
+
6
+ class PreProcessorData:
7
+ def __init__(self, instruction: str, symbol: str, value: str):
8
+ self.instruction = instruction
9
+ self.symbol = symbol
10
+ self.value = value
11
+
12
+ def define(code: str, pre_processor_data: PreProcessorData) -> str:
13
+ """
14
+ Replace all symbol with the current value
15
+ :param code: the code before tokenization
16
+ :param pre_processor_data: all data for the ##define pre-processor
17
+ :return:
18
+ """
19
+ return sub(f"(?<![a-zA-Z]){pre_processor_data.symbol}(?![a-zA-Z])", pre_processor_data.value, code)
20
+
21
+ pre_processor_pattern = compile(r"^\s*## *([a-zA-Z]+) +([a-zA-Z]+)(?: +(.*))?$", flags=MULTILINE|DOTALL)
22
+ def preProcessor(code: list[str]) -> list[str]:
23
+ """
24
+ Execute preprocessor line
25
+ :param code: a list of code line before tokenization
26
+ :return: a new list of string after pre-processor and since pre-processor instructions
27
+ """
28
+ pre_processor_data: list[PreProcessorData] = list()
29
+ new_code: list[str] = []
30
+
31
+ for line in code:
32
+
33
+ line = removeCommentPreProcessor(line)
34
+
35
+ if pre_processor_match := search(pre_processor_pattern, line):
36
+
37
+ instruction: str = pre_processor_match.group(1).lower()
38
+ symbol: str = pre_processor_match.group(2)
39
+ value: str = pre_processor_match.group(3)
40
+ if instruction in PreProcessorInstructions:
41
+ pre_processor_data.append(PreProcessorData(instruction, symbol, value))
42
+
43
+ else:
44
+ # apply pre-processor instructions
45
+ line = applyPreProcessor(line, pre_processor_data)
46
+ new_code.append(line)
47
+
48
+ return new_code
49
+
50
+
51
+
52
+ def applyPreProcessor(code: str, pre_processor_data: list[PreProcessorData]) -> str:
53
+ """
54
+ Apply preprocessor data to code
55
+ :param line: the current line before tokenization
56
+ :param pre_processor_data:
57
+ :return:
58
+ """
59
+ for pre_processor in pre_processor_data:
60
+ match pre_processor.instruction:
61
+ case "define":
62
+ code = define(code, pre_processor)
63
+
64
+ return code
65
+
66
+ def removeCommentPreProcessor(code: str) -> str:
67
+ """
68
+ Remove all comment in the code
69
+ :param code:
70
+ :return:
71
+ """
72
+ # add \ before or in :: like \:: or :\: disable comment. Usable in string to add :: characters sequence.
73
+ return sub(r"(?<!\\):(?<!\\)?:.*", "", code, flags=MULTILINE)
@@ -10,9 +10,10 @@ __all__ = [
10
10
 
11
11
  from ..DISCORD_COMMANDS import *
12
12
 
13
- from ..full_import import Callable
13
+ from ..full_import import Callable, ChainMap
14
14
  from ..DshellInterpreteur.dshell_iterators import IntIterator
15
15
 
16
+
16
17
  dshell_keyword: set[str] = {
17
18
  'if', 'else', 'elif', 'loop', '#end', 'var', '#loop', '#if', 'sleep',
18
19
  'param', '#param', 'code', '#code', 'eval', 'return', 'break',
@@ -174,7 +175,16 @@ Tuple format: (function, precedence, number of operands min, number of operands
174
175
  Optional[index to translate into value])
175
176
  """
176
177
 
177
- dshell_mathematical_operators: dict[str, tuple[Callable, int, int, int, Optional[int]]] = {
178
+ Operator = tuple[
179
+ Callable,
180
+ int,
181
+ int,
182
+ int,
183
+ Optional[int],
184
+ ]
185
+
186
+
187
+ dshell_mathematical_operators: dict[str, Operator] = {
178
188
 
179
189
  r"++": (lambda a: a + 1, 6, 1, 1, None),
180
190
  r"--": (lambda a: a - 1, 6, 1, 1, None),
@@ -197,14 +207,14 @@ dshell_mathematical_operators: dict[str, tuple[Callable, int, int, int, Optional
197
207
 
198
208
  }
199
209
 
200
- dshell_logical_word_operators: dict[str, tuple[Callable, int, int, int, Optional[int]]] = {
210
+ dshell_logical_word_operators: dict[str, Operator] = {
201
211
  r"and": (lambda a, b: bool(a and b), 2, 2, 2, None),
202
212
  r"or": (lambda a, b: bool(a or b), 1, 2, 2, None),
203
213
  r"not": (lambda a: not a, 3, 1, 1, None),
204
214
  r"in": (lambda a, b: a in b, 4, 2, 2, None),
205
215
  }
206
216
 
207
- dshell_logical_operators: dict[str, tuple[Callable, int, int, int, Optional[int]]] = {
217
+ dshell_logical_operators: dict[str, Operator] = {
208
218
 
209
219
  r"==": (lambda a, b: a == b, 4, 2, 2, None),
210
220
  r"<=": (lambda a, b: a <= b, 4, 2, 2, None),
@@ -222,10 +232,13 @@ dshell_logical_operators: dict[str, tuple[Callable, int, int, int, Optional[int]
222
232
  r">": (lambda a, b: a > b, 4, 2, 2, None),
223
233
  r"!": (lambda a: not a, 3, 1, 1, None),
224
234
  r"?": (lambda condition, first, second: first if condition else second, 1, 3, 3, 1),
225
- #r".": (lambda target, attribute: target.call(attribute) if hasattr(target, attribute) and hasattr(target, 'call') else None, 9, 2, 2), # attribute access operator
226
-
227
235
  }
228
236
 
229
- dshell_operators: dict[str, tuple[Callable, int, int, int, Optional[int]]] = dshell_logical_operators.copy()
237
+ dshell_operators: ChainMap[str, Operator] = ChainMap(
238
+ dshell_logical_operators,
239
+ dshell_logical_word_operators,
240
+ dshell_mathematical_operators)
241
+
242
+ """dshell_operators: dict[str, tuple[Callable, int, int, int, Optional[int]]] = dshell_logical_operators.copy()
230
243
  dshell_operators.update(dshell_logical_word_operators)
231
- dshell_operators.update(dshell_mathematical_operators)
244
+ dshell_operators.update(dshell_mathematical_operators)"""
@@ -7,16 +7,15 @@ __all__ = [
7
7
  from .dshell_token_type import Token
8
8
  from .dshell_token_type import DshellTokenType as DTT
9
9
 
10
- from Dshell.full_import import (Optional,
10
+ from Dshell.full_import import (
11
11
  Pattern,
12
12
  ASCII,
13
13
  DOTALL,
14
14
  IGNORECASE,
15
- MULTILINE,
16
15
  compile,
17
16
  escape,
18
- findall,
19
17
  finditer,
18
+ Match,
20
19
  sub)
21
20
 
22
21
  from .dshell_keywords import (dshell_keyword,
@@ -26,7 +25,7 @@ from .dshell_keywords import (dshell_keyword,
26
25
  dshell_logical_operators,
27
26
  dshell_logical_word_operators)
28
27
 
29
- from ..DshellPreProcess.dshell_preprocess import preProcessor, applyPreProcessor, PreProcessorData
28
+ from ..DshellPreProcess.dshell_preprocess import preProcessor
30
29
 
31
30
  MASK_CHARACTER = '§'
32
31
 
@@ -36,24 +35,23 @@ def is_line_empty(line: str) -> bool:
36
35
  :param line: The line to check.
37
36
  :return: True if the line is empty, False otherwise.
38
37
  """
39
- return all(c in (MASK_CHARACTER, ' ') for c in line)
38
+ return all(c in (MASK_CHARACTER, ' ', '\n') for c in line)
40
39
 
41
40
  table_regex: dict[DTT, Pattern] = {
42
- DTT.COMMENT: compile(r"::(.*)", flags=MULTILINE),
43
41
  DTT.STR: compile(r'"((?:[^\\"]|\\.)*)"', flags=DOTALL),
44
42
  DTT.EVAL_L_EXPRESSION: compile(r"({)"),
45
43
  DTT.EVAL_R_EXPRESSION: compile(r"(})"),
46
- DTT.EVAL_L_GROUP: compile(r"(\()"),
44
+ DTT.EVAL_L_GROUP: compile(rf"(\()"),
47
45
  DTT.EVAL_R_GROUP: compile(r"(\))"),
48
46
  DTT.EVAL_OUTDATED_GROUP: compile(r"(`)"),
49
47
  DTT.LIST_L: compile(r"(\[)"),
50
48
  DTT.LIST_R: compile(r"(])"),
51
49
  DTT.PARAMETERS: compile(rf"--\*\s*([A-Za-z_]+)\s*", flags=ASCII),
52
- DTT.STR_PARAMETER: compile(rf"--\'\s*([A-Za-z_]+)\s*", flags=ASCII),
50
+ DTT.STR_PARAMETER: compile(rf"--'\s*([A-Za-z_]+)\s*", flags=ASCII),
53
51
  DTT.PARAMETER: compile(rf"--\s*([A-Za-z_]+)\s*", flags=ASCII),
54
- DTT.MENTION: compile(r'<(?:@!?|@&|#)([0-9]+)>'),
52
+ DTT.MENTION: compile(r'<(?:@[!&]?|#)([0-9]+)>'),
55
53
  DTT.KEYWORD: compile(rf"(?<!\w)(#?{'|'.join(dshell_keyword)})(?!\w)"),
56
- DTT.DISCORD_KEYWORD: compile(rf"(?<!\w|-)(#?{'|'.join(dshell_discord_keyword)})(?!\w|-)", flags=IGNORECASE),
54
+ DTT.DISCORD_KEYWORD: compile(rf"(?<![\w\-])(#?{'|'.join(dshell_discord_keyword)})(?![\w\-])" , flags=IGNORECASE),
57
55
  DTT.COMMAND: compile(rf"\b({'|'.join(dshell_commands.keys())})\b", flags=IGNORECASE),
58
56
  DTT.FLOAT: compile(r"(\d+\.\d+)"),
59
57
  DTT.HEXA: compile(r"(0[Xx][0-9a-fA-F]+)"),
@@ -65,21 +63,19 @@ table_regex: dict[DTT, Pattern] = {
65
63
  DTT.BOOL: compile(r"(True|False)", flags=IGNORECASE),
66
64
  DTT.NONE: compile(r"(None)", flags=IGNORECASE),
67
65
  DTT.IDENT: compile(rf"([A-Za-z0-9_]+)"),
68
- DTT.ANY_CHARACTER: compile(rf"([^{MASK_CHARACTER}\n]+)"),
69
66
  }
70
67
 
68
+ backslash_pattern = compile(r"\\(.)", flags=DOTALL)
71
69
 
72
70
  class DshellTokenizer:
73
71
 
74
- def __init__(self, code: str, match_any_character: bool = False):
72
+ def __init__(self, code: str):
75
73
  """
76
74
  Initialize the tokenizer.
77
75
  :param code: The code to tokenize
78
76
  :param match_any_character: Whether to match any character
79
77
  """
80
78
  self.code: str = code
81
- self.match_any_character: bool = match_any_character
82
- self.data_pre_processor: list[PreProcessorData] = []
83
79
 
84
80
  def start(self):
85
81
  """
@@ -87,6 +83,7 @@ class DshellTokenizer:
87
83
  Returns an array of tokens per line (normally separated by \\n)
88
84
  """
89
85
  split_commands = self.split(self.code)
86
+ split_commands = preProcessor(split_commands)
90
87
  return self.tokenizer(split_commands)
91
88
 
92
89
  def tokenizer(self, command_lines: list[str]) -> list[list[Token]]:
@@ -95,73 +92,47 @@ class DshellTokenizer:
95
92
  :param command_lines: The code separated into multiple lines by the split method
96
93
  """
97
94
  tokens: list[list[Token]] = []
95
+ ident_tokens = 0
98
96
 
99
- line_number = 1
100
- for line in command_lines: # iterate each line of code
101
- tokens_per_line: list[Token] = []
102
- is_comment: bool = False
97
+ for line_number, line in enumerate(command_lines, start=1):
98
+ line: str = command_lines[line_number-1] # get the current line
103
99
 
100
+ # if the line is empty or already tokenized, pass the line.
104
101
  if is_line_empty(line):
105
- line_number += 1
106
102
  continue
107
103
 
108
- if pre_processor_data := preProcessor(line):
109
- self.data_pre_processor.append(pre_processor_data)
110
- continue
111
- else:
112
- if self.data_pre_processor:
113
- for processor_data in self.data_pre_processor:
114
- line = applyPreProcessor(line, processor_data)
115
-
116
- for token_type, pattern in table_regex.items(): # iterate the regex table to test all patterns on the line
117
-
118
- if is_comment:
119
- is_comment = False
120
- break
121
-
122
- if is_line_empty(line):
123
- break
104
+ tokens.append([])
105
+ tokens_per_line = tokens[ident_tokens]
124
106
 
125
- if not self.match_any_character and token_type == DTT.ANY_CHARACTER:
126
- continue
107
+ for token_type, regex in table_regex.items():
108
+ for match in finditer(regex, line):
109
+ match: Match
127
110
 
128
- for match in finditer(pattern, line): # iterate the match results to get their positions
111
+ start_match = match.start()
112
+ end_match = match.end()
129
113
 
130
- if token_type == DTT.COMMENT: # if we encounter a comment, stop tokenizing the line
131
- if len(match.group(0)) == len(line):
132
- is_comment = True
133
- break
134
- else:
135
- line = line[:match.start()]
136
- continue
137
-
138
- start_match = match.start() # start position of the match
139
-
140
- token = Token(token_type, match.group(1), (line_number, start_match)) # record its token
114
+ token = Token(token_type, match.group(1), (line_number, start_match))
141
115
  tokens_per_line.append(token)
142
116
 
143
117
  if token_type == DTT.STR:
144
- token.value = token.value.replace(r'\"', '"')
145
-
146
- len_match = len(match.group(0)) # length of the match found
147
- line = line[:start_match] + (MASK_CHARACTER * len_match) + line[
148
- match.end():] # replace the match to avoid matching it a second time
149
-
150
- tokens_per_line.sort(key=lambda
151
- token: token.position[1]) # sort the position based on token match positions to have them in code order
152
- if tokens_per_line:
118
+ token.value = sub(backslash_pattern, lambda m: escape(m.group(1)), token.value)
153
119
 
154
- tokens_per_line = self.parse_group(tokens_per_line,
155
- line_number) # parse list in the current line to regroup them in a single token with all the elements of the list as value
120
+ len_match = len(match.group(0))
121
+ line = line[:start_match] + MASK_CHARACTER * len_match + line[end_match:]
156
122
 
157
- tokens.append(tokens_per_line)
123
+ tokens_per_line.sort(key=lambda t: t.position[1])
158
124
 
159
- line_number += 1 # increment the line number for the next line
125
+ if tokens_per_line:
126
+ tokens[ident_tokens] = self.parse_group(tokens_per_line, line_number)
160
127
 
128
+ ident_tokens += 1
161
129
  return tokens
162
130
 
163
131
  @staticmethod
164
- def split(command: str, global_split='\n', keep_grouping_character=True, grouping_character='"') -> list[
132
+ def split(command: str,
133
+ global_split='\n',
134
+ keep_grouping_character=True,
135
+ grouping_character='"') -> list[
165
136
  str]:
166
137
  """
167
138
  Separate commands into a list while respecting strings between quotes.
@@ -173,31 +144,39 @@ class DshellTokenizer:
173
144
  :return: A list of split commands with restored strings.
174
145
  """
175
146
 
176
- commands: str = command.strip()
177
- temporary_replacement = '[REPLACE]'
178
- pattern_find_regrouped_part = compile(fr'({grouping_character}(?:[^\\{grouping_character}]|\\.)*{grouping_character})', flags=DOTALL)
179
- between_grouping_character = findall(pattern_find_regrouped_part, commands) # find parts between quotes and save them
180
-
181
- res = sub(pattern_find_regrouped_part,
182
- temporary_replacement,
183
- commands,
184
- ) # replace parts between quotes
185
-
186
- res = res.split(global_split) # split commands without quotes
187
-
188
- # restore quotes to their place
189
- result = []
190
- for i in res:
191
- while temporary_replacement in i:
192
- i = i.replace(temporary_replacement,
193
- between_grouping_character[0][1: -1] if not keep_grouping_character else
194
- between_grouping_character[0], 1)
195
- between_grouping_character.pop(0)
196
- result.append(i)
147
+ result: list[str] = []
148
+ in_string: bool = False # True if we are in a string
149
+ backslash: bool = False
150
+ line: list[str] = [] # buffer for each line
151
+
152
+ for char in command:
153
+
154
+ if backslash:
155
+ line.append('\\')
156
+ line.append(char)
157
+ backslash = False
158
+
159
+ elif char == global_split and not in_string:
160
+ result.append(''.join(line))
161
+ line.clear()
162
+
163
+ elif char == grouping_character:
164
+ in_string = not in_string
165
+ if keep_grouping_character:
166
+ line.append(char)
167
+
168
+ elif char == '\\':
169
+ backslash = True
170
+
171
+ else:
172
+ line.append(char)
173
+
174
+ result.append(''.join(line))
175
+
197
176
  return result
198
177
 
199
178
  @staticmethod
200
- def parse_group(line: list[Token],
179
+ def parse_group(tokens_in_line: list[Token],
201
180
  line_position: int):
202
181
  """
203
182
  Parse all list in the current token line and return the modified list line.
@@ -225,8 +204,8 @@ class DshellTokenizer:
225
204
  last_tokens[-1].value.append(token)
226
205
 
227
206
  i = 0
228
- while i < len(line):
229
- token = line[i]
207
+ while i < len(tokens_in_line):
208
+ token = tokens_in_line[i]
230
209
 
231
210
  if token.type == DTT.LIST_L:
232
211
  if last_tokens:
@@ -15,3 +15,4 @@ from contextvars import ContextVar
15
15
  from re import *
16
16
  from random import randint, choice, random
17
17
  from enum import Enum, auto, StrEnum
18
+ from collections import ChainMap
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dshellInterpreter
3
- Version: 1.2.7.2
3
+ Version: 1.2.7.4
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.2
3
+ Version: 1.2.7.4
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.2",
8
+ version="1.2.7.4",
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,38 +0,0 @@
1
- from ..full_import import search, sub, Optional
2
-
3
- class PreProcessorData:
4
- def __init__(self, instruction: str, symbol: str, value: str):
5
- self.instruction = instruction
6
- self.symbol = symbol
7
- self.value = value
8
-
9
- def define(code, pre_processor_data: PreProcessorData) -> str:
10
- """
11
- Replace all symbol with the current value
12
- :param symbol:
13
- :param value:
14
- :return:
15
- """
16
- return sub(f"(?<![a-zA-Z]){pre_processor_data.symbol}(?![a-zA-Z])", pre_processor_data.value, code)
17
-
18
- def preProcessor(code: str) -> Optional[PreProcessorData]:
19
- """
20
- Execute preprocessor code
21
- :param code:
22
- :return:
23
- """
24
- pre_process_match = search(r"^\s*##([a-zA-Z]+)\s+([a-zA-Z]+)\s+(.*)$", code)
25
- if pre_process_match:
26
- return PreProcessorData(pre_process_match.group(1).lower(), pre_process_match.group(2), pre_process_match.group(3))
27
- return None
28
-
29
- def applyPreProcessor(code: str, pre_processor_data: PreProcessorData):
30
- """
31
- Apply preprocessor data to code
32
- :param code:
33
- :param pre_processor_data:
34
- :return:
35
- """
36
- match pre_processor_data.instruction:
37
- case "define":
38
- return define(code, pre_processor_data)