dshellInterpreter 0.1.15__py3-none-any.whl → 0.1.16__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.

@@ -2,7 +2,6 @@ from asyncio import sleep
2
2
  from re import findall
3
3
  from typing import TypeVar, Union, Any, Optional, Callable
4
4
 
5
-
6
5
  from discord import AutoShardedBot, Embed, Colour, PermissionOverwrite, Permissions, Guild, Member, Role, Message
7
6
  from discord.abc import PrivateChannel
8
7
 
@@ -24,7 +23,7 @@ class DshellInterpreteur:
24
23
  Make what you want with Dshell code to interact with Discord !
25
24
  """
26
25
 
27
- def __init__(self, code: str, ctx: context, debug: bool = False, vars: Optional[dict[str, Any]] = None):
26
+ def __init__(self, code: str, ctx: context, debug: bool = False, vars: Optional[str] = None):
28
27
  """
29
28
  Interpreter Dshell code
30
29
  :param code: The code to interpret. Each line must end with a newline character, except SEPARATOR and SUB_SEPARATOR tokens.
@@ -33,11 +32,12 @@ class DshellInterpreteur:
33
32
  :param vars: Optional dictionary of variables to initialize in the interpreter's environment.
34
33
  """
35
34
  self.ast: list[ASTNode] = parse(DshellTokenizer(code).start(), StartNode([]))[0]
36
- self.env: dict[str, Any] = vars or {}
35
+ self.env: dict[str, Any] = {
36
+ '__ret__': None} # environment variables, '__ret__' is used to store the return value of commands
37
+ self.vars = vars if vars is not None else ''
37
38
  self.ctx: context = ctx
38
39
  if debug:
39
40
  print_ast(self.ast)
40
- self.env['__ret__'] = None
41
41
 
42
42
  async def execute(self, ast: Optional[list[All_nodes]] = None):
43
43
  """
@@ -63,6 +63,10 @@ class DshellInterpreteur:
63
63
  if isinstance(node, CommandNode):
64
64
  self.env['__ret__'] = await call_function(dshell_commands[node.name], node.body, self)
65
65
 
66
+ elif isinstance(node, ParamNode):
67
+ params = get_params(node, self)
68
+ self.env.update(params) # update the environment
69
+
66
70
  elif isinstance(node, IfNode):
67
71
  elif_valid = False
68
72
  if eval_expression(node.condition, self):
@@ -153,6 +157,37 @@ class DshellInterpreteur:
153
157
  return token.value # fallback
154
158
 
155
159
 
160
+ def get_params(node: ParamNode, interpreter: DshellInterpreteur) -> dict[str, Any]:
161
+ """
162
+ Get the parameters from a ParamNode.
163
+ :param node: The ParamNode to get the parameters from.
164
+ :param interpreter: The Dshell interpreter instance.
165
+ :return: A dictionary of parameters.
166
+ """
167
+ regrouped_args: dict[str, list] = regroupe_commandes(node.body, interpreter)[
168
+ 0] # just regroup the commands, no need to do anything else
169
+ regrouped_args.pop('*', ())
170
+ obligate = [i for i in regrouped_args.keys() if regrouped_args[i] == '*'] # get the obligatory parameters
171
+
172
+ g: list[list[Token]] = DshellTokenizer(interpreter.vars).start()
173
+ env_give_variables = regroupe_commandes(g[0], interpreter)[0] if g else {}
174
+
175
+ gived_variables = env_give_variables.pop('*', ()) # get the variables given in the environment
176
+ for key, value in zip(regrouped_args.keys(), gived_variables):
177
+ regrouped_args[key] = value
178
+
179
+ for key, value in env_give_variables.items():
180
+ if key in regrouped_args:
181
+ regrouped_args[key] = value # update the regrouped args with the env variables
182
+ else:
183
+ raise Exception(f"'{key}' is not a valid parameter, but was given in the environment.")
184
+
185
+ for key in obligate:
186
+ if regrouped_args[key] == '*':
187
+ raise Exception(f"'{key}' is an obligatory parameter, but no value was given for it.")
188
+
189
+ return regrouped_args
190
+
156
191
  def eval_expression_inline(if_node: IfNode, interpreter: DshellInterpreteur) -> Token:
157
192
  """
158
193
  Eval a conditional expression inline.
@@ -231,7 +266,7 @@ def regroupe_commandes(body: list[Token], interpreter: DshellInterpreteur) -> li
231
266
  Non-mandatory parameters will be stored in a list in the form of tokens with the key ‘*’.
232
267
  The others, having been specified via a separator, will be in the form of a list of tokens with the IDENT token as key, following the separator for each argument.
233
268
  If two parameters have the same name, the last one will overwrite the previous one.
234
- To accept duplicates, use the SUB_SEPARATOR (~~) to create a sub-dictionary for parameters with the same name.
269
+ To accept duplicates, use the SUB_SEPARATOR (~~) to create a sub-dictionary for parameters with the same name (sub-dictionary is added to the list returned).
235
270
 
236
271
  :param body: The list of tokens to group.
237
272
  :param interpreter: The Dshell interpreter instance.
@@ -404,7 +439,8 @@ class DshellPermissions:
404
439
 
405
440
  if 'members' in target_keys:
406
441
  for member_id in (
407
- self.target['members'] if isinstance(self.target['members'], ListNode) else [self.target['members']]): # allow a single ID
442
+ self.target['members'] if isinstance(self.target['members'], ListNode) else [
443
+ self.target['members']]): # allow a single ID
408
444
  member = self.get_member(guild, member_id)
409
445
  permissions[member] = PermissionOverwrite.from_pair(
410
446
  allow=Permissions(permissions=self.target.get('allow', 0)),
@@ -413,7 +449,8 @@ class DshellPermissions:
413
449
 
414
450
  elif 'roles' in target_keys:
415
451
  for role_id in (
416
- self.target['roles'] if isinstance(self.target['roles'], ListNode) else [self.target['roles']]): # allow a single ID
452
+ self.target['roles'] if isinstance(self.target['roles'], ListNode) else [
453
+ self.target['roles']]): # allow a single ID
417
454
  role = self.get_role(guild, role_id)
418
455
  permissions[role] = PermissionOverwrite.from_pair(
419
456
  allow=Permissions(permissions=self.target.get('allow', 0)),
@@ -19,7 +19,8 @@ __all__ = [
19
19
  'SleepNode',
20
20
  'IdentOperationNode',
21
21
  'ListNode',
22
- 'PermissionNode'
22
+ 'PermissionNode',
23
+ 'ParamNode'
23
24
  ]
24
25
 
25
26
 
@@ -395,6 +396,33 @@ class IdentOperationNode(ASTNode):
395
396
  "args": self.args.to_dict()
396
397
  }
397
398
 
399
+ class ParamNode(ASTNode):
400
+ """
401
+ Node representing a parameter in the AST.
402
+ This is used to define parameters for variables passed to the dshell interpreter.
403
+ """
404
+
405
+ def __init__(self, body: list[Token]):
406
+ """
407
+ :param name: Token representing the parameter name
408
+ :param body: list of tokens representing the body of the parameter
409
+ """
410
+ self.body = body
411
+
412
+ def __repr__(self):
413
+ return f"<PARAM> - {self.name} *- {self.body}"
414
+
415
+ def to_dict(self):
416
+ """
417
+ Convert the ParamNode to a dictionary representation.
418
+ :return: Dictionary representation of the ParamNode.
419
+ """
420
+ return {
421
+ "type": "ParamNode",
422
+ "name": self.name.to_dict(),
423
+ "body": [token.to_dict() for token in self.body]
424
+ }
425
+
398
426
  class ListNode(ASTNode):
399
427
  """
400
428
  Node representing a list structure in the AST.
@@ -23,6 +23,7 @@ from .ast_nodes import (ASTNode,
23
23
  EmbedNode,
24
24
  FieldEmbedNode,
25
25
  PermissionNode,
26
+ ParamNode,
26
27
  StartNode)
27
28
  from .._DshellTokenizer.dshell_token_type import DshellTokenType as DTT
28
29
  from .._DshellTokenizer.dshell_token_type import Token
@@ -66,7 +67,7 @@ def parse(token_lines: list[list[Token]], start_node: ASTNode) -> tuple[list[AST
66
67
 
67
68
  elif first_token_line.value == '#if':
68
69
  if not isinstance(last_block, (IfNode, ElseNode, ElifNode)):
69
- raise SyntaxError(f'[#IF] Aucun bloc conditionnel ouvert ligne {first_token_line.position} !')
70
+ raise SyntaxError(f'[#IF] No conditional bloc open on line {first_token_line.position} !')
70
71
 
71
72
  if isinstance(last_block, (ElifNode, ElseNode)):
72
73
 
@@ -78,7 +79,7 @@ def parse(token_lines: list[list[Token]], start_node: ASTNode) -> tuple[list[AST
78
79
 
79
80
  elif first_token_line.value == 'elif':
80
81
  if not isinstance(last_block, (IfNode, ElifNode)):
81
- raise SyntaxError(f'[ELIF] Aucun bloc conditionnel ouvert ligne {first_token_line.position} !')
82
+ raise SyntaxError(f'[ELIF] No conditional bloc open on line {first_token_line.position} !')
82
83
  elif_node = ElifNode(condition=tokens_by_line[1:], body=[],
83
84
  parent=last_block if isinstance(last_block, IfNode) else last_block.parent)
84
85
 
@@ -94,10 +95,10 @@ def parse(token_lines: list[list[Token]], start_node: ASTNode) -> tuple[list[AST
94
95
 
95
96
  elif first_token_line.value == 'else':
96
97
  if not isinstance(last_block, (IfNode, ElifNode)):
97
- raise SyntaxError(f'[ELSE] Aucun bloc conditionnel ouvert ligne {first_token_line.position} !')
98
+ raise SyntaxError(f'[ELSE] No conditional bloc open on line {first_token_line.position} !')
98
99
 
99
100
  if isinstance(last_block, ElseNode) and last_block.else_body is not None:
100
- raise SyntaxError(f'[ELSE] Déjà définit et n\'accepte pas les doublons dans un même if !')
101
+ raise SyntaxError(f'[ELSE] already define !')
101
102
 
102
103
  else_node = ElseNode(body=[])
103
104
 
@@ -116,7 +117,7 @@ def parse(token_lines: list[list[Token]], start_node: ASTNode) -> tuple[list[AST
116
117
 
117
118
  elif first_token_line.value == '#loop': # si rencontré
118
119
  if not isinstance(last_block, LoopNode):
119
- raise SyntaxError(f'[#LOOP] Aucune loop ouverte ligne {first_token_line.position} !')
120
+ raise SyntaxError(f'[#LOOP] No loop open on line {first_token_line.position} !')
120
121
  blocks.pop()
121
122
  return blocks, pointeur # on renvoie les informations parsé à la dernière loop ouverte
122
123
 
@@ -135,6 +136,18 @@ def parse(token_lines: list[list[Token]], start_node: ASTNode) -> tuple[list[AST
135
136
  sleep_node = SleepNode(tokens_by_line[1:])
136
137
  last_block.body.append(sleep_node)
137
138
 
139
+ elif first_token_line.value == 'param':
140
+ param_node = ParamNode(body=[])
141
+ last_block.body.append(param_node)
142
+ _, p = parse(token_lines[pointeur + 1:], param_node)
143
+ pointeur += p + 1 # on avance le pointeur de la ligne suivante
144
+
145
+ elif first_token_line.value == '#param':
146
+ if not isinstance(last_block, ParamNode):
147
+ raise SyntaxError(f'[#PARAM] No parameters open on line {first_token_line.position} !')
148
+ blocks.pop() # on supprime le dernier bloc (le paramètre)
149
+ return blocks, pointeur # on renvoie les informations parsé à la dernière paramètre ouverte
150
+
138
151
  elif first_token_line.value == '#end': # node pour arrêter le programme si elle est rencontré
139
152
  end_node = EndNode()
140
153
  last_block.body.append(end_node)
@@ -152,13 +165,13 @@ def parse(token_lines: list[list[Token]], start_node: ASTNode) -> tuple[list[AST
152
165
 
153
166
  elif first_token_line.value == '#embed':
154
167
  if not isinstance(last_block, EmbedNode):
155
- raise SyntaxError(f'[#EMBED] Aucun embed ouvert ligne {first_token_line.position} !')
168
+ raise SyntaxError(f'[#EMBED] No embed open on line {first_token_line.position} !')
156
169
  blocks.pop()
157
170
  return blocks, pointeur
158
171
 
159
172
  elif first_token_line.value == 'field':
160
173
  if not isinstance(last_block, EmbedNode):
161
- raise SyntaxError(f'[FIELD] Aucun embed ouvert ligne {first_token_line.position} !')
174
+ raise SyntaxError(f'[FIELD] No embed open on line {first_token_line.position} !')
162
175
  last_block.fields.append(FieldEmbedNode(tokens_by_line[1:]))
163
176
 
164
177
  elif first_token_line.value in ('perm', 'permission'):
@@ -170,7 +183,7 @@ def parse(token_lines: list[list[Token]], start_node: ASTNode) -> tuple[list[AST
170
183
 
171
184
  elif first_token_line.value in ('#perm', '#permission'):
172
185
  if not isinstance(last_block, PermissionNode):
173
- raise SyntaxError(f'[#PERM] Aucun permission ouvert ligne {first_token_line.position} !')
186
+ raise SyntaxError(f'[#PERM] No permission open on line {first_token_line.position} !')
174
187
  blocks.pop()
175
188
  return blocks, pointeur
176
189
 
@@ -345,3 +358,9 @@ def print_ast(ast: list[ASTNode], decalage: int = 0):
345
358
  elif isinstance(i, FieldEmbedNode):
346
359
  for field in i.body:
347
360
  print(f"{' ' * decalage}FIELD -> {field.value}")
361
+
362
+ elif isinstance(i, PermissionNode):
363
+ print(f"{' ' * decalage}PERMISSION -> {i.body}")
364
+
365
+ elif isinstance(i, ParamNode):
366
+ print(f"{' ' * decalage}PARAM -> {i.body}")
@@ -14,7 +14,7 @@ from ..DISCORD_COMMANDS.dshell_message import *
14
14
  from ..DISCORD_COMMANDS.dshell_member import *
15
15
 
16
16
  dshell_keyword: set[str] = {
17
- 'if', 'else', 'elif', 'loop', '#end', 'var', '#loop', '#if', 'sleep'
17
+ 'if', 'else', 'elif', 'loop', '#end', 'var', '#loop', '#if', 'sleep', 'param', '#param'
18
18
  }
19
19
 
20
20
  dshell_discord_keyword: set[str] = {
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dshellInterpreter
3
- Version: 0.1.15
3
+ Version: 0.1.16
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
@@ -4,16 +4,16 @@ Dshell/DISCORD_COMMANDS/dshell_channel.py,sha256=3tOVZhfFSie_Kjv8HhgsPfzWQuppLbZ
4
4
  Dshell/DISCORD_COMMANDS/dshell_member.py,sha256=4cmpcu9RGHVUOGcF9pSnnxh7kEJQWAel4BBQ6Vicvbs,3126
5
5
  Dshell/DISCORD_COMMANDS/dshell_message.py,sha256=Q0LjP_4f-VX31Q0T2oix387qAJ0CQGawqCbMGCiUbM8,4381
6
6
  Dshell/_DshellInterpreteur/__init__.py,sha256=xy5-J-R3YmY99JF3NBHTRRLsComFxpjnCA5xacISctU,35
7
- Dshell/_DshellInterpreteur/dshell_interpreter.py,sha256=zvJ6zDgbtyQLVRTSsUrAMt3wk6Zf8ZgLXgeXxY7HL9g,17636
7
+ Dshell/_DshellInterpreteur/dshell_interpreter.py,sha256=viZBYDF7rw1Juasu-B4Fc7Kvdm4j_7SGhOeR_DZpL00,19485
8
8
  Dshell/_DshellParser/__init__.py,sha256=ONDfhZMvClqP_6tE8SLjp-cf3pXL-auQYnfYRrHZxC4,56
9
- Dshell/_DshellParser/ast_nodes.py,sha256=o21a9Rdgq00koQSX8uj1uKMVdEHsgJK55bgaqK8bZAM,14447
10
- Dshell/_DshellParser/dshell_parser.py,sha256=87VWNHOeLfNTieie4oKPYw9ieqtqFOVU6tEV9h1VVzI,14657
9
+ Dshell/_DshellParser/ast_nodes.py,sha256=-zFmSeb6FnvcXd2gB3koy93apVJ-PiCY8PTUFj-_bG8,15307
10
+ Dshell/_DshellParser/dshell_parser.py,sha256=aCsIbPlQeQQAAfMr2SCnjRODDLZXtea8os4cA4PW7m4,15548
11
11
  Dshell/_DshellTokenizer/__init__.py,sha256=LIQSRhDx2B9pmPx5ADMwwD0Xr9ybneVLhHH8qrJWw_s,172
12
- Dshell/_DshellTokenizer/dshell_keywords.py,sha256=1aq5z23AFJ1V4th_3qOJrAiEKcMH7gpZJn6n4ln9OQA,4234
12
+ Dshell/_DshellTokenizer/dshell_keywords.py,sha256=Hk8rdW62Lb246lNZ8ETgYl4sOuXiCKla9f3Lx5N5MuQ,4253
13
13
  Dshell/_DshellTokenizer/dshell_token_type.py,sha256=Q8Jpil9S3-Tin1sXEHW7cnNdDdUh7Fiv8_lki0OWC0E,1443
14
14
  Dshell/_DshellTokenizer/dshell_tokenizer.py,sha256=DSGiSHj9jLqP7RkC-8WFRqFvitJ7P4b7p0CJn4ek7hE,5831
15
- dshellinterpreter-0.1.15.dist-info/licenses/LICENSE,sha256=lNgcw1_xb7QENAQi3uHGymaFtbs0RV-ihiCd7AoLQjA,1082
16
- dshellinterpreter-0.1.15.dist-info/METADATA,sha256=MGsckyWIhOVDxV7e2wzJQS9lltp7iVLKdhoPvyqltOA,1096
17
- dshellinterpreter-0.1.15.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
18
- dshellinterpreter-0.1.15.dist-info/top_level.txt,sha256=B4CMhtmchGwPQJLuqUy0GhRG-0cUGxKL4GqEbCiB_vE,7
19
- dshellinterpreter-0.1.15.dist-info/RECORD,,
15
+ dshellinterpreter-0.1.16.dist-info/licenses/LICENSE,sha256=lNgcw1_xb7QENAQi3uHGymaFtbs0RV-ihiCd7AoLQjA,1082
16
+ dshellinterpreter-0.1.16.dist-info/METADATA,sha256=m-rvSmVUOMrGTwAK5PrZXZwzpeEbHlLniu-UsxhkTv0,1096
17
+ dshellinterpreter-0.1.16.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
18
+ dshellinterpreter-0.1.16.dist-info/top_level.txt,sha256=B4CMhtmchGwPQJLuqUy0GhRG-0cUGxKL4GqEbCiB_vE,7
19
+ dshellinterpreter-0.1.16.dist-info/RECORD,,