Commit a9e7c938 authored by Rob Pike's avatar Rob Pike

regexp: change Expr() to String(); add HasOperator method to Regexp.

It reports whether a regular expression has operators
as opposed to matching literal text.

R=rsc, gri
CC=golang-dev
https://golang.org/cl/3731041
parent 8ec6f7cd
...@@ -229,18 +229,21 @@ func TestReplaceAllFunc(t *testing.T) { ...@@ -229,18 +229,21 @@ func TestReplaceAllFunc(t *testing.T) {
} }
} }
type QuoteMetaTest struct { type MetaTest struct {
pattern, output string pattern, output, literal string
isLiteral bool
} }
var quoteMetaTests = []QuoteMetaTest{ var metaTests = []MetaTest{
{``, ``}, {``, ``, ``, true},
{`foo`, `foo`}, {`foo`, `foo`, `foo`, true},
{`!@#$%^&*()_+-=[{]}\|,<.>/?~`, `!@#\$%\^&\*\(\)_\+-=\[{\]}\\\|,<\.>/\?~`}, {`foo\.\$`, `foo\\\.\\\$`, `foo.$`, true}, // has meta but no operator
{`foo.\$`, `foo\.\\\$`, `foo`, false}, // has escaped operators and real operators
{`!@#$%^&*()_+-=[{]}\|,<.>/?~`, `!@#\$%\^&\*\(\)_\+-=\[{\]}\\\|,<\.>/\?~`, `!@#`, false},
} }
func TestQuoteMeta(t *testing.T) { func TestQuoteMeta(t *testing.T) {
for _, tc := range quoteMetaTests { for _, tc := range metaTests {
// Verify that QuoteMeta returns the expected string. // Verify that QuoteMeta returns the expected string.
quoted := QuoteMeta(tc.pattern) quoted := QuoteMeta(tc.pattern)
if quoted != tc.output { if quoted != tc.output {
...@@ -269,14 +272,16 @@ func TestQuoteMeta(t *testing.T) { ...@@ -269,14 +272,16 @@ func TestQuoteMeta(t *testing.T) {
} }
} }
func TestHasMeta(t *testing.T) { func TestLiteralPrefix(t *testing.T) {
for _, tc := range quoteMetaTests { for _, tc := range metaTests {
// HasMeta should be false if QuoteMeta returns the original string; // Literal method needs to scan the pattern.
// true otherwise. re := MustCompile(tc.pattern)
quoted := QuoteMeta(tc.pattern) str, complete := re.LiteralPrefix()
if HasMeta(tc.pattern) != (quoted != tc.pattern) { if complete != tc.isLiteral {
t.Errorf("HasMeta(`%s`) = %t; want %t", t.Errorf("LiteralPrefix(`%s`) = %t; want %t", tc.pattern, complete, tc.isLiteral)
tc.pattern, HasMeta(tc.pattern), quoted != tc.pattern) }
if str != tc.literal {
t.Errorf("LiteralPrefix(`%s`) = `%s`; want `%s`", tc.pattern, str, tc.literal)
} }
} }
} }
......
...@@ -120,8 +120,8 @@ func build(n int, x ...int) [][]int { ...@@ -120,8 +120,8 @@ func build(n int, x ...int) [][]int {
func TestFind(t *testing.T) { func TestFind(t *testing.T) {
for _, test := range findTests { for _, test := range findTests {
re := MustCompile(test.pat) re := MustCompile(test.pat)
if re.Expr() != test.pat { if re.String() != test.pat {
t.Errorf("Expr() = `%s`; should be `%s`", re.Expr(), test.pat) t.Errorf("String() = `%s`; should be `%s`", re.String(), test.pat)
} }
result := re.Find([]byte(test.text)) result := re.Find([]byte(test.text))
switch { switch {
......
...@@ -599,8 +599,8 @@ Loop: ...@@ -599,8 +599,8 @@ Loop:
re.prefix = string(b) re.prefix = string(b)
} }
// Expr returns the source text used to compile the regular expression. // String returns the source text used to compile the regular expression.
func (re *Regexp) Expr() string { func (re *Regexp) String() string {
return re.expr return re.expr
} }
...@@ -849,6 +849,24 @@ func (re *Regexp) doExecute(str string, bytestr []byte, pos int) []int { ...@@ -849,6 +849,24 @@ func (re *Regexp) doExecute(str string, bytestr []byte, pos int) []int {
return final.match.m return final.match.m
} }
// LiteralPrefix returns a literal string that must begin any match
// of the regular expression re. It returns the boolean true if the
// literal string comprises the entire regular expression.
func (re *Regexp) LiteralPrefix() (prefix string, complete bool) {
c := make([]int, len(re.inst)-2) // minus start and end.
// First instruction is start; skip that.
i := 0
for inst := re.inst[0].next; inst.kind != iEnd; inst = inst.next {
// stop if this is not a char
if inst.kind != iChar {
return string(c[:i]), false
}
c[i] = inst.char
i++
}
return string(c[:i]), true
}
// MatchString returns whether the Regexp matches the string s. // MatchString returns whether the Regexp matches the string s.
// The return value is a boolean: true for match, false for no match. // The return value is a boolean: true for match, false for no match.
func (re *Regexp) MatchString(s string) bool { return len(re.doExecute(s, nil, 0)) > 0 } func (re *Regexp) MatchString(s string) bool { return len(re.doExecute(s, nil, 0)) > 0 }
...@@ -1003,18 +1021,6 @@ func QuoteMeta(s string) string { ...@@ -1003,18 +1021,6 @@ func QuoteMeta(s string) string {
return string(b[0:j]) return string(b[0:j])
} }
// HasMeta returns a boolean indicating whether the string contains
// any regular expression metacharacters.
func HasMeta(s string) bool {
// A byte loop is correct because all metacharacters are ASCII.
for i := 0; i < len(s); i++ {
if special(int(s[i])) {
return true
}
}
return false
}
// Find matches in slice b if b is non-nil, otherwise find matches in string s. // Find matches in slice b if b is non-nil, otherwise find matches in string s.
func (re *Regexp) allMatches(s string, b []byte, n int, deliver func([]int)) { func (re *Regexp) allMatches(s string, b []byte, n int, deliver func([]int)) {
var end int var end int
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment