# Path: penman/exceptions.py
# class ConstantError(PenmanError):
#     """Raised when working with invalid constant values."""
# 
# Path: penman/constant.py
# SYMBOL = 'Symbol'
# 
# STRING = 'String'
# 
# INTEGER = 'Integer'
# 
# FLOAT = 'Float'
# 
# NULL = 'Null'
# 
# def type(constant_string: Union[str, None]) -> Type:
#     """
#     Return the type of constant encoded by *constant_string*.
# 
#     Examples:
#         >>> from penman import constant
#         >>> constant.type('-')
#         <Type.SYMBOL: 'Symbol'>
#         >>> constant.type('"foo"')
#         <Type.STRING: 'String'>
#         >>> constant.type('1')
#         <Type.INTEGER: 'Integer'>
#         >>> constant.type('1.2')
#         <Type.FLOAT: 'Float'>
#         >>> constant.type('')
#         <Type.NULL: 'Null'>
#     """
#     if constant_string is None:
#         typ = NULL
#     else:
#         assert isinstance(constant_string, str)
#         value = evaluate(constant_string)
#         typ = _typemap[pytype(value)]
#         if (typ == Type.SYMBOL
#                 and constant_string.startswith('"')
#                 and constant_string.endswith('"')):
#             typ = Type.STRING
#     return typ
# 
# def evaluate(constant_string: Union[str, None]) -> Constant:
#     """
#     Evaluate and return *constant_string*.
# 
#     If *constant_string* is ``None`` or an empty symbol (``''``), this
#     function returns ``None``, while an empty string constant
#     (``'""'``) returns an empty :py:class:`str` object
#     (``''``). Otherwise, symbols are returned unchanged while strings
#     get quotes removed and escape sequences are unescaped. Note that
#     this means it is impossible to recover the original type of
#     strings and symbols once they have been evaluated. For integer and
#     float constants, this function returns the equivalent Python
#     :py:class:`int` or :py:class:`float` objects.
# 
#     Examples:
#         >>> from penman import constant
#         >>> constant.evaluate('-')
#         '-'
#         >>> constant.evaluate('"foo"')
#         'foo'
#         >>> constant.evaluate('1')
#         1
#         >>> constant.evaluate('1.2')
#         1.2
#         >>> constant.evaluate('') is None
#         True
#     """
#     value: Constant = constant_string
#     if value is None or value == '':
#         value = None
#     else:
#         assert isinstance(constant_string, str)
#         if constant_string.startswith('"') ^ constant_string.endswith('"'):
#             raise ConstantError(f'unbalanced quotes: {constant_string}')
#         if constant_string not in ('true', 'false', 'null'):
#             try:
#                 value = json.loads(constant_string, parse_constant=str)
#             except json.JSONDecodeError:
#                 value = constant_string
# 
#     if not (value is None or isinstance(value, (str, int, float))):
#         raise ConstantError(f'invalid constant: {value!r}')
# 
#     return value
# 
# def quote(constant: Constant) -> str:
#     """
#     Return *constant* as a quoted string.
# 
#     If *constant* is ``None``, this function returns an empty string
#     constant (``'""'``). All other types are cast to a string and
#     quoted.
# 
#     Examples:
#         >>> from penman import constant
#         >>> constant.quote(None)
#         '""'
#         >>> constant.quote('')
#         '""'
#         >>> constant.quote('foo')
#         '"foo"'
#         >>> constant.quote('"foo"')
#         '"\\\\"foo\\\\""'
#         >>> constant.quote(1)
#         '"1"'
#         >>> constant.quote(1.5)
#         '"1.5"'
#     """
#     if constant is None:
#         return '""'
#     else:
#         return json.dumps(str(constant))


import pytest
from penman.exceptions import ConstantError
from penman.constant import (
    SYMBOL,
    STRING,
    INTEGER,
    FLOAT,
    NULL,
    type,
    evaluate,
    quote,
)
    assert type('""') == STRING
    assert type('a') == SYMBOL
    assert type('"a"') == STRING
    assert type('"1"') == STRING
    assert type('"escapes \\" and spaces"') == STRING
    assert type('1') == INTEGER
    assert type('1.5') == FLOAT
    assert type('1e-3') == FLOAT


def test_evaluate():
    assert evaluate(None) is None
    assert evaluate('') is None
    assert evaluate('""') == ''
    assert evaluate('a') == 'a'
    assert evaluate('"a"') == 'a'
    assert evaluate('"1"') == '1'
    assert evaluate('"escapes \\" and spaces"') == 'escapes " and spaces'
    assert evaluate('1') == 1
    assert evaluate('1.5') == 1.5
    assert evaluate('1e-3') == 0.001

    # make sure these don't get converted
    assert evaluate('.1') == '.1'
    assert evaluate('true') == 'true'
    assert evaluate('false') == 'false'
    assert evaluate('null') == 'null'
    assert evaluate('NaN') == 'NaN'
    assert evaluate('Infinity') == 'Infinity'
    assert evaluate('+Infinity') == '+Infinity'

# Path: penman/exceptions.py
# class ConstantError(PenmanError):
#     """Raised when working with invalid constant values."""
# 
# Path: penman/constant.py
# SYMBOL = 'Symbol'
# 
# STRING = 'String'
# 
# INTEGER = 'Integer'
# 
# FLOAT = 'Float'
# 
# NULL = 'Null'
# 
# def type(constant_string: Union[str, None]) -> Type:
#     """
#     Return the type of constant encoded by *constant_string*.
# 
#     Examples:
#         >>> from penman import constant
#         >>> constant.type('-')
#         <Type.SYMBOL: 'Symbol'>
#         >>> constant.type('"foo"')
#         <Type.STRING: 'String'>
#         >>> constant.type('1')
#         <Type.INTEGER: 'Integer'>
#         >>> constant.type('1.2')
#         <Type.FLOAT: 'Float'>
#         >>> constant.type('')
#         <Type.NULL: 'Null'>
#     """
#     if constant_string is None:
#         typ = NULL
#     else:
#         assert isinstance(constant_string, str)
#         value = evaluate(constant_string)
#         typ = _typemap[pytype(value)]
#         if (typ == Type.SYMBOL
#                 and constant_string.startswith('"')
#                 and constant_string.endswith('"')):
#             typ = Type.STRING
#     return typ
# 
# def evaluate(constant_string: Union[str, None]) -> Constant:
#     """
#     Evaluate and return *constant_string*.
# 
#     If *constant_string* is ``None`` or an empty symbol (``''``), this
#     function returns ``None``, while an empty string constant
#     (``'""'``) returns an empty :py:class:`str` object
#     (``''``). Otherwise, symbols are returned unchanged while strings
#     get quotes removed and escape sequences are unescaped. Note that
#     this means it is impossible to recover the original type of
#     strings and symbols once they have been evaluated. For integer and
#     float constants, this function returns the equivalent Python
#     :py:class:`int` or :py:class:`float` objects.
# 
#     Examples:
#         >>> from penman import constant
#         >>> constant.evaluate('-')
#         '-'
#         >>> constant.evaluate('"foo"')
#         'foo'
#         >>> constant.evaluate('1')
#         1
#         >>> constant.evaluate('1.2')
#         1.2
#         >>> constant.evaluate('') is None
#         True
#     """
#     value: Constant = constant_string
#     if value is None or value == '':
#         value = None
#     else:
#         assert isinstance(constant_string, str)
#         if constant_string.startswith('"') ^ constant_string.endswith('"'):
#             raise ConstantError(f'unbalanced quotes: {constant_string}')
#         if constant_string not in ('true', 'false', 'null'):
#             try:
#                 value = json.loads(constant_string, parse_constant=str)
#             except json.JSONDecodeError:
#                 value = constant_string
# 
#     if not (value is None or isinstance(value, (str, int, float))):
#         raise ConstantError(f'invalid constant: {value!r}')
# 
#     return value
# 
# def quote(constant: Constant) -> str:
#     """
#     Return *constant* as a quoted string.
# 
#     If *constant* is ``None``, this function returns an empty string
#     constant (``'""'``). All other types are cast to a string and
#     quoted.
# 
#     Examples:
#         >>> from penman import constant
#         >>> constant.quote(None)
#         '""'
#         >>> constant.quote('')
#         '""'
#         >>> constant.quote('foo')
#         '"foo"'
#         >>> constant.quote('"foo"')
#         '"\\\\"foo\\\\""'
#         >>> constant.quote(1)
#         '"1"'
#         >>> constant.quote(1.5)
#         '"1.5"'
#     """
#     if constant is None:
#         return '""'
#     else:
#         return json.dumps(str(constant))


# Path: tests/test_constant.py
import pytest
from penman.exceptions import ConstantError
from penman.constant import (
    SYMBOL,
    STRING,
    INTEGER,
    FLOAT,
    NULL,
    type,
    evaluate,
    quote,
)

    assert type('""') == STRING
    assert type('a') == SYMBOL
    assert type('"a"') == STRING
    assert type('"1"') == STRING
    assert type('"escapes \\" and spaces"') == STRING
    assert type('1') == INTEGER
    assert type('1.5') == FLOAT
    assert type('1e-3') == FLOAT


def test_evaluate():
    assert evaluate(None) is None
    assert evaluate('') is None
    assert evaluate('""') == ''
    assert evaluate('a') == 'a'
    assert evaluate('"a"') == 'a'
    assert evaluate('"1"') == '1'
    assert evaluate('"escapes \\" and spaces"') == 'escapes " and spaces'
    assert evaluate('1') == 1
    assert evaluate('1.5') == 1.5
    assert evaluate('1e-3') == 0.001

    # make sure these don't get converted
    assert evaluate('.1') == '.1'
    assert evaluate('true') == 'true'
    assert evaluate('false') == 'false'
    assert evaluate('null') == 'null'
    assert evaluate('NaN') == 'NaN'
    assert evaluate('Infinity') == 'Infinity'
    assert evaluate('+Infinity') == '+Infinity'

    assert evaluate('-Infinity') == '-Infinity'