bare-script 4.0.3__tar.gz → 4.1.1__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.
- {bare_script-4.0.3/src/bare_script.egg-info → bare_script-4.1.1}/PKG-INFO +9 -1
- {bare_script-4.0.3 → bare_script-4.1.1}/README.md +8 -0
- {bare_script-4.0.3 → bare_script-4.1.1}/setup.cfg +1 -1
- bare_script-4.1.1/setup.py +46 -0
- {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/__init__.py +11 -3
- {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/bare.py +9 -1
- {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/library.py +8 -5
- {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/runtime.py +118 -81
- bare_script-4.1.1/src/bare_script/runtime_c.c +1900 -0
- {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/value.py +56 -37
- {bare_script-4.0.3 → bare_script-4.1.1/src/bare_script.egg-info}/PKG-INFO +9 -1
- {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script.egg-info/SOURCES.txt +2 -0
- {bare_script-4.0.3 → bare_script-4.1.1}/LICENSE +0 -0
- {bare_script-4.0.3 → bare_script-4.1.1}/pyproject.toml +0 -0
- {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/__main__.py +0 -0
- {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/include/__init__.py +0 -0
- {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/include/args.bare +0 -0
- {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/include/baredoc.bare +0 -0
- {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/include/baredocCLI.bare +0 -0
- {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/include/data.bare +0 -0
- {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/include/dataLineChart.bare +0 -0
- {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/include/dataTable.bare +0 -0
- {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/include/dataUtil.bare +0 -0
- {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/include/diff.bare +0 -0
- {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/include/draw.bare +0 -0
- {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/include/elementModel.bare +0 -0
- {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/include/forms.bare +0 -0
- {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/include/markdown.bare +0 -0
- {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/include/markdownElements.bare +0 -0
- {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/include/markdownHighlight.bare +0 -0
- {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/include/markdownParser.bare +0 -0
- {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/include/markdownUp.bare +0 -0
- {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/include/pager.bare +0 -0
- {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/include/qrcode.bare +0 -0
- {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/include/schemaDoc.bare +0 -0
- {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/include/unittest.bare +0 -0
- {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/include/unittestMock.bare +0 -0
- {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/model.py +0 -0
- {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/options.py +0 -0
- {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/parser.py +0 -0
- {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script.egg-info/dependency_links.txt +0 -0
- {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script.egg-info/entry_points.txt +0 -0
- {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script.egg-info/requires.txt +0 -0
- {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: bare-script
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.1.1
|
|
4
4
|
Summary: bare-script
|
|
5
5
|
Home-page: https://github.com/craigahobbs/bare-script
|
|
6
6
|
Author: Craig A. Hobbs
|
|
@@ -191,6 +191,14 @@ markdownPrint('Hello, Markdown!')
|
|
|
191
191
|
~~~
|
|
192
192
|
|
|
193
193
|
|
|
194
|
+
## C Runtime
|
|
195
|
+
|
|
196
|
+
The package ships with an optional CPython C extension (`runtime_c`) that mirrors the pure-Python
|
|
197
|
+
runtime for faster script execution. When the compiled extension is available, it is used
|
|
198
|
+
automatically; otherwise the pure-Python runtime is used as a fallback. Set the environment
|
|
199
|
+
variable `BARESCRIPT_RUNTIME_PY=1` to force the pure-Python runtime.
|
|
200
|
+
|
|
201
|
+
|
|
194
202
|
## Development
|
|
195
203
|
|
|
196
204
|
This package is developed using [python-build](https://github.com/craigahobbs/python-build#readme).
|
|
@@ -166,6 +166,14 @@ markdownPrint('Hello, Markdown!')
|
|
|
166
166
|
~~~
|
|
167
167
|
|
|
168
168
|
|
|
169
|
+
## C Runtime
|
|
170
|
+
|
|
171
|
+
The package ships with an optional CPython C extension (`runtime_c`) that mirrors the pure-Python
|
|
172
|
+
runtime for faster script execution. When the compiled extension is available, it is used
|
|
173
|
+
automatically; otherwise the pure-Python runtime is used as a fallback. Set the environment
|
|
174
|
+
variable `BARESCRIPT_RUNTIME_PY=1` to force the pure-Python runtime.
|
|
175
|
+
|
|
176
|
+
|
|
169
177
|
## Development
|
|
170
178
|
|
|
171
179
|
This package is developed using [python-build](https://github.com/craigahobbs/python-build#readme).
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Licensed under the MIT License
|
|
2
|
+
# https://github.com/craigahobbs/bare-script-py/blob/main/LICENSE
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
bare-script setup
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from setuptools import setup, Extension
|
|
9
|
+
from setuptools.command.build_ext import build_ext
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class OptionalBuildExt(build_ext):
|
|
13
|
+
"""
|
|
14
|
+
Build C extensions optionally - if compilation fails, the package still installs
|
|
15
|
+
and falls back to the pure-Python implementation.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def run(self):
|
|
19
|
+
try:
|
|
20
|
+
super().run()
|
|
21
|
+
except Exception: # pylint: disable=broad-except
|
|
22
|
+
self._unavailable()
|
|
23
|
+
|
|
24
|
+
def build_extension(self, ext):
|
|
25
|
+
try:
|
|
26
|
+
super().build_extension(ext)
|
|
27
|
+
except Exception: # pylint: disable=broad-except
|
|
28
|
+
self._unavailable()
|
|
29
|
+
|
|
30
|
+
@staticmethod
|
|
31
|
+
def _unavailable():
|
|
32
|
+
print('*' * 70)
|
|
33
|
+
print('WARNING: C extension could not be compiled.')
|
|
34
|
+
print(' Falling back to pure-Python implementation.')
|
|
35
|
+
print('*' * 70)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
setup(
|
|
39
|
+
ext_modules=[
|
|
40
|
+
Extension(
|
|
41
|
+
'bare_script.runtime_c',
|
|
42
|
+
sources=['src/bare_script/runtime_c.c']
|
|
43
|
+
)
|
|
44
|
+
],
|
|
45
|
+
cmdclass={'build_ext': OptionalBuildExt}
|
|
46
|
+
)
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
bare-script package
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
+
import os as _os
|
|
9
|
+
|
|
8
10
|
from .model import \
|
|
9
11
|
lint_script, \
|
|
10
12
|
validate_expression, \
|
|
@@ -25,6 +27,12 @@ from .parser import \
|
|
|
25
27
|
parse_script
|
|
26
28
|
|
|
27
29
|
from .runtime import \
|
|
28
|
-
BareScriptRuntimeError
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
BareScriptRuntimeError
|
|
31
|
+
|
|
32
|
+
if not _os.environ.get('BARESCRIPT_RUNTIME_PY'): # pragma: no cover
|
|
33
|
+
try:
|
|
34
|
+
from .runtime_c import evaluate_expression, execute_script
|
|
35
|
+
except ImportError:
|
|
36
|
+
from .runtime import evaluate_expression, execute_script
|
|
37
|
+
else:
|
|
38
|
+
from .runtime import evaluate_expression, execute_script
|
|
@@ -7,14 +7,22 @@ bare-script command-line interface (CLI)
|
|
|
7
7
|
|
|
8
8
|
import argparse
|
|
9
9
|
from functools import partial
|
|
10
|
+
import os as _os
|
|
10
11
|
import sys
|
|
11
12
|
import time
|
|
12
13
|
|
|
13
14
|
from .model import lint_script
|
|
14
15
|
from .options import FETCH_SYSTEM_PREFIX, fetch_read_write, fetch_system, log_stdout, url_file_relative
|
|
15
16
|
from .parser import parse_expression, parse_script
|
|
16
|
-
from .runtime import SYSTEM_GLOBAL_INCLUDES_NAME
|
|
17
|
+
from .runtime import SYSTEM_GLOBAL_INCLUDES_NAME
|
|
17
18
|
from .value import value_boolean
|
|
19
|
+
if not _os.environ.get('BARESCRIPT_RUNTIME_PY'): # pragma: no cover
|
|
20
|
+
try:
|
|
21
|
+
from .runtime_c import evaluate_expression, execute_script
|
|
22
|
+
except ImportError:
|
|
23
|
+
from .runtime import evaluate_expression, execute_script
|
|
24
|
+
else:
|
|
25
|
+
from .runtime import evaluate_expression, execute_script
|
|
18
26
|
|
|
19
27
|
|
|
20
28
|
def main(argv=None):
|
|
@@ -11,6 +11,7 @@ import functools
|
|
|
11
11
|
import importlib
|
|
12
12
|
import json
|
|
13
13
|
import math
|
|
14
|
+
import os as _os
|
|
14
15
|
import random
|
|
15
16
|
import re
|
|
16
17
|
import urllib
|
|
@@ -27,16 +28,18 @@ from .value import R_NUMBER_CLEANUP, ValueArgsError, value_args_model, value_arg
|
|
|
27
28
|
# Helper to dynamically import evaluate_expression to avoid the circular dependency
|
|
28
29
|
def _import_evaluate_expression():
|
|
29
30
|
if not _EVALUATE_EXPRESSION:
|
|
30
|
-
|
|
31
|
+
if not _os.environ.get('BARESCRIPT_RUNTIME_PY'): # pragma: no cover
|
|
32
|
+
try:
|
|
33
|
+
_EVALUATE_EXPRESSION.append(importlib.import_module('bare_script.runtime_c').evaluate_expression)
|
|
34
|
+
except (ImportError, AttributeError):
|
|
35
|
+
_EVALUATE_EXPRESSION.append(importlib.import_module('bare_script.runtime').evaluate_expression)
|
|
36
|
+
else:
|
|
37
|
+
_EVALUATE_EXPRESSION.append(importlib.import_module('bare_script.runtime').evaluate_expression)
|
|
31
38
|
return _EVALUATE_EXPRESSION[0]
|
|
32
39
|
|
|
33
40
|
_EVALUATE_EXPRESSION = []
|
|
34
41
|
|
|
35
42
|
|
|
36
|
-
# The default maximum statements for executeScript
|
|
37
|
-
DEFAULT_MAX_STATEMENTS = 1e9
|
|
38
|
-
|
|
39
|
-
|
|
40
43
|
#
|
|
41
44
|
# Array functions
|
|
42
45
|
#
|
|
@@ -8,13 +8,17 @@ The BareScript runtime
|
|
|
8
8
|
import datetime
|
|
9
9
|
import functools
|
|
10
10
|
|
|
11
|
-
from .library import
|
|
11
|
+
from .library import EXPRESSION_FUNCTIONS, SCRIPT_FUNCTIONS
|
|
12
12
|
from .model import lint_script
|
|
13
13
|
from .options import url_file_relative
|
|
14
14
|
from .parser import parse_script
|
|
15
15
|
from .value import ValueArgsError, value_boolean, value_compare, value_normalize_datetime, value_round_number, value_string
|
|
16
16
|
|
|
17
17
|
|
|
18
|
+
# The default maximum statements for executeScript
|
|
19
|
+
DEFAULT_MAX_STATEMENTS = 1e9
|
|
20
|
+
|
|
21
|
+
|
|
18
22
|
# Coverage configuration object global variable name
|
|
19
23
|
SYSTEM_GLOBAL_COVERAGE_NAME = '__barescriptCoverage'
|
|
20
24
|
|
|
@@ -54,6 +58,13 @@ def execute_script(script, options=None):
|
|
|
54
58
|
|
|
55
59
|
def _execute_script_helper(script, statements, options, locals_):
|
|
56
60
|
globals_ = options['globals']
|
|
61
|
+
max_statements = options.get('maxStatements', DEFAULT_MAX_STATEMENTS)
|
|
62
|
+
options.setdefault('statementCount', 0)
|
|
63
|
+
|
|
64
|
+
# Coverage configuration is invariant across this helper invocation
|
|
65
|
+
coverage_global = globals_.get(SYSTEM_GLOBAL_COVERAGE_NAME)
|
|
66
|
+
has_coverage = coverage_global is not None and isinstance(coverage_global, dict) and \
|
|
67
|
+
coverage_global.get('enabled') and not script.get('system')
|
|
57
68
|
|
|
58
69
|
# Iterate each script statement
|
|
59
70
|
label_indexes = None
|
|
@@ -61,25 +72,22 @@ def _execute_script_helper(script, statements, options, locals_):
|
|
|
61
72
|
ix_statement = 0
|
|
62
73
|
while ix_statement < statements_length:
|
|
63
74
|
statement = statements[ix_statement]
|
|
64
|
-
statement_key = next(iter(statement.keys()))
|
|
65
75
|
|
|
66
76
|
# Increment the statement counter
|
|
67
|
-
options['statementCount']
|
|
68
|
-
max_statements = options.get('maxStatements', DEFAULT_MAX_STATEMENTS)
|
|
77
|
+
options['statementCount'] += 1
|
|
69
78
|
if max_statements > 0 and options['statementCount'] > max_statements:
|
|
70
79
|
raise BareScriptRuntimeError(script, statement, f'Exceeded maximum script statements ({max_statements})')
|
|
71
80
|
|
|
72
81
|
# Record the statement coverage
|
|
73
|
-
coverage_global = globals_.get(SYSTEM_GLOBAL_COVERAGE_NAME)
|
|
74
|
-
has_coverage = coverage_global is not None and isinstance(coverage_global, dict) and \
|
|
75
|
-
coverage_global.get('enabled') and not script.get('system')
|
|
76
82
|
if has_coverage:
|
|
83
|
+
statement_key = next(iter(statement.keys()))
|
|
77
84
|
_record_statement_coverage(script, statement, statement_key, coverage_global)
|
|
78
85
|
|
|
79
86
|
# Expression?
|
|
80
|
-
if
|
|
81
|
-
|
|
82
|
-
|
|
87
|
+
if 'expr' in statement:
|
|
88
|
+
stmt_expr = statement['expr']
|
|
89
|
+
expr_value = evaluate_expression(stmt_expr['expr'], options, locals_, False, script, statement)
|
|
90
|
+
expr_name = stmt_expr.get('name')
|
|
83
91
|
if expr_name is not None:
|
|
84
92
|
if locals_ is not None:
|
|
85
93
|
locals_[expr_name] = expr_value
|
|
@@ -87,24 +95,25 @@ def _execute_script_helper(script, statements, options, locals_):
|
|
|
87
95
|
globals_[expr_name] = expr_value
|
|
88
96
|
|
|
89
97
|
# Jump?
|
|
90
|
-
elif
|
|
98
|
+
elif 'jump' in statement:
|
|
99
|
+
stmt_jump = statement['jump']
|
|
91
100
|
# Evaluate the expression (if any)
|
|
92
|
-
if 'expr' not in
|
|
93
|
-
value_boolean(evaluate_expression(
|
|
101
|
+
if 'expr' not in stmt_jump or \
|
|
102
|
+
value_boolean(evaluate_expression(stmt_jump['expr'], options, locals_, False, script, statement)):
|
|
94
103
|
# Find the label
|
|
95
|
-
|
|
96
|
-
|
|
104
|
+
jump_label = stmt_jump['label']
|
|
105
|
+
if label_indexes is not None and jump_label in label_indexes:
|
|
106
|
+
ix_statement = label_indexes[jump_label]
|
|
97
107
|
else:
|
|
98
|
-
jump_label = statement['jump']['label']
|
|
99
108
|
ix_label = next(
|
|
100
109
|
(ix_stmt for ix_stmt, stmt in enumerate(statements) if 'label' in stmt and stmt['label']['name'] == jump_label),
|
|
101
110
|
-1
|
|
102
111
|
)
|
|
103
112
|
if ix_label == -1:
|
|
104
|
-
raise BareScriptRuntimeError(script, statement, f"Unknown jump label \"{
|
|
113
|
+
raise BareScriptRuntimeError(script, statement, f"Unknown jump label \"{jump_label}\"")
|
|
105
114
|
if label_indexes is None:
|
|
106
115
|
label_indexes = {}
|
|
107
|
-
label_indexes[
|
|
116
|
+
label_indexes[jump_label] = ix_label
|
|
108
117
|
ix_statement = ix_label
|
|
109
118
|
|
|
110
119
|
# Record the label statement coverage
|
|
@@ -114,17 +123,19 @@ def _execute_script_helper(script, statements, options, locals_):
|
|
|
114
123
|
_record_statement_coverage(script, label_statement, label_statement_key, coverage_global)
|
|
115
124
|
|
|
116
125
|
# Return?
|
|
117
|
-
elif
|
|
118
|
-
|
|
119
|
-
|
|
126
|
+
elif 'return' in statement:
|
|
127
|
+
stmt_return = statement['return']
|
|
128
|
+
if 'expr' in stmt_return:
|
|
129
|
+
return evaluate_expression(stmt_return['expr'], options, locals_, False, script, statement)
|
|
120
130
|
return None
|
|
121
131
|
|
|
122
132
|
# Function?
|
|
123
|
-
elif
|
|
124
|
-
|
|
133
|
+
elif 'function' in statement:
|
|
134
|
+
stmt_function = statement['function']
|
|
135
|
+
globals_[stmt_function['name']] = functools.partial(_script_function, script, stmt_function)
|
|
125
136
|
|
|
126
137
|
# Include?
|
|
127
|
-
elif
|
|
138
|
+
elif 'include' in statement:
|
|
128
139
|
system_prefix = options.get('systemPrefix')
|
|
129
140
|
fetch_fn = options.get('fetchFn')
|
|
130
141
|
log_fn = options.get('logFn')
|
|
@@ -239,50 +250,56 @@ def evaluate_expression(expr, options=None, locals_=None, builtins=True, script=
|
|
|
239
250
|
:raises BareScriptRuntimeError: A script runtime error occurred
|
|
240
251
|
"""
|
|
241
252
|
|
|
242
|
-
expr_key, = expr.keys()
|
|
243
253
|
globals_ = options.get('globals') if options is not None else None
|
|
244
254
|
|
|
245
255
|
# Number
|
|
246
|
-
if
|
|
256
|
+
if 'number' in expr:
|
|
247
257
|
return expr['number']
|
|
248
258
|
|
|
249
259
|
# String
|
|
250
|
-
if
|
|
260
|
+
if 'string' in expr:
|
|
251
261
|
return expr['string']
|
|
252
262
|
|
|
253
263
|
# Variable
|
|
254
|
-
if
|
|
264
|
+
if 'variable' in expr:
|
|
265
|
+
variable = expr['variable']
|
|
266
|
+
|
|
255
267
|
# Keywords
|
|
256
|
-
if
|
|
268
|
+
if variable == 'null':
|
|
257
269
|
return None
|
|
258
|
-
if
|
|
270
|
+
if variable == 'false':
|
|
259
271
|
return False
|
|
260
|
-
if
|
|
272
|
+
if variable == 'true':
|
|
261
273
|
return True
|
|
262
274
|
|
|
263
275
|
# Get the local or global variable value or None if undefined
|
|
264
|
-
if locals_ is not None and
|
|
265
|
-
return locals_[
|
|
276
|
+
if locals_ is not None and variable in locals_:
|
|
277
|
+
return locals_[variable]
|
|
266
278
|
else:
|
|
267
|
-
return globals_.get(
|
|
279
|
+
return globals_.get(variable) if globals_ is not None else None
|
|
268
280
|
|
|
269
281
|
# Function
|
|
270
|
-
if
|
|
282
|
+
if 'function' in expr:
|
|
283
|
+
func = expr['function']
|
|
284
|
+
|
|
271
285
|
# "if" built-in function?
|
|
272
|
-
func_name =
|
|
286
|
+
func_name = func['name']
|
|
273
287
|
if func_name == 'if':
|
|
274
|
-
args_expr =
|
|
288
|
+
args_expr = func.get('args', ())
|
|
275
289
|
args_expr_length = len(args_expr)
|
|
276
290
|
value_expr = args_expr[0] if args_expr_length >= 1 else None
|
|
277
291
|
true_expr = args_expr[1] if args_expr_length >= 2 else None
|
|
278
292
|
false_expr = args_expr[2] if args_expr_length >= 3 else None
|
|
279
|
-
value = evaluate_expression(value_expr, options, locals_, builtins, script, statement)
|
|
293
|
+
value = evaluate_expression(value_expr, options, locals_, builtins, script, statement) \
|
|
294
|
+
if value_expr is not None else False
|
|
280
295
|
result_expr = true_expr if value_boolean(value) else false_expr
|
|
281
|
-
return evaluate_expression(result_expr, options, locals_, builtins, script, statement)
|
|
296
|
+
return evaluate_expression(result_expr, options, locals_, builtins, script, statement) \
|
|
297
|
+
if result_expr is not None else None
|
|
282
298
|
|
|
283
299
|
# Compute the function arguments
|
|
284
|
-
|
|
285
|
-
|
|
300
|
+
args_expr = func.get('args')
|
|
301
|
+
func_args = [evaluate_expression(arg, options, locals_, builtins, script, statement) for arg in args_expr] \
|
|
302
|
+
if args_expr is not None else None
|
|
286
303
|
|
|
287
304
|
# Global/local function?
|
|
288
305
|
if locals_ is not None and func_name in locals_:
|
|
@@ -300,7 +317,10 @@ def evaluate_expression(expr, options=None, locals_=None, builtins=True, script=
|
|
|
300
317
|
except Exception as error:
|
|
301
318
|
# Log and return null
|
|
302
319
|
if options is not None and 'logFn' in options and options.get('debug'):
|
|
303
|
-
|
|
320
|
+
error_message = BareScriptRuntimeError(
|
|
321
|
+
script, statement, f'BareScript: Function "{func_name}" failed with error: {error}'
|
|
322
|
+
)
|
|
323
|
+
options['logFn'](str(error_message))
|
|
304
324
|
if isinstance(error, ValueArgsError):
|
|
305
325
|
return error.return_value
|
|
306
326
|
return None
|
|
@@ -308,54 +328,57 @@ def evaluate_expression(expr, options=None, locals_=None, builtins=True, script=
|
|
|
308
328
|
raise BareScriptRuntimeError(script, statement, f'Undefined function "{func_name}"')
|
|
309
329
|
|
|
310
330
|
# Binary expression
|
|
311
|
-
if
|
|
312
|
-
|
|
313
|
-
|
|
331
|
+
if 'binary' in expr:
|
|
332
|
+
binary = expr['binary']
|
|
333
|
+
bin_op = binary['op']
|
|
334
|
+
left_value = evaluate_expression(binary['left'], options, locals_, builtins, script, statement)
|
|
314
335
|
|
|
315
336
|
# Short-circuiting "and" binary operator
|
|
316
337
|
if bin_op == '&&':
|
|
317
338
|
if not value_boolean(left_value):
|
|
318
339
|
return left_value
|
|
319
|
-
return evaluate_expression(
|
|
340
|
+
return evaluate_expression(binary['right'], options, locals_, builtins, script, statement)
|
|
320
341
|
|
|
321
342
|
# Short-circuiting "or" binary operator
|
|
322
343
|
elif bin_op == '||':
|
|
323
344
|
if value_boolean(left_value):
|
|
324
345
|
return left_value
|
|
325
|
-
return evaluate_expression(
|
|
346
|
+
return evaluate_expression(binary['right'], options, locals_, builtins, script, statement)
|
|
326
347
|
|
|
327
348
|
# Non-short-circuiting binary operators
|
|
328
|
-
right_value = evaluate_expression(
|
|
349
|
+
right_value = evaluate_expression(binary['right'], options, locals_, builtins, script, statement)
|
|
350
|
+
left_type = type(left_value)
|
|
351
|
+
right_type = type(right_value)
|
|
329
352
|
if bin_op == '+':
|
|
330
353
|
# number + number
|
|
331
|
-
if (
|
|
332
|
-
|
|
354
|
+
if ((left_type is int or left_type is float) and
|
|
355
|
+
(right_type is int or right_type is float)):
|
|
333
356
|
return left_value + right_value
|
|
334
357
|
|
|
335
358
|
# string + string
|
|
336
|
-
elif
|
|
359
|
+
elif left_type is str and right_type is str:
|
|
337
360
|
return left_value + right_value
|
|
338
361
|
|
|
339
362
|
# string + <any>
|
|
340
|
-
elif
|
|
363
|
+
elif left_type is str:
|
|
341
364
|
return left_value + value_string(right_value)
|
|
342
|
-
elif
|
|
365
|
+
elif right_type is str:
|
|
343
366
|
return value_string(left_value) + right_value
|
|
344
367
|
|
|
345
368
|
# datetime + number
|
|
346
369
|
elif (isinstance(left_value, datetime.date) and
|
|
347
|
-
|
|
370
|
+
(right_type is int or right_type is float)):
|
|
348
371
|
left_dt = value_normalize_datetime(left_value)
|
|
349
372
|
return left_dt + datetime.timedelta(milliseconds=right_value)
|
|
350
|
-
elif (
|
|
373
|
+
elif ((left_type is int or left_type is float) and
|
|
351
374
|
isinstance(right_value, datetime.date)):
|
|
352
375
|
right_dt = value_normalize_datetime(right_value)
|
|
353
376
|
return right_dt + datetime.timedelta(milliseconds=left_value)
|
|
354
377
|
|
|
355
378
|
elif bin_op == '-':
|
|
356
379
|
# number - number
|
|
357
|
-
if (
|
|
358
|
-
|
|
380
|
+
if ((left_type is int or left_type is float) and
|
|
381
|
+
(right_type is int or right_type is float)):
|
|
359
382
|
return left_value - right_value
|
|
360
383
|
|
|
361
384
|
# datetime - datetime
|
|
@@ -366,90 +389,104 @@ def evaluate_expression(expr, options=None, locals_=None, builtins=True, script=
|
|
|
366
389
|
|
|
367
390
|
elif bin_op == '*':
|
|
368
391
|
# number * number
|
|
369
|
-
if (
|
|
370
|
-
|
|
392
|
+
if ((left_type is int or left_type is float) and
|
|
393
|
+
(right_type is int or right_type is float)):
|
|
371
394
|
return left_value * right_value
|
|
372
395
|
|
|
373
396
|
elif bin_op == '/':
|
|
374
397
|
# number / number
|
|
375
|
-
if (
|
|
376
|
-
|
|
398
|
+
if ((left_type is int or left_type is float) and
|
|
399
|
+
(right_type is int or right_type is float)):
|
|
377
400
|
return left_value / right_value
|
|
378
401
|
|
|
379
402
|
elif bin_op == '==':
|
|
403
|
+
if (left_type is int or left_type is float) and (right_type is int or right_type is float):
|
|
404
|
+
return left_value == right_value
|
|
380
405
|
return value_compare(left_value, right_value) == 0
|
|
381
406
|
|
|
382
407
|
elif bin_op == '!=':
|
|
408
|
+
if (left_type is int or left_type is float) and (right_type is int or right_type is float):
|
|
409
|
+
return left_value != right_value
|
|
383
410
|
return value_compare(left_value, right_value) != 0
|
|
384
411
|
|
|
385
412
|
elif bin_op == '<=':
|
|
413
|
+
if (left_type is int or left_type is float) and (right_type is int or right_type is float):
|
|
414
|
+
return left_value <= right_value
|
|
386
415
|
return value_compare(left_value, right_value) <= 0
|
|
387
416
|
|
|
388
417
|
elif bin_op == '<':
|
|
418
|
+
if (left_type is int or left_type is float) and (right_type is int or right_type is float):
|
|
419
|
+
return left_value < right_value
|
|
389
420
|
return value_compare(left_value, right_value) < 0
|
|
390
421
|
|
|
391
422
|
elif bin_op == '>=':
|
|
423
|
+
if (left_type is int or left_type is float) and (right_type is int or right_type is float):
|
|
424
|
+
return left_value >= right_value
|
|
392
425
|
return value_compare(left_value, right_value) >= 0
|
|
393
426
|
|
|
394
427
|
elif bin_op == '>':
|
|
428
|
+
if (left_type is int or left_type is float) and (right_type is int or right_type is float):
|
|
429
|
+
return left_value > right_value
|
|
395
430
|
return value_compare(left_value, right_value) > 0
|
|
396
431
|
|
|
397
432
|
elif bin_op == '%':
|
|
398
433
|
# number % number
|
|
399
|
-
if (
|
|
400
|
-
|
|
434
|
+
if ((left_type is int or left_type is float) and
|
|
435
|
+
(right_type is int or right_type is float)):
|
|
401
436
|
return left_value % right_value
|
|
402
437
|
|
|
403
438
|
elif bin_op == '**':
|
|
404
439
|
# number ** number
|
|
405
|
-
if (
|
|
406
|
-
|
|
440
|
+
if ((left_type is int or left_type is float) and
|
|
441
|
+
(right_type is int or right_type is float)):
|
|
407
442
|
return left_value ** right_value
|
|
408
443
|
|
|
409
444
|
elif bin_op == '&':
|
|
410
445
|
# int & int
|
|
411
|
-
if (
|
|
412
|
-
|
|
446
|
+
if ((left_type is int or left_type is float) and int(left_value) == left_value and
|
|
447
|
+
(right_type is int or right_type is float) and int(right_value) == right_value):
|
|
413
448
|
return int(left_value) & int(right_value)
|
|
414
449
|
|
|
415
450
|
elif bin_op == '|':
|
|
416
451
|
# int & int
|
|
417
|
-
if (
|
|
418
|
-
|
|
452
|
+
if ((left_type is int or left_type is float) and int(left_value) == left_value and
|
|
453
|
+
(right_type is int or right_type is float) and int(right_value) == right_value):
|
|
419
454
|
return int(left_value) | int(right_value)
|
|
420
455
|
|
|
421
456
|
elif bin_op == '^':
|
|
422
457
|
# int & int
|
|
423
|
-
if (
|
|
424
|
-
|
|
458
|
+
if ((left_type is int or left_type is float) and int(left_value) == left_value and
|
|
459
|
+
(right_type is int or right_type is float) and int(right_value) == right_value):
|
|
425
460
|
return int(left_value) ^ int(right_value)
|
|
426
461
|
|
|
427
462
|
elif bin_op == '<<':
|
|
428
463
|
# int & int
|
|
429
|
-
if (
|
|
430
|
-
|
|
464
|
+
if ((left_type is int or left_type is float) and int(left_value) == left_value and
|
|
465
|
+
(right_type is int or right_type is float) and int(right_value) == right_value):
|
|
431
466
|
return int(left_value) << int(right_value)
|
|
432
467
|
|
|
433
468
|
else: # bin_op == '>>':
|
|
434
469
|
# int & int
|
|
435
|
-
if (
|
|
436
|
-
|
|
470
|
+
if ((left_type is int or left_type is float) and int(left_value) == left_value and
|
|
471
|
+
(right_type is int or right_type is float) and int(right_value) == right_value):
|
|
437
472
|
return int(left_value) >> int(right_value)
|
|
438
473
|
|
|
439
474
|
# Invalid operation values
|
|
440
475
|
return None
|
|
441
476
|
|
|
442
477
|
# Unary expression
|
|
443
|
-
if
|
|
444
|
-
|
|
445
|
-
|
|
478
|
+
if 'unary' in expr:
|
|
479
|
+
unary = expr['unary']
|
|
480
|
+
unary_op = unary['op']
|
|
481
|
+
value = evaluate_expression(unary['expr'], options, locals_, builtins, script, statement)
|
|
446
482
|
if unary_op == '!':
|
|
447
483
|
return not value_boolean(value)
|
|
448
|
-
|
|
449
|
-
|
|
484
|
+
val_type = type(value)
|
|
485
|
+
if unary_op == '-':
|
|
486
|
+
if val_type is int or val_type is float:
|
|
450
487
|
return -value
|
|
451
488
|
else: # unary_op == '~':
|
|
452
|
-
if
|
|
489
|
+
if (val_type is int or val_type is float) and int(value) == value:
|
|
453
490
|
return ~int(value)
|
|
454
491
|
|
|
455
492
|
# Invalid operation value
|