blob: f153b5116904d426a7f2205840c522f54854755e [file] [log] [blame]
// Copyright 2019 CUE Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ast
import (
"strconv"
"unicode"
"unicode/utf8"
"cuelang.org/go/cue/errors"
"cuelang.org/go/cue/token"
)
func isLetter(ch rune) bool {
return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch >= utf8.RuneSelf && unicode.IsLetter(ch)
}
func isDigit(ch rune) bool {
// TODO(mpvl): Is this correct?
return '0' <= ch && ch <= '9' || ch >= utf8.RuneSelf && unicode.IsDigit(ch)
}
// QuoteIdent quotes an identifier, if needed, and reports
// an error if the identifier is invalid.
func QuoteIdent(ident string) (string, error) {
if ident != "" && ident[0] == '`' {
if _, err := strconv.Unquote(ident); err != nil {
return "", errors.Newf(token.NoPos, "invalid quoted identifier %q", ident)
}
return ident, nil
}
// TODO: consider quoting keywords
// switch ident {
// case "for", "in", "if", "let", "true", "false", "null":
// goto escape
// }
for _, r := range ident {
if isLetter(r) || isDigit(r) || r == '_' {
continue
}
if r == '-' {
goto escape
}
return "", errors.Newf(token.NoPos, "invalid character '%s' in identifier", string(r))
}
return ident, nil
escape:
return "`" + ident + "`", nil
}
// ParseIdent unquotes a possibly quoted identifier and validates
// if the result is valid.
func ParseIdent(n *Ident) (string, error) {
ident := n.Name
if ident == "" {
return "", errors.Newf(n.Pos(), "empty identifier")
}
quoted := false
if ident[0] == '`' {
u, err := strconv.Unquote(ident)
if err != nil {
return "", errors.Newf(n.Pos(), "invalid quoted identifier")
}
ident = u
quoted = true
}
for _, r := range ident {
if isLetter(r) || isDigit(r) || r == '_' {
continue
}
if r == '-' && quoted {
continue
}
return "", errors.Newf(n.Pos(), "invalid character '%s' in identifier", string(r))
}
return ident, nil
}