dshellInterpreter 0.1.0__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 dshellInterpreter might be problematic. Click here for more details.

@@ -0,0 +1,2 @@
1
+ from .dshell_channel import *
2
+ from .dshell_message import *
@@ -0,0 +1,60 @@
1
+ from asyncio import sleep
2
+ from re import search
3
+
4
+ from discord import MISSING
5
+ from discord.abc import GuildChannel
6
+
7
+ __all__ = [
8
+ 'dshell_create_text_channel',
9
+ 'dshell_delete_channel',
10
+ 'dshell_delete_channels'
11
+ ]
12
+
13
+ async def dshell_create_text_channel(ctx: GuildChannel, name, category=None, position=MISSING, slowmode=MISSING,
14
+ topic=MISSING, nsfw=MISSING):
15
+ """
16
+ Crée un salon textuel sur le serveur
17
+ """
18
+
19
+ channel_category = ctx.guild.get_channel(category)
20
+
21
+ created_channel = await ctx.guild.create_text_channel(name,
22
+ category=channel_category,
23
+ position=position,
24
+ slowmode_delay=slowmode,
25
+ topic=topic,
26
+ nsfw=nsfw)
27
+
28
+ return created_channel.id
29
+
30
+
31
+ async def dshell_delete_channel(ctx: GuildChannel, channel=None, reason=None, timeout=0):
32
+ """
33
+ Supprime un salon.
34
+ Possibilité de lui rajouter un temps d'attente avant qu'il ne le supprime (en seconde)
35
+ """
36
+
37
+ channel_to_delete = ctx if channel is None else ctx.guild.get_channel(channel)
38
+
39
+ if channel_to_delete is None:
40
+ raise Exception(f"Le channel {channel} n'existe pas !")
41
+
42
+ await sleep(timeout)
43
+
44
+ await channel_to_delete.delete(reason=reason)
45
+
46
+ return channel_to_delete.id
47
+
48
+
49
+ async def dshell_delete_channels(ctx: GuildChannel, name=None, regex=None, reason=None):
50
+ """
51
+ Supprime tous les salons ayant le même nom et/ou le même regex.
52
+ Si aucun des deux n'est mis, il supprimera tous les salons comportant le même nom que celui ou a été fait la commande
53
+ """
54
+ for channel in ctx.guild.channels:
55
+
56
+ if name is not None and channel.name == str(name):
57
+ await channel.delete(reason=reason)
58
+
59
+ elif regex is not None and search(regex, channel.name):
60
+ await channel.delete(reason=reason)
@@ -0,0 +1,61 @@
1
+ from discord import Embed
2
+ from discord.abc import GuildChannel
3
+
4
+ from .._DshellParser.ast_nodes import ListNode
5
+
6
+ __all__ = [
7
+ 'dshell_send_message',
8
+ 'dshell_delete_message',
9
+ 'dshell_purge_message'
10
+ ]
11
+
12
+ async def dshell_send_message(ctx: GuildChannel, message=None, delete=None, channel=None, embeds: ListNode = None,
13
+ embed=None):
14
+ """
15
+ Envoie un message sur Discord
16
+ """
17
+ channel_to_send = ctx if channel is None else ctx.guild.get_channel(channel)
18
+
19
+ if channel_to_send is None:
20
+ raise Exception(f'Le channel {channel} est introuvable !')
21
+
22
+ if embeds is None:
23
+ embeds = ListNode([])
24
+
25
+ elif isinstance(embeds, Embed):
26
+ embeds = ListNode([embeds])
27
+
28
+ if embed is not None and isinstance(embed, Embed):
29
+ embeds.add(embed)
30
+
31
+ sended_message = await channel_to_send.send(message,
32
+ delete_after=delete,
33
+ embeds=embeds)
34
+
35
+ return sended_message.id
36
+
37
+
38
+ async def dshell_delete_message(ctx: GuildChannel, message, reason=None, delay=0):
39
+ """
40
+ Supprime un message
41
+ """
42
+
43
+ delete_message = ctx.get_partial_message(message) # construit une référence au message (même s'il n'existe pas)
44
+
45
+ if delay > 3600:
46
+ raise Exception(f'Le délait de suppression du message est trop grand ! ({delay} secondes)')
47
+
48
+ await delete_message.delete(delay=delay, reason=reason)
49
+
50
+
51
+ async def dshell_purge_message(ctx: GuildChannel, message_number, channel=None, reason=None):
52
+ """
53
+ Purge les messages d'un salon
54
+ """
55
+
56
+ purge_channel = ctx if channel is None else ctx.guild.get_channel(channel)
57
+
58
+ if purge_channel is None:
59
+ raise Exception(f"Le salon {channel} à purgé est introuvable !")
60
+
61
+ await purge_channel.purge(limit=message_number, reason=reason)
@@ -0,0 +1 @@
1
+ from .dshell_interpreter import *
@@ -0,0 +1,267 @@
1
+ from asyncio import sleep
2
+ from re import findall
3
+ from typing import TypeVar, Union, Any, Optional, Callable
4
+
5
+ from discord import AutoShardedBot, Embed, Colour
6
+ from discord.abc import GuildChannel, PrivateChannel
7
+
8
+ from .._DshellTokenizer.dshell_keywords import *
9
+ from .._DshellParser.ast_nodes import *
10
+ from .._DshellParser.dshell_parser import parse
11
+ from .._DshellParser.dshell_parser import to_postfix, print_ast
12
+ from .._DshellTokenizer.dshell_token_type import DshellTokenType as DTT
13
+ from .._DshellTokenizer.dshell_token_type import Token
14
+ from .._DshellTokenizer.dshell_tokenizer import DshellTokenizer
15
+
16
+ All_nodes = TypeVar('All_nodes', IfNode, LoopNode, ElseNode, ElifNode, ArgsCommandNode, VarNode, IdentOperationNode)
17
+ context = TypeVar('context', AutoShardedBot, GuildChannel, PrivateChannel)
18
+
19
+
20
+ class DshellInterpreteur:
21
+
22
+ def __init__(self, ast_or_code: Union[list[All_nodes], str], ctx: context, auto=False, debug: bool = False):
23
+ """
24
+ Interpreter Dshell code or AST.
25
+ """
26
+ self.ast: StartNode = parse(DshellTokenizer(ast_or_code).start(), StartNode([]))[0][0]
27
+ self.env: dict[str, Any] = {}
28
+ self.ctx: context = ctx
29
+ if debug:
30
+ print_ast(self.ast)
31
+
32
+ async def execute(self, ast: Optional[list[All_nodes]] = None):
33
+ """
34
+ Execute l'arbre syntaxique.
35
+ """
36
+
37
+ for node in ast:
38
+
39
+ if isinstance(node, StartNode):
40
+ await self.execute(node.body)
41
+
42
+ if isinstance(node, CommandNode):
43
+ self.env['__cr__'] = await call_function(dshell_commands[node.name], node.body, self)
44
+
45
+ elif isinstance(node, IfNode):
46
+ elif_valid = False
47
+ if eval_expression(node.condition, self):
48
+ await self.execute(node.body)
49
+ return
50
+ elif node.elif_nodes:
51
+
52
+ for i in node.elif_nodes:
53
+ if eval_expression(i.condition, self):
54
+ await self.execute(i.body)
55
+ elif_valid = True
56
+ break
57
+
58
+ if not elif_valid and node.else_body is not None:
59
+ await self.execute(node.else_body.body)
60
+
61
+ elif isinstance(node, LoopNode):
62
+ self.env[node.variable.name.value] = 0
63
+ for i in DshellIterator(eval_expression(node.variable.body, self)):
64
+ self.env[node.variable.name.value] = i
65
+ await self.execute(node.body)
66
+
67
+ elif isinstance(node, VarNode):
68
+
69
+ first_node = node.body[0]
70
+ if isinstance(first_node, IfNode):
71
+ self.env[node.name.value] = eval_expression_inline(first_node, self)
72
+
73
+ elif isinstance(first_node, EmbedNode):
74
+ self.env[node.name.value] = build_embed(first_node.body, first_node.fields, self)
75
+
76
+ else:
77
+ self.env[node.name.value] = eval_expression(node.body, self)
78
+
79
+ elif isinstance(node, IdentOperationNode):
80
+ function = self.eval_data_token(node.function)
81
+ listNode = self.eval_data_token(node.ident)
82
+ if hasattr(listNode, function):
83
+ getattr(listNode, function)(self.eval_data_token(node.args))
84
+
85
+ elif isinstance(node, SleepNode):
86
+ sleep_time = eval_expression(node.body, self)
87
+ if sleep_time > 3600:
88
+ raise Exception(f'Le temps maximal de sommeil est de 3600 secondes !')
89
+ elif sleep_time < 1:
90
+ raise Exception(f'Le temps minimal de sommeil est de 1 seconde !')
91
+
92
+ await sleep(sleep_time)
93
+
94
+ elif isinstance(node, EndNode):
95
+ raise RuntimeError(f"Execution interromput -> #end atteint")
96
+
97
+ def eval_data_token(self, token: Token):
98
+ """
99
+ Evalue les tokens de data
100
+ """
101
+
102
+ if not hasattr(token, 'type'):
103
+ return token
104
+
105
+ if token.type in (DTT.INT, DTT.MENTION):
106
+ return int(token.value)
107
+ elif token.type == DTT.FLOAT:
108
+ return float(token.value)
109
+ elif token.type == DTT.BOOL:
110
+ return token.value.lower() == "true"
111
+ elif token.type == DTT.LIST:
112
+ return ListNode(
113
+ [self.eval_data_token(tok) for tok in token.value]) # token.value contient déjà une liste de Token
114
+ elif token.type == DTT.IDENT:
115
+ if token.value in self.env.keys():
116
+ return self.env[token.value]
117
+ return token.value
118
+ elif token.type == DTT.CALL_ARGS:
119
+ return (self.eval_data_token(tok) for tok in token.value)
120
+ elif token.type == DTT.STR:
121
+ for match in findall(rf"\$({'|'.join(self.env.keys())})", token.value):
122
+ token.value = token.value.replace('$' + match, str(self.env[match]))
123
+ return token.value
124
+ else:
125
+ return token.value # fallback
126
+
127
+
128
+ def eval_expression_inline(if_node: IfNode, interpreter: DshellInterpreteur) -> Token:
129
+ """
130
+ Evalue une expression en ligne des variables
131
+ """
132
+ if eval_expression(if_node.condition, interpreter):
133
+ return eval_expression(if_node.body, interpreter)
134
+ else:
135
+ return eval_expression(if_node.else_body.body, interpreter)
136
+
137
+
138
+ def eval_expression(tokens: list[Token], interpreter: DshellInterpreteur) -> Any:
139
+ """
140
+ Evalue une expressions arithmétique et logique et renvoie son résultat. Cela peut-être un booléen, un entier, un flottant, une chaîne de caractère ou une liste
141
+ """
142
+ postfix = to_postfix(tokens)
143
+ stack = []
144
+
145
+ for token in postfix:
146
+
147
+ if token.type in {DTT.INT, DTT.FLOAT, DTT.BOOL, DTT.STR, DTT.LIST, DTT.IDENT}:
148
+ stack.append(interpreter.eval_data_token(token))
149
+
150
+ elif token.type in (DTT.MATHS_OPERATOR, DTT.LOGIC_OPERATOR):
151
+ op = token.value
152
+
153
+ if op == "not":
154
+ a = stack.pop()
155
+ result = dshell_operators[op][0](a)
156
+
157
+ else:
158
+ b = stack.pop()
159
+ a = stack.pop()
160
+ result = dshell_operators[op][0](a, b)
161
+
162
+ stack.append(result)
163
+
164
+ else:
165
+ raise SyntaxError(f"Token inattendu en condition: {token}")
166
+
167
+ if len(stack) != 1:
168
+ raise SyntaxError("Condition mal formée")
169
+
170
+ return stack[0]
171
+
172
+
173
+ async def call_function(function: Callable, args: ArgsCommandNode, interpreter: DshellInterpreteur):
174
+ """
175
+ Appelle une fonction avec évaluation des arguments Dshell en valeurs Python
176
+ """
177
+ reformatted = regroupe_commandes(args.body, interpreter)
178
+
179
+ # conversion des args en valeurs Python
180
+ absolute_args = reformatted.pop('*', list())
181
+
182
+ reformatted: dict[str, Token] # ne sert à rien, juste à indiquer ce qu'il contient dorénanvant
183
+
184
+ absolute_args.insert(0, interpreter.ctx)
185
+ keyword_args = {
186
+ key: value for key, value in reformatted.items()
187
+ }
188
+ return await function(*absolute_args, **keyword_args)
189
+
190
+
191
+ def regroupe_commandes(body: list[Token], interpreter: DshellInterpreteur) -> dict[Union[str, Token], list[Token]]:
192
+ """
193
+ Regroupe les arguments de la commande sous la forme d'un dictionnaire python.
194
+ Sachant que l'on peut spécifier le paramètre que l'on souhaite passer via -- suivit du nom du paramètre. Mais ce n'est pas obligatoire !
195
+ Les paramètres non obligatoire seront stocké dans une liste sous la forme de tokens avec comme clé '*'.
196
+ Les autres ayant été spécifié via un séparateur, ils seront sous la forme d'une liste de tokens avec comme clé le token IDENT qui suivra le séparateur pour chaque argument.
197
+ """
198
+ tokens = {'*': []} # les tokens à renvoyer
199
+ current_arg = '*' # les clés des arguments sont les types auquels ils appartiennent. L'* sert à tous les arguments non explicité par un séparateur et un IDENT
200
+ n = len(body)
201
+
202
+ i = 0
203
+ while i < n:
204
+ if body[i].type == DTT.SEPARATOR and body[
205
+ i + 1].type == DTT.IDENT: # On regarde si c'est un séparateur et si le token suivant est un IDENT
206
+ current_arg = body[i + 1].value # on change l'argument actuel. Il sera donc impossible de revenir à l'*
207
+ tokens[current_arg] = '' # on lui crée une paire clé/valeur
208
+ i += 2 # on skip l'IDENT qu'il y a après le séparateur car on vient de le traiter
209
+ else:
210
+ if current_arg == '*':
211
+ tokens[current_arg].append(interpreter.eval_data_token(body[i]))
212
+ else:
213
+ tokens[current_arg] = interpreter.eval_data_token(body[i]) # on ajoute le token à l'argument actuel
214
+ i += 1
215
+ return tokens
216
+
217
+
218
+ def build_embed(body: list[Token], fields: list[FieldEmbedNode], interpreter: DshellInterpreteur) -> Embed:
219
+ """
220
+ Construit un embed à partir des informations de la commande.
221
+ """
222
+ args_main_embed: dict[Union[str, Token], list[Token]] = regroupe_commandes(body, interpreter)
223
+ args_main_embed.pop('*') # on enlève les paramètres non spécifié pour l'embed
224
+ args_main_embed: dict[str, Token] # on précise se qu'il contient dorénavant
225
+
226
+ args_fields: list[dict[str, Token]] = []
227
+ for field in fields: # on fait la même chose pour tous les fields
228
+ a = regroupe_commandes(field.body, interpreter)
229
+ a.pop('*')
230
+ a: dict[str, Token]
231
+ args_fields.append(a)
232
+
233
+ if 'color' in args_main_embed and isinstance(args_main_embed['color'],
234
+ ListNode): # si on passe l'argument de la couleur sous la forme d'une liste RGB
235
+ args_main_embed['color'] = Colour.from_rgb(*args_main_embed['color'])
236
+
237
+ embed = Embed(**args_main_embed) # on construit l'embed principal
238
+ for field in args_fields:
239
+ embed.add_field(**field) # on joute tous les fields
240
+
241
+ return embed
242
+
243
+
244
+ class DshellIterator:
245
+ """
246
+ Utilisé pour transformer n'importe quoi en un iterable
247
+ """
248
+
249
+ def __init__(self, data):
250
+ if isinstance(data, ListNode):
251
+ self.data = data
252
+ else:
253
+ self.data = data if isinstance(data, (str, list)) else range(int(data))
254
+ self.current = 0
255
+
256
+ def __iter__(self):
257
+ return self
258
+
259
+ def __next__(self):
260
+
261
+ if self.current >= len(self.data):
262
+ self.current = 0
263
+ raise StopIteration
264
+
265
+ value = self.data[self.current]
266
+ self.current += 1
267
+ return value
@@ -0,0 +1,2 @@
1
+ from .ast_nodes import *
2
+ from .dshell_parser import *
@@ -0,0 +1,204 @@
1
+ from typing import Optional, Any
2
+
3
+ from .._DshellTokenizer.dshell_token_type import Token
4
+
5
+ __all__ = [
6
+ 'ASTNode',
7
+ 'StartNode',
8
+ 'ElseNode',
9
+ 'ElifNode',
10
+ 'IfNode',
11
+ 'LoopNode',
12
+ 'ArgsCommandNode',
13
+ 'CommandNode',
14
+ 'VarNode',
15
+ 'EndNode',
16
+ 'FieldEmbedNode',
17
+ 'EmbedNode',
18
+ 'SleepNode',
19
+ 'IdentOperationNode',
20
+ 'ListNode'
21
+ ]
22
+
23
+ class ASTNode:
24
+ pass
25
+
26
+
27
+ class StartNode(ASTNode):
28
+ def __init__(self, body: list):
29
+ self.body = body
30
+
31
+ def __repr__(self):
32
+ return f"<Command> - {self.body}"
33
+
34
+
35
+ class ElseNode(ASTNode):
36
+ def __init__(self, body: list[Token]):
37
+ self.body = body
38
+
39
+ def __repr__(self):
40
+ return f"<Else> - {self.body}"
41
+
42
+
43
+ class ElifNode(ASTNode):
44
+ def __init__(self, condition: list[Token], body: list[Token], parent: "IfNode"):
45
+ self.condition = condition
46
+ self.body = body
47
+ self.parent = parent
48
+
49
+ def __repr__(self):
50
+ return f"<Elif> - {self.condition} - {self.body}"
51
+
52
+
53
+ class IfNode(ASTNode):
54
+ def __init__(self, condition: list[Token], body: list[Token], elif_nodes: Optional[list[ElifNode]] = None,
55
+ else_body: Optional[ElseNode] = None):
56
+ self.condition = condition
57
+ self.body = body
58
+ self.elif_nodes = elif_nodes
59
+ self.else_body = else_body
60
+
61
+ def __repr__(self):
62
+ return f"<If> - {self.condition} - {self.body} *- {self.elif_nodes} **- {self.else_body}"
63
+
64
+
65
+ class LoopNode(ASTNode):
66
+ def __init__(self, variable: "VarNode", body: list):
67
+ self.variable = variable # content l'itérable dans son body
68
+ self.body = body
69
+
70
+ def __repr__(self):
71
+ return f"<Loop> - {self.variable.name} -> {self.variable.body} *- {self.body}"
72
+
73
+
74
+ class ArgsCommandNode(ASTNode):
75
+ def __init__(self, body: list[Token]):
76
+ self.body: list[Token] = body
77
+
78
+ def __repr__(self):
79
+ return f"<Args Command> - {self.body}"
80
+
81
+
82
+ class CommandNode(ASTNode):
83
+ def __init__(self, name: str, body: ArgsCommandNode):
84
+ self.name = name
85
+ self.body = body
86
+
87
+ def __repr__(self):
88
+ return f"<{self.name}> - {self.body}"
89
+
90
+
91
+ class VarNode(ASTNode):
92
+ def __init__(self, name: Token, body: list[Token]):
93
+ self.name = name
94
+ self.body = body
95
+
96
+ def __repr__(self):
97
+ return f"<VAR> - {self.name} *- {self.body}"
98
+
99
+
100
+ class EndNode(ASTNode):
101
+ def __init__(self):
102
+ pass
103
+
104
+ def __repr__(self):
105
+ return f"<END>"
106
+
107
+
108
+ class FieldEmbedNode(ASTNode):
109
+ def __init__(self, body: list[Token]):
110
+ self.body: list[Token] = body
111
+
112
+ def __repr__(self):
113
+ return f"<EMBED_FIELD> - {self.body}"
114
+
115
+
116
+ class EmbedNode(ASTNode):
117
+ def __init__(self, body: list[Token], fields: list[FieldEmbedNode]):
118
+ self.body = body
119
+ self.fields = fields
120
+
121
+ def __repr__(self):
122
+ return f"<EMBED> - {self.body}"
123
+
124
+
125
+ class SleepNode(ASTNode):
126
+ def __init__(self, body: list[Token]):
127
+ self.body = body
128
+
129
+ def __repr__(self):
130
+ return f"<SLEEP> - {self.body}"
131
+
132
+
133
+ class IdentOperationNode(ASTNode):
134
+ """
135
+ Gère les opération sur les idendificateur (appel de fonctions)
136
+ Faire en sorte que l'appel de la fonction renvoie la class associé pour permettre les imbrications. Pas obligatoire en soit si elle renvoie quelque chose
137
+ """
138
+
139
+ def __init__(self, ident: Token, function: Token, args: Token):
140
+ self.ident = ident # content la "class"
141
+ self.function = function # contient la méthode appelé
142
+ self.args = args # contient une liste de tokens des arguments passé en paramètre
143
+
144
+ def __repr__(self):
145
+ return f"<IDENT OPERATION> - {self.ident}.{self.function}({self.args})"
146
+
147
+
148
+ class ListNode(ASTNode):
149
+ """
150
+ Class iterable permettant de parcourir les listes créé à partir du code Dshell.
151
+ Cette class permet aussi d'intéragir avec la liste via des méthodes spécifique non built-in par python.
152
+ """
153
+
154
+ def __init__(self, body: list[Any]):
155
+ self.iterable: list[Any] = body
156
+ self.len_iterable: int = len(body)
157
+ self.iterateur_count: int = 0
158
+
159
+ def add(self, value: Any):
160
+ """
161
+ Ajoute un token à la liste
162
+ """
163
+ if self.len_iterable > 10000:
164
+ raise PermissionError('Une liste ne peut dépasser les 10.000 éléments !')
165
+
166
+ self.iterable.append(value)
167
+ self.len_iterable += 1
168
+
169
+ def remove(self, value: Any, number: int = 1):
170
+ """
171
+ Enlève un ou plusieurs token de la liste
172
+ """
173
+ if number < 1:
174
+ raise Exception(f"Le nombre d'élément à retirer doit-être égale ou supperieur à 1 !")
175
+
176
+ def __add__(self, other: "ListNode"):
177
+ for i in other:
178
+ self.add(i)
179
+ return self
180
+
181
+ def __iter__(self):
182
+ return self
183
+
184
+ def __next__(self):
185
+
186
+ if self.iterateur_count >= self.len_iterable:
187
+ self.iterateur_count = 0
188
+ raise StopIteration()
189
+
190
+ v = self.iterable[self.iterateur_count]
191
+ self.iterateur_count += 1
192
+ return v
193
+
194
+ def __len__(self):
195
+ return self.len_iterable
196
+
197
+ def __getitem__(self, item):
198
+ return self.iterable[item]
199
+
200
+ def __bool__(self):
201
+ return bool(self.iterable)
202
+
203
+ def __repr__(self):
204
+ return f"<LIST> - {self.iterable}"
@@ -0,0 +1,308 @@
1
+ __all__ = [
2
+ "parse",
3
+ "parser_inline",
4
+ "to_postfix",
5
+ "parse_postfix_expression",
6
+ "print_ast"
7
+ ]
8
+
9
+ from typing import Union, TYPE_CHECKING
10
+
11
+ from .ast_nodes import (ASTNode,
12
+ CommandNode,
13
+ IfNode,
14
+ LoopNode,
15
+ ElseNode,
16
+ ArgsCommandNode,
17
+ ElifNode,
18
+ VarNode,
19
+ EndNode,
20
+ SleepNode,
21
+ IdentOperationNode,
22
+ EmbedNode,
23
+ FieldEmbedNode)
24
+ from .._DshellTokenizer.dshell_token_type import DshellTokenType as DTT
25
+ from .._DshellTokenizer.dshell_token_type import Token
26
+
27
+ if TYPE_CHECKING:
28
+ from .._DshellTokenizer import dshell_operators
29
+
30
+
31
+ def parse(token_lines: list[list[Token]], start_node: ASTNode) -> tuple[list[ASTNode], int]:
32
+ """
33
+ Commande de base pour parser les tokens
34
+ """
35
+ pointeur = 0 # pointeur sur les listes de tokens pour savoir ou parser
36
+ blocks: list[Union[ASTNode, EndNode]] = [start_node] # liste d'imbrication des blocks pour gérer l'imbrication
37
+ len_token_lines = len(token_lines)
38
+
39
+ while pointeur < len_token_lines:
40
+
41
+ tokens_by_line = token_lines[pointeur] # on récupère la liste de token par rapport au pointeur
42
+ first_token_line = tokens_by_line[0] # on récupère le premier token de la ligne
43
+ last_block = blocks[-1]
44
+
45
+ if first_token_line.type == DTT.COMMAND: # si le token est une comande
46
+ body = tokens_by_line[1:] # on récupère ses arguments
47
+ last_block.body.append(CommandNode(first_token_line.value,
48
+ ArgsCommandNode(body))) # on ajoute la commande au body du dernier bloc
49
+
50
+ ############################## DSHELL KEYWORDS ##############################
51
+
52
+ elif first_token_line.type == DTT.KEYWORD: # si c'est un mot clé
53
+
54
+ if first_token_line.value == 'if': # si c'est une condition
55
+ if_node = IfNode(condition=tokens_by_line[1:],
56
+ body=[]) # on crée la node avec les arguments de condition du if
57
+ last_block.body.append(if_node)
58
+ _, p = parse(token_lines[pointeur + 1:],
59
+ if_node) # on parse le reste du code avec la node if_node comme commancement du nouveau parsing
60
+ pointeur += p + 1 # essentielle pour ne pas parser les lignes déjà faite
61
+
62
+ elif first_token_line.value == '#if':
63
+ if not isinstance(last_block, (IfNode, ElseNode, ElifNode)):
64
+ raise SyntaxError(f'[#IF] Aucun bloc conditionnel ouvert ligne {first_token_line.position} !')
65
+
66
+ if isinstance(last_block, (ElifNode, ElseNode)):
67
+
68
+ while isinstance(last_block, (ElifNode, ElseNode)):
69
+ blocks.pop()
70
+ last_block = blocks[-1]
71
+ blocks.pop()
72
+ return blocks, pointeur
73
+
74
+ elif first_token_line.value == 'elif':
75
+ if not isinstance(last_block, (IfNode, ElifNode)):
76
+ raise SyntaxError(f'[ELIF] Aucun bloc conditionnel ouvert ligne {first_token_line.position} !')
77
+ elif_node = ElifNode(condition=tokens_by_line[1:], body=[],
78
+ parent=last_block if isinstance(last_block, IfNode) else last_block.parent)
79
+
80
+ if isinstance(last_block, ElifNode):
81
+ last_block.parent.elif_nodes.append(elif_node)
82
+ else:
83
+ if last_block.elif_nodes is None:
84
+ last_block.elif_nodes = [elif_node]
85
+ else:
86
+ last_block.elif_nodes.append(elif_node)
87
+
88
+ blocks.append(elif_node)
89
+
90
+ elif first_token_line.value == 'else':
91
+ if not isinstance(last_block, (IfNode, ElifNode)):
92
+ raise SyntaxError(f'[ELSE] Aucun bloc conditionnel ouvert ligne {first_token_line.position} !')
93
+
94
+ if isinstance(last_block, ElseNode) and last_block.else_body is not None:
95
+ raise SyntaxError(f'[ELSE] Déjà définit et n\'accepte pas les doublons dans un même if !')
96
+
97
+ else_node = ElseNode(body=[])
98
+
99
+ if isinstance(last_block, ElifNode): # si le dernier bloc est un elif
100
+ last_block.parent.else_body = else_node # on ajoute le bloc else à son parent (qui est le dernier if)
101
+ else:
102
+ last_block.else_body = else_node # une fois le parsing fini, on l'ajoute au dernier bloc
103
+ blocks.append(else_node)
104
+
105
+ elif first_token_line.value == 'loop':
106
+ loop_node = LoopNode(VarNode(tokens_by_line[1], to_postfix(tokens_by_line[2:])), body=[])
107
+ last_block.body.append(loop_node)
108
+ _, p = parse(token_lines[pointeur + 1:],
109
+ loop_node) # on parse tous ce qu'il y a après l'instruction loop
110
+ pointeur += p + 1
111
+
112
+ elif first_token_line.value == '#loop': # si rencontré
113
+ if not isinstance(last_block, LoopNode):
114
+ raise SyntaxError(f'[#LOOP] Aucune loop ouverte ligne {first_token_line.position} !')
115
+ blocks.pop()
116
+ return blocks, pointeur # on renvoie les informations parsé à la dernière loop ouverte
117
+
118
+ elif first_token_line.value == 'var':
119
+ var_node = VarNode(name=token_lines[pointeur][1], body=[])
120
+ last_block.body.append(var_node)
121
+ result, status = parser_inline(tokens_by_line[
122
+ 2:]) # on fait en sorte de mettre les tokens de la ligne séparé par des retour à la ligne à chaque condition/else
123
+ if status:
124
+ parse(result, var_node) # on parse le tout dans la variable
125
+ else:
126
+ # var_node.body = parse(result, StartNode([]))[0][0].body
127
+ var_node.body = result[0]
128
+
129
+ elif first_token_line.value == 'sleep':
130
+ sleep_node = SleepNode(tokens_by_line[1:])
131
+ last_block.body.append(sleep_node)
132
+
133
+ elif first_token_line.value == '#end': # node pour arrêter le programme si elle est rencontré
134
+ end_node = EndNode()
135
+ last_block.body.append(end_node)
136
+
137
+ ############################## DISCORD KEYWORDS ##############################
138
+
139
+ elif first_token_line.type == DTT.DISCORD_KEYWORD:
140
+
141
+ if first_token_line.value == 'embed':
142
+ embed_node = EmbedNode(body=[], fields=[])
143
+ var_node = VarNode(tokens_by_line[1], body=[embed_node])
144
+ last_block.body.append(var_node)
145
+ _, p = parse(token_lines[pointeur + 1:], embed_node)
146
+ pointeur += p + 1
147
+
148
+ elif first_token_line.value == '#embed':
149
+ if not isinstance(last_block, EmbedNode):
150
+ raise SyntaxError(f'[#EMBED] Aucun embed ouvert ligne {first_token_line.position} !')
151
+ blocks.pop()
152
+ return blocks, pointeur
153
+
154
+ elif first_token_line.value == 'field':
155
+ if not isinstance(last_block, EmbedNode):
156
+ raise SyntaxError(f'[FIELD] Aucun embed ouvert ligne {first_token_line.position} !')
157
+ last_block.fields.append(FieldEmbedNode(tokens_by_line[1:]))
158
+
159
+ ############################## AUTRE ##############################
160
+
161
+ elif first_token_line.type == DTT.IDENT:
162
+ if len(tokens_by_line) == 1:
163
+ last_block.body.append(CommandNode(name='sm', body=ArgsCommandNode([first_token_line])))
164
+
165
+ else:
166
+ last_block.body += parse_postfix_expression(to_postfix(tokens_by_line))
167
+
168
+ elif first_token_line.type == DTT.STR:
169
+ last_block.body.append(CommandNode(name='sm', body=ArgsCommandNode([first_token_line])))
170
+
171
+
172
+ else:
173
+ last_block.body += tokens_by_line
174
+
175
+ pointeur += 1
176
+
177
+ return blocks, pointeur
178
+
179
+
180
+ """
181
+ elif first_token_line.type == DTT.LIST: # si le token est une liste (qui comporte une liste python avec des Tokens)
182
+ list_node = ListNode(first_token_line.value) # le .value est une liste python
183
+ last_block.body.append(list_node)
184
+ """
185
+
186
+
187
+ def parser_inline(tokens: list[Token]) -> tuple[list[list[Token]], bool]:
188
+ """
189
+ Transforme une ligne avec un if/else inline en structure multilignes
190
+ """
191
+ result: list[list[Token]] = []
192
+
193
+ try:
194
+ if_index = next(i for i, tok in enumerate(tokens) if tok.value == 'if')
195
+ else_index = next(i for i, tok in enumerate(tokens) if tok.value == 'else')
196
+ except StopIteration:
197
+ return [tokens], False # ligne normale
198
+
199
+ value_tokens = tokens[:if_index]
200
+ condition_tokens = tokens[if_index + 1:else_index]
201
+ else_tokens = tokens[else_index + 1:]
202
+
203
+ # On génère :
204
+ result.append([tokens[if_index]] + condition_tokens) # ligne "if cond"
205
+ result.append(value_tokens) # body du if
206
+ result.append([tokens[else_index]]) # ligne "else"
207
+ result.append(else_tokens) # body du else
208
+ return result, True
209
+
210
+
211
+ def to_postfix(expression):
212
+ """
213
+ Transforme l'expression en notation postfixée (RPN)
214
+ :param expression: l'expression donné par le tokenizer
215
+ :return: l'expression en notation postfixée
216
+ """
217
+ output = []
218
+ operators: list[Token] = []
219
+
220
+ for token in expression:
221
+ if token.type in (DTT.IDENT, DTT.CALL_ARGS, DTT.INT, DTT.FLOAT, DTT.LIST, DTT.STR): # Si c'est un ident
222
+ output.append(token)
223
+ elif token.value in dshell_operators:
224
+ while (operators and operators[-1].value in dshell_operators and
225
+ dshell_operators[operators[-1].value][1] >= dshell_operators[token.value][1]):
226
+ output.append(operators.pop())
227
+ operators.append(token)
228
+ else:
229
+ raise ValueError(f"Token inconnu : {token}")
230
+
231
+ while operators:
232
+ output.append(operators.pop())
233
+
234
+ return output
235
+
236
+
237
+ def parse_postfix_expression(postfix_tokens: list[Token]) -> list[IdentOperationNode]:
238
+ stack = []
239
+
240
+ for tok in postfix_tokens:
241
+
242
+ if tok.type in (DTT.IDENT, DTT.CALL_ARGS, DTT.INT, DTT.STR, DTT.LIST):
243
+ stack.append(tok)
244
+
245
+ elif tok.type == DTT.MATHS_OPERATOR:
246
+ if tok.value == '.':
247
+ args = stack.pop()
248
+ func = stack.pop()
249
+ base = stack.pop()
250
+ node = IdentOperationNode(ident=base, function=func, args=args)
251
+ stack.append(node)
252
+
253
+ elif tok.value == '->':
254
+ value = stack.pop()
255
+ base = stack.pop()
256
+ fake_func = Token(DTT.IDENT, 'at', tok.position)
257
+ fake_args = Token(DTT.CALL_ARGS, value.value, value.position)
258
+ node = IdentOperationNode(ident=base, function=fake_func, args=fake_args)
259
+ stack.append(node)
260
+
261
+ else:
262
+ raise SyntaxError(f"Opérateur non supporté dans les appels : {tok.value}")
263
+
264
+ else:
265
+ raise SyntaxError(f"Token inattendu : {tok}")
266
+
267
+ if len(stack) != 1:
268
+ raise SyntaxError("Expression mal formée ou incomplète")
269
+
270
+ return stack
271
+
272
+
273
+ def print_ast(ast: ASTNode, decalage: int = 0):
274
+ for i in ast.body:
275
+
276
+ if isinstance(i, LoopNode):
277
+ print(f"{' ' * decalage}LOOP -> {i.variable.name} : {i.variable.body}")
278
+ print_ast(i, decalage + 5)
279
+
280
+ elif isinstance(i, IfNode):
281
+ print(f"{' ' * decalage}IF -> {i.condition}")
282
+ print_ast(i, decalage + 5)
283
+
284
+ if i.elif_nodes is not None:
285
+ for elif_body in i.elif_nodes:
286
+ print(f"{' ' * decalage}ELIF -> {elif_body.condition}")
287
+ print_ast(elif_body, decalage + 5)
288
+
289
+ if i.else_body is not None:
290
+ print(f"{' ' * decalage}ELSE -> ...")
291
+ print_ast(i.else_body, decalage + 5)
292
+
293
+ elif isinstance(i, IdentOperationNode):
294
+ print(f"{' ' * decalage}IDENT_OPERATION -> {i.ident}.{i.function}({i.args})")
295
+
296
+ elif isinstance(i, CommandNode):
297
+ print(f"{' ' * decalage}COMMAND -> {i.name} : {i.body}")
298
+
299
+ elif isinstance(i, VarNode):
300
+ print(f"{' ' * decalage}VAR -> {i.name} : {i.body}")
301
+
302
+ elif isinstance(i, EmbedNode):
303
+ print(f"{' ' * decalage}EMBED :")
304
+ print_ast(i.fields, decalage + 5)
305
+
306
+ elif isinstance(i, FieldEmbedNode):
307
+ for field in i.body:
308
+ print(f"{' ' * decalage}FIELD -> {field.value}")
@@ -0,0 +1,4 @@
1
+ from .dshell_keywords import *
2
+ from .dshell_token_type import DshellTokenType as DTT
3
+ from .dshell_token_type import Token
4
+ from .dshell_tokenizer import DshellTokenizer
@@ -0,0 +1,125 @@
1
+ __all__ = [
2
+ "dshell_keyword",
3
+ "dshell_discord_keyword",
4
+ "dshell_commands",
5
+ "dshell_mathematical_operators",
6
+ "dshell_logical_operators",
7
+ "dshell_operators"
8
+ ]
9
+ from typing import Callable
10
+ from ..DISCORD_COMMANDS.dshell_channel import *
11
+ from ..DISCORD_COMMANDS.dshell_message import *
12
+
13
+ dshell_keyword: set[str] = {
14
+ 'if', 'else', 'elif', 'loop', '#end', 'var', '#loop', '#if', 'sleep'
15
+ }
16
+
17
+ dshell_discord_keyword: set[str] = {
18
+ 'embed', '#embed', 'field'
19
+ }
20
+
21
+ dshell_commands: dict[str, Callable] = {
22
+ "sm": dshell_send_message, # send message
23
+ "dm": dshell_delete_message,
24
+ "pm": dshell_purge_message,
25
+ "cc": dshell_create_text_channel, # create channel
26
+ "dc": dshell_delete_channel, # delete channel
27
+ "dcs": dshell_delete_channels,
28
+ "uc": dshell_send_message,
29
+ # update channel (aura toutes les modifications possible -> servira à ne faire qu'une commande pour modifier plusieurs chose sur le salon)
30
+ "rc": dshell_send_message # rename channel
31
+ }
32
+
33
+ dshell_mathematical_operators: dict[str, tuple[Callable, int]] = {
34
+ r"<": (lambda a, b: a < b, 4),
35
+ r"<=": (lambda a, b: a <= b, 4),
36
+ r"=<": (lambda a, b: a <= b, 4),
37
+ r"=": (lambda a, b: a == b, 4),
38
+ r"!=": (lambda a, b: a != b, 4),
39
+ r"=!": (lambda a, b: a != b, 4),
40
+ r">": (lambda a, b: a > b, 4),
41
+ r">=": (lambda a, b: a >= b, 4),
42
+ r"=>": (lambda a, b: a >= b, 4),
43
+
44
+ r".": (lambda a, b: a.b, 9),
45
+ r"->": (lambda a: a.at, 10), # équivalent à l'appel .at(key)
46
+
47
+ r"+": (lambda a, b: a + b, 6),
48
+ r"-": (lambda a, b=None: -a if b is None else a - b, 6),
49
+ # attention : ambiguïté entre unaire et binaire à traiter dans ton parseur
50
+ r"**": (lambda a, b: a ** b, 8),
51
+ r"*": (lambda a, b: a * b, 7),
52
+ r"%": (lambda a, b: a % b, 7),
53
+ r"//": (lambda a, b: a // b, 7),
54
+ r"/": (lambda a, b: a / b, 7),
55
+ }
56
+
57
+ dshell_logical_operators: dict[str, tuple[Callable, int]] = {
58
+
59
+ r"and": (lambda a, b: bool(a and b), 2),
60
+ r"&": (lambda a, b: a & b, 2),
61
+ r"or": (lambda a, b: bool(a or b), 1),
62
+ r"|": (lambda a, b: a | b, 1),
63
+ r"in": (lambda a, b: a in b, 4),
64
+ r"not": (lambda a: not a, 3),
65
+
66
+ }
67
+
68
+ dshell_operators: dict[str, tuple[Callable, int]] = dshell_logical_operators.copy()
69
+ dshell_operators.update(dshell_mathematical_operators)
70
+
71
+
72
+
73
+ '''
74
+ C_create_var = "var"
75
+ C_obligate_var = "ovar" # rend obligatoire les variables
76
+
77
+ # guild
78
+ C_create_channel = "cc"
79
+ C_create_voice_channel = "cvc"
80
+ C_create_forum_channel = "cfc"
81
+ C_create_category = "cca"
82
+ C_create_role = "cr"
83
+
84
+ C_delete_channel = "dc"
85
+ C_delete_category = "dca"
86
+ C_delete_role = "dr"
87
+
88
+ C_edit_channel = "ec"
89
+ C_edit_voice_channel = "evc"
90
+ C_edit_forum_channel = "efc"
91
+ C_edit_category = "eca"
92
+ C_edit_role = "er"
93
+ C_edit_guild = "eg"
94
+
95
+ # forum
96
+ C_edit_ForumTag = "eft"
97
+ C_create_thread = "ct"
98
+ C_delete_tread = "dt"
99
+
100
+ # member
101
+ C_edit_nickname = "en"
102
+ C_ban_member = "bm"
103
+ C_unban_member = "um"
104
+ C_kick_member = "km"
105
+ C_timeout_member = "tm"
106
+ C_move_member = "mm"
107
+ C_add_roles = "ar"
108
+ C_remove_roles = "rr"
109
+
110
+ # message
111
+ C_send_message = "sm"
112
+ C_respond_message = "rm"
113
+ C_edit_message = "em"
114
+ C_send_user_message = "sum"
115
+ C_delete_message = "dm"
116
+ C_purge_message = "pm"
117
+ C_create_embed = "e"
118
+ C_regex = "regex"
119
+ C_add_emoji = "ae"
120
+ C_remove_emoji = "re"
121
+ C_clear_emoji = "ce"
122
+ C_remove_reaction = "rre"
123
+
124
+ # bouton
125
+ C_create_button = "b"'''
@@ -0,0 +1,39 @@
1
+ from enum import Enum, auto
2
+ from typing import Union
3
+
4
+ __all__ = [
5
+ 'DshellTokenType',
6
+ 'Token',
7
+ 'MASK_CHARACTER'
8
+ ]
9
+
10
+ MASK_CHARACTER = '§'
11
+
12
+
13
+ class DshellTokenType(Enum):
14
+ INT = auto()
15
+ FLOAT = auto()
16
+ STR = auto()
17
+ BOOL = auto()
18
+ LIST = auto()
19
+ CALL_ARGS = auto()
20
+ DICT = auto()
21
+ MENTION = auto()
22
+ IDENT = auto() # nom de variable, fonction
23
+ KEYWORD = auto() # if, let, end, etc.
24
+ DISCORD_KEYWORD = auto() # embed, #embed...
25
+ COMMAND = auto()
26
+ SEPARATOR = auto()
27
+ MATHS_OPERATOR = auto() # ==, +, -, *, etc.
28
+ LOGIC_OPERATOR = auto(),
29
+ COMMENT = auto() # lignes commençant par ##
30
+
31
+
32
+ class Token:
33
+ def __init__(self, type_: "DTT", value: Union[str, list], position: int):
34
+ self.type = type_
35
+ self.value = value
36
+ self.position = position
37
+
38
+ def __repr__(self):
39
+ return f"<{self.type.name} '{self.value}'>"
@@ -0,0 +1,122 @@
1
+ __all__ = [
2
+ "DshellTokenizer",
3
+ "table_regex",
4
+ "MASK_CHARACTER"
5
+ ]
6
+
7
+ from re import DOTALL, IGNORECASE
8
+ from re import compile, Pattern, finditer, escape, sub, findall
9
+
10
+ from .dshell_keywords import *
11
+ from .dshell_token_type import DshellTokenType as DTT
12
+ from .dshell_token_type import Token
13
+
14
+ MASK_CHARACTER = '§'
15
+
16
+ table_regex: dict[DTT, Pattern] = {
17
+ DTT.COMMENT: compile(r"::(.*)"),
18
+ # DTT.DICT: compile(r"\{(.*?)\}"),
19
+ DTT.CALL_ARGS: compile(r"\((.*?)\)"),
20
+ DTT.STR: compile(r"\"(.*?)\"", flags=DOTALL),
21
+ DTT.LIST: compile(r"\[(.*?)\]"),
22
+ DTT.MENTION: compile(r'<(?:@!?|@&|#)([0-9]+)>'),
23
+ DTT.KEYWORD: compile(rf"(?<!\w)(#?{'|'.join(dshell_keyword)})(?!\w)"),
24
+ DTT.DISCORD_KEYWORD: compile(rf"(?<!\w|-)(#?{'|'.join(dshell_discord_keyword)})(?!\w|-)"),
25
+ DTT.SEPARATOR: compile(rf"(--)"),
26
+ DTT.COMMAND: compile(rf"\b({'|'.join(dshell_commands.keys())})\b"),
27
+ DTT.MATHS_OPERATOR: compile(rf"({'|'.join([escape(i) for i in dshell_mathematical_operators.keys()])})"),
28
+ DTT.LOGIC_OPERATOR: compile(rf"(?<!\w)({'|'.join([escape(i) for i in dshell_logical_operators.keys()])})(?<!\w)"),
29
+ DTT.FLOAT: compile(r"(\d+\.\d+)"),
30
+ DTT.INT: compile(r"(\d+)"),
31
+ DTT.BOOL: compile(r"(True|False)", flags=IGNORECASE),
32
+ DTT.IDENT: compile(rf"([A-Za-z0-9_]+)")
33
+ }
34
+
35
+
36
+ class DshellTokenizer:
37
+
38
+ def __init__(self, code: str):
39
+ """
40
+ Init le tokenizer.
41
+ :param code: Le code à tokenizer
42
+ """
43
+ self.code: str = code
44
+
45
+ def start(self):
46
+ """
47
+ Démarre le tokenizer pour qu'il traîte le code actuel.
48
+ Renvoie un tableau de tokens par ligne (séparé normalement pas des \n)
49
+ """
50
+ splited_commandes = self.split(self.code)
51
+ return self.tokenizer(splited_commandes)
52
+
53
+ def tokenizer(self, commandes_lines: list[str]) -> list[list[Token]]:
54
+ """
55
+ Tokenize chaque ligne de code
56
+ :param commandes_lines: Le code séparé en plusieurs lignes par la méthode split
57
+ """
58
+ tokens: list[list[Token]] = []
59
+
60
+ for ligne in commandes_lines: # iter chaque ligne du code
61
+ tokens_par_ligne: list[Token] = []
62
+
63
+ for token_type, pattern in table_regex.items(): # iter la table de régex pour tous les tester sur la ligne
64
+
65
+ for match in finditer(pattern, ligne): # iter les résultat du match pour avoir leur position
66
+ if token_type != DTT.COMMENT: # si ce n'est pas un commentaire
67
+ token = Token(token_type, match.group(1), match.start()) # on enregistre son token
68
+ tokens_par_ligne.append(token)
69
+
70
+ len_match = len(match.group(0))
71
+ ligne = ligne[:match.start()] + (MASK_CHARACTER * len_match) + ligne[
72
+ match.end():] # remplace la match qui vient d'avoir lieu pour ne pas le rematch une seconde fois
73
+
74
+ if token_type in (
75
+ DTT.LIST,
76
+ DTT.CALL_ARGS): # si c'est un regrouppement de donnée, on tokenize ce qu'il contient
77
+ result = self.tokenizer([token.value])
78
+ token.value = result[0] if len(
79
+ result) > 0 else result # gère si la structure de donnée est vide ou non
80
+
81
+ tokens_par_ligne.sort(key=lambda
82
+ token: token.position) # trie la position par rapport aux positions de match des tokens pour les avoir dans l'ordre du code
83
+ if tokens_par_ligne:
84
+ tokens.append(tokens_par_ligne)
85
+
86
+ return tokens
87
+
88
+ @staticmethod
89
+ def split(commande: str, global_split='\n', garder_carractere_regroupant=True, carractere_regroupant='"') -> list[
90
+ str]:
91
+ """
92
+ Sépare les commandes en une liste en respectant les chaînes entre guillemets.
93
+ :param commande: La chaîne de caractères à découper.
94
+ :param global_split: Le séparateur utilisé (par défaut '\n').
95
+ :param garder_carractere_regroupant: Si False, enlève les guillemets autour des chaînes.
96
+ :param caractere_regroupant: Le caractère utilisé pour regrouper une chaîne (par défaut '"').
97
+ :return: Une liste des commandes découpées avec les chaînes restaurées.
98
+ """
99
+
100
+ commandes: str = commande.strip()
101
+ remplacement_temporaire = '[REMPLACER]'
102
+ entre_caractere_regroupant = findall(fr'({carractere_regroupant}.*?{carractere_regroupant})', commandes,
103
+ flags=DOTALL) # repère les parties entre guillemets et les save
104
+
105
+ # current_command_text = [i[1: -1] for i in
106
+ # entre_carractere_regroupant.copy()] # enregistre les parties entre guillemets pour cette commande
107
+
108
+ res = sub(fr'({carractere_regroupant}.*?{carractere_regroupant})', remplacement_temporaire, commandes,
109
+ flags=DOTALL) # remplace les parties entre guillemets
110
+
111
+ res = res.split(global_split) # split les commandes sans les guillemets
112
+
113
+ # remet les guillemets à leurs place
114
+ result = []
115
+ for i in res:
116
+ while remplacement_temporaire in i:
117
+ i = i.replace(remplacement_temporaire,
118
+ entre_caractere_regroupant[0][1: -1] if not garder_carractere_regroupant else
119
+ entre_caractere_regroupant[0], 1)
120
+ entre_caractere_regroupant.pop(0)
121
+ result.append(i)
122
+ return result
Dshell/__init__.py ADDED
@@ -0,0 +1 @@
1
+ from ._DshellInterpreteur.dshell_interpreter import DshellInterpreteur
@@ -0,0 +1,34 @@
1
+ Metadata-Version: 2.4
2
+ Name: dshellInterpreter
3
+ Version: 0.1.0
4
+ Summary: A Discord bot interpreter for creating custom commands and automations.
5
+ Home-page: https://github.com/BOXERRMD/Dshell_Interpreter
6
+ Author: Chronos
7
+ Author-email: vagabonwalybi@gmail.com
8
+ License: MIT
9
+ Project-URL: Bug Tracker, https://github.com/BOXERRMD/Dshell_Interpreter/issues
10
+ Project-URL: Source, https://github.com/BOXERRMD/Dshell_Interpreter
11
+ Keywords: discord bot interpreter automation commands
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Requires-Python: >=3.9
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: py-cord==2.6.1
19
+ Dynamic: author
20
+ Dynamic: author-email
21
+ Dynamic: classifier
22
+ Dynamic: description
23
+ Dynamic: description-content-type
24
+ Dynamic: home-page
25
+ Dynamic: keywords
26
+ Dynamic: license
27
+ Dynamic: license-file
28
+ Dynamic: project-url
29
+ Dynamic: requires-dist
30
+ Dynamic: requires-python
31
+ Dynamic: summary
32
+
33
+ # Dshell_Interpreter
34
+ Python interpreter for Discord.
@@ -0,0 +1,18 @@
1
+ Dshell/__init__.py,sha256=UPvXnewe_8FX9aoevMA78UN1k8AY-u8LTY3vEVxaDxw,72
2
+ Dshell/DISCORD_COMMANDS/__init__.py,sha256=s58iMazDXNPDLZ3Hvymh__C5w7lcgEvaYfX0-SHocRo,62
3
+ Dshell/DISCORD_COMMANDS/dshell_channel.py,sha256=IubzNzBtGEcbgkL_NJOWRY1oqE3Yb-ySDMUmoHKjGzM,2135
4
+ Dshell/DISCORD_COMMANDS/dshell_message.py,sha256=jgSGBeDDHVy8u9fX2WN6wzh7I9WqVkeySlpmbJsBZjs,1919
5
+ Dshell/_DshellInterpreteur/__init__.py,sha256=xy5-J-R3YmY99JF3NBHTRRLsComFxpjnCA5xacISctU,35
6
+ Dshell/_DshellInterpreteur/dshell_interpreter.py,sha256=bjeSkdLw6dJucNnf-bISDT_vHCOvQ_qRl_-9GJ1rbjM,10780
7
+ Dshell/_DshellParser/__init__.py,sha256=ONDfhZMvClqP_6tE8SLjp-cf3pXL-auQYnfYRrHZxC4,56
8
+ Dshell/_DshellParser/ast_nodes.py,sha256=ArTyuk7AykJ3xKiW656BOdZg92YEwbfAlzZF1YQeZoA,5483
9
+ Dshell/_DshellParser/dshell_parser.py,sha256=iSlrSwoLz_yL4nXHMnPvycRik9iJ-cRzkXZMHRZw5qo,13129
10
+ Dshell/_DshellTokenizer/__init__.py,sha256=LIQSRhDx2B9pmPx5ADMwwD0Xr9ybneVLhHH8qrJWw_s,172
11
+ Dshell/_DshellTokenizer/dshell_keywords.py,sha256=NFa4VdfX26X5lU1Ps05hZkZg5IyBDpG9WgT9L1oTckM,3617
12
+ Dshell/_DshellTokenizer/dshell_token_type.py,sha256=Gp-Vg2P96oTaKpOEKGHAvER7l98mbcuwmapLVJgmoCI,937
13
+ Dshell/_DshellTokenizer/dshell_tokenizer.py,sha256=9ycZhz2X2uvZynE0illgi5AhtNyR_c5o1KX-pwCP1xM,5692
14
+ dshellinterpreter-0.1.0.dist-info/licenses/LICENSE,sha256=lNgcw1_xb7QENAQi3uHGymaFtbs0RV-ihiCd7AoLQjA,1082
15
+ dshellinterpreter-0.1.0.dist-info/METADATA,sha256=hkfhSw9LZKtUrbxV9xF-tow1leliffWOpaz_ijj94To,1093
16
+ dshellinterpreter-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
17
+ dshellinterpreter-0.1.0.dist-info/top_level.txt,sha256=B4CMhtmchGwPQJLuqUy0GhRG-0cUGxKL4GqEbCiB_vE,7
18
+ dshellinterpreter-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 BOXER
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ Dshell