Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions sqlparse/keywords.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,11 +158,17 @@ def find_delimited_spans(text):
# because `re.match` doesn't work with look-behind regexp feature
(r'(?<=\.)[A-Z脌-脺]\w*', tokens.Name), # .'Name'
(r'[A-Z脌-脺]\w*(?=\()', tokens.Name), # side effect: change kw to func
(r'-?0x[\dA-F]+', tokens.Number.Hexadecimal),
(r'-?\d+(\.\d+)?E-?\d+', tokens.Number.Float),
(r'(?![_A-Z脌-脺])-?(\d+(\.\d*)|\.\d+)(?![_A-Z脌-脺])',
# A leading minus is only part of the number if it can't be a binary
# operator, i.e. if it doesn't directly follow an operand (a name, a
# number, a closing bracket or a quoted string or name), so that
# ``1-1`` is lexed as ``1``, ``-``, ``1``. See issue #370.
(r"(?:(?<![\w)\]'\"`])-)?0x[\dA-F]+", tokens.Number.Hexadecimal),
(r"(?:(?<![\w)\]'\"`])-)?\d+(\.\d+)?E-?\d+", tokens.Number.Float),
(r"(?![_A-Z脌-脺])(?:(?<![\w)\]'\"`])-)?(\d+(\.\d*)|\.\d+)"
r"(?![_A-Z脌-脺])",
tokens.Number.Float),
(r'(?![_A-Z脌-脺])-?\d+(?![_A-Z脌-脺])', tokens.Number.Integer),
(r"(?![_A-Z脌-脺])(?:(?<![\w)\]'\"`])-)?\d+(?![_A-Z脌-脺])",
tokens.Number.Integer),
(r"'(''|\\'|[^'])*'", tokens.String.Single),
# not a real string literal in ANSI SQL:
(r'"(""|\\"|[^"])*"', tokens.String.Symbol),
Expand Down
7 changes: 7 additions & 0 deletions tests/test_grouping.py
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,13 @@ def test_identifier_with_operators():
assert isinstance(p.tokens[0], sql.Operation)


def test_operation_minus_without_whitespace():
# issue370
p = sqlparse.parse('select a-1 from foo')[0]
assert isinstance(p.tokens[2], sql.Operation)
assert p.tokens[2].value == 'a-1'


def test_identifier_with_op_trailing_ws():
# make sure trailing whitespace isn't grouped with identifier
p = sqlparse.parse('foo || bar ')[0]
Expand Down
10 changes: 10 additions & 0 deletions tests/test_tokenize.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,16 @@ def test_tokenize_negative_numbers():
assert tokens[2][1] == '-1'


@pytest.mark.parametrize('s', ['1-1', 'a-1', 'a-1.5', 'a-.5', 'a-1e3',
'a-0x1F', 'f(a)-1', 'a[1]-1', "'a'-1",
'"a"-1', '`a`-1'])
def test_tokenize_minus_after_operand(s):
# issue370
tokens = list(lexer.tokenize(s))
assert tokens[-2] == (T.Operator, '-')
assert tokens[-1][1] == s.rsplit('-', 1)[1]


def test_token_str():
token = sql.Token(None, 'FoO')
assert str(token) == 'FoO'
Expand Down