chemql-python 1.2.0__py3-none-any.whl → 1.2.1__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.
chemql/__init__.py CHANGED
@@ -45,7 +45,7 @@ __all__ = [
45
45
  "__version__"
46
46
  ]
47
47
 
48
- __version__ = "1.2.0"
48
+ __version__ = "1.2.1"
49
49
 
50
50
  # Persistent preprocessor/function state for the current ChemQL session.
51
51
  DEFINITIONS = {}
@@ -107,16 +107,18 @@ def _split_function_arguments(source):
107
107
  return args
108
108
 
109
109
  def _call_function(name, arguments):
110
- """Call a ChemQL function; arguments are evaluated Python expressions."""
110
+ """Call a ChemQL function, including `let` assignments and block syntax."""
111
111
  if name not in FUNCTIONS:
112
112
  raise NameError(f"Unknown ChemQL function `{name}`")
113
+
113
114
  values = [evaluate_python(arg) for arg in arguments]
114
115
  definition = FUNCTIONS[name]
115
116
  saved_globals = python_globals.copy()
116
117
  saved_chemql_variables = CHEMQL_VARIABLES.copy()
117
118
  had_arguments = "arguments" in python_globals
118
119
  saved_arguments = python_globals.get("arguments")
119
- python_globals["arguments"] = [name, *values]
120
+ # `arguments` contains only the supplied arguments, not the function name.
121
+ python_globals["arguments"] = list(values)
120
122
 
121
123
  def substitute_parameters(source):
122
124
  # Replace positional placeholders as tokens, so \1 does not corrupt \10.
@@ -127,6 +129,23 @@ def _call_function(name, arguments):
127
129
  source,
128
130
  )
129
131
 
132
+ def normalize_let_line(source):
133
+ """Translate `let name = expression` to a Python assignment block."""
134
+ match = re.fullmatch(r"\s*let\s+([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*?)\s*", source)
135
+ if not match:
136
+ return source
137
+ variable, expression = match.groups()
138
+ expression = substitute_parameters(expression)
139
+ # In ChemQL function expressions, [i] means evaluate i, rather than
140
+ # create a Python list containing i. Keep this deliberately simple and
141
+ # avoid changing arbitrary Python list literals.
142
+ expression = re.sub(
143
+ r"\[\s*([A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*)\s*\]",
144
+ r"(\1)",
145
+ expression,
146
+ )
147
+ return f"[{variable} = {expression}]"
148
+
130
149
  try:
131
150
  body = definition["body"]
132
151
  i = 0
@@ -149,6 +168,29 @@ def _call_function(name, arguments):
149
168
  i += 1
150
169
  continue
151
170
 
171
+ # Support ChemQL's [for ...], [if ...], and [while ...] blocks
172
+ # inside functions, using the same block executors as the REPL.
173
+ if is_python_block_header(line):
174
+ end = find_endblock(body, i + 1)
175
+ block_body = [
176
+ normalize_let_line(substitute_parameters(source_line))
177
+ for source_line in body[i + 1:end]
178
+ ]
179
+ code = line[1:-1].strip()
180
+ if code.startswith("for "):
181
+ execute_for_block(line, block_body)
182
+ elif code.startswith("while "):
183
+ execute_while_block(line, block_body)
184
+ elif code.startswith("if "):
185
+ execute_if_block(line, block_body)
186
+ else:
187
+ raise SyntaxError(f"Unsupported function block `{code}`")
188
+ i = end + 1
189
+ continue
190
+
191
+ if line == "[endblock]":
192
+ raise SyntaxError(f"Unexpected `[endblock]` in function `{name}`")
193
+
152
194
  if line.startswith("return"):
153
195
  expression = line[len("return"):].strip()
154
196
  if expression.startswith("[") and expression.endswith("]"):
@@ -156,7 +198,12 @@ def _call_function(name, arguments):
156
198
  expression = substitute_parameters(expression)
157
199
  return evaluate_python(expression) if expression else None
158
200
 
159
- evaluate_python(substitute_parameters(line))
201
+ normalized = normalize_let_line(line)
202
+ if normalized != line:
203
+ # The wrapper is ChemQL syntax; pass the assignment itself to Python.
204
+ evaluate_python(normalized[1:-1])
205
+ else:
206
+ evaluate_python(substitute_parameters(line))
160
207
  i += 1
161
208
  return None
162
209
  finally:
@@ -2719,6 +2766,14 @@ def process_lines(lines):
2719
2766
  print("No definitions.")
2720
2767
  i += 1
2721
2768
  continue
2769
+ if stripped.casefold() == "macros":
2770
+ if MACROS:
2771
+ for name, value in MACROS.items():
2772
+ print(f"@{name} = {value}")
2773
+ else:
2774
+ print("No macros.")
2775
+ i += 1
2776
+ continue
2722
2777
 
2723
2778
  # Function definitions use `func NAME ... endfunc`.
2724
2779
  function_match = re.fullmatch(r"func\s+(" + _DEFINITION_NAME + r")", stripped, re.IGNORECASE)
@@ -3167,7 +3222,14 @@ def execute(arguments, query_text=None, positions=None):
3167
3222
 
3168
3223
  if arguments[0].lower() == "version":
3169
3224
  return __version__
3170
-
3225
+ if arguments[0].lower() == "exit":
3226
+ return sys.exit(0)
3227
+ if arguments[0].lower() == "definitions":
3228
+ return "\n".join([f"@{name} = {value}" for name, value in DEFINITIONS.items()]) if DEFINITIONS else "No definitions."
3229
+
3230
+ if arguments[0].lower() == "macros":
3231
+ return "\n".join([f"@{name} = {value}" for name, value in MACROS.items()]) if MACROS else "No macros."
3232
+
3171
3233
  if arguments[0] == "conditions":
3172
3234
  if len(arguments) != 1:
3173
3235
  return "Usage: conditions"
@@ -3522,6 +3584,7 @@ class ChemqlLexer(Lexer):
3522
3584
  "findmol", "findre", "set", "add", "remove", "react",
3523
3585
  "balance", "conditions", "view", "help", "dump", "version",
3524
3586
  "return", "union", "intersection", "let", "unset", "variables",
3587
+ "macros", "definitions", "arguments"
3525
3588
  }
3526
3589
 
3527
3590
  OPERATORS = {
@@ -3736,7 +3799,7 @@ class ChemqlCompleter(Completer):
3736
3799
  "findel", "findmol", "findre", "set", "add", "remove",
3737
3800
  "react", "balance", "conditions", "version", "help", "view",
3738
3801
  "dump", "let", "unset", "variables", "func", "define",
3739
- "import",
3802
+ "import", "definitions", "macros",
3740
3803
  )
3741
3804
 
3742
3805
  QUERY_OPERATORS = [
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: chemql-python
3
- Version: 1.2.0
3
+ Version: 1.2.1
4
4
  Summary: A chemistry-focused query language and REPL
5
5
  Author-email: Mohammad Rakibul Islam <rakibulofficial1017@gmail.com>
6
6
  Requires-Python: >=3.10
@@ -1,4 +1,4 @@
1
- chemql/__init__.py,sha256=Y7pZXMQU_yj1pmCKAniTQXDuZqEjhxZkM_aa3rINFik,149710
1
+ chemql/__init__.py,sha256=7YotC6BZV7ZRPQjQwFfmOmlbri9tdKPloQ40Lm_nRZg,152580
2
2
  chemql/__main__.py,sha256=3Q52s0hyuouhvSSqFEQFDr6mtJ-azkCwI2AD4c7f7A8,28
3
3
  chemql/data/BondEnergies.json,sha256=QZN3iWZrD8125jV41EG1GiRohBtP0CzAP_EDQTfaEKc,8269
4
4
  chemql/data/MoleculeBonds.json,sha256=Iw2shYxJScH_tI80IytBYoqVDSbgT7OSqPJUIgKZme4,84262
@@ -8,9 +8,9 @@ chemql/data/Reactions.json,sha256=BFpkKEWwLzG54ot96BVOWwU3btpimRJlOM57yGclGgc,15
8
8
  chemql/data/THIRD_PARTY_LICENSES/PeriodicTableJSON.txt,sha256=9wih5q8s58qR3vJEGpooa4ESyAGZHcmGvzwMMkMHNKk,322
9
9
  chemql/data/THIRD_PARTY_LICENSES/RHEA.txt,sha256=nXTeCP6vJBW5B_mtaVGFLr0w7vnjrSTBnv6_eOk3KKo,634
10
10
  chemql/html/periodic_table.html,sha256=ftVgXWjRO2AsTAGEekTrvgE5ZvcwRfoby9UWoixc9Y8,361560
11
- chemql_python-1.2.0.dist-info/licenses/LICENSE,sha256=o4ywHC0EwRy2zgIASiRD2sK3Vcsf7LBcTHjDvoJgRE0,1079
12
- chemql_python-1.2.0.dist-info/METADATA,sha256=lcINiZUWMHfiyPv8lxvp-uMHfIgPvCB5K2PMtCl9BcE,30529
13
- chemql_python-1.2.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
14
- chemql_python-1.2.0.dist-info/entry_points.txt,sha256=j98ObVWR743an524z24AiWeRsppag-py5cc7l0D0HkI,39
15
- chemql_python-1.2.0.dist-info/top_level.txt,sha256=9UzHwnk_F6W19Q9nayaFgVyAELQXXwoYjeBgB6MWNr8,7
16
- chemql_python-1.2.0.dist-info/RECORD,,
11
+ chemql_python-1.2.1.dist-info/licenses/LICENSE,sha256=o4ywHC0EwRy2zgIASiRD2sK3Vcsf7LBcTHjDvoJgRE0,1079
12
+ chemql_python-1.2.1.dist-info/METADATA,sha256=iKKC3niSxAHMvcc0CxxgfeDw9EhQDx0RZXt7_kdbDck,30529
13
+ chemql_python-1.2.1.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
14
+ chemql_python-1.2.1.dist-info/entry_points.txt,sha256=j98ObVWR743an524z24AiWeRsppag-py5cc7l0D0HkI,39
15
+ chemql_python-1.2.1.dist-info/top_level.txt,sha256=9UzHwnk_F6W19Q9nayaFgVyAELQXXwoYjeBgB6MWNr8,7
16
+ chemql_python-1.2.1.dist-info/RECORD,,