edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/IronRuby.Tests/RubyTests.cs;C448258 File: RubyTests.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/IronRuby.Tests/RubyTests.cs;C448258 (server) 5/24/2008 10:11 PM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/IronRuby.Tests/RubyTests.cs;Regex2 @@ -56,6 +56,8 @@ Regex1, Regex2, + RegexTransform1, + RegexTransform2, Scenario_RubyScopeParsing, Scenario_RubyScopes1, @@ -263,6 +265,8 @@ Scenario_RubyRescueStatement1, Scenario_RubyRescueExpression1, Scenario_RubyRescueExpression2, + ExceptionArg1, + ExceptionArg2, ClassVariables1, UnqualifiedConstants2, =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/IronRuby.Tests/Runtime/ExceptionTests.cs;C429806 File: ExceptionTests.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/IronRuby.Tests/Runtime/ExceptionTests.cs;C429806 (server) 5/25/2008 10:12 AM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/IronRuby.Tests/Runtime/ExceptionTests.cs;Regex2 @@ -566,5 +566,30 @@ "); }, @""); } + + public void ExceptionArg1() { + AssertOutput(delegate() { + CompilerTest(@" +begin + raise SystemExit, 42 +rescue SystemExit + puts $!.status +end +"); + }, @"42"); + } + + [Run] + public void ExceptionArg2() { + AssertOutput(delegate() { + CompilerTest(@" +begin + raise SystemExit, 'foo' +rescue SystemExit + puts $!.message +end +"); + }, @"foo"); + } } } =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/IronRuby.Tests/Runtime/RegexTests.cs;C438696 File: RegexTests.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/IronRuby.Tests/Runtime/RegexTests.cs;C438696 (server) 5/24/2008 10:12 PM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/IronRuby.Tests/Runtime/RegexTests.cs;Regex2 @@ -27,6 +27,8 @@ using Ruby.Compiler; using Ruby.Compiler.Ast; using Ruby.Runtime; +using Ruby.Builtins; +using System.Text.RegularExpressions; namespace Ruby.Tests { public partial class Tests { @@ -63,6 +65,95 @@ (?-mix:b(?-mix:a)) "); } + + public void RegexTransform1() { + string[] incorrectPatterns = new string[] { + @"\", + @"\", + + @"\\\", + @"\\\", + + @"\x", + @"\x", + + @"\1", // TODO + @"\1", + }; + + string[] correctPatterns = new string[] { + @"", + @"", + + @"\\", + @"\\", + + @"\_", + @"_", + + @"abc\0\01\011\a\sabc\Wabc\w", + @"abc\0\01\011\a\sabc\Wabc\w", + + @"\xd", + @"\x0d", + + @"\xdz", + @"\x0dz", + + @"\*", + @"\*", + + @"\[", + @"\[", + + @"\#", + @"\#", + + @"\0", + @"\0", + + @"\G", + @"\G", + }; + + for (int i = 0; i < incorrectPatterns.Length; i += 2) { + string expected = incorrectPatterns[i + 1]; + string actual = StringRegex.TransformPattern(incorrectPatterns[i], RubyRegexOptions.NONE); + Assert(actual == expected); + } + + for (int i = 0; i < correctPatterns.Length; i += 2) { + string expected = correctPatterns[i + 1]; + string actual = StringRegex.TransformPattern(correctPatterns[i], RubyRegexOptions.NONE); + Assert(actual == expected); + new Regex(expected); + } + } + + public void RegexTransform2() { + string e = @"^ + ([a-zA-Z][-+.a-zA-Z\d]*): (?# 1: scheme) + (?: + ((?:[-_.!~*'()a-zA-Z\d;?:@&=+$,]|%[a-fA-F\d]{2})(?:[-_.!~*'()a-zA-Z\d;/?:@&=+$,\[\]]|%[a-fA-F\d]{2})*) (?# 2: opaque) + | + (?:(?: + //(?: + (?:(?:((?:[-_.!~*'()a-zA-Z\d;:&=+$,]|%[a-fA-F\d]{2})*)@)? (?# 3: userinfo) + (?:((?:(?:(?:[a-zA-Z\d](?:[-a-zA-Z\d]*[a-zA-Z\d])?)\.)*(?:[a-zA-Z](?:[-a-zA-Z\d]*[a-zA-Z\d])?)\.?|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|\[(?:(?:[a-fA-F\d]{1,4}:)*(?:[a-fA-F\d]{1,4}|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})|(?:(?:[a-fA-F\d]{1,4}:)*[a-fA-F\d]{1,4})?::(?:(?:[a-fA-F\d]{1,4}:)*(?:[a-fA-F\d]{1,4}|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}))?)\]))(?::(\d*))?))?(?# 4: host, 5: port) + | + ((?:[-_.!~*'()a-zA-Z\d$,;+@&=+]|%[a-fA-F\d]{2})+) (?# 6: registry) + ) + | + (?!//)) (?# XXX: '//' is the mark for hostport) + (/(?:[-_.!~*'()a-zA-Z\d:@&=+$,]|%[a-fA-F\d]{2})*(?:;(?:[-_.!~*'()a-zA-Z\d:@&=+$,]|%[a-fA-F\d]{2})*)*(?:/(?:[-_.!~*'()a-zA-Z\d:@&=+$,]|%[a-fA-F\d]{2})*(?:;(?:[-_.!~*'()a-zA-Z\d:@&=+$,]|%[a-fA-F\d]{2})*)*)*)? (?# 7: path) + )(?:\?((?:[-_.!~*'()a-zA-Z\d;/?:@&=+$,\[\]]|%[a-fA-F\d]{2})*))? (?# 8: query) + ) + (?:\#((?:[-_.!~*'()a-zA-Z\d;/?:@&=+$,\[\]]|%[a-fA-F\d]{2})*))? (?# 9: fragment) + $"; + string t = StringRegex.TransformPattern(e, RubyRegexOptions.Extended | RubyRegexOptions.Multiline); + Assert(e == t); + new Regex(t); + } } } =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Initializers.Generated.cs;C448472 File: Initializers.Generated.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Initializers.Generated.cs;C448472 (server) 5/25/2008 11:48 AM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Initializers.Generated.cs;Regex2 @@ -149,6 +149,7 @@ }); #endif DefineGlobalClass("SystemExit", typeof(Ruby.Builtins.SystemExit), def28, new System.Action(LoadSystemExit_Instance), null, Ruby.Builtins.RubyModule.EmptyArray, new System.Delegate[] { + new Microsoft.Scripting.Utils.Function(Ruby.Builtins.SystemExitOps.Factory), new Microsoft.Scripting.Utils.Function(Ruby.Builtins.SystemExitOps.Factory), }); DefineGlobalClass("SystemStackError", typeof(Ruby.Builtins.SystemStackError), def28, null, null, Ruby.Builtins.RubyModule.EmptyArray, new System.Delegate[] { @@ -985,7 +986,7 @@ module.DefineLibraryMethod("exception", 0x29, new System.Delegate[] { new Microsoft.Scripting.Utils.Function(Ruby.Builtins.ExceptionOps.GetException), - new Microsoft.Scripting.Utils.Function(Ruby.Builtins.ExceptionOps.GetException), + new Microsoft.Scripting.Utils.Function(Ruby.Builtins.ExceptionOps.GetException), }); module.DefineLibraryMethod("initialize", 0x2a, new System.Delegate[] { @@ -1020,7 +1021,7 @@ module.DefineLibraryMethod("exception", 0x31, new System.Delegate[] { new Microsoft.Scripting.Utils.Function(Ruby.Builtins.ExceptionOps.CreateException), - new Microsoft.Scripting.Utils.Function(Ruby.Builtins.ExceptionOps.CreateException), + new Microsoft.Scripting.Utils.Function(Ruby.Builtins.ExceptionOps.CreateException), }); } @@ -2267,8 +2268,8 @@ module.DefineLibraryMethod("fail", 0x2a, new System.Delegate[] { new Microsoft.Scripting.Utils.Action(Ruby.Builtins.Kernel.RaiseException), new Microsoft.Scripting.Utils.Action(Ruby.Builtins.Kernel.RaiseException), - new Microsoft.Scripting.Utils.Action(Ruby.Builtins.Kernel.RaiseException), - new Microsoft.Scripting.Utils.Action>(Ruby.Builtins.Kernel.RaiseException), + new Microsoft.Scripting.Utils.Action(Ruby.Builtins.Kernel.RaiseException), + new Microsoft.Scripting.Utils.Action>(Ruby.Builtins.Kernel.RaiseException), }); module.DefineLibraryMethod("Float", 0x2a, new System.Delegate[] { @@ -2430,8 +2431,8 @@ module.DefineLibraryMethod("raise", 0x2a, new System.Delegate[] { new Microsoft.Scripting.Utils.Action(Ruby.Builtins.Kernel.RaiseException), new Microsoft.Scripting.Utils.Action(Ruby.Builtins.Kernel.RaiseException), - new Microsoft.Scripting.Utils.Action(Ruby.Builtins.Kernel.RaiseException), - new Microsoft.Scripting.Utils.Action>(Ruby.Builtins.Kernel.RaiseException), + new Microsoft.Scripting.Utils.Action(Ruby.Builtins.Kernel.RaiseException), + new Microsoft.Scripting.Utils.Action>(Ruby.Builtins.Kernel.RaiseException), }); module.DefineLibraryMethod("require", 0x2a, new System.Delegate[] { @@ -2553,8 +2554,8 @@ module.DefineLibraryMethod("fail", 0x31, new System.Delegate[] { new Microsoft.Scripting.Utils.Action(Ruby.Builtins.Kernel.RaiseException), new Microsoft.Scripting.Utils.Action(Ruby.Builtins.Kernel.RaiseException), - new Microsoft.Scripting.Utils.Action(Ruby.Builtins.Kernel.RaiseException), - new Microsoft.Scripting.Utils.Action>(Ruby.Builtins.Kernel.RaiseException), + new Microsoft.Scripting.Utils.Action(Ruby.Builtins.Kernel.RaiseException), + new Microsoft.Scripting.Utils.Action>(Ruby.Builtins.Kernel.RaiseException), }); module.DefineLibraryMethod("Float", 0x31, new System.Delegate[] { @@ -2639,8 +2640,8 @@ module.DefineLibraryMethod("raise", 0x31, new System.Delegate[] { new Microsoft.Scripting.Utils.Action(Ruby.Builtins.Kernel.RaiseException), new Microsoft.Scripting.Utils.Action(Ruby.Builtins.Kernel.RaiseException), - new Microsoft.Scripting.Utils.Action(Ruby.Builtins.Kernel.RaiseException), - new Microsoft.Scripting.Utils.Action>(Ruby.Builtins.Kernel.RaiseException), + new Microsoft.Scripting.Utils.Action(Ruby.Builtins.Kernel.RaiseException), + new Microsoft.Scripting.Utils.Action>(Ruby.Builtins.Kernel.RaiseException), }); module.DefineLibraryMethod("require", 0x31, new System.Delegate[] { @@ -3616,6 +3617,10 @@ new Microsoft.Scripting.Utils.Function(Ruby.Builtins.RegexpOps.Escape), }); + module.DefineLibraryMethod("union", 0x31, new System.Delegate[] { + new Microsoft.Scripting.Utils.Function(Ruby.Builtins.RegexpOps.Union), + }); + } #if !SILVERLIGHT =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/ExceptionOps.cs;C443395 File: ExceptionOps.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/ExceptionOps.cs;C443395 (server) 5/25/2008 9:58 AM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/ExceptionOps.cs;Regex2 @@ -61,7 +61,9 @@ /// /// Creates an instance of a specified exception class. /// - internal static Exception/*!*/ MakeException(CodeContext/*!*/ context, RubyClass/*!*/ exceptionClass, MutableString message, RubyArray/*!*/ backtrace) { + internal static Exception/*!*/ MakeException(CodeContext/*!*/ context, RubyClass/*!*/ exceptionClass, + object exceptionArg, RubyArray/*!*/ backtrace) { + if (exceptionClass == null) { throw RubyExceptions.CreateTypeError("exception object expected"); } @@ -70,7 +72,7 @@ throw RubyExceptions.CreateTypeError("exception class/object expected"); } - Exception result = CreateException(context, exceptionClass, message); + Exception result = CreateException(context, exceptionClass, exceptionArg); SetBacktrace(result, backtrace); return result; } @@ -153,9 +155,9 @@ } [RubyMethod("exception", RubyMethodAttributes.PublicInstance)] - public static Exception/*!*/ GetException(CodeContext/*!*/ context, Exception/*!*/ self, MutableString message) { + public static Exception/*!*/ GetException(CodeContext/*!*/ context, Exception/*!*/ self, object exceptionArg) { Assert.NotNull(context, self); - return MakeException(context, RubyUtils.GetExecutionContext(context).GetClassOf(self), message, null); + return MakeException(context, RubyUtils.GetExecutionContext(context).GetClassOf(self), exceptionArg, null); } [RubyMethod("to_s")] @@ -196,12 +198,12 @@ return NewCallSite1.Invoke(context, self); } - private static DynamicSite NewCallSite2 = - DynamicSite.Create(RubySites.InstanceCallAction("new", ArgumentKind.Simple)); + private static DynamicSite NewCallSite2 = + DynamicSite.Create(RubySites.InstanceCallAction("new", ArgumentKind.Simple)); [RubyMethod("exception", RubyMethodAttributes.PublicSingleton)] - public static Exception/*!*/ CreateException(CodeContext/*!*/ context, RubyClass/*!*/ self, MutableString message) { - return NewCallSite2.Invoke(context, self, message); + public static Exception/*!*/ CreateException(CodeContext/*!*/ context, RubyClass/*!*/ self, object exceptionArg) { + return NewCallSite2.Invoke(context, self, exceptionArg); } #endregion =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/Exceptions.cs;C443395 File: Exceptions.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/Exceptions.cs;C443395 (server) 5/25/2008 10:20 AM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/Exceptions.cs;Regex2 @@ -25,6 +25,7 @@ using Ruby.Hosting; using Ruby.Runtime; using Microsoft.Scripting.Utils; +using Microsoft.Scripting.Runtime; namespace Ruby.Builtins { @@ -106,6 +107,11 @@ } [RubyConstructor] + public static SystemExit/*!*/ Factory([NotNull]MutableString/*!*/ message) { + return Factory(0, message); + } + + [RubyConstructor] public static SystemExit/*!*/ Factory([Optional]int status, [Optional]MutableString message) { SystemExit result = new SystemExit(status, ExceptionOps.MakeMessage(message, "SystemExit")); ExceptionOps.InitializeException(result, message); =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/Kernel.cs;C448462 File: Kernel.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/Kernel.cs;C448462 (server) 5/25/2008 9:58 AM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/Kernel.cs;Regex2 @@ -26,6 +26,7 @@ using Ruby.Runtime; using Ruby.Runtime.Calls; using System.Diagnostics; +using System.Reflection; namespace Ruby.Builtins { @@ -471,9 +472,10 @@ [RubyMethod("fail", RubyMethodAttributes.PrivateInstance)] [RubyMethod("fail", RubyMethodAttributes.PublicSingleton)] [RubyStackTraceHidden] - public static void RaiseException(CodeContext/*!*/ context, object self, [NotNull]RubyClass/*!*/ exceptionClass, [Optional]MutableString message, [Optional]RubyArray stackTrace) { + public static void RaiseException(CodeContext/*!*/ context, object self, [NotNull]RubyClass/*!*/ exceptionClass, + [DefaultParameterValue(null)]object exceptionArg, [Optional]RubyArray stackTrace) { RubyExecutionContext ec = RubyUtils.GetExecutionContext(context); - Exception e = ExceptionOps.MakeException(context, exceptionClass, message, stackTrace); + Exception e = ExceptionOps.MakeException(context, exceptionClass, exceptionArg, stackTrace); RubyExceptionData.ClearBacktrace(e); throw e; } @@ -483,14 +485,15 @@ [RubyMethod("fail", RubyMethodAttributes.PrivateInstance)] [RubyMethod("fail", RubyMethodAttributes.PublicSingleton)] [RubyStackTraceHidden] - public static void RaiseException(CodeContext/*!*/ context, object self, object/*!*/ exceptionObject, [Optional]MutableString message, [Optional]List stackTrace) { + public static void RaiseException(CodeContext/*!*/ context, object self, object/*!*/ exceptionObject, + [Optional]object exceptionArg, [Optional]List stackTrace) { // exceptionObject <# { exception: void -> Exception } RubyExecutionContext ec = RubyUtils.GetExecutionContext(context); if (RubySites.RespondTo(context, exceptionObject, "exception")) { Exception e; - if (message != null) { - e = RubySites.ExceptionWithMessage(context, exceptionObject, message); + if (exceptionArg != Missing.Value) { + e = RubySites.ExceptionWithArgument(context, exceptionObject, exceptionArg); } else { e = RubySites.Exception(context, exceptionObject); } =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/MatchDataOps.cs;C448469 File: MatchDataOps.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/MatchDataOps.cs;C448469 (server) 5/25/2008 1:30 AM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/MatchDataOps.cs;Regex2 @@ -132,7 +132,11 @@ } private static RubyArray/*!*/ ReturnMatchingGroups(CodeContext/*!*/ context, MatchData/*!*/ self, int group) { - AssertValidGroup(self, group); + // TODO: AssertValidGroup(self, group); + if (self.Groups.Count < group) { + return new RubyArray(); + } + RubyArray result = new RubyArray(self.Groups.Count - group); for (int i = group; i < self.Groups.Count; i++) { result.Add(Kernel.FlowTaint(context, self, MatchData.GetGroupValue(self.Groups[i]))); =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/RubyRegexOps.cs;C448469 File: RubyRegexOps.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/RubyRegexOps.cs;C448469 (server) 5/25/2008 9:51 AM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/RubyRegexOps.cs;Regex2 @@ -32,8 +32,6 @@ // == // eql? // hash - // singleton: - // union //------------------------------------------------------------ Regexp::new // Regexp.new(string [, options [, lang]]) => regexp @@ -374,6 +372,21 @@ public static MutableString/*!*/ LastMatch(CodeContext/*!*/ context, object/*!*/ self, object group) { return LastMatch(context, self, Protocols.CastToFixnum(context, group)); } + + [RubyMethod("union", RubyMethodAttributes.PublicSingleton)] + public static RubyRegex/*!*/ Union(CodeContext/*!*/ context, object/*!*/ self, [NotNull]params object[]/*!*/ strings) { + MutableString result = MutableString.CreateMutable(); + for (int i = 0; i < strings.Length; i++) { + MutableString str = Protocols.CastToString(context, strings[i]); + if (i > 0) { + result.Append('|'); + } + result.Append(RubyRegex.Escape(str)); + } + + return new RubyRegex(result, RubyRegexOptions.NONE); + } + } } =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/socket/TCPServer.cs;C448462 File: TCPServer.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/socket/TCPServer.cs;C448462 (server) 5/25/2008 9:28 AM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/socket/TCPServer.cs;Regex2 @@ -41,6 +41,10 @@ // acquire the result and replace it by null, so that no other thread can acquire it: IAsyncResult result = Interlocked.Exchange(ref _acceptResult, null); + if (result == null) { + return Socket.Accept(); + } + // wait until accept finishes: return Socket.EndAccept(result); } =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Builtins/RubyRegex.cs;C444052 File: RubyRegex.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Builtins/RubyRegex.cs;C444052 (server) 5/24/2008 10:07 PM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Builtins/RubyRegex.cs;Regex2 @@ -21,6 +21,8 @@ using Ruby.Runtime; using System; using System.Text; +using System.Diagnostics; +using Ruby.Compiler; namespace Ruby.Builtins { public abstract class GenericRegex { @@ -73,21 +75,35 @@ internal static readonly StringRegex Empty = new StringRegex(new Regex(String.Empty, RubyRegex.ToClrOptions(RubyRegexOptions.NONE))); private readonly Regex/*!*/ _regex; + private readonly string/*!*/ _pattern; internal protected StringRegex(string/*!*/ pattern, RubyRegexOptions options) : base(options) { Assert.NotNull(pattern); - _regex = new Regex(pattern, RubyRegex.ToClrOptions(options)); + _pattern = pattern; + + string transformed = TransformPattern(pattern, options); + try { + _regex = new Regex(transformed, RubyRegex.ToClrOptions(options)); + } catch (ArgumentException e) { + Debug.WriteLine("-- original ---" + new String('-', 50), "REGEX ERROR"); + Debug.WriteLine(pattern, "REGEX ERROR"); + Debug.WriteLine("-- transformed " + new String('-', 50), "REGEX ERROR"); + Debug.WriteLine(transformed, "REGEX ERROR"); + Debug.WriteLine("---------------" + new String('-', 50), "REGEX ERROR"); + throw new RegexpError(e.Message, e); + } } internal protected StringRegex(Regex/*!*/ regex) : base(RubyRegexOptions.NONE) { Assert.NotNull(regex); _regex = regex; + _pattern = regex.ToString(); } public override bool IsEmpty { - get { return _regex.ToString().Length == 0; } + get { return _pattern.Length == 0; } } public override Match/*!*/ Match(MutableString/*!*/ input, int start, int count) { @@ -99,17 +115,155 @@ } public override MutableString/*!*/ GetPattern() { - return MutableString.Create(_regex.ToString()); + return MutableString.Create(_pattern); } public override MutableString[]/*!*/ Split(MutableString/*!*/ input, int count, int start) { return MutableString.MakeArray(_regex.Split(input.ConvertToString(), count, start)); } + + private static int SkipWellEscaped(string/*!*/ pattern, int i) { + while (i < pattern.Length - 1) { + if (pattern[i] == '\\') { + switch (pattern[i + 1]) { + // metacharacters: + case '$': + case '^': + case '|': + case '[': + case ']': + case '(': + case ')': + case '\\': + case '.': + case '#': + + case '{': + case '}': + case '*': + case '+': + case '?': + + case '0': // octal + case 't': + case 'v': + case 'n': + case 'r': + case 'f': + case 'a': + case 'e': // characters + + case 'b': // word boundary or backslash in character group + case 'B': // not word boundary + case 'A': // beginning of string + case 'Z': // end of string, or before newline at the end + case 'z': // end of string + case 'G': + + case 'd': + case 'D': + case 's': + case 'S': + case 'w': // word character + case 'W': // non word char + // TODO: may need non-Unicode adjustment + + case 'C': // control characters + case 'M': // meta characters + // TODO: replace + + // Oniguruma + .NET - character classes, they don't match so some fixups would be needed + // MRI: doesn't support, but is also an error since it is followed by {name}, which is illegal + case 'p': + case 'P': + // keep + break; + + default: + return i; + } + i += 2; + } else { + i += 1; + } + } + return -1; + } + + // fixes escapes + // - unescapes non-special characters + // - fixes \xF -> \x0F + internal static string/*!*/ TransformPattern(string/*!*/ pattern, RubyRegexOptions options) { + + int first = 0; + int i = SkipWellEscaped(pattern, 0); + + // trailing backslash is an error in both MRI, .NET + if (i == -1) { + return pattern; + } + + StringBuilder result = new StringBuilder(pattern.Length); + + do { + Debug.Assert(i + 1 < pattern.Length); + Debug.Assert(pattern[i] == '\\'); + + result.Append(pattern, first, i - first); + i++; + + char c = pattern[i++]; + switch (c) { + case 'x': + result.Append('\\'); + result.Append('x'); + + // error: + if (i == pattern.Length) { + break; + } + + // fix single digit: + c = pattern[i++]; + if (i == pattern.Length || !Tokenizer.IsHexadecimalDigit(pattern[i])) { + result.Append('0'); + } + result.Append(c); + break; + + case 'h': // Oniguruma only: [0-9A-Fa-f] + case 'H': // Oniguruma only: [^0-9A-Fa-f] + case 'g': // Oniguruma only + case 'k': // Oniguruma, .NET: named backreference, MRI not supported + // remove backslash + + default: + if (Tokenizer.IsDecimalDigit(c)) { + // TODO: + // \([1-9][0-9]*) where there is no group of such number (replace by an empty string) + result.Append('\\'); + } + + // .NET throws invalid escape exception, remove backslash: + result.Append(c); + break; + } + Debug.Assert(i <= pattern.Length); + + first = i; + i = SkipWellEscaped(pattern, i); + } while (i >= 0); + + result.Append(pattern, first, pattern.Length - first); + return result.ToString(); + } } public class RubyRegex : IDuplicable { private GenericRegex/*!*/ _regex; + #region Construction + public RubyRegex() { _regex = StringRegex.Empty; } @@ -139,6 +293,8 @@ _regex = regex._regex; } + #endregion + public bool IsEmpty { get { return _regex.IsEmpty; } } @@ -151,6 +307,8 @@ return _regex.GetPattern(); } + #region Match, ReverseMatch, Matches, Split + public Match/*!*/ Match(MutableString/*!*/ input) { return Match(input, 0); } @@ -165,6 +323,21 @@ return _regex.Match(input, start, count); } + public MatchData ReverseMatch(CodeContext/*!*/ context, MutableString/*!*/ str) { + ContractUtils.RequiresNotNull(str, "str"); + return ReverseMatch(context, str, str.Length); + } + + public MatchData ReverseMatch(CodeContext/*!*/ context, MutableString/*!*/ str, int offset) { + ContractUtils.RequiresNotNull(str, "str"); + + // We are cloning a Regex object just so that we can do right to left eval. yuck. + Regex regex = new Regex(_regex.GetPattern().ConvertToString(), ToClrOptions(Options) | RegexOptions.RightToLeft); + MatchData result = new MatchData(regex.Match(str.ConvertToString(), offset), str); + RubyUtils.GetScope(context).CurrentMatch = result.Success ? result : null; + return result; + } + public MatchCollection/*!*/ Matches(MutableString/*!*/ input) { return Matches(input, 0); } @@ -186,26 +359,13 @@ return _regex.Split(input, count, start); } + #endregion + // TODO: public static MutableString/*!*/ Escape(MutableString/*!*/ str) { return MutableString.Create(Regex.Escape(str.ConvertToString())); } - public MatchData ReverseMatch(CodeContext/*!*/ context, MutableString/*!*/ str) { - ContractUtils.RequiresNotNull(str, "str"); - return ReverseMatch(context, str, str.Length); - } - - public MatchData ReverseMatch(CodeContext/*!*/ context, MutableString/*!*/ str, int offset) { - ContractUtils.RequiresNotNull(str, "str"); - - // We are cloning a Regex object just so that we can do right to left eval. yuck. - Regex regex = new Regex(_regex.GetPattern().ConvertToString(), ToClrOptions(Options) | RegexOptions.RightToLeft); - MatchData result = new MatchData(regex.Match(str.ConvertToString(), offset), str); - RubyUtils.GetScope(context).CurrentMatch = result.Success ? result : null; - return result; - } - #region IDuplicable Members public void InitializeFrom(object other) { =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Builtins/RubySites.cs;C434537 File: RubySites.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Builtins/RubySites.cs;C434537 (server) 5/25/2008 10:03 AM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Builtins/RubySites.cs;Regex2 @@ -143,11 +143,11 @@ return ModuleConstMissingSharedSite.Invoke(context, self, name); } - private static readonly DynamicSite ExceptionWithMessageSharedSite = DynamicSite.Create( + private static readonly DynamicSite ExceptionWithMessageSharedSite = DynamicSite.Create( InstanceCallAction("exception", ArgumentKind.Simple)); - public static Exception ExceptionWithMessage(CodeContext/*!*/ context, object self, MutableString msg) { - return ExceptionWithMessageSharedSite.Invoke(context, self, msg); + public static Exception ExceptionWithArgument(CodeContext/*!*/ context, object self, object exceptionArg) { + return ExceptionWithMessageSharedSite.Invoke(context, self, exceptionArg); } private static readonly DynamicSite ExceptionSharedSite = DynamicSite.Create( =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Compiler/Parser/Tokenizer.cs;C448462 File: Tokenizer.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Compiler/Parser/Tokenizer.cs;C448462 (server) 5/24/2008 11:27 PM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Compiler/Parser/Tokenizer.cs;Regex2 @@ -3256,13 +3256,13 @@ s++; } return retval; - } + } - private static bool IsDecimalDigit(int c) { + internal static bool IsDecimalDigit(int c) { return (uint)c - '0' <= (uint)'9' - '0'; } - private static bool IsHexadecimalDigit(int c) { + internal static bool IsHexadecimalDigit(int c) { return IsDecimalDigit(c) || (uint)c - 'a' <= (uint)'f' - 'a' || (uint)c - 'A' <= (uint)'F' - 'A'; ===================================================================