json: parsing of keywords
This commit is contained in:
parent
f754328a91
commit
f6bd122f4b
20
zcl/json/didyoumean.go
Normal file
20
zcl/json/didyoumean.go
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package json
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/agext/levenshtein"
|
||||||
|
)
|
||||||
|
|
||||||
|
var keywords = []string{"false", "true", "null"}
|
||||||
|
|
||||||
|
// keywordSuggestion tries to find a valid JSON keyword that is close to the
|
||||||
|
// given string and returns it if found. If no keyword is close enough, returns
|
||||||
|
// the empty string.
|
||||||
|
func keywordSuggestion(given string) string {
|
||||||
|
for _, kw := range keywords {
|
||||||
|
dist := levenshtein.Distance(given, kw, nil)
|
||||||
|
if dist < 3 { // threshold determined experimentally
|
||||||
|
return kw
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
49
zcl/json/didyoumean_test.go
Normal file
49
zcl/json/didyoumean_test.go
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
package json
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestKeywordSuggestion(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
Input, Want string
|
||||||
|
}{
|
||||||
|
{"true", "true"},
|
||||||
|
{"false", "false"},
|
||||||
|
{"null", "null"},
|
||||||
|
{"bananas", ""},
|
||||||
|
{"NaN", ""},
|
||||||
|
{"Inf", ""},
|
||||||
|
{"Infinity", ""},
|
||||||
|
{"void", ""},
|
||||||
|
{"undefined", ""},
|
||||||
|
|
||||||
|
{"ture", "true"},
|
||||||
|
{"tru", "true"},
|
||||||
|
{"tre", "true"},
|
||||||
|
{"treu", "true"},
|
||||||
|
{"rtue", "true"},
|
||||||
|
|
||||||
|
{"flase", "false"},
|
||||||
|
{"fales", "false"},
|
||||||
|
{"flse", "false"},
|
||||||
|
{"fasle", "false"},
|
||||||
|
{"fasel", "false"},
|
||||||
|
{"flue", "false"},
|
||||||
|
|
||||||
|
{"nil", "null"},
|
||||||
|
{"nul", "null"},
|
||||||
|
{"unll", "null"},
|
||||||
|
{"nll", "null"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.Input, func(t *testing.T) {
|
||||||
|
got := keywordSuggestion(test.Input)
|
||||||
|
if got != test.Want {
|
||||||
|
t.Errorf(
|
||||||
|
"wrong result\ninput: %q\ngot: %q\nwant: %q",
|
||||||
|
test.Input, got, test.Want,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,8 @@
|
|||||||
package json
|
package json
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/apparentlymart/go-zcl/zcl"
|
"github.com/apparentlymart/go-zcl/zcl"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -180,5 +182,46 @@ func parseString(p *peeker) (node, zcl.Diagnostics) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func parseKeyword(p *peeker) (node, zcl.Diagnostics) {
|
func parseKeyword(p *peeker) (node, zcl.Diagnostics) {
|
||||||
return nil, nil
|
tok := p.Read()
|
||||||
|
s := string(tok.Bytes)
|
||||||
|
|
||||||
|
switch s {
|
||||||
|
case "true":
|
||||||
|
return &booleanVal{
|
||||||
|
Value: true,
|
||||||
|
SrcRange: tok.Range,
|
||||||
|
}, nil
|
||||||
|
case "false":
|
||||||
|
return &booleanVal{
|
||||||
|
Value: false,
|
||||||
|
SrcRange: tok.Range,
|
||||||
|
}, nil
|
||||||
|
case "null":
|
||||||
|
return &nullVal{
|
||||||
|
SrcRange: tok.Range,
|
||||||
|
}, nil
|
||||||
|
case "undefined", "NaN", "Infinity":
|
||||||
|
return nil, zcl.Diagnostics{
|
||||||
|
{
|
||||||
|
Severity: zcl.DiagError,
|
||||||
|
Summary: "Invalid JSON keyword",
|
||||||
|
Detail: fmt.Sprintf("The JavaScript identifier %q cannot be used in JSON.", s),
|
||||||
|
Subject: &tok.Range,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
var dym string
|
||||||
|
if suggest := keywordSuggestion(s); suggest != "" {
|
||||||
|
dym = fmt.Sprintf(" Did you mean %q?", suggest)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, zcl.Diagnostics{
|
||||||
|
{
|
||||||
|
Severity: zcl.DiagError,
|
||||||
|
Summary: "Invalid JSON keyword",
|
||||||
|
Detail: fmt.Sprintf("%q is not a valid JSON keyword.%s", s, dym),
|
||||||
|
Subject: &tok.Range,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
80
zcl/json/parser_test.go
Normal file
80
zcl/json/parser_test.go
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
package json
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/apparentlymart/go-zcl/zcl"
|
||||||
|
"github.com/davecgh/go-spew/spew"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParse(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
Input string
|
||||||
|
Want node
|
||||||
|
DiagCount int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
`true`,
|
||||||
|
&booleanVal{
|
||||||
|
Value: true,
|
||||||
|
SrcRange: zcl.Range{
|
||||||
|
Filename: "",
|
||||||
|
Start: zcl.Pos{Line: 1, Column: 1, Byte: 0},
|
||||||
|
End: zcl.Pos{Line: 1, Column: 5, Byte: 4},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
`false`,
|
||||||
|
&booleanVal{
|
||||||
|
Value: false,
|
||||||
|
SrcRange: zcl.Range{
|
||||||
|
Filename: "",
|
||||||
|
Start: zcl.Pos{Line: 1, Column: 1, Byte: 0},
|
||||||
|
End: zcl.Pos{Line: 1, Column: 6, Byte: 5},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
`null`,
|
||||||
|
&nullVal{
|
||||||
|
SrcRange: zcl.Range{
|
||||||
|
Filename: "",
|
||||||
|
Start: zcl.Pos{Line: 1, Column: 1, Byte: 0},
|
||||||
|
End: zcl.Pos{Line: 1, Column: 5, Byte: 4},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
`undefined`,
|
||||||
|
nil,
|
||||||
|
1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
`flase`,
|
||||||
|
nil,
|
||||||
|
1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.Input, func(t *testing.T) {
|
||||||
|
got, diag := parseFileContent([]byte(test.Input), "")
|
||||||
|
|
||||||
|
if len(diag) != test.DiagCount {
|
||||||
|
t.Errorf("got %d diagnostics; want %d\n%s", len(diag), test.DiagCount, spew.Sdump(diag))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(got, test.Want) {
|
||||||
|
t.Errorf(
|
||||||
|
"wrong result\ninput: %s\ngot: %#v\nwant: %#v",
|
||||||
|
test.Input, got, test.Want,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user