summaryrefslogtreecommitdiff
path: root/libgo/go/regexp/syntax/compile.go
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/regexp/syntax/compile.go')
-rw-r--r--libgo/go/regexp/syntax/compile.go29
1 files changed, 23 insertions, 6 deletions
diff --git a/libgo/go/regexp/syntax/compile.go b/libgo/go/regexp/syntax/compile.go
index 7524d628fe1..c9f9fa024bf 100644
--- a/libgo/go/regexp/syntax/compile.go
+++ b/libgo/go/regexp/syntax/compile.go
@@ -57,8 +57,9 @@ func (l1 patchList) append(p *Prog, l2 patchList) patchList {
// A frag represents a compiled program fragment.
type frag struct {
- i uint32 // index of first instruction
- out patchList // where to record end instruction
+ i uint32 // index of first instruction
+ out patchList // where to record end instruction
+ nullable bool // whether fragment can match empty string
}
type compiler struct {
@@ -159,7 +160,7 @@ func (c *compiler) compile(re *Regexp) frag {
func (c *compiler) inst(op InstOp) frag {
// TODO: impose length limit
- f := frag{i: uint32(len(c.p.Inst))}
+ f := frag{i: uint32(len(c.p.Inst)), nullable: true}
c.p.Inst = append(c.p.Inst, Inst{Op: op})
return f
}
@@ -194,7 +195,7 @@ func (c *compiler) cat(f1, f2 frag) frag {
// TODO: elide nop
f1.out.patch(c.p, f2.i)
- return frag{f1.i, f2.out}
+ return frag{f1.i, f2.out, f1.nullable && f2.nullable}
}
func (c *compiler) alt(f1, f2 frag) frag {
@@ -211,6 +212,7 @@ func (c *compiler) alt(f1, f2 frag) frag {
i.Out = f1.i
i.Arg = f2.i
f.out = f1.out.append(c.p, f2.out)
+ f.nullable = f1.nullable || f2.nullable
return f
}
@@ -228,7 +230,12 @@ func (c *compiler) quest(f1 frag, nongreedy bool) frag {
return f
}
-func (c *compiler) star(f1 frag, nongreedy bool) frag {
+// loop returns the fragment for the main loop of a plus or star.
+// For plus, it can be used after changing the entry to f1.i.
+// For star, it can be used directly when f1 can't match an empty string.
+// (When f1 can match an empty string, f1* must be implemented as (f1+)?
+// to get the priority match order correct.)
+func (c *compiler) loop(f1 frag, nongreedy bool) frag {
f := c.inst(InstAlt)
i := &c.p.Inst[f.i]
if nongreedy {
@@ -242,8 +249,17 @@ func (c *compiler) star(f1 frag, nongreedy bool) frag {
return f
}
+func (c *compiler) star(f1 frag, nongreedy bool) frag {
+ if f1.nullable {
+ // Use (f1+)? to get priority match order correct.
+ // See golang.org/issue/46123.
+ return c.quest(c.plus(f1, nongreedy), nongreedy)
+ }
+ return c.loop(f1, nongreedy)
+}
+
func (c *compiler) plus(f1 frag, nongreedy bool) frag {
- return frag{f1.i, c.star(f1, nongreedy).out}
+ return frag{f1.i, c.loop(f1, nongreedy).out, f1.nullable}
}
func (c *compiler) empty(op EmptyOp) frag {
@@ -255,6 +271,7 @@ func (c *compiler) empty(op EmptyOp) frag {
func (c *compiler) rune(r []rune, flags Flags) frag {
f := c.inst(InstRune)
+ f.nullable = false
i := &c.p.Inst[f.i]
i.Rune = r
flags &= FoldCase // only relevant flag is FoldCase