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 +69 -6
- {chemql_python-1.2.0.dist-info → chemql_python-1.2.1.dist-info}/METADATA +1 -1
- {chemql_python-1.2.0.dist-info → chemql_python-1.2.1.dist-info}/RECORD +7 -7
- {chemql_python-1.2.0.dist-info → chemql_python-1.2.1.dist-info}/WHEEL +0 -0
- {chemql_python-1.2.0.dist-info → chemql_python-1.2.1.dist-info}/entry_points.txt +0 -0
- {chemql_python-1.2.0.dist-info → chemql_python-1.2.1.dist-info}/licenses/LICENSE +0 -0
- {chemql_python-1.2.0.dist-info → chemql_python-1.2.1.dist-info}/top_level.txt +0 -0
chemql/__init__.py
CHANGED
|
@@ -45,7 +45,7 @@ __all__ = [
|
|
|
45
45
|
"__version__"
|
|
46
46
|
]
|
|
47
47
|
|
|
48
|
-
__version__ = "1.2.
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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,4 +1,4 @@
|
|
|
1
|
-
chemql/__init__.py,sha256=
|
|
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.
|
|
12
|
-
chemql_python-1.2.
|
|
13
|
-
chemql_python-1.2.
|
|
14
|
-
chemql_python-1.2.
|
|
15
|
-
chemql_python-1.2.
|
|
16
|
-
chemql_python-1.2.
|
|
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,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|