using System;
using System.Text.RegularExpressions;
using Jint.Native.Function;
using Jint.Native.Object;
using Jint.Runtime;
namespace Jint.Native.RegExp
{
public sealed class RegExpConstructor : FunctionInstance, IConstructor
{
public RegExpConstructor(Engine engine)
: base(engine, null, null, false)
{
}
public static RegExpConstructor CreateRegExpConstructor(Engine engine)
{
var obj = new RegExpConstructor(engine);
obj.Extensible = true;
// The value of the [[Prototype]] internal property of the RegExp constructor is the Function prototype object
obj.Prototype = engine.Function.PrototypeObject;
obj.PrototypeObject = RegExpPrototype.CreatePrototypeObject(engine, obj);
obj.FastAddProperty("length", 2, false, false, false);
// The initial value of RegExp.prototype is the RegExp prototype object
obj.FastAddProperty("prototype", obj.PrototypeObject, false, false, false);
return obj;
}
public void Configure()
{
}
public override JsValue Call(JsValue thisObject, JsValue[] arguments)
{
var pattern = arguments.At(0);
var flags = arguments.At(1);
if (pattern != Undefined.Instance && flags == Undefined.Instance && TypeConverter.ToObject(Engine, pattern).Class == "Regex")
{
return pattern;
}
return Construct(arguments);
}
///
/// http://www.ecma-international.org/ecma-262/5.1/#sec-7.8.5
/// http://www.ecma-international.org/ecma-262/5.1/#sec-15.10.4
///
///
///
public ObjectInstance Construct(JsValue[] arguments)
{
string p;
string f;
var pattern = arguments.At(0);
var flags = arguments.At(1);
var r = pattern.TryCast();
if (flags == Undefined.Instance && r != null)
{
return r;
}
else if (flags != Undefined.Instance && r != null)
{
throw new JavaScriptException(Engine.TypeError);
}
else
{
if (pattern == Undefined.Instance)
{
p = "";
}
else
{
p = TypeConverter.ToString(pattern);
}
f = flags != Undefined.Instance ? TypeConverter.ToString(flags) : "";
}
r = new RegExpInstance(Engine);
r.Prototype = PrototypeObject;
r.Extensible = true;
var options = ParseOptions(r, f);
try
{
r.Value = new Regex(p, options);
}
catch (Exception e)
{
throw new JavaScriptException(Engine.SyntaxError, e.Message);
}
string s;
s = p;
if (System.String.IsNullOrEmpty(s))
{
s = "(?:)";
}
r.Flags = f;
r.Source = s;
r.FastAddProperty("global", r.Global, false, false, false);
r.FastAddProperty("ignoreCase", r.IgnoreCase, false, false, false);
r.FastAddProperty("multiline", r.Multiline, false, false, false);
r.FastAddProperty("source", r.Source, false, false, false);
r.FastAddProperty("lastIndex", 0, true, false, false);
return r;
}
public RegExpInstance Construct(string regExp)
{
var r = new RegExpInstance(Engine);
r.Prototype = PrototypeObject;
r.Extensible = true;
if (regExp[0] != '/')
{
throw new JavaScriptException(Engine.SyntaxError, "Regexp should start with slash");
}
var lastSlash = regExp.LastIndexOf('/');
// Unescape escaped forward slashes (\/)
var pattern = regExp.Substring(1, lastSlash - 1).Replace("\\/", "/");
var flags = regExp.Substring(lastSlash + 1);
var options = ParseOptions(r, flags);
try
{
if((RegexOptions.Multiline & options) == RegexOptions.Multiline)
{
// Replace all non-escaped $ occurences by \r?$
// c.f. http://programmaticallyspeaking.com/regular-expression-multiline-mode-whats-a-newline.html
int index = 0;
var newPattern = pattern;
while((index = newPattern.IndexOf("$", index)) != -1)
{
if(index > 0 && newPattern[index - 1] != '\\')
{
newPattern = newPattern.Substring(0, index) + @"\r?" + newPattern.Substring(index);
index += 4;
}
}
r.Value = new Regex(newPattern, options);
}
else
{
r.Value = new Regex(pattern, options);
}
}
catch (Exception e)
{
throw new JavaScriptException(Engine.SyntaxError, e.Message);
}
r.Flags = flags;
r.Source = System.String.IsNullOrEmpty(pattern) ? "(?:)" : pattern;
r.FastAddProperty("global", r.Global, false, false, false);
r.FastAddProperty("ignoreCase", r.IgnoreCase, false, false, false);
r.FastAddProperty("multiline", r.Multiline, false, false, false);
r.FastAddProperty("source", r.Source, false, false, false);
r.FastAddProperty("lastIndex", 0, true, false, false);
return r;
}
private RegexOptions ParseOptions(RegExpInstance r, string flags)
{
for (int k = 0; k < flags.Length; k++)
{
var c = flags[k];
if (c == 'g')
{
if (r.Global)
{
throw new JavaScriptException(Engine.SyntaxError);
}
r.Global = true;
}
else if (c == 'i')
{
if (r.IgnoreCase)
{
throw new JavaScriptException(Engine.SyntaxError);
}
r.IgnoreCase = true;
}
else if (c == 'm')
{
if (r.Multiline)
{
throw new JavaScriptException(Engine.SyntaxError);
}
r.Multiline = true;
}
else
{
throw new JavaScriptException(Engine.SyntaxError);
}
}
var options = RegexOptions.ECMAScript;
if (r.Multiline)
{
options = options | RegexOptions.Multiline;
}
if (r.IgnoreCase)
{
options = options | RegexOptions.IgnoreCase;
}
return options;
}
public RegExpPrototype PrototypeObject { get; private set; }
}
}