2015-02-16 05:28:33 -08:00
|
|
|
package otto
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"regexp"
|
|
|
|
"unicode/utf8"
|
|
|
|
|
|
|
|
"github.com/robertkrimen/otto/parser"
|
|
|
|
)
|
|
|
|
|
|
|
|
type _regExpObject struct {
|
|
|
|
regularExpression *regexp.Regexp
|
|
|
|
global bool
|
|
|
|
ignoreCase bool
|
|
|
|
multiline bool
|
|
|
|
source string
|
|
|
|
flags string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (runtime *_runtime) newRegExpObject(pattern string, flags string) *_object {
|
|
|
|
self := runtime.newObject()
|
|
|
|
self.class = "RegExp"
|
|
|
|
|
|
|
|
global := false
|
|
|
|
ignoreCase := false
|
|
|
|
multiline := false
|
|
|
|
re2flags := ""
|
|
|
|
|
|
|
|
// TODO Maybe clean up the panicking here... TypeError, SyntaxError, ?
|
|
|
|
|
|
|
|
for _, chr := range flags {
|
|
|
|
switch chr {
|
|
|
|
case 'g':
|
|
|
|
if global {
|
2015-03-20 05:22:01 -07:00
|
|
|
panic(runtime.panicSyntaxError("newRegExpObject: %s %s", pattern, flags))
|
2015-02-16 05:28:33 -08:00
|
|
|
}
|
|
|
|
global = true
|
|
|
|
case 'm':
|
|
|
|
if multiline {
|
2015-03-20 05:22:01 -07:00
|
|
|
panic(runtime.panicSyntaxError("newRegExpObject: %s %s", pattern, flags))
|
2015-02-16 05:28:33 -08:00
|
|
|
}
|
|
|
|
multiline = true
|
|
|
|
re2flags += "m"
|
|
|
|
case 'i':
|
|
|
|
if ignoreCase {
|
2015-03-20 05:22:01 -07:00
|
|
|
panic(runtime.panicSyntaxError("newRegExpObject: %s %s", pattern, flags))
|
2015-02-16 05:28:33 -08:00
|
|
|
}
|
|
|
|
ignoreCase = true
|
|
|
|
re2flags += "i"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
re2pattern, err := parser.TransformRegExp(pattern)
|
|
|
|
if err != nil {
|
2015-03-20 05:22:01 -07:00
|
|
|
panic(runtime.panicTypeError("Invalid regular expression: %s", err.Error()))
|
2015-02-16 05:28:33 -08:00
|
|
|
}
|
|
|
|
if len(re2flags) > 0 {
|
|
|
|
re2pattern = fmt.Sprintf("(?%s:%s)", re2flags, re2pattern)
|
|
|
|
}
|
|
|
|
|
|
|
|
regularExpression, err := regexp.Compile(re2pattern)
|
|
|
|
if err != nil {
|
2015-03-20 05:22:01 -07:00
|
|
|
panic(runtime.panicSyntaxError("Invalid regular expression: %s", err.Error()[22:]))
|
2015-02-16 05:28:33 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
self.value = _regExpObject{
|
|
|
|
regularExpression: regularExpression,
|
|
|
|
global: global,
|
|
|
|
ignoreCase: ignoreCase,
|
|
|
|
multiline: multiline,
|
|
|
|
source: pattern,
|
|
|
|
flags: flags,
|
|
|
|
}
|
|
|
|
self.defineProperty("global", toValue_bool(global), 0, false)
|
|
|
|
self.defineProperty("ignoreCase", toValue_bool(ignoreCase), 0, false)
|
|
|
|
self.defineProperty("multiline", toValue_bool(multiline), 0, false)
|
|
|
|
self.defineProperty("lastIndex", toValue_int(0), 0100, false)
|
|
|
|
self.defineProperty("source", toValue_string(pattern), 0, false)
|
|
|
|
return self
|
|
|
|
}
|
|
|
|
|
|
|
|
func (self *_object) regExpValue() _regExpObject {
|
|
|
|
value, _ := self.value.(_regExpObject)
|
|
|
|
return value
|
|
|
|
}
|
|
|
|
|
|
|
|
func execRegExp(this *_object, target string) (match bool, result []int) {
|
|
|
|
if this.class != "RegExp" {
|
2015-03-20 05:22:01 -07:00
|
|
|
panic(this.runtime.panicTypeError("Calling RegExp.exec on a non-RegExp object"))
|
2015-02-16 05:28:33 -08:00
|
|
|
}
|
2015-03-20 05:22:01 -07:00
|
|
|
lastIndex := this.get("lastIndex").number().int64
|
2015-02-16 05:28:33 -08:00
|
|
|
index := lastIndex
|
2015-03-20 05:22:01 -07:00
|
|
|
global := this.get("global").bool()
|
2015-02-16 05:28:33 -08:00
|
|
|
if !global {
|
|
|
|
index = 0
|
|
|
|
}
|
|
|
|
if 0 > index || index > int64(len(target)) {
|
|
|
|
} else {
|
|
|
|
result = this.regExpValue().regularExpression.FindStringSubmatchIndex(target[index:])
|
|
|
|
}
|
|
|
|
if result == nil {
|
|
|
|
//this.defineProperty("lastIndex", toValue_(0), 0111, true)
|
|
|
|
this.put("lastIndex", toValue_int(0), true)
|
|
|
|
return // !match
|
|
|
|
}
|
|
|
|
match = true
|
|
|
|
startIndex := index
|
|
|
|
endIndex := int(lastIndex) + result[1]
|
|
|
|
// We do this shift here because the .FindStringSubmatchIndex above
|
|
|
|
// was done on a local subordinate slice of the string, not the whole string
|
|
|
|
for index, _ := range result {
|
|
|
|
result[index] += int(startIndex)
|
|
|
|
}
|
|
|
|
if global {
|
|
|
|
//this.defineProperty("lastIndex", toValue_(endIndex), 0111, true)
|
|
|
|
this.put("lastIndex", toValue_int(endIndex), true)
|
|
|
|
}
|
|
|
|
return // match
|
|
|
|
}
|
|
|
|
|
|
|
|
func execResultToArray(runtime *_runtime, target string, result []int) *_object {
|
|
|
|
captureCount := len(result) / 2
|
|
|
|
valueArray := make([]Value, captureCount)
|
|
|
|
for index := 0; index < captureCount; index++ {
|
|
|
|
offset := 2 * index
|
|
|
|
if result[offset] != -1 {
|
|
|
|
valueArray[index] = toValue_string(target[result[offset]:result[offset+1]])
|
|
|
|
} else {
|
2015-03-20 05:22:01 -07:00
|
|
|
valueArray[index] = Value{}
|
2015-02-16 05:28:33 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
matchIndex := result[0]
|
|
|
|
if matchIndex != 0 {
|
|
|
|
matchIndex = 0
|
|
|
|
// Find the rune index in the string, not the byte index
|
|
|
|
for index := 0; index < result[0]; {
|
|
|
|
_, size := utf8.DecodeRuneInString(target[index:])
|
|
|
|
matchIndex += 1
|
|
|
|
index += size
|
|
|
|
}
|
|
|
|
}
|
|
|
|
match := runtime.newArrayOf(valueArray)
|
|
|
|
match.defineProperty("input", toValue_string(target), 0111, false)
|
|
|
|
match.defineProperty("index", toValue_int(matchIndex), 0111, false)
|
|
|
|
return match
|
|
|
|
}
|