Skip to content

Commit

Permalink
Replaces the Angle parser with one written in BNF form using PLY. Rep…
Browse files Browse the repository at this point in the history
…laces the existing pyparsing-based parsers in the units package with PLY-based ones.
  • Loading branch information
mdboom committed May 10, 2013
1 parent e07f417 commit f90bc56
Show file tree
Hide file tree
Showing 23 changed files with 6,221 additions and 8,115 deletions.
590 changes: 295 additions & 295 deletions astropy/coordinates/angle_utilities.py

Large diffs are not rendered by default.

118 changes: 18 additions & 100 deletions astropy/coordinates/angles.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,6 @@
TWOPI = math.pi * 2.0 # no need to calculate this all the time


#used in Angle initializer to convert various strings into their parseable forms
_unitstrmap = OrderedDict([
("degrees", 'd'),
("degree", 'd'),
("deg", 'd'),
("°", 'd'),
("hours", 'h'),
("hour", 'h'),
("hr", 'h'),
("radians", ''),
("radian", ''),
("rad", ''),
("d", 'd'),
("h", 'h')])


class Angle(object):
""" An angle.
Expand Down Expand Up @@ -90,85 +74,26 @@ def __init__(self, angle, unit=None, bounds=(-360, 360)):
# -------------------------------
# unit validation and angle value
# -------------------------------
if isinstance(unit, u.UnitBase):
pass
elif isinstance(unit, basestring):
if unit is not None:
unit = u.Unit(unit)
elif unit is None:
# try to determine unit from the "angle" value
if self.is_array:
# this is currently unreachable, but in principal should work when arrays are added in the future
try:
angle = [x.lower() for x in angle]
except AttributeError:
# If units are not specified as a parameter, the only chance
# to determine them is in a string value - if it's not a string,
# then there's not enough information to create an Angle.
raise u.UnitsException("Could not parse an angle value "
"in the array provided - units could "
"not be determined.".format(angle[idx]))
for idx, a in enumerate(angle):
a_unit = None
# order is important here - longest name first
for unitStr in ["degrees", "degree", "deg", "°"]:
if unitStr in a:
a_unit = u.radian
a = angle.replace(unitStr, "")
angle[idx] = math.radians(util.parse_degrees(a))
break
if unit is None:
for unitStr in ["hours", "hour", "hr"]:
if unitStr in a:
a_unit = u.radian
a = angle.replace(unitStr, "")
angle[idx] = math.radians(util.parse_hours(a) * 15.)
break
if unit is None:
for unitStr in ["radians", "radian", "rad"]:
if unitStr in angle:
a_unit = u.radian
a = angle.replace(unitStr, "")
angle[idx] = util.parse_radians(a)
break
if a_unit is None:
raise u.UnitsException('Could not parse the angle value '
'"{0}" - units could not be determined.'
.format(angle[idx]))
unit = u.radian

else: # single value
if isinstance(angle, basestring):
inputangle = angle
angle = angle.lower().strip()

for fromstr, tostr in _unitstrmap.iteritems():
if fromstr in angle:
angle = angle.replace(fromstr, tostr)
if tostr == "h":
unit = u.hour
# this is for "1:2:3.4 hours" case
if angle[-1] == 'h':
angle = angle[:-1]
elif tostr == "d":
unit = u.degree
# this is for "1:2:3.4 degrees" case
if angle[-1] == 'd':
angle = angle[:-1]
elif tostr == "":
unit = u.radian
else:
raise ValueError('Unrecognized tostr... this should never happen!')
break
else:
raise u.UnitsException('Could not infer Angle units '
'from provided string "{0}"'.format(inputangle))

if unit is u.hour:
unit = u.hourangle

if isinstance(angle, basestring):
angle, found_unit = util.parse_angle(angle, unit)
unit = found_unit

else:
raise u.UnitsException('Requested unit "{0}" for Angle, which could not be '
'interpreted as a unit - should be a string or astropy.units '
'unit object'.format(unit))
if isinstance(angle, tuple):
if unit is u.hourangle:
util.check_hms_ranges(*angle)
angle = util.hms_to_hours(*angle)
elif unit is u.degree:
angle = util.dms_to_degrees(*angle)
else:
raise u.UnitsException(
"Can not parse '{0}' as unit '{1}'".format(
angle, unit))

if unit is None:
raise u.UnitsException("No unit was specified in Angle initializer; the "
Expand All @@ -179,14 +104,7 @@ def __init__(self, angle, unit=None, bounds=(-360, 360)):
if self.is_array:
pass # already performed conversions to radians above
else:
if unit is u.degree:
self._radians = math.radians(util.parse_degrees(angle))
elif unit is u.radian:
self._radians = float(angle)
elif unit is u.hour:
self._radians = util.hours_to_radians(util.parse_hours(angle))
else:
raise u.UnitsException("The unit value provided was not one of u.degree, u.hour, u.radian'.")
self._radians = unit.to(u.radian, angle)

# ---------------
# bounds checking
Expand All @@ -203,7 +121,7 @@ def __init__(self, angle, unit=None, bounds=(-360, 360)):
elif unit is u.degree:
lower_bound = math.radians(bounds[0])
upper_bound = math.radians(bounds[1])
elif unit is u.hour:
elif unit is u.hourangle:
lower_bound = math.radians(bounds[0] * 15.)
upper_bound = math.radians(bounds[1] * 15.)
# invalid units handled above
Expand Down
3 changes: 2 additions & 1 deletion astropy/coordinates/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ def test_create_angles():
a5 = Angle("54.12412 degrees")
a6 = Angle(u"54.12412°") # because we like Unicode
a7 = Angle((54, 7, 26.832), unit=u.degree)
a8 = Angle(u"54°07'26.832\"")
# (deg,min,sec) *tuples* are acceptable, but lists/arrays are *not*
# because of the need to eventually support arrays of coordinates
with raises(NotImplementedError):
Expand Down Expand Up @@ -148,7 +149,7 @@ def test_angle_ops():
with raises(NotImplementedError):
a1 * a2

assert (a1 * 2).hours == 2 * 3.60827466667
npt.assert_almost_equal((a1 * 2).hours, 2 * 3.6082746666700003)
assert abs((a1 / 3.123456).hours - 3.60827466667 / 3.123456) < 1e-10

# commutativity
Expand Down
4 changes: 4 additions & 0 deletions astropy/extern/ply/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# PLY package
# Author: David Beazley ([email protected])

__all__ = ['lex','yacc']
Loading

0 comments on commit f90bc56

Please sign in to comment.