protected internal override object GetInstanceIdValue(int id) { int shifted = id - base.MaxInstanceId; if (1 <= shifted && shifted <= MAX_INSTANCE_ID) { RegExpImpl impl = Impl; object stringResult; switch (shifted) { case Id_multiline: case Id_STAR: return(impl.multiline); case Id_input: case Id_UNDERSCORE: stringResult = impl.input; break; case Id_lastMatch: case Id_AMPERSAND: stringResult = impl.lastMatch; break; case Id_lastParen: case Id_PLUS: stringResult = impl.lastParen; break; case Id_leftContext: case Id_BACK_QUOTE: stringResult = impl.leftContext; break; case Id_rightContext: case Id_QUOTE: stringResult = impl.rightContext; break; default: { // Must be one of $1..$9, convert to 0..8 int substring_number = shifted - DOLLAR_ID_BASE - 1; stringResult = impl.getParenSubString(substring_number); break; } } return((stringResult == null) ? "" : stringResult.ToString()); } return(base.GetInstanceIdValue(id)); }
/// <summary> Analog of do_replace in jsstr.c</summary> private static void do_replace(GlobData rdata, Context cx, RegExpImpl regExpImpl) { System.Text.StringBuilder charBuf = rdata.charBuf; int cp = 0; string da = rdata.repstr; int dp = rdata.dollar; if (dp != -1) { int [] skip = new int [1]; do { int len = dp - cp; charBuf.Append(da.Substring(cp, (dp) - (cp))); cp = dp; SubString sub = interpretDollar(cx, regExpImpl, da, dp, skip); if (sub != null) { len = sub.length; if (len > 0) { charBuf.Append(sub.charArray, sub.index, len); } cp += skip [0]; dp += skip [0]; } else { ++dp; } dp = da.IndexOf((char)'$', dp); }while (dp >= 0); } int daL = da.Length; if (daL > cp) { charBuf.Append(da.Substring(cp, (daL) - (cp))); } }
/// <summary> Analog of C match_or_replace.</summary> private static object matchOrReplace (Context cx, IScriptable scope, IScriptable thisObj, object [] args, RegExpImpl reImpl, GlobData data, bool forceFlat) { BuiltinRegExp re; string str = ScriptConvert.ToString (thisObj); data.str = str; IScriptable topScope = ScriptableObject.GetTopLevelScope (scope); if (args.Length == 0) { object compiled = BuiltinRegExp.compileRE ("", "", false); re = new BuiltinRegExp (topScope, compiled); } else if (args [0] is BuiltinRegExp) { re = (BuiltinRegExp)args [0]; } else { string src = ScriptConvert.ToString (args [0]); string opt; if (data.optarg < args.Length) { args [0] = src; opt = ScriptConvert.ToString (args [data.optarg]); } else { opt = null; } object compiled = BuiltinRegExp.compileRE (src, opt, forceFlat); re = new BuiltinRegExp (topScope, compiled); } data.regexp = re; data.global = (re.Flags & BuiltinRegExp.JSREG_GLOB) != 0; int [] indexp = new int [] { 0 }; object result = null; if (data.mode == EcmaScript.NET.RegExpActions.Search) { result = re.executeRegExp (cx, scope, reImpl, str, indexp, BuiltinRegExp.TEST); if (result != null && result.Equals (true)) result = (int)reImpl.leftContext.length; else result = -1; } else if (data.global) { re.lastIndex = 0; for (int count = 0; indexp [0] <= str.Length; count++) { result = re.executeRegExp (cx, scope, reImpl, str, indexp, BuiltinRegExp.TEST); if (result == null || !result.Equals (true)) break; if (data.mode == EcmaScript.NET.RegExpActions.Match) { match_glob (data, cx, scope, count, reImpl); } else { if (data.mode != EcmaScript.NET.RegExpActions.Replace) Context.CodeBug (); SubString lastMatch = reImpl.lastMatch; int leftIndex = data.leftIndex; int leftlen = lastMatch.index - leftIndex; data.leftIndex = lastMatch.index + lastMatch.length; replace_glob (data, cx, scope, reImpl, leftIndex, leftlen); } if (reImpl.lastMatch.length == 0) { if (indexp [0] == str.Length) break; indexp [0]++; } } } else { result = re.executeRegExp (cx, scope, reImpl, str, indexp, ((data.mode == EcmaScript.NET.RegExpActions.Replace) ? BuiltinRegExp.TEST : BuiltinRegExp.MATCH)); } return result; }
/// <summary> Analog of do_replace in jsstr.c</summary> private static void do_replace (GlobData rdata, Context cx, RegExpImpl regExpImpl) { System.Text.StringBuilder charBuf = rdata.charBuf; int cp = 0; string da = rdata.repstr; int dp = rdata.dollar; if (dp != -1) { int [] skip = new int [1]; do { int len = dp - cp; charBuf.Append (da.Substring (cp, (dp) - (cp))); cp = dp; SubString sub = interpretDollar (cx, regExpImpl, da, dp, skip); if (sub != null) { len = sub.length; if (len > 0) { charBuf.Append (sub.charArray, sub.index, len); } cp += skip [0]; dp += skip [0]; } else { ++dp; } dp = da.IndexOf ((char)'$', dp); } while (dp >= 0); } int daL = da.Length; if (daL > cp) { charBuf.Append (da.Substring (cp, (daL) - (cp))); } }
private static SubString interpretDollar (Context cx, RegExpImpl res, string da, int dp, int [] skip) { char dc; int num, tmp; if (da [dp] != '$') Context.CodeBug (); /* Allow a real backslash (literal "\\") to escape "$1" etc. */ Context.Versions version = cx.Version; if (version != Context.Versions.Default && version <= Context.Versions.JS1_4) { if (dp > 0 && da [dp - 1] == '\\') return null; } int daL = da.Length; if (dp + 1 >= daL) return null; /* Interpret all Perl match-induced dollar variables. */ dc = da [dp + 1]; if (BuiltinRegExp.isDigit (dc)) { int cp; if (version != Context.Versions.Default && version <= Context.Versions.JS1_4) { if (dc == '0') return null; /* Check for overflow to avoid gobbling arbitrary decimal digits. */ num = 0; cp = dp; while (++cp < daL && BuiltinRegExp.isDigit (dc = da [cp])) { tmp = 10 * num + (dc - '0'); if (tmp < num) break; num = tmp; } } else { /* ECMA 3, 1-9 or 01-99 */ int parenCount = (res.parens == null) ? 0 : res.parens.Length; num = dc - '0'; if (num > parenCount) return null; cp = dp + 2; if ((dp + 2) < daL) { dc = da [dp + 2]; if (BuiltinRegExp.isDigit (dc)) { tmp = 10 * num + (dc - '0'); if (tmp <= parenCount) { cp++; num = tmp; } } } if (num == 0) return null; /* $0 or $00 is not valid */ } /* Adjust num from 1 $n-origin to 0 array-index-origin. */ num--; skip [0] = cp - dp; return res.getParenSubString (num); } skip [0] = 2; switch (dc) { case '$': return new SubString ("$"); case '&': return res.lastMatch; case '+': return res.lastParen; case '`': if (version == Context.Versions.JS1_2) { /* * JS1.2 imitated the Perl4 bug where left context at each step * in an iterative use of a global regexp started from last match, * not from the start of the target string. But Perl4 does start * $` at the beginning of the target string when it is used in a * substitution, so we emulate that special case here. */ res.leftContext.index = 0; res.leftContext.length = res.lastMatch.index; } return res.leftContext; case '\'': return res.rightContext; } return null; }
/* * Analog of replace_glob() in jsstr.c */ private static void replace_glob (GlobData rdata, Context cx, IScriptable scope, RegExpImpl reImpl, int leftIndex, int leftlen) { int replen; string lambdaStr; if (rdata.lambda != null) { // invoke lambda function with args lastMatch, $1, $2, ... $n, // leftContext.length, whole string. SubString [] parens = reImpl.parens; int parenCount = (parens == null) ? 0 : parens.Length; object [] args = new object [parenCount + 3]; args [0] = reImpl.lastMatch.ToString (); for (int i = 0; i < parenCount; i++) { SubString sub = parens [i]; if (sub != null) { args [i + 1] = sub.ToString (); } else { args [i + 1] = Undefined.Value; } } args [parenCount + 1] = (int)reImpl.leftContext.length; args [parenCount + 2] = rdata.str; // This is a hack to prevent expose of reImpl data to // JS function which can run new regexps modifing // regexp that are used later by the engine. // TODO: redesign is necessary if (reImpl != cx.RegExpProxy) Context.CodeBug (); RegExpImpl re2 = new RegExpImpl (); re2.multiline = reImpl.multiline; re2.input = reImpl.input; cx.RegExpProxy = re2; try { IScriptable parent = ScriptableObject.GetTopLevelScope (scope); object result = rdata.lambda.Call (cx, parent, parent, args); lambdaStr = ScriptConvert.ToString (result); } finally { cx.RegExpProxy = reImpl; } replen = lambdaStr.Length; } else { lambdaStr = null; replen = rdata.repstr.Length; if (rdata.dollar >= 0) { int [] skip = new int [1]; int dp = rdata.dollar; do { SubString sub = interpretDollar (cx, reImpl, rdata.repstr, dp, skip); if (sub != null) { replen += sub.length - skip [0]; dp += skip [0]; } else { ++dp; } dp = rdata.repstr.IndexOf ((char)'$', dp); } while (dp >= 0); } } int growth = leftlen + replen + reImpl.rightContext.length; System.Text.StringBuilder charBuf = rdata.charBuf; if (charBuf == null) { charBuf = new System.Text.StringBuilder (growth); rdata.charBuf = charBuf; } else { charBuf.EnsureCapacity (rdata.charBuf.Length + growth); } charBuf.Append (reImpl.leftContext.charArray, leftIndex, leftlen); if (rdata.lambda != null) { charBuf.Append (lambdaStr); } else { do_replace (rdata, cx, reImpl); } }
/* * Analog of match_glob() in jsstr.c */ private static void match_glob (GlobData mdata, Context cx, IScriptable scope, int count, RegExpImpl reImpl) { if (mdata.arrayobj == null) { IScriptable s = ScriptableObject.GetTopLevelScope (scope); mdata.arrayobj = ScriptRuntime.NewObject (cx, s, "Array", null); } SubString matchsub = reImpl.lastMatch; string matchstr = matchsub.ToString (); mdata.arrayobj.Put (count, mdata.arrayobj, matchstr); }
/* * indexp is assumed to be an array of length 1 */ internal virtual object executeRegExp(Context cx, IScriptable scopeObj, RegExpImpl res, string str, int [] indexp, int matchType) { REGlobalData gData = new REGlobalData (); int start = indexp [0]; char [] charArray = str.ToCharArray (); int end = charArray.Length; if (start > end) start = end; // // Call the recursive matcher to do the real work. // bool matches = matchRegExp (gData, re, charArray, start, end, res.multiline); if (!matches) { if (matchType != PREFIX) return null; return Undefined.Value; } int index = gData.cp; int i = index; indexp [0] = i; int matchlen = i - (start + gData.skipped); int ep = index; index -= matchlen; object result; IScriptable obj; if (matchType == TEST) { /* * Testing for a match and updating cx.regExpImpl: don't allocate * an array object, do return true. */ result = true; obj = null; } else { /* * The array returned on match has element 0 bound to the matched * string, elements 1 through re.parenCount bound to the paren * matches, an index property telling the length of the left context, * and an input property referring to the input string. */ IScriptable scope = GetTopLevelScope (scopeObj); result = ScriptRuntime.NewObject (cx, scope, "Array", null); obj = (IScriptable)result; string matchstr = new string (charArray, index, matchlen); obj.Put (0, obj, matchstr); } if (re.parenCount == 0) { res.parens = null; res.lastParen = SubString.EmptySubString; } else { SubString parsub = null; int num; res.parens = new SubString [re.parenCount]; for (num = 0; num < re.parenCount; num++) { int cap_index = gData.parens_index (num); string parstr; if (cap_index != -1) { int cap_length = gData.parens_length (num); parsub = new SubString (charArray, cap_index, cap_length); res.parens [num] = parsub; if (matchType == TEST) continue; parstr = parsub.ToString (); obj.Put (num + 1, obj, parstr); } else { if (matchType != TEST) obj.Put (num + 1, obj, Undefined.Value); } } res.lastParen = parsub; } if (!(matchType == TEST)) { /* * Define the index and input properties last for better for/in loop * order (so they come after the elements). */ obj.Put ("index", obj, (object)(start + gData.skipped)); obj.Put ("input", obj, str); } if (res.lastMatch == null) { res.lastMatch = new SubString (); res.leftContext = new SubString (); res.rightContext = new SubString (); } res.lastMatch.charArray = charArray; res.lastMatch.index = index; res.lastMatch.length = matchlen; res.leftContext.charArray = charArray; if (cx.Version == Context.Versions.JS1_2) { /* * JS1.2 emulated Perl4.0.1.8 (patch level 36) for global regexps used * in scalar contexts, and unintentionally for the string.match "list" * psuedo-context. On "hi there bye", the following would result: * * Language while(/ /g){print("$`");} s/ /$`/g * perl4.036 "hi", "there" "hihitherehi therebye" * perl5 "hi", "hi there" "hihitherehi therebye" * js1.2 "hi", "there" "hihitheretherebye" * * Insofar as JS1.2 always defined $` as "left context from the last * match" for global regexps, it was more consistent than perl4. */ res.leftContext.index = start; res.leftContext.length = gData.skipped; } else { /* * For JS1.3 and ECMAv2, emulate Perl5 exactly: * * js1.3 "hi", "hi there" "hihitherehi therebye" */ res.leftContext.index = 0; res.leftContext.length = start + gData.skipped; } res.rightContext.charArray = charArray; res.rightContext.index = ep; res.rightContext.length = end - ep; return result; }
private static SubString interpretDollar(Context cx, RegExpImpl res, string da, int dp, int [] skip) { char dc; int num, tmp; if (da [dp] != '$') { Context.CodeBug(); } /* Allow a real backslash (literal "\\") to escape "$1" etc. */ Context.Versions version = cx.Version; if (version != Context.Versions.Default && version <= Context.Versions.JS1_4) { if (dp > 0 && da [dp - 1] == '\\') { return(null); } } int daL = da.Length; if (dp + 1 >= daL) { return(null); } /* Interpret all Perl match-induced dollar variables. */ dc = da [dp + 1]; if (BuiltinRegExp.isDigit(dc)) { int cp; if (version != Context.Versions.Default && version <= Context.Versions.JS1_4) { if (dc == '0') { return(null); } /* Check for overflow to avoid gobbling arbitrary decimal digits. */ num = 0; cp = dp; while (++cp < daL && BuiltinRegExp.isDigit(dc = da [cp])) { tmp = 10 * num + (dc - '0'); if (tmp < num) { break; } num = tmp; } } else { /* ECMA 3, 1-9 or 01-99 */ int parenCount = (res.parens == null) ? 0 : res.parens.Length; num = dc - '0'; if (num > parenCount) { return(null); } cp = dp + 2; if ((dp + 2) < daL) { dc = da [dp + 2]; if (BuiltinRegExp.isDigit(dc)) { tmp = 10 * num + (dc - '0'); if (tmp <= parenCount) { cp++; num = tmp; } } } if (num == 0) { return(null); /* $0 or $00 is not valid */ } } /* Adjust num from 1 $n-origin to 0 array-index-origin. */ num--; skip [0] = cp - dp; return(res.getParenSubString(num)); } skip [0] = 2; switch (dc) { case '$': return(new SubString("$")); case '&': return(res.lastMatch); case '+': return(res.lastParen); case '`': if (version == Context.Versions.JS1_2) { /* * JS1.2 imitated the Perl4 bug where left context at each step * in an iterative use of a global regexp started from last match, * not from the start of the target string. But Perl4 does start * $` at the beginning of the target string when it is used in a * substitution, so we emulate that special case here. */ res.leftContext.index = 0; res.leftContext.length = res.lastMatch.index; } return(res.leftContext); case '\'': return(res.rightContext); } return(null); }
/* * Analog of replace_glob() in jsstr.c */ private static void replace_glob(GlobData rdata, Context cx, IScriptable scope, RegExpImpl reImpl, int leftIndex, int leftlen) { int replen; string lambdaStr; if (rdata.lambda != null) { // invoke lambda function with args lastMatch, $1, $2, ... $n, // leftContext.length, whole string. SubString [] parens = reImpl.parens; int parenCount = (parens == null) ? 0 : parens.Length; object [] args = new object [parenCount + 3]; args [0] = reImpl.lastMatch.ToString(); for (int i = 0; i < parenCount; i++) { SubString sub = parens [i]; if (sub != null) { args [i + 1] = sub.ToString(); } else { args [i + 1] = Undefined.Value; } } args [parenCount + 1] = (int)reImpl.leftContext.length; args [parenCount + 2] = rdata.str; // This is a hack to prevent expose of reImpl data to // JS function which can run new regexps modifing // regexp that are used later by the engine. // TODO: redesign is necessary if (reImpl != cx.RegExpProxy) { Context.CodeBug(); } RegExpImpl re2 = new RegExpImpl(); re2.multiline = reImpl.multiline; re2.input = reImpl.input; cx.RegExpProxy = re2; try { IScriptable parent = ScriptableObject.GetTopLevelScope(scope); object result = rdata.lambda.Call(cx, parent, parent, args); lambdaStr = ScriptConvert.ToString(result); } finally { cx.RegExpProxy = reImpl; } replen = lambdaStr.Length; } else { lambdaStr = null; replen = rdata.repstr.Length; if (rdata.dollar >= 0) { int [] skip = new int [1]; int dp = rdata.dollar; do { SubString sub = interpretDollar(cx, reImpl, rdata.repstr, dp, skip); if (sub != null) { replen += sub.length - skip [0]; dp += skip [0]; } else { ++dp; } dp = rdata.repstr.IndexOf((char)'$', dp); }while (dp >= 0); } } int growth = leftlen + replen + reImpl.rightContext.length; System.Text.StringBuilder charBuf = rdata.charBuf; if (charBuf == null) { charBuf = new System.Text.StringBuilder(growth); rdata.charBuf = charBuf; } else { charBuf.EnsureCapacity(rdata.charBuf.Length + growth); } charBuf.Append(reImpl.leftContext.charArray, leftIndex, leftlen); if (rdata.lambda != null) { charBuf.Append(lambdaStr); } else { do_replace(rdata, cx, reImpl); } }
/* * Analog of match_glob() in jsstr.c */ private static void match_glob(GlobData mdata, Context cx, IScriptable scope, int count, RegExpImpl reImpl) { if (mdata.arrayobj == null) { IScriptable s = ScriptableObject.GetTopLevelScope(scope); mdata.arrayobj = ScriptRuntime.NewObject(cx, s, "Array", null); } SubString matchsub = reImpl.lastMatch; string matchstr = matchsub.ToString(); mdata.arrayobj.Put(count, mdata.arrayobj, matchstr); }
/// <summary> Analog of C match_or_replace.</summary> private static object matchOrReplace(Context cx, IScriptable scope, IScriptable thisObj, object [] args, RegExpImpl reImpl, GlobData data, bool forceFlat) { BuiltinRegExp re; string str = ScriptConvert.ToString(thisObj); data.str = str; IScriptable topScope = ScriptableObject.GetTopLevelScope(scope); if (args.Length == 0) { object compiled = BuiltinRegExp.compileRE("", "", false); re = new BuiltinRegExp(topScope, compiled); } else if (args [0] is BuiltinRegExp) { re = (BuiltinRegExp)args [0]; } else { string src = ScriptConvert.ToString(args [0]); string opt; if (data.optarg < args.Length) { args [0] = src; opt = ScriptConvert.ToString(args [data.optarg]); } else { opt = null; } object compiled = BuiltinRegExp.compileRE(src, opt, forceFlat); re = new BuiltinRegExp(topScope, compiled); } data.regexp = re; data.global = (re.Flags & BuiltinRegExp.JSREG_GLOB) != 0; int [] indexp = new int [] { 0 }; object result = null; if (data.mode == EcmaScript.NET.RegExpActions.Search) { result = re.executeRegExp(cx, scope, reImpl, str, indexp, BuiltinRegExp.TEST); if (result != null && result.Equals(true)) { result = (int)reImpl.leftContext.length; } else { result = -1; } } else if (data.global) { re.lastIndex = 0; for (int count = 0; indexp [0] <= str.Length; count++) { result = re.executeRegExp(cx, scope, reImpl, str, indexp, BuiltinRegExp.TEST); if (result == null || !result.Equals(true)) { break; } if (data.mode == EcmaScript.NET.RegExpActions.Match) { match_glob(data, cx, scope, count, reImpl); } else { if (data.mode != EcmaScript.NET.RegExpActions.Replace) { Context.CodeBug(); } SubString lastMatch = reImpl.lastMatch; int leftIndex = data.leftIndex; int leftlen = lastMatch.index - leftIndex; data.leftIndex = lastMatch.index + lastMatch.length; replace_glob(data, cx, scope, reImpl, leftIndex, leftlen); } if (reImpl.lastMatch.length == 0) { if (indexp [0] == str.Length) { break; } indexp [0]++; } } } else { result = re.executeRegExp(cx, scope, reImpl, str, indexp, ((data.mode == EcmaScript.NET.RegExpActions.Replace) ? BuiltinRegExp.TEST : BuiltinRegExp.MATCH)); } return(result); }