forked from yt-project/unyt
/
_parsing.py
101 lines (83 loc) · 3.05 KB
/
_parsing.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
"""
parsing utilities
"""
# -----------------------------------------------------------------------------
# Copyright (c) 2019, yt Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the LICENSE file, distributed with this software.
# -----------------------------------------------------------------------------
from sympy.parsing.sympy_parser import parse_expr, auto_number, rationalize
from sympy import Basic, Float, Integer, Rational, sqrt, Symbol
import token
from unyt.exceptions import UnitParseError
from unyt._unit_lookup_table import inv_name_alternatives
def _auto_positive_symbol(tokens, local_dict, global_dict):
"""
Inserts calls to ``Symbol`` for undefined variables.
Passes in positive=True as a keyword argument.
Adapted from sympy.sympy.parsing.sympy_parser.auto_symbol
"""
result = []
tokens.append((None, None)) # so zip traverses all tokens
for tok, nextTok in zip(tokens, tokens[1:]):
tokNum, tokVal = tok
nextTokNum, nextTokVal = nextTok
if tokNum == token.NAME:
name = tokVal
if name in global_dict:
obj = global_dict[name]
if isinstance(obj, (Basic, type)) or callable(obj):
result.append((token.NAME, name))
continue
# try to resolve known alternative unit name
try:
used_name = inv_name_alternatives[str(name)]
except KeyError:
# if we don't know this name it's a user-defined unit name
# so we should create a new symbol for it
used_name = str(name)
result.extend(
[
(token.NAME, "Symbol"),
(token.OP, "("),
(token.NAME, repr(used_name)),
(token.OP, ","),
(token.NAME, "positive"),
(token.OP, "="),
(token.NAME, "True"),
(token.OP, ")"),
]
)
else:
result.append((tokNum, tokVal))
return result
global_dict = {
"Symbol": Symbol,
"Integer": Integer,
"Float": Float,
"Rational": Rational,
"sqrt": sqrt,
}
unit_text_transform = (_auto_positive_symbol, auto_number, rationalize)
def parse_unyt_expr(unit_expr):
if not unit_expr:
# Bug catch...
# if unit_expr is an empty string, parse_expr fails hard...
unit_expr = "1"
# Avoid a parse error if someone uses the percent unit and the
# parser tries to interpret it as the modulo operator
unit_expr = unit_expr.replace("%", "percent")
unit_expr = unit_expr.replace("°", "deg")
try:
unit_expr = parse_expr(
unit_expr, global_dict=global_dict, transformations=unit_text_transform
)
except Exception as e:
msg = "Unit expression '%s' raised an error during parsing:\n%s" % (
unit_expr,
repr(e),
)
raise UnitParseError(msg)
return unit_expr