Rename grammar's NameRead to PathRead and CheckName to CheckIdentifier, so a name in the engine is only a bound name
This commit is contained in:
@@ -66,7 +66,7 @@ type Token struct {
|
||||
Body string // the braces' content, as written
|
||||
Fn string // BuiltinCall
|
||||
Args []string // BuiltinCall
|
||||
Arms []string // NameRead: the '|' alternatives
|
||||
Arms []string // PathRead: the '|' alternatives
|
||||
// NameBind: the reference it binds, and the name.
|
||||
BoundRef, Bound string
|
||||
}
|
||||
@@ -77,7 +77,7 @@ const (
|
||||
BuiltinCall TokenKind = iota + 1
|
||||
LiteralRun
|
||||
NameBind
|
||||
NameRead
|
||||
PathRead
|
||||
)
|
||||
|
||||
const AsWord = " as "
|
||||
@@ -104,7 +104,7 @@ func ParseFormat(format string) ([]Token, error) {
|
||||
return nil
|
||||
}
|
||||
if indexOutside(u.body, '(') < 0 {
|
||||
toks = append(toks, Token{Kind: NameRead, Body: u.body, Arms: splitOutside(u.body, '|')})
|
||||
toks = append(toks, Token{Kind: PathRead, Body: u.body, Arms: splitOutside(u.body, '|')})
|
||||
return nil
|
||||
}
|
||||
name, args, ok := FuncCall(u.body)
|
||||
|
||||
@@ -27,7 +27,7 @@ func TestParseFormatReadsEachTokenKind(t *testing.T) {
|
||||
}
|
||||
want := []Token{
|
||||
{Kind: LiteralRun, Lit: "a{"},
|
||||
{Kind: NameRead, Body: "x|/y.z[a.b]", Arms: []string{"x", "/y.z[a.b]"}},
|
||||
{Kind: PathRead, Body: "x|/y.z[a.b]", Arms: []string{"x", "/y.z[a.b]"}},
|
||||
{Kind: NameBind, Body: "/p as q", BoundRef: "/p", Bound: "q"},
|
||||
{Kind: BuiltinCall, Body: "f(1, n)", Fn: "f", Args: []string{"1", "n"}},
|
||||
}
|
||||
|
||||
@@ -5,18 +5,18 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// reservedInName is what a category, folder or field name may not contain: a dot
|
||||
// reservedInIdentifier is what an identifier may not contain: a dot
|
||||
// separates the segments of a path, '|' the arms of a token, '(' opens a function
|
||||
// call, braces delimit the token, '/' starts a reference, and brackets and a quote
|
||||
// open a JSON value. A name carrying one is rejected where it is authored rather
|
||||
// open a JSON value. An identifier carrying one is rejected where it is authored rather
|
||||
// than where it would be unreachable.
|
||||
const reservedInName = ".|({}/[]\""
|
||||
const reservedInIdentifier = ".|({}/[]\""
|
||||
|
||||
var reservedList = strings.Join(strings.Split(reservedInName, ""), " ")
|
||||
var reservedList = strings.Join(strings.Split(reservedInIdentifier, ""), " ")
|
||||
|
||||
// CheckName rejects a name the dot path, {token} and JSON grammars cannot spell, or a struct
|
||||
// tag cannot read.
|
||||
func CheckName(name string) error {
|
||||
// CheckIdentifier rejects an identifier, the name of a folder, category, field or column, that the
|
||||
// dot path, {token} and JSON grammars cannot spell, or a struct tag cannot read.
|
||||
func CheckIdentifier(name string) error {
|
||||
if name == "" {
|
||||
return fmt.Errorf("%q is empty, which is not a path segment, so List never offers it", name)
|
||||
}
|
||||
@@ -26,21 +26,21 @@ func CheckName(name string) error {
|
||||
if strings.Contains(name, AsWord) {
|
||||
return fmt.Errorf("%q contains %q, which a token reads as binding a name; rename it", name, AsWord)
|
||||
}
|
||||
if i := strings.IndexAny(name, reservedInName); i >= 0 {
|
||||
if i := strings.IndexAny(name, reservedInIdentifier); i >= 0 {
|
||||
return fmt.Errorf("%q contains %q; a name may not use %s, which the dot path, {token} and JSON grammars reserve",
|
||||
name, name[i:i+1], reservedList)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckPathNames rejects a dotted path with a segment no name may be.
|
||||
func CheckPathNames(path string) error {
|
||||
// CheckPathIdentifiers rejects a dotted path with a segment no identifier may be.
|
||||
func CheckPathIdentifiers(path string) error {
|
||||
segs, err := SplitPath(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, seg := range NameSegments(segs) {
|
||||
if err := CheckName(seg); err != nil {
|
||||
for _, seg := range IdentifierSegments(segs) {
|
||||
if err := CheckIdentifier(seg); err != nil {
|
||||
return fmt.Errorf("path %w", err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package grammar
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestCheckIdentifierRefusesWhatAGrammarReserves(t *testing.T) {
|
||||
for _, name := range []string{"", "-", "a as b", "a.b", "a|b", "a(b", "a{b", "a}b", "a/b", "a[b", "a]b", `a"b`} {
|
||||
if CheckIdentifier(name) == nil {
|
||||
t.Errorf("CheckIdentifier(%q) = nil, want an error", name)
|
||||
}
|
||||
}
|
||||
for _, name := range []string{"a", "postal-code", "é", "a_b"} {
|
||||
if err := CheckIdentifier(name); err != nil {
|
||||
t.Errorf("CheckIdentifier(%q) = %v, want nil", name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package grammar
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestCheckNameRefusesWhatAGrammarReserves(t *testing.T) {
|
||||
for _, name := range []string{"", "-", "a as b", "a.b", "a|b", "a(b", "a{b", "a}b", "a/b", "a[b", "a]b", `a"b`} {
|
||||
if CheckName(name) == nil {
|
||||
t.Errorf("CheckName(%q) = nil, want an error", name)
|
||||
}
|
||||
}
|
||||
for _, name := range []string{"a", "postal-code", "é", "a_b"} {
|
||||
if err := CheckName(name); err != nil {
|
||||
t.Errorf("CheckName(%q) = %v, want nil", name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -150,8 +150,8 @@ func UnspellableInSelector(s string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// NameSegments is the segments of a path that are names, its selectors and ".." left out.
|
||||
func NameSegments(segs []string) []string {
|
||||
// IdentifierSegments is the segments of a path that are identifiers, its selectors and ".." left out.
|
||||
func IdentifierSegments(segs []string) []string {
|
||||
out := segs[:0:0]
|
||||
for _, s := range segs {
|
||||
if !IsSelector(s) && s != ".." {
|
||||
|
||||
@@ -33,9 +33,9 @@ func TestSplitPathRefusesAStraySelectorBracket(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestNameSegmentsDropSelectorsAndStepsUp(t *testing.T) {
|
||||
if got := NameSegments([]string{"a", "[b]", "..", "c"}); !reflect.DeepEqual(got, []string{"a", "c"}) {
|
||||
t.Errorf("NameSegments = %q, want [a c]", got)
|
||||
func TestIdentifierSegmentsDropSelectorsAndStepsUp(t *testing.T) {
|
||||
if got := IdentifierSegments([]string{"a", "[b]", "..", "c"}); !reflect.DeepEqual(got, []string{"a", "c"}) {
|
||||
t.Errorf("IdentifierSegments = %q, want [a c]", got)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ func isJSONStart(arg string) bool {
|
||||
|
||||
// pathAdvice refuses a spelling of path, naming path to write, or why no name can spell it.
|
||||
func pathAdvice(path, refusal string) error {
|
||||
if err := CheckPathNames(path); err != nil {
|
||||
if err := CheckPathIdentifiers(path); err != nil {
|
||||
return err
|
||||
}
|
||||
return fmt.Errorf("%s; write %s", refusal, path)
|
||||
@@ -71,7 +71,7 @@ func loneReference(arg string) (string, bool) {
|
||||
|
||||
// loneRef is the reference a format of one reference token and nothing else reads.
|
||||
func loneRef(toks []Token) (string, bool) {
|
||||
if len(toks) != 1 || toks[0].Kind != NameRead || len(toks[0].Arms) != 1 || !IsRef(toks[0].Arms[0]) {
|
||||
if len(toks) != 1 || toks[0].Kind != PathRead || len(toks[0].Arms) != 1 || !IsRef(toks[0].Arms[0]) {
|
||||
return "", false
|
||||
}
|
||||
return toks[0].Arms[0], true
|
||||
|
||||
Reference in New Issue
Block a user