Skip to content

Commit

Permalink
Parsing negative numbers a-la ruby, closes #383 and #382
Browse files Browse the repository at this point in the history
This bugfix introduces negative numbers into our parser:
before, -10 would be parsed as (minus, number) whereas
right now it's going to be parsed as (number) only.

This fixes a bug with precedences, where `-1.str()`
would be parsed as (-, (1, str)), leading to first
calling the str method on the positive number, then
applying the minus.

Before:

```
⧐  -1.234.str()
ERROR: unknown operator: -STRING
	[1:1]	-1.234.str()

```

After:

```
⧐  -1.234.str()
-1.234
```

If a space is found between the minus and the number,
the old parsing mechanism still applies.

I found inspiration in Ruby, where:

```
$ - 1.to_s()
-@: undefined method `-@' for "1":String

$-1.to_s()
-1

$ -10.3.floor()
-11

$ - 10.3.floor()
-10
```
  • Loading branch information
odino committed Jul 28, 2020
1 parent 71f1a74 commit 127b4df
Show file tree
Hide file tree
Showing 6 changed files with 27 additions and 3 deletions.
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2.2.1
2.2.2
2 changes: 2 additions & 0 deletions evaluator/builtin_functions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,8 @@ func TestBetween(t *testing.T) {
{`1.between(0, 0.9)`, false},
{`1.between(1, 0)`, "arguments to between(min, max) must satisfy min < max (1 < 0 given)"},
{`1.between(1, 2)`, true},
{`-1.between(-10, 0)`, true},
{`-1.between(-10, -2)`, false},
}

testBuiltinFunction(tests, t)
Expand Down
7 changes: 7 additions & 0 deletions lexer/lexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,13 @@ func (l *Lexer) NextToken() token.Token {
tok.Position = l.position
tok.Literal = "-="
l.readChar()
} else if isDigit(l.peekChar()) {
tok.Position = l.position
l.readChar()
literal, kind := l.readNumber()
tok.Type = kind
tok.Literal = "-" + literal
return tok
} else {
tok = l.newToken(token.MINUS)
}
Expand Down
12 changes: 12 additions & 0 deletions lexer/lexer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import (
func TestNextToken(t *testing.T) {
input := `five = 5;
ten = 10;
-10;
- 10;
1 - 2;
add = f(x, y) {
x + y;
};
Expand Down Expand Up @@ -127,6 +130,15 @@ f hello(x, y) {
{token.ASSIGN, "="},
{token.NUMBER, "10"},
{token.SEMICOLON, ";"},
{token.NUMBER, "-10"},
{token.SEMICOLON, ";"},
{token.MINUS, "-"},
{token.NUMBER, "10"},
{token.SEMICOLON, ";"},
{token.NUMBER, "1"},
{token.MINUS, "-"},
{token.NUMBER, "2"},
{token.SEMICOLON, ";"},
{token.IDENT, "add"},
{token.ASSIGN, "="},
{token.FUNCTION, "f"},
Expand Down
1 change: 0 additions & 1 deletion parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,6 @@ func (p *Parser) parsePrefixExpression() ast.Expression {
}

p.nextToken()

expression.Right = p.parseExpression(PREFIX)

return expression
Expand Down
6 changes: 5 additions & 1 deletion parser/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ func TestParsingPrefixExpressions(t *testing.T) {
value interface{}
}{
{"!5;", "!", 5},
{"-15;", "-", 15},
{"- 15;", "-", 15},
{"!foobar;", "!", "foobar"},
{"-foobar;", "-", "foobar"},
{"!true;", "!", true},
Expand Down Expand Up @@ -310,6 +310,10 @@ func TestOperatorPrecedenceParsing(t *testing.T) {
},
{
"3 + 4; -5 * 5",
"(3 + 4)(-5 * 5)",
},
{
"3 + 4; - 5 * 5",
"(3 + 4)((-5) * 5)",
},
{
Expand Down

0 comments on commit 127b4df

Please sign in to comment.