Skip to content

Commit 87c6a94

Browse files
ckganesanGanesan Karuppasamy
and
Ganesan Karuppasamy
authored
Enhance the number parser to include support for parsing hexadecimal, binary, and octal literals. (#483)
* Enhance the number parser to include support for parsing hexadecimal, binary, and octal literals. * added invalid literal test cases * Refactoring code as per comment --------- Co-authored-by: Ganesan Karuppasamy <[email protected]>
1 parent 128b621 commit 87c6a94

File tree

3 files changed

+103
-21
lines changed

3 files changed

+103
-21
lines changed

parser/lexer/lexer_test.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,16 @@ func TestLex(t *testing.T) {
1818
tokens []Token
1919
}{
2020
{
21-
".5 0.025 1 02 1e3 0xFF 1.2e-4 1_000_000 _42 -.5",
21+
".5 0.025 1 02 1e3 0xFF 0b0101 0o600 1.2e-4 1_000_000 _42 -.5",
2222
[]Token{
2323
{Kind: Number, Value: ".5"},
2424
{Kind: Number, Value: "0.025"},
2525
{Kind: Number, Value: "1"},
2626
{Kind: Number, Value: "02"},
2727
{Kind: Number, Value: "1e3"},
2828
{Kind: Number, Value: "0xFF"},
29+
{Kind: Number, Value: "0b0101"},
30+
{Kind: Number, Value: "0o600"},
2931
{Kind: Number, Value: "1.2e-4"},
3032
{Kind: Number, Value: "1_000_000"},
3133
{Kind: Identifier, Value: "_42"},

parser/parser.go

+40-20
Original file line numberDiff line numberDiff line change
@@ -306,40 +306,44 @@ func (p *parser) parseSecondary() Node {
306306
case Number:
307307
p.next()
308308
value := strings.Replace(token.Value, "_", "", -1)
309-
if strings.Contains(value, "x") {
309+
var node Node
310+
valueLower := strings.ToLower(value)
311+
switch {
312+
case strings.HasPrefix(valueLower, "0x"):
310313
number, err := strconv.ParseInt(value, 0, 64)
311314
if err != nil {
312315
p.error("invalid hex literal: %v", err)
313316
}
314-
if number > math.MaxInt {
315-
p.error("integer literal is too large")
316-
return nil
317-
}
318-
node := &IntegerNode{Value: int(number)}
319-
node.SetLocation(token.Location)
320-
return node
321-
} else if strings.ContainsAny(value, ".eE") {
317+
node = p.toIntegerNode(number)
318+
case strings.ContainsAny(valueLower, ".e"):
322319
number, err := strconv.ParseFloat(value, 64)
323320
if err != nil {
324321
p.error("invalid float literal: %v", err)
325322
}
326-
node := &FloatNode{Value: number}
327-
node.SetLocation(token.Location)
328-
return node
329-
} else {
323+
node = p.toFloatNode(number)
324+
case strings.HasPrefix(valueLower, "0b"):
325+
number, err := strconv.ParseInt(value, 0, 64)
326+
if err != nil {
327+
p.error("invalid binary literal: %v", err)
328+
}
329+
node = p.toIntegerNode(number)
330+
case strings.HasPrefix(valueLower, "0o"):
331+
number, err := strconv.ParseInt(value, 0, 64)
332+
if err != nil {
333+
p.error("invalid octal literal: %v", err)
334+
}
335+
node = p.toIntegerNode(number)
336+
default:
330337
number, err := strconv.ParseInt(value, 10, 64)
331338
if err != nil {
332339
p.error("invalid integer literal: %v", err)
333340
}
334-
if number > math.MaxInt {
335-
p.error("integer literal is too large")
336-
return nil
337-
}
338-
node := &IntegerNode{Value: int(number)}
341+
node = p.toIntegerNode(number)
342+
}
343+
if node != nil {
339344
node.SetLocation(token.Location)
340-
return node
341345
}
342-
346+
return node
343347
case String:
344348
p.next()
345349
node := &StringNode{Value: token.Value}
@@ -359,6 +363,22 @@ func (p *parser) parseSecondary() Node {
359363
return p.parsePostfixExpression(node)
360364
}
361365

366+
func (p *parser) toIntegerNode(number int64) Node {
367+
if number > math.MaxInt {
368+
p.error("integer literal is too large")
369+
return nil
370+
}
371+
return &IntegerNode{Value: int(number)}
372+
}
373+
374+
func (p *parser) toFloatNode(number float64) Node {
375+
if number > math.MaxFloat64 {
376+
p.error("float literal is too large")
377+
return nil
378+
}
379+
return &FloatNode{Value: number}
380+
}
381+
362382
func (p *parser) parseCall(token Token) Node {
363383
var node Node
364384
if p.current.Is(Bracket, "(") {

parser/parser_test.go

+60
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,26 @@ func TestParse(t *testing.T) {
3636
"0x6E",
3737
&IntegerNode{Value: 110},
3838
},
39+
{
40+
"0X63",
41+
&IntegerNode{Value: 99},
42+
},
43+
{
44+
"0o600",
45+
&IntegerNode{Value: 384},
46+
},
47+
{
48+
"0O45",
49+
&IntegerNode{Value: 37},
50+
},
51+
{
52+
"0b10",
53+
&IntegerNode{Value: 2},
54+
},
55+
{
56+
"0B101011",
57+
&IntegerNode{Value: 43},
58+
},
3959
{
4060
"10_000_000",
4161
&IntegerNode{Value: 10_000_000},
@@ -549,6 +569,46 @@ foo ?? bar || baz
549569
Operator (||) and coalesce expressions (??) cannot be mixed. Wrap either by parentheses. (1:12)
550570
| foo ?? bar || baz
551571
| ...........^
572+
573+
0b15
574+
bad number syntax: "0b15" (1:5)
575+
| 0b15
576+
| ....^
577+
578+
0X10G
579+
bad number syntax: "0X10G" (1:6)
580+
| 0X10G
581+
| .....^
582+
583+
0o1E
584+
invalid float literal: strconv.ParseFloat: parsing "0o1E": invalid syntax (1:4)
585+
| 0o1E
586+
| ...^
587+
588+
0b1E
589+
invalid float literal: strconv.ParseFloat: parsing "0b1E": invalid syntax (1:4)
590+
| 0b1E
591+
| ...^
592+
593+
0b1E+6
594+
bad number syntax: "0b1E+6" (1:7)
595+
| 0b1E+6
596+
| ......^
597+
598+
0b1E+1
599+
invalid float literal: strconv.ParseFloat: parsing "0b1E+1": invalid syntax (1:6)
600+
| 0b1E+1
601+
| .....^
602+
603+
0o1E+1
604+
invalid float literal: strconv.ParseFloat: parsing "0o1E+1": invalid syntax (1:6)
605+
| 0o1E+1
606+
| .....^
607+
608+
1E
609+
invalid float literal: strconv.ParseFloat: parsing "1E": invalid syntax (1:2)
610+
| 1E
611+
| .^
552612
`
553613

554614
func TestParse_error(t *testing.T) {

0 commit comments

Comments
 (0)