dshellInterpreter 0.1.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.
Potentially problematic release.
This version of dshellInterpreter might be problematic. Click here for more details.
- dshellinterpreter-0.1.0/Dshell/DISCORD_COMMANDS/__init__.py +2 -0
- dshellinterpreter-0.1.0/Dshell/DISCORD_COMMANDS/dshell_channel.py +60 -0
- dshellinterpreter-0.1.0/Dshell/DISCORD_COMMANDS/dshell_message.py +61 -0
- dshellinterpreter-0.1.0/Dshell/_DshellInterpreteur/__init__.py +1 -0
- dshellinterpreter-0.1.0/Dshell/_DshellInterpreteur/dshell_interpreter.py +267 -0
- dshellinterpreter-0.1.0/Dshell/_DshellParser/__init__.py +2 -0
- dshellinterpreter-0.1.0/Dshell/_DshellParser/ast_nodes.py +204 -0
- dshellinterpreter-0.1.0/Dshell/_DshellParser/dshell_parser.py +308 -0
- dshellinterpreter-0.1.0/Dshell/_DshellTokenizer/__init__.py +4 -0
- dshellinterpreter-0.1.0/Dshell/_DshellTokenizer/dshell_keywords.py +125 -0
- dshellinterpreter-0.1.0/Dshell/_DshellTokenizer/dshell_token_type.py +39 -0
- dshellinterpreter-0.1.0/Dshell/_DshellTokenizer/dshell_tokenizer.py +122 -0
- dshellinterpreter-0.1.0/Dshell/__init__.py +1 -0
- dshellinterpreter-0.1.0/LICENSE +21 -0
- dshellinterpreter-0.1.0/PKG-INFO +34 -0
- dshellinterpreter-0.1.0/README.md +2 -0
- dshellinterpreter-0.1.0/dshellInterpreter.egg-info/PKG-INFO +34 -0
- dshellinterpreter-0.1.0/dshellInterpreter.egg-info/SOURCES.txt +21 -0
- dshellinterpreter-0.1.0/dshellInterpreter.egg-info/dependency_links.txt +1 -0
- dshellinterpreter-0.1.0/dshellInterpreter.egg-info/requires.txt +1 -0
- dshellinterpreter-0.1.0/dshellInterpreter.egg-info/top_level.txt +1 -0
- dshellinterpreter-0.1.0/setup.cfg +4 -0
- dshellinterpreter-0.1.0/setup.py +29 -0
|
@@ -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,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}"
|