Commit cd82d490 authored by Rob Pike's avatar Rob Pike

eliminate goroutine from testing/regexp too.

R=rsc
CC=go-dev
http://go/go-review/1018021
parent ad67a866
...@@ -375,7 +375,7 @@ func (p *parser) term() (start, end instr) { ...@@ -375,7 +375,7 @@ func (p *parser) term() (start, end instr) {
return nil, nil; return nil, nil;
case '*', '+': case '*', '+':
p.error = ErrBareClosure; p.error = ErrBareClosure;
return; return
case ')': case ')':
if p.nlpar == 0 { if p.nlpar == 0 {
p.error = ErrUnmatchedRpar; p.error = ErrUnmatchedRpar;
...@@ -589,7 +589,7 @@ func (re *Regexp) dump() { ...@@ -589,7 +589,7 @@ func (re *Regexp) dump() {
} }
} }
func (re *Regexp) doParse() os.Error{ func (re *Regexp) doParse() os.Error {
p := newParser(re); p := newParser(re);
start := new(_Start); start := new(_Start);
re.add(start); re.add(start);
......
...@@ -25,7 +25,6 @@ ...@@ -25,7 +25,6 @@
package testing package testing
import ( import (
"runtime";
"utf8"; "utf8";
) )
...@@ -78,8 +77,6 @@ func (c *common) setIndex(i int) { ...@@ -78,8 +77,6 @@ func (c *common) setIndex(i int) {
// The public interface is entirely through methods. // The public interface is entirely through methods.
type Regexp struct { type Regexp struct {
expr string; // the original expression expr string; // the original expression
ch chan<- *Regexp; // reply channel when we're done
error string; // compile- or run-time error; nil if OK
inst []instr; inst []instr;
start instr; start instr;
nbra int; // number of brackets in expression, for subexpressions nbra int; // number of brackets in expression, for subexpressions
...@@ -307,13 +304,6 @@ func (nop *_Nop) print() { ...@@ -307,13 +304,6 @@ func (nop *_Nop) print() {
print("nop"); print("nop");
} }
// report error and exit compiling/executing goroutine
func (re *Regexp) setError(err string) {
re.error = err;
re.ch <- re;
runtime.Goexit();
}
func (re *Regexp) add(i instr) instr { func (re *Regexp) add(i instr) instr {
n := len(re.inst); n := len(re.inst);
i.setIndex(len(re.inst)); i.setIndex(len(re.inst));
...@@ -331,6 +321,7 @@ func (re *Regexp) add(i instr) instr { ...@@ -331,6 +321,7 @@ func (re *Regexp) add(i instr) instr {
type parser struct { type parser struct {
re *Regexp; re *Regexp;
error string;
nlpar int; // number of unclosed lpars nlpar int; // number of unclosed lpars
pos int; pos int;
ch int; ch int;
...@@ -360,8 +351,6 @@ func newParser(re *Regexp) *parser { ...@@ -360,8 +351,6 @@ func newParser(re *Regexp) *parser {
return p; return p;
} }
var iNULL instr
func special(c int) bool { func special(c int) bool {
s := `\.+*?()|[]^$`; s := `\.+*?()|[]^$`;
for i := 0; i < len(s); i++ { for i := 0; i < len(s); i++ {
...@@ -393,7 +382,8 @@ func (p *parser) charClass() instr { ...@@ -393,7 +382,8 @@ func (p *parser) charClass() instr {
switch c := p.c(); c { switch c := p.c(); c {
case ']', endOfFile: case ']', endOfFile:
if left >= 0 { if left >= 0 {
p.re.setError(ErrBadRange); p.error = ErrBadRange;
return nil;
} }
// Is it [^\n]? // Is it [^\n]?
if cc.negate && len(cc.ranges) == 2 && if cc.negate && len(cc.ranges) == 2 &&
...@@ -405,18 +395,21 @@ func (p *parser) charClass() instr { ...@@ -405,18 +395,21 @@ func (p *parser) charClass() instr {
p.re.add(cc); p.re.add(cc);
return cc; return cc;
case '-': // do this before backslash processing case '-': // do this before backslash processing
p.re.setError(ErrBadRange); p.error = ErrBadRange;
return nil;
case '\\': case '\\':
c = p.nextc(); c = p.nextc();
switch { switch {
case c == endOfFile: case c == endOfFile:
p.re.setError(ErrExtraneousBackslash); p.error = ErrExtraneousBackslash;
return nil;
case c == 'n': case c == 'n':
c = '\n'; c = '\n';
case specialcclass(c): case specialcclass(c):
// c is as delivered // c is as delivered
default: default:
p.re.setError(ErrBadBackslash); p.error = ErrBadBackslash;
return nil;
} }
fallthrough; fallthrough;
default: default:
...@@ -433,26 +426,37 @@ func (p *parser) charClass() instr { ...@@ -433,26 +426,37 @@ func (p *parser) charClass() instr {
cc.addRange(left, c); cc.addRange(left, c);
left = -1; left = -1;
default: default:
p.re.setError(ErrBadRange); p.error = ErrBadRange;
return nil;
} }
} }
} }
return iNULL; return nil;
} }
func (p *parser) term() (start, end instr) { func (p *parser) term() (start, end instr) {
// term() is the leaf of the recursion, so it's sufficient to pick off the
// error state here for early exit.
// The other functions (closure(), concatenation() etc.) assume
// it's safe to recur to here.
if p.error != "" {
return
}
switch c := p.c(); c { switch c := p.c(); c {
case '|', endOfFile: case '|', endOfFile:
return iNULL, iNULL; return nil, nil;
case '*', '+': case '*', '+':
p.re.setError(ErrBareClosure); p.error = ErrBareClosure;
return;
case ')': case ')':
if p.nlpar == 0 { if p.nlpar == 0 {
p.re.setError(ErrUnmatchedRpar); p.error = ErrUnmatchedRpar;
return;
} }
return iNULL, iNULL; return nil, nil;
case ']': case ']':
p.re.setError(ErrUnmatchedRbkt); p.error = ErrUnmatchedRbkt;
return;
case '^': case '^':
p.nextc(); p.nextc();
start = p.re.add(new(_Bot)); start = p.re.add(new(_Bot));
...@@ -468,8 +472,12 @@ func (p *parser) term() (start, end instr) { ...@@ -468,8 +472,12 @@ func (p *parser) term() (start, end instr) {
case '[': case '[':
p.nextc(); p.nextc();
start = p.charClass(); start = p.charClass();
if p.error != "" {
return
}
if p.c() != ']' { if p.c() != ']' {
p.re.setError(ErrUnmatchedLbkt); p.error = ErrUnmatchedLbkt;
return;
} }
p.nextc(); p.nextc();
return start, start; return start, start;
...@@ -480,7 +488,8 @@ func (p *parser) term() (start, end instr) { ...@@ -480,7 +488,8 @@ func (p *parser) term() (start, end instr) {
nbra := p.re.nbra; nbra := p.re.nbra;
start, end = p.regexp(); start, end = p.regexp();
if p.c() != ')' { if p.c() != ')' {
p.re.setError(ErrUnmatchedLpar); p.error = ErrUnmatchedLpar;
return;
} }
p.nlpar--; p.nlpar--;
p.nextc(); p.nextc();
...@@ -490,9 +499,10 @@ func (p *parser) term() (start, end instr) { ...@@ -490,9 +499,10 @@ func (p *parser) term() (start, end instr) {
p.re.add(ebra); p.re.add(ebra);
bra.n = nbra; bra.n = nbra;
ebra.n = nbra; ebra.n = nbra;
if start == iNULL { if start == nil {
if end == iNULL { if end == nil {
p.re.setError(ErrInternal); p.error = ErrInternal;
return;
} }
start = ebra; start = ebra;
} else { } else {
...@@ -504,13 +514,15 @@ func (p *parser) term() (start, end instr) { ...@@ -504,13 +514,15 @@ func (p *parser) term() (start, end instr) {
c = p.nextc(); c = p.nextc();
switch { switch {
case c == endOfFile: case c == endOfFile:
p.re.setError(ErrExtraneousBackslash); p.error = ErrExtraneousBackslash;
return;
case c == 'n': case c == 'n':
c = '\n'; c = '\n';
case special(c): case special(c):
// c is as delivered // c is as delivered
default: default:
p.re.setError(ErrBadBackslash); p.error = ErrBadBackslash;
return;
} }
fallthrough; fallthrough;
default: default:
...@@ -524,7 +536,7 @@ func (p *parser) term() (start, end instr) { ...@@ -524,7 +536,7 @@ func (p *parser) term() (start, end instr) {
func (p *parser) closure() (start, end instr) { func (p *parser) closure() (start, end instr) {
start, end = p.term(); start, end = p.term();
if start == iNULL { if start == nil || p.error != "" {
return; return;
} }
switch p.c() { switch p.c() {
...@@ -559,23 +571,25 @@ func (p *parser) closure() (start, end instr) { ...@@ -559,23 +571,25 @@ func (p *parser) closure() (start, end instr) {
} }
switch p.nextc() { switch p.nextc() {
case '*', '+', '?': case '*', '+', '?':
p.re.setError(ErrBadClosure); p.error = ErrBadClosure;
} }
return; return;
} }
func (p *parser) concatenation() (start, end instr) { func (p *parser) concatenation() (start, end instr) {
start, end = iNULL, iNULL;
for { for {
nstart, nend := p.closure(); nstart, nend := p.closure();
if p.error != "" {
return
}
switch { switch {
case nstart == iNULL: // end of this concatenation case nstart == nil: // end of this concatenation
if start == iNULL { // this is the empty string if start == nil { // this is the empty string
nop := p.re.add(new(_Nop)); nop := p.re.add(new(_Nop));
return nop, nop; return nop, nop;
} }
return; return;
case start == iNULL: // this is first element of concatenation case start == nil: // this is first element of concatenation
start, end = nstart, nend; start, end = nstart, nend;
default: default:
end.setNext(nstart); end.setNext(nstart);
...@@ -587,6 +601,9 @@ func (p *parser) concatenation() (start, end instr) { ...@@ -587,6 +601,9 @@ func (p *parser) concatenation() (start, end instr) {
func (p *parser) regexp() (start, end instr) { func (p *parser) regexp() (start, end instr) {
start, end = p.concatenation(); start, end = p.concatenation();
if p.error != "" {
return
}
for { for {
switch p.c() { switch p.c() {
default: default:
...@@ -594,6 +611,9 @@ func (p *parser) regexp() (start, end instr) { ...@@ -594,6 +611,9 @@ func (p *parser) regexp() (start, end instr) {
case '|': case '|':
p.nextc(); p.nextc();
nstart, nend := p.concatenation(); nstart, nend := p.concatenation();
if p.error != "" {
return
}
alt := new(_Alt); alt := new(_Alt);
p.re.add(alt); p.re.add(alt);
alt.left = start; alt.left = start;
...@@ -629,58 +649,40 @@ func (re *Regexp) eliminateNops() { ...@@ -629,58 +649,40 @@ func (re *Regexp) eliminateNops() {
} }
} }
func (re *Regexp) dump() { func (re *Regexp) doParse() string {
for i := 0; i < len(re.inst); i++ {
inst := re.inst[i];
print(inst.index(), ": ");
inst.print();
if inst.kind() != _END {
print(" -> ", inst.next().index());
}
print("\n");
}
}
func (re *Regexp) doParse() {
p := newParser(re); p := newParser(re);
start := new(_Start); start := new(_Start);
re.add(start); re.add(start);
s, e := p.regexp(); s, e := p.regexp();
if p.error != "" {
return p.error;
}
start.setNext(s); start.setNext(s);
re.start = start; re.start = start;
e.setNext(re.add(new(_End))); e.setNext(re.add(new(_End)));
if debug {
re.dump();
println();
}
re.eliminateNops(); re.eliminateNops();
return p.error;
if debug {
re.dump();
println();
}
}
func compiler(str string, ch chan *Regexp) {
re := new(Regexp);
re.expr = str;
re.inst = make([]instr, 0, 20);
re.ch = ch;
re.doParse();
ch <- re;
} }
// CompileRegexp parses a regular expression and returns, if successful, a Regexp // CompileRegexp parses a regular expression and returns, if successful, a Regexp
// object that can be used to match against text. // object that can be used to match against text.
func CompileRegexp(str string) (regexp *Regexp, error string) { func CompileRegexp(str string) (regexp *Regexp, error string) {
// Compile in a separate goroutine and wait for the result. regexp = new(Regexp);
ch := make(chan *Regexp); regexp.expr = str;
go compiler(str, ch); regexp.inst = make([]instr, 0, 20);
re := <-ch; error = regexp.doParse();
return re, re.error; return;
}
// MustCompileRegexp is like CompileRegexp but panics if the expression cannot be parsed.
// It simplifies safe initialization of global variables holding compiled regular
// expressions.
func MustCompile(str string) *Regexp {
regexp, error := CompileRegexp(str);
if error != "" {
panicln(`regexp: compiling "`, str, `": `, error);
}
return regexp;
} }
type state struct { type state struct {
......
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