Commit 7de610cc authored by Rob Pike's avatar Rob Pike

regexp: use panic/recover to handle errors

R=rsc, gri
CC=golang-dev
https://golang.org/cl/821046
parent 63e878a7
...@@ -33,18 +33,25 @@ import ( ...@@ -33,18 +33,25 @@ import (
var debug = false var debug = false
// Error is the local type for a parsing error.
type Error string
func (e Error) String() string {
return string(e)
}
// Error codes returned by failures to parse an expression. // Error codes returned by failures to parse an expression.
var ( var (
ErrInternal = os.NewError("internal error") ErrInternal = Error("internal error")
ErrUnmatchedLpar = os.NewError("unmatched '('") ErrUnmatchedLpar = Error("unmatched '('")
ErrUnmatchedRpar = os.NewError("unmatched ')'") ErrUnmatchedRpar = Error("unmatched ')'")
ErrUnmatchedLbkt = os.NewError("unmatched '['") ErrUnmatchedLbkt = Error("unmatched '['")
ErrUnmatchedRbkt = os.NewError("unmatched ']'") ErrUnmatchedRbkt = Error("unmatched ']'")
ErrBadRange = os.NewError("bad range in character class") ErrBadRange = Error("bad range in character class")
ErrExtraneousBackslash = os.NewError("extraneous backslash") ErrExtraneousBackslash = Error("extraneous backslash")
ErrBadClosure = os.NewError("repeated closure (**, ++, etc.)") ErrBadClosure = Error("repeated closure (**, ++, etc.)")
ErrBareClosure = os.NewError("closure applies to nothing") ErrBareClosure = Error("closure applies to nothing")
ErrBadBackslash = os.NewError("illegal backslash escape") ErrBadBackslash = Error("illegal backslash escape")
) )
// An instruction executed by the NFA // An instruction executed by the NFA
...@@ -252,12 +259,16 @@ func (re *Regexp) add(i instr) instr { ...@@ -252,12 +259,16 @@ func (re *Regexp) add(i instr) instr {
type parser struct { type parser struct {
re *Regexp re *Regexp
error os.Error
nlpar int // number of unclosed lpars nlpar int // number of unclosed lpars
pos int pos int
ch int ch int
} }
func (p *parser) error(err os.Error) {
p.re = nil
panic(err)
}
const endOfFile = -1 const endOfFile = -1
func (p *parser) c() int { return p.ch } func (p *parser) c() int { return p.ch }
...@@ -309,8 +320,7 @@ func (p *parser) charClass() instr { ...@@ -309,8 +320,7 @@ 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.error = ErrBadRange p.error(ErrBadRange)
return nil
} }
// Is it [^\n]? // Is it [^\n]?
if cc.negate && cc.ranges.Len() == 2 && if cc.negate && cc.ranges.Len() == 2 &&
...@@ -328,21 +338,18 @@ func (p *parser) charClass() instr { ...@@ -328,21 +338,18 @@ 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.error = ErrBadRange p.error(ErrBadRange)
return nil
case '\\': case '\\':
c = p.nextc() c = p.nextc()
switch { switch {
case c == endOfFile: case c == endOfFile:
p.error = 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.error = ErrBadBackslash p.error(ErrBadBackslash)
return nil
} }
fallthrough fallthrough
default: default:
...@@ -359,8 +366,7 @@ func (p *parser) charClass() instr { ...@@ -359,8 +366,7 @@ func (p *parser) charClass() instr {
cc.addRange(left, c) cc.addRange(left, c)
left = -1 left = -1
default: default:
p.error = ErrBadRange p.error(ErrBadRange)
return nil
} }
} }
} }
...@@ -368,28 +374,18 @@ func (p *parser) charClass() instr { ...@@ -368,28 +374,18 @@ func (p *parser) charClass() instr {
} }
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 != nil {
return
}
switch c := p.c(); c { switch c := p.c(); c {
case '|', endOfFile: case '|', endOfFile:
return nil, nil return nil, nil
case '*', '+': case '*', '+':
p.error = ErrBareClosure p.error(ErrBareClosure)
return
case ')': case ')':
if p.nlpar == 0 { if p.nlpar == 0 {
p.error = ErrUnmatchedRpar p.error(ErrUnmatchedRpar)
return
} }
return nil, nil return nil, nil
case ']': case ']':
p.error = ErrUnmatchedRbkt p.error(ErrUnmatchedRbkt)
return
case '^': case '^':
p.nextc() p.nextc()
start = p.re.add(new(_Bot)) start = p.re.add(new(_Bot))
...@@ -405,12 +401,8 @@ func (p *parser) term() (start, end instr) { ...@@ -405,12 +401,8 @@ func (p *parser) term() (start, end instr) {
case '[': case '[':
p.nextc() p.nextc()
start = p.charClass() start = p.charClass()
if p.error != nil {
return
}
if p.c() != ']' { if p.c() != ']' {
p.error = ErrUnmatchedLbkt p.error(ErrUnmatchedLbkt)
return
} }
p.nextc() p.nextc()
return start, start return start, start
...@@ -421,8 +413,7 @@ func (p *parser) term() (start, end instr) { ...@@ -421,8 +413,7 @@ 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.error = ErrUnmatchedLpar p.error(ErrUnmatchedLpar)
return
} }
p.nlpar-- p.nlpar--
p.nextc() p.nextc()
...@@ -434,7 +425,7 @@ func (p *parser) term() (start, end instr) { ...@@ -434,7 +425,7 @@ func (p *parser) term() (start, end instr) {
ebra.n = nbra ebra.n = nbra
if start == nil { if start == nil {
if end == nil { if end == nil {
p.error = ErrInternal p.error(ErrInternal)
return return
} }
start = ebra start = ebra
...@@ -447,15 +438,13 @@ func (p *parser) term() (start, end instr) { ...@@ -447,15 +438,13 @@ func (p *parser) term() (start, end instr) {
c = p.nextc() c = p.nextc()
switch { switch {
case c == endOfFile: case c == endOfFile:
p.error = 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.error = ErrBadBackslash p.error(ErrBadBackslash)
return
} }
fallthrough fallthrough
default: default:
...@@ -469,7 +458,7 @@ func (p *parser) term() (start, end instr) { ...@@ -469,7 +458,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 == nil || p.error != nil { if start == nil {
return return
} }
switch p.c() { switch p.c() {
...@@ -504,7 +493,7 @@ func (p *parser) closure() (start, end instr) { ...@@ -504,7 +493,7 @@ func (p *parser) closure() (start, end instr) {
} }
switch p.nextc() { switch p.nextc() {
case '*', '+', '?': case '*', '+', '?':
p.error = ErrBadClosure p.error(ErrBadClosure)
} }
return return
} }
...@@ -512,9 +501,6 @@ func (p *parser) closure() (start, end instr) { ...@@ -512,9 +501,6 @@ func (p *parser) closure() (start, end instr) {
func (p *parser) concatenation() (start, end instr) { func (p *parser) concatenation() (start, end instr) {
for { for {
nstart, nend := p.closure() nstart, nend := p.closure()
if p.error != nil {
return
}
switch { switch {
case nstart == nil: // end of this concatenation case nstart == nil: // end of this concatenation
if start == nil { // this is the empty string if start == nil { // this is the empty string
...@@ -534,9 +520,6 @@ func (p *parser) concatenation() (start, end instr) { ...@@ -534,9 +520,6 @@ 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 != nil {
return
}
for { for {
switch p.c() { switch p.c() {
default: default:
...@@ -544,9 +527,6 @@ func (p *parser) regexp() (start, end instr) { ...@@ -544,9 +527,6 @@ func (p *parser) regexp() (start, end instr) {
case '|': case '|':
p.nextc() p.nextc()
nstart, nend := p.concatenation() nstart, nend := p.concatenation()
if p.error != nil {
return
}
alt := new(_Alt) alt := new(_Alt)
p.re.add(alt) p.re.add(alt)
alt.left = start alt.left = start
...@@ -595,14 +575,11 @@ func (re *Regexp) dump() { ...@@ -595,14 +575,11 @@ func (re *Regexp) dump() {
} }
} }
func (re *Regexp) doParse() os.Error { 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 != nil {
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)))
...@@ -617,14 +594,11 @@ func (re *Regexp) doParse() os.Error { ...@@ -617,14 +594,11 @@ func (re *Regexp) doParse() os.Error {
re.dump() re.dump()
println() println()
} }
if p.error == nil { re.setPrefix()
re.setPrefix() if debug {
if debug { re.dump()
re.dump() println()
println()
}
} }
return p.error
} }
// Extract regular text from the beginning of the pattern. // Extract regular text from the beginning of the pattern.
...@@ -661,12 +635,16 @@ Loop: ...@@ -661,12 +635,16 @@ Loop:
// object that can be used to match against text. // object that can be used to match against text.
func Compile(str string) (regexp *Regexp, error os.Error) { func Compile(str string) (regexp *Regexp, error os.Error) {
regexp = new(Regexp) regexp = new(Regexp)
// doParse will panic if there is a parse error.
defer func() {
if e := recover(); e != nil {
regexp = nil
error = e.(Error) // Will re-panic if error was not an Error, e.g. nil-pointer exception
}
}()
regexp.expr = str regexp.expr = str
regexp.inst = new(vector.Vector) regexp.inst = new(vector.Vector)
error = regexp.doParse() regexp.doParse()
if error != nil {
regexp = nil
}
return return
} }
......
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