1864 lines
63 KiB
C#
1864 lines
63 KiB
C#
|
using System;
|
||
|
using System.Collections;
|
||
|
using System.Globalization;
|
||
|
using System.Reflection;
|
||
|
using System.Reflection.Emit;
|
||
|
using System.Threading;
|
||
|
|
||
|
using System.Collections.Generic;
|
||
|
|
||
|
namespace System.Text.RegularExpressions {
|
||
|
|
||
|
//
|
||
|
// Compiler which generates IL bytecode to perform the matching instead of
|
||
|
// interpreting a program.
|
||
|
// For simplicity, we inherit from RxCompiler, and generate the IL code based
|
||
|
// on the program generated by it. This also allows us to fallback to interpretation
|
||
|
// if we can't handle something.
|
||
|
// This is net 2.0, since 1.0 doesn't support DynamicMethods
|
||
|
// FIXME: Add support for 1.0, and CompileToAssembly
|
||
|
// FIXME: Overwrite RxCompiler methods so we don't have to decode char
|
||
|
// matching opcodes
|
||
|
//
|
||
|
|
||
|
class CILCompiler : RxCompiler, ICompiler {
|
||
|
DynamicMethod[] eval_methods;
|
||
|
bool[] eval_methods_defined;
|
||
|
|
||
|
/*
|
||
|
* To avoid the overhead of decoding the countless opcode variants created
|
||
|
* by RxCompiler, we save the original, 'generic' version and its flags
|
||
|
* in these two tables.
|
||
|
*/
|
||
|
private Dictionary<int, int> generic_ops;
|
||
|
private Dictionary<int, int> op_flags;
|
||
|
private Dictionary<int, Label> labels;
|
||
|
|
||
|
static FieldInfo fi_str = typeof (RxInterpreter).GetField ("str", BindingFlags.Instance|BindingFlags.NonPublic);
|
||
|
static FieldInfo fi_string_start = typeof (RxInterpreter).GetField ("string_start", BindingFlags.Instance|BindingFlags.NonPublic);
|
||
|
static FieldInfo fi_string_end = typeof (RxInterpreter).GetField ("string_end", BindingFlags.Instance|BindingFlags.NonPublic);
|
||
|
static FieldInfo fi_program = typeof (RxInterpreter).GetField ("program", BindingFlags.Instance|BindingFlags.NonPublic);
|
||
|
static FieldInfo fi_marks = typeof (RxInterpreter).GetField ("marks", BindingFlags.Instance|BindingFlags.NonPublic);
|
||
|
static FieldInfo fi_groups = typeof (RxInterpreter).GetField ("groups", BindingFlags.Instance|BindingFlags.NonPublic);
|
||
|
static FieldInfo fi_deep = typeof (RxInterpreter).GetField ("deep", BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic);
|
||
|
static FieldInfo fi_stack = typeof (RxInterpreter).GetField ("stack", BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic);
|
||
|
static FieldInfo fi_mark_start = typeof (Mark).GetField ("Start", BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic);
|
||
|
static FieldInfo fi_mark_end = typeof (Mark).GetField ("End", BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic);
|
||
|
//static FieldInfo fi_mark_index = typeof (Mark).GetField ("Index", BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic);
|
||
|
|
||
|
static MethodInfo mi_stack_get_count, mi_stack_set_count, mi_stack_push, mi_stack_pop;
|
||
|
static MethodInfo mi_set_start_of_match, mi_is_word_char, mi_reset_groups;
|
||
|
static MethodInfo mi_checkpoint, mi_backtrack, mi_open, mi_close;
|
||
|
static MethodInfo mi_get_last_defined, mi_mark_get_index, mi_mark_get_length;
|
||
|
|
||
|
public static readonly bool trace_compile = Environment.GetEnvironmentVariable ("MONO_TRACE_RX_COMPILE") != null;
|
||
|
|
||
|
public CILCompiler () {
|
||
|
generic_ops = new Dictionary <int, int> ();
|
||
|
op_flags = new Dictionary <int, int> ();
|
||
|
}
|
||
|
|
||
|
IMachineFactory ICompiler.GetMachineFactory () {
|
||
|
byte[] code = new byte [curpos];
|
||
|
Buffer.BlockCopy (program, 0, code, 0, curpos);
|
||
|
|
||
|
eval_methods = new DynamicMethod [code.Length];
|
||
|
eval_methods_defined = new bool [code.Length];
|
||
|
|
||
|
// The main eval method
|
||
|
DynamicMethod main = GetEvalMethod (code, 11);
|
||
|
|
||
|
if (main != null)
|
||
|
return new RxInterpreterFactory (code, (EvalDelegate)main.CreateDelegate (typeof (EvalDelegate)));
|
||
|
else
|
||
|
return new RxInterpreterFactory (code, null);
|
||
|
}
|
||
|
|
||
|
DynamicMethod GetEvalMethod (byte[] program, int pc) {
|
||
|
if (eval_methods_defined [pc])
|
||
|
return eval_methods [pc];
|
||
|
|
||
|
// FIXME: Recursion ?
|
||
|
eval_methods_defined [pc] = true;
|
||
|
|
||
|
eval_methods [pc] = CreateEvalMethod (program, pc);
|
||
|
return eval_methods [pc];
|
||
|
}
|
||
|
|
||
|
private MethodInfo GetMethod (Type t, string name, ref MethodInfo cached) {
|
||
|
if (cached == null) {
|
||
|
cached = t.GetMethod (name, BindingFlags.Static|BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic);
|
||
|
if (cached == null)
|
||
|
throw new Exception ("Method not found: " + name);
|
||
|
}
|
||
|
return cached;
|
||
|
}
|
||
|
|
||
|
private MethodInfo GetMethod (string name, ref MethodInfo cached) {
|
||
|
return GetMethod (typeof (RxInterpreter), name, ref cached);
|
||
|
}
|
||
|
|
||
|
private int ReadInt (byte[] code, int pc) {
|
||
|
int val = code [pc];
|
||
|
val |= code [pc + 1] << 8;
|
||
|
val |= code [pc + 2] << 16;
|
||
|
val |= code [pc + 3] << 24;
|
||
|
return val;
|
||
|
}
|
||
|
|
||
|
static OpFlags MakeFlags (bool negate, bool ignore, bool reverse, bool lazy) {
|
||
|
OpFlags flags = 0;
|
||
|
if (negate) flags |= OpFlags.Negate;
|
||
|
if (ignore) flags |= OpFlags.IgnoreCase;
|
||
|
if (reverse) flags |= OpFlags.RightToLeft;
|
||
|
if (lazy) flags |= OpFlags.Lazy;
|
||
|
|
||
|
return flags;
|
||
|
}
|
||
|
|
||
|
void EmitGenericOp (RxOp op, bool negate, bool ignore, bool reverse, bool lazy) {
|
||
|
generic_ops [curpos] = (int)op;
|
||
|
op_flags [curpos] = (int)MakeFlags (negate, ignore, reverse, false);
|
||
|
}
|
||
|
|
||
|
public override void EmitOp (RxOp op, bool negate, bool ignore, bool reverse) {
|
||
|
EmitGenericOp (op, negate, ignore, reverse, false);
|
||
|
base.EmitOp (op, negate, ignore, reverse);
|
||
|
}
|
||
|
|
||
|
public override void EmitOpIgnoreReverse (RxOp op, bool ignore, bool reverse) {
|
||
|
EmitGenericOp (op, false, ignore, reverse, false);
|
||
|
base.EmitOpIgnoreReverse (op, ignore, reverse);
|
||
|
}
|
||
|
|
||
|
public override void EmitOpNegateReverse (RxOp op, bool negate, bool reverse) {
|
||
|
EmitGenericOp (op, negate, false, reverse, false);
|
||
|
base.EmitOpNegateReverse (op, negate, reverse);
|
||
|
}
|
||
|
|
||
|
class Frame {
|
||
|
public Label label_pass, label_fail;
|
||
|
|
||
|
public Frame (ILGenerator ilgen) {
|
||
|
label_fail = ilgen.DefineLabel ();
|
||
|
label_pass = ilgen.DefineLabel ();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
LocalBuilder local_textinfo;
|
||
|
|
||
|
/*
|
||
|
* Create a dynamic method which is equivalent to the RxInterpreter.EvalByteCode
|
||
|
* method specialized to the given program and a given pc. Return the newly
|
||
|
* created method or null if a not-supported opcode was encountered.
|
||
|
*/
|
||
|
DynamicMethod CreateEvalMethod (byte[] program, int pc) {
|
||
|
DynamicMethod m = new DynamicMethod ("Eval_" + pc, typeof (bool), new Type [] { typeof (RxInterpreter), typeof (int), typeof (int).MakeByRefType () }, typeof (RxInterpreter), true);
|
||
|
ILGenerator ilgen = m.GetILGenerator ();
|
||
|
|
||
|
/*
|
||
|
Args:
|
||
|
interp - 0
|
||
|
strpos - 1
|
||
|
strpos_result - 2
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* Recursive calls to EvalByteCode are inlined manually by calling
|
||
|
* EmitEvalMethodBody with the pc of the recursive call. Frame objects hold
|
||
|
* the information required to link together the code generated by the recursive
|
||
|
* call with the rest of the code.
|
||
|
*/
|
||
|
Frame frame = new Frame (ilgen);
|
||
|
|
||
|
/* Cache the textinfo used by Char.ToLower () */
|
||
|
local_textinfo = ilgen.DeclareLocal (typeof (TextInfo));
|
||
|
ilgen.Emit (OpCodes.Call, typeof (Thread).GetMethod ("get_CurrentThread"));
|
||
|
ilgen.Emit (OpCodes.Call, typeof (Thread).GetMethod ("get_CurrentCulture"));
|
||
|
ilgen.Emit (OpCodes.Call, typeof (CultureInfo).GetMethod ("get_TextInfo"));
|
||
|
ilgen.Emit (OpCodes.Stloc, local_textinfo);
|
||
|
|
||
|
m = EmitEvalMethodBody (m, ilgen, frame, program, pc, program.Length, false, false, out pc);
|
||
|
if (m == null)
|
||
|
return null;
|
||
|
|
||
|
ilgen.MarkLabel (frame.label_pass);
|
||
|
ilgen.Emit (OpCodes.Ldarg_2);
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Stind_I4);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4_1);
|
||
|
ilgen.Emit (OpCodes.Ret);
|
||
|
|
||
|
ilgen.MarkLabel (frame.label_fail);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4_0);
|
||
|
ilgen.Emit (OpCodes.Ret);
|
||
|
|
||
|
return m;
|
||
|
}
|
||
|
|
||
|
private int ReadShort (byte[] program, int pc) {
|
||
|
return (int)program [pc] | ((int)program [pc + 1] << 8);
|
||
|
}
|
||
|
|
||
|
private Label CreateLabelForPC (ILGenerator ilgen, int pc) {
|
||
|
if (labels == null)
|
||
|
labels = new Dictionary <int, Label> ();
|
||
|
Label l;
|
||
|
if (!labels.TryGetValue (pc, out l)) {
|
||
|
l = ilgen.DefineLabel ();
|
||
|
labels [pc] = l;
|
||
|
}
|
||
|
|
||
|
return l;
|
||
|
}
|
||
|
|
||
|
private int GetILOffset (ILGenerator ilgen) {
|
||
|
return (int)typeof (ILGenerator).GetField ("code_len", BindingFlags.Instance|BindingFlags.NonPublic).GetValue (ilgen);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Emit IL code for a sequence of opcodes between pc and end_pc. If there is a
|
||
|
* match, set strpos (Arg 1) to the position after the match, then
|
||
|
* branch to frame.label_pass. Otherwise branch to frame.label_fail,
|
||
|
* and leave strpos at an undefined position. The caller should
|
||
|
* generate code to save the original value of strpos if it needs it.
|
||
|
* If one_op is true, only generate code for one opcode and set out_pc
|
||
|
* to the next pc after the opcode.
|
||
|
* If no_bump is true, don't bump strpos in char matching opcodes.
|
||
|
* Keep this in synch with RxInterpreter.EvalByteCode (). It it is sync with
|
||
|
* the version in r111969.
|
||
|
* FIXME: Modify the regex tests so they are run with RegexOptions.Compiled as
|
||
|
* well.
|
||
|
*/
|
||
|
private DynamicMethod EmitEvalMethodBody (DynamicMethod m, ILGenerator ilgen,
|
||
|
Frame frame, byte[] program,
|
||
|
int pc, int end_pc,
|
||
|
bool one_op, bool no_bump,
|
||
|
out int out_pc)
|
||
|
{
|
||
|
int start, length, end;
|
||
|
|
||
|
out_pc = 0;
|
||
|
|
||
|
int group_count = 1 + ReadShort (program, 1);
|
||
|
|
||
|
while (pc < end_pc) {
|
||
|
RxOp op = (RxOp)program [pc];
|
||
|
|
||
|
// FIXME: Optimize this
|
||
|
if (generic_ops.ContainsKey (pc))
|
||
|
op = (RxOp)generic_ops [pc];
|
||
|
|
||
|
if (trace_compile) {
|
||
|
Console.WriteLine ("compiling {0} pc={1} end_pc={2}, il_offset=0x{3:x}", op, pc, end_pc, GetILOffset (ilgen));
|
||
|
}
|
||
|
|
||
|
if (labels != null) {
|
||
|
Label l;
|
||
|
if (labels.TryGetValue (pc, out l)) {
|
||
|
ilgen.MarkLabel (l);
|
||
|
labels.Remove (pc);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (RxInterpreter.trace_rx) {
|
||
|
//Console.WriteLine ("evaluating: {0} at pc: {1}, strpos: {2}", op, pc, strpos);
|
||
|
ilgen.Emit (OpCodes.Ldstr, "evaluating: {0} at pc: {1}, strpos: {2}");
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, (int)op);
|
||
|
ilgen.Emit (OpCodes.Box, typeof (RxOp));
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, pc);
|
||
|
ilgen.Emit (OpCodes.Box, typeof (int));
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Box, typeof (int));
|
||
|
ilgen.Emit (OpCodes.Call, typeof (Console).GetMethod ("WriteLine", new Type [] { typeof (string), typeof (object), typeof (object), typeof (object) }));
|
||
|
}
|
||
|
|
||
|
switch (op) {
|
||
|
case RxOp.Anchor:
|
||
|
case RxOp.AnchorReverse: {
|
||
|
bool reverse = (RxOp)program [pc] == RxOp.AnchorReverse;
|
||
|
length = ReadShort (program, pc + 3);
|
||
|
pc += ReadShort (program, pc + 1);
|
||
|
|
||
|
// Optimize some common cases by inlining the code generated for the
|
||
|
// anchor body
|
||
|
RxOp anch_op = (RxOp)program [pc];
|
||
|
|
||
|
// FIXME: Do this even if the archor op is not the last in the regex
|
||
|
if (!reverse && group_count == 1 && anch_op == RxOp.Char && (RxOp)program [pc + 2] == RxOp.True) {
|
||
|
|
||
|
/*
|
||
|
* while (strpos < string_end) {
|
||
|
* if (str [strpos] == program [pc + 1]) {
|
||
|
* match_start = strpos;
|
||
|
* strpos_result = strpos + 1;
|
||
|
* marks [groups [0]].Start = strpos;
|
||
|
* if (groups.Length > 1)
|
||
|
* marks [groups [0]].End = res;
|
||
|
* return true;
|
||
|
* }
|
||
|
* strpos ++;
|
||
|
* }
|
||
|
* return false;
|
||
|
*/
|
||
|
// Add some locals to avoid an indirection
|
||
|
LocalBuilder local_string_end = ilgen.DeclareLocal (typeof (int));
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Ldfld, fi_string_end);
|
||
|
ilgen.Emit (OpCodes.Stloc, local_string_end);
|
||
|
LocalBuilder local_str = ilgen.DeclareLocal (typeof (string));
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Ldfld, fi_str);
|
||
|
ilgen.Emit (OpCodes.Stloc, local_str);
|
||
|
|
||
|
//while (strpos < string_end) {
|
||
|
// -> Done at the end of the loop like mcs does
|
||
|
Label l1 = ilgen.DefineLabel ();
|
||
|
Label l2 = ilgen.DefineLabel ();
|
||
|
ilgen.Emit (OpCodes.Br, l2);
|
||
|
ilgen.MarkLabel (l1);
|
||
|
|
||
|
// if (str [strpos] == program [pc + 1]) {
|
||
|
Label l3 = ilgen.DefineLabel ();
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_str);
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Callvirt, typeof (string).GetMethod ("get_Chars"));
|
||
|
ilgen.Emit (OpCodes.Conv_I4);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, (int)program [pc + 1]);
|
||
|
ilgen.Emit (OpCodes.Beq, l3);
|
||
|
|
||
|
// The true case is done after the loop
|
||
|
|
||
|
// }
|
||
|
// strpos++;
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4_1);
|
||
|
ilgen.Emit (OpCodes.Add);
|
||
|
ilgen.Emit (OpCodes.Starg, 1);
|
||
|
//}
|
||
|
ilgen.MarkLabel (l2);
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_string_end);
|
||
|
ilgen.Emit (OpCodes.Blt, l1);
|
||
|
|
||
|
//return false;
|
||
|
ilgen.Emit (OpCodes.Br, frame.label_fail);
|
||
|
|
||
|
// True case
|
||
|
ilgen.MarkLabel (l3);
|
||
|
// call SetStartOfMatch (strpos)
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Call, GetMethod (typeof (RxInterpreter), "SetStartOfMatch", ref mi_set_start_of_match));
|
||
|
// strpos++;
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4_1);
|
||
|
ilgen.Emit (OpCodes.Add);
|
||
|
ilgen.Emit (OpCodes.Starg, 1);
|
||
|
// return true;
|
||
|
ilgen.Emit (OpCodes.Br, frame.label_pass);
|
||
|
|
||
|
} else {
|
||
|
// General case
|
||
|
|
||
|
//Console.WriteLine ("Anchor op " + anch_op);
|
||
|
|
||
|
// Add some locals to avoid an indirection
|
||
|
LocalBuilder local_string_end = ilgen.DeclareLocal (typeof (int));
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Ldfld, fi_string_end);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4_1);
|
||
|
ilgen.Emit (OpCodes.Add);
|
||
|
ilgen.Emit (OpCodes.Stloc, local_string_end);
|
||
|
|
||
|
//while (strpos < string_end + 1) {
|
||
|
// -> Done at the end of the loop like mcs does
|
||
|
Label l1 = ilgen.DefineLabel ();
|
||
|
Label l2 = ilgen.DefineLabel ();
|
||
|
ilgen.Emit (OpCodes.Br, l2);
|
||
|
ilgen.MarkLabel (l1);
|
||
|
|
||
|
//if (groups.Length > 1) {
|
||
|
// ResetGroups ();
|
||
|
// marks [groups [0]].Start = strpos;
|
||
|
//}
|
||
|
if (group_count > 1) {
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Call, GetMethod ("ResetGroups", ref mi_reset_groups));
|
||
|
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Ldfld, fi_marks);
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Ldfld, fi_groups);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4_0);
|
||
|
ilgen.Emit (OpCodes.Ldelem_I4);
|
||
|
ilgen.Emit (OpCodes.Ldelema, typeof (Mark));
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Stfld, fi_mark_start);
|
||
|
}
|
||
|
|
||
|
// if (EvalByteCode (pc, strpos, ref res)) {
|
||
|
|
||
|
Frame new_frame = new Frame (ilgen);
|
||
|
|
||
|
// old_stros = strpos;
|
||
|
LocalBuilder local_old_strpos = ilgen.DeclareLocal (typeof (int));
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Stloc, local_old_strpos);
|
||
|
|
||
|
m = EmitEvalMethodBody (m, ilgen, new_frame, program, pc, end_pc, false, false, out out_pc);
|
||
|
if (m == null)
|
||
|
return null;
|
||
|
|
||
|
// Pass
|
||
|
ilgen.MarkLabel (new_frame.label_pass);
|
||
|
// marks [groups [0]].Start = old_strpos;
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Ldfld, fi_marks);
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Ldfld, fi_groups);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4_0);
|
||
|
ilgen.Emit (OpCodes.Ldelem_I4);
|
||
|
ilgen.Emit (OpCodes.Ldelema, typeof (Mark));
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_old_strpos);
|
||
|
ilgen.Emit (OpCodes.Stfld, fi_mark_start);
|
||
|
// if (groups.Length > 1)
|
||
|
// marks [groups [0]].End = res;
|
||
|
if (group_count > 1) {
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Ldfld, fi_marks);
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Ldfld, fi_groups);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4_0);
|
||
|
ilgen.Emit (OpCodes.Ldelem_I4);
|
||
|
ilgen.Emit (OpCodes.Ldelema, typeof (Mark));
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Stfld, fi_mark_end);
|
||
|
}
|
||
|
|
||
|
// return true;
|
||
|
ilgen.Emit (OpCodes.Br, frame.label_pass);
|
||
|
|
||
|
// Fail
|
||
|
ilgen.MarkLabel (new_frame.label_fail);
|
||
|
// strpos = old_strpos +/- 1;
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_old_strpos);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4_1);
|
||
|
if (reverse)
|
||
|
ilgen.Emit (OpCodes.Sub);
|
||
|
else
|
||
|
ilgen.Emit (OpCodes.Add);
|
||
|
ilgen.Emit (OpCodes.Starg, 1);
|
||
|
//}
|
||
|
ilgen.MarkLabel (l2);
|
||
|
if (reverse) {
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4_0);
|
||
|
ilgen.Emit (OpCodes.Bge, l1);
|
||
|
} else {
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_string_end);
|
||
|
ilgen.Emit (OpCodes.Blt, l1);
|
||
|
}
|
||
|
//return false;
|
||
|
ilgen.Emit (OpCodes.Br, frame.label_fail);
|
||
|
}
|
||
|
|
||
|
goto End;
|
||
|
}
|
||
|
case RxOp.Branch: {
|
||
|
//if (EvalByteCode (pc + 3, strpos, ref res)) {
|
||
|
|
||
|
int target_pc = pc + ReadShort (program, pc + 1);
|
||
|
|
||
|
// Emit the rest of the code inline instead of making a recursive call
|
||
|
Frame new_frame = new Frame (ilgen);
|
||
|
|
||
|
// old_strpos = strpos;
|
||
|
LocalBuilder local_old_strpos = ilgen.DeclareLocal (typeof (int));
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Stloc, local_old_strpos);
|
||
|
|
||
|
m = EmitEvalMethodBody (m, ilgen, new_frame, program, pc + 3, target_pc, false, false, out out_pc);
|
||
|
if (m == null)
|
||
|
return null;
|
||
|
|
||
|
// Pass
|
||
|
ilgen.MarkLabel (new_frame.label_pass);
|
||
|
// return true;
|
||
|
ilgen.Emit (OpCodes.Br, frame.label_pass);
|
||
|
|
||
|
// Fail
|
||
|
ilgen.MarkLabel (new_frame.label_fail);
|
||
|
// strpos = old_strpos;
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_old_strpos);
|
||
|
ilgen.Emit (OpCodes.Starg, 1);
|
||
|
|
||
|
pc = target_pc;
|
||
|
break;
|
||
|
}
|
||
|
case RxOp.Char:
|
||
|
case RxOp.UnicodeChar:
|
||
|
case RxOp.Range:
|
||
|
case RxOp.UnicodeRange: {
|
||
|
OpFlags flags = (OpFlags)op_flags [pc];
|
||
|
bool negate = (flags & OpFlags.Negate) > 0;
|
||
|
bool ignore = (flags & OpFlags.IgnoreCase) > 0;
|
||
|
bool reverse = (flags & OpFlags.RightToLeft) > 0;
|
||
|
|
||
|
//if (strpos < string_end) {
|
||
|
Label l1 = ilgen.DefineLabel ();
|
||
|
if (reverse) {
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4_0);
|
||
|
ilgen.Emit (OpCodes.Ble, l1);
|
||
|
} else {
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Ldfld, fi_string_end);
|
||
|
ilgen.Emit (OpCodes.Bge, l1);
|
||
|
}
|
||
|
|
||
|
if (ignore)
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_textinfo);
|
||
|
|
||
|
// int c = str [strpos];
|
||
|
LocalBuilder local_c = ilgen.DeclareLocal (typeof (char));
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Ldfld, fi_str);
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
if (reverse) {
|
||
|
ilgen.Emit (OpCodes.Ldc_I4_1);
|
||
|
ilgen.Emit (OpCodes.Sub);
|
||
|
}
|
||
|
ilgen.Emit (OpCodes.Callvirt, typeof (string).GetMethod ("get_Chars"));
|
||
|
if (ignore)
|
||
|
ilgen.Emit (OpCodes.Callvirt, typeof (TextInfo).GetMethod ("ToLower", new Type [] { typeof (char) }));
|
||
|
|
||
|
if (op == RxOp.Char) {
|
||
|
ilgen.Emit (OpCodes.Conv_I4);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, (int)program [pc + 1]);
|
||
|
ilgen.Emit (negate ? OpCodes.Beq : OpCodes.Bne_Un, l1);
|
||
|
|
||
|
pc += 2;
|
||
|
} else if (op == RxOp.UnicodeChar) {
|
||
|
ilgen.Emit (OpCodes.Conv_I4);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, ReadShort (program, pc + 1));
|
||
|
ilgen.Emit (negate ? OpCodes.Beq : OpCodes.Bne_Un, l1);
|
||
|
|
||
|
pc += 3;
|
||
|
} else if (op == RxOp.Range) {
|
||
|
ilgen.Emit (OpCodes.Stloc, local_c);
|
||
|
|
||
|
// if (c >= program [pc + 1] && c <= program [pc + 2]) {
|
||
|
if (negate) {
|
||
|
Label l3 = ilgen.DefineLabel ();
|
||
|
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_c);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, (int)program [pc + 1]);
|
||
|
ilgen.Emit (OpCodes.Blt, l3);
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_c);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, (int)program [pc + 2]);
|
||
|
ilgen.Emit (OpCodes.Bgt, l3);
|
||
|
ilgen.Emit (OpCodes.Br, l1);
|
||
|
ilgen.MarkLabel (l3);
|
||
|
} else {
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_c);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, (int)program [pc + 1]);
|
||
|
ilgen.Emit (OpCodes.Blt, l1);
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_c);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, (int)program [pc + 2]);
|
||
|
ilgen.Emit (OpCodes.Bgt, l1);
|
||
|
}
|
||
|
|
||
|
pc += 3;
|
||
|
} else if (op == RxOp.UnicodeRange) {
|
||
|
ilgen.Emit (OpCodes.Stloc, local_c);
|
||
|
|
||
|
// if (c >= program [pc + 1] && c <= program [pc + 2]) {
|
||
|
if (negate) {
|
||
|
Label l3 = ilgen.DefineLabel ();
|
||
|
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_c);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, ReadShort (program, pc + 1));
|
||
|
ilgen.Emit (OpCodes.Blt, l3);
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_c);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, ReadShort (program, pc + 3));
|
||
|
ilgen.Emit (OpCodes.Bgt, l3);
|
||
|
ilgen.Emit (OpCodes.Br, l1);
|
||
|
ilgen.MarkLabel (l3);
|
||
|
} else {
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_c);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, ReadShort (program, pc + 1));
|
||
|
ilgen.Emit (OpCodes.Blt, l1);
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_c);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, ReadShort (program, pc + 3));
|
||
|
ilgen.Emit (OpCodes.Bgt, l1);
|
||
|
}
|
||
|
|
||
|
pc += 5;
|
||
|
} else {
|
||
|
throw new NotSupportedException ();
|
||
|
}
|
||
|
|
||
|
//ilgen.EmitWriteLine ("HIT:" + (char)program [pc + 1]);
|
||
|
if (!no_bump) {
|
||
|
// strpos++ / strpos--;
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4_1);
|
||
|
if (reverse)
|
||
|
ilgen.Emit (OpCodes.Sub);
|
||
|
else
|
||
|
ilgen.Emit (OpCodes.Add);
|
||
|
ilgen.Emit (OpCodes.Starg, 1);
|
||
|
}
|
||
|
Label l2 = ilgen.DefineLabel ();
|
||
|
ilgen.Emit (OpCodes.Br, l2);
|
||
|
//}
|
||
|
ilgen.MarkLabel (l1);
|
||
|
//return false;
|
||
|
ilgen.Emit (OpCodes.Br, frame.label_fail);
|
||
|
ilgen.MarkLabel (l2);
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
case RxOp.True: {
|
||
|
// return true;
|
||
|
ilgen.Emit (OpCodes.Br, frame.label_pass);
|
||
|
pc++;
|
||
|
break;
|
||
|
}
|
||
|
case RxOp.False: {
|
||
|
// return false;
|
||
|
ilgen.Emit (OpCodes.Br, frame.label_fail);
|
||
|
pc++;
|
||
|
break;
|
||
|
}
|
||
|
case RxOp.AnyPosition: {
|
||
|
pc++;
|
||
|
break;
|
||
|
}
|
||
|
case RxOp.StartOfString: {
|
||
|
//if (strpos != 0)
|
||
|
// return false;
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4_0);
|
||
|
ilgen.Emit (OpCodes.Bgt, frame.label_fail);
|
||
|
pc++;
|
||
|
break;
|
||
|
}
|
||
|
case RxOp.StartOfLine: {
|
||
|
// FIXME: windows line endings
|
||
|
//if (!(strpos == 0 || str [strpos - 1] == '\n'))
|
||
|
// return false;
|
||
|
Label l = ilgen.DefineLabel ();
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4_0);
|
||
|
ilgen.Emit (OpCodes.Beq, l);
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Ldfld, fi_str);
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4_1);
|
||
|
ilgen.Emit (OpCodes.Sub);
|
||
|
ilgen.Emit (OpCodes.Callvirt, typeof (string).GetMethod ("get_Chars"));
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, (int)'\n');
|
||
|
ilgen.Emit (OpCodes.Beq, l);
|
||
|
ilgen.Emit (OpCodes.Br, frame.label_fail);
|
||
|
ilgen.MarkLabel (l);
|
||
|
|
||
|
pc++;
|
||
|
break;
|
||
|
}
|
||
|
case RxOp.StartOfScan: {
|
||
|
//if (strpos != string_start)
|
||
|
// return false;
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Ldfld, fi_string_start);
|
||
|
ilgen.Emit (OpCodes.Bne_Un, frame.label_fail);
|
||
|
pc++;
|
||
|
break;
|
||
|
}
|
||
|
case RxOp.End: {
|
||
|
//if (!(strpos == string_end || (strpos == string_end - 1 && str [strpos] == '\n')))
|
||
|
// return false;
|
||
|
Label l = ilgen.DefineLabel ();
|
||
|
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Ldfld, fi_string_end);
|
||
|
ilgen.Emit (OpCodes.Beq, l);
|
||
|
|
||
|
Label l2 = ilgen.DefineLabel ();
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Ldfld, fi_string_end);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4_1);
|
||
|
ilgen.Emit (OpCodes.Sub);
|
||
|
ilgen.Emit (OpCodes.Bne_Un, l2);
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Ldfld, fi_str);
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Callvirt, typeof (string).GetMethod ("get_Chars"));
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, (int)'\n');
|
||
|
ilgen.Emit (OpCodes.Bne_Un, l2);
|
||
|
ilgen.Emit (OpCodes.Br, l);
|
||
|
ilgen.MarkLabel (l2);
|
||
|
|
||
|
ilgen.Emit (OpCodes.Br, frame.label_fail);
|
||
|
ilgen.MarkLabel (l);
|
||
|
|
||
|
pc++;
|
||
|
break;
|
||
|
}
|
||
|
case RxOp.EndOfString: {
|
||
|
//if (strpos != string_end)
|
||
|
// return false;
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Ldfld, fi_string_end);
|
||
|
ilgen.Emit (OpCodes.Bne_Un, frame.label_fail);
|
||
|
pc++;
|
||
|
break;
|
||
|
}
|
||
|
case RxOp.EndOfLine: {
|
||
|
//if (!(strpos == string_end || str [strpos] == '\n'))
|
||
|
// return false;
|
||
|
Label l_match = ilgen.DefineLabel ();
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Ldfld, fi_string_end);
|
||
|
ilgen.Emit (OpCodes.Beq, l_match);
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Ldfld, fi_str);
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Callvirt, typeof (string).GetMethod ("get_Chars"));
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, (int)'\n');
|
||
|
ilgen.Emit (OpCodes.Beq, l_match);
|
||
|
ilgen.Emit (OpCodes.Br, frame.label_fail);
|
||
|
ilgen.MarkLabel (l_match);
|
||
|
|
||
|
pc++;
|
||
|
break;
|
||
|
}
|
||
|
case RxOp.WordBoundary:
|
||
|
case RxOp.NoWordBoundary: {
|
||
|
bool negate = op == RxOp.NoWordBoundary;
|
||
|
|
||
|
//if (string_end == 0)
|
||
|
// return false;
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Ldfld, fi_string_end);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4_0);
|
||
|
ilgen.Emit (OpCodes.Beq, frame.label_fail);
|
||
|
|
||
|
Label l_match = ilgen.DefineLabel ();
|
||
|
|
||
|
//if (strpos == 0) {
|
||
|
Label l1 = ilgen.DefineLabel ();
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4_0);
|
||
|
ilgen.Emit (OpCodes.Bne_Un, l1);
|
||
|
//if (!IsWordChar (str [strpos])) {
|
||
|
// return false;
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Ldfld, fi_str);
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Callvirt, typeof (string).GetMethod ("get_Chars"));
|
||
|
ilgen.Emit (OpCodes.Call, GetMethod ("IsWordChar", ref mi_is_word_char));
|
||
|
ilgen.Emit (negate ? OpCodes.Brtrue : OpCodes.Brfalse, frame.label_fail);
|
||
|
ilgen.Emit (OpCodes.Br, l_match);
|
||
|
|
||
|
//} else if (strpos == string_end) {
|
||
|
ilgen.MarkLabel (l1);
|
||
|
Label l2 = ilgen.DefineLabel ();
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Ldfld, fi_string_end);
|
||
|
ilgen.Emit (OpCodes.Bne_Un, l2);
|
||
|
//if (!IsWordChar (str [strpos - 1])) {
|
||
|
// return false;
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Ldfld, fi_str);
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4_1);
|
||
|
ilgen.Emit (OpCodes.Sub);
|
||
|
ilgen.Emit (OpCodes.Callvirt, typeof (string).GetMethod ("get_Chars"));
|
||
|
ilgen.Emit (OpCodes.Call, GetMethod ("IsWordChar", ref mi_is_word_char));
|
||
|
ilgen.Emit (negate ? OpCodes.Brtrue : OpCodes.Brfalse, frame.label_fail);
|
||
|
ilgen.Emit (OpCodes.Br, l_match);
|
||
|
|
||
|
//} else {
|
||
|
ilgen.MarkLabel (l2);
|
||
|
//if (IsWordChar (str [strpos]) == IsWordChar (str [strpos - 1])) {
|
||
|
// return false;
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Ldfld, fi_str);
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Callvirt, typeof (string).GetMethod ("get_Chars"));
|
||
|
ilgen.Emit (OpCodes.Call, GetMethod ("IsWordChar", ref mi_is_word_char));
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Ldfld, fi_str);
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4_1);
|
||
|
ilgen.Emit (OpCodes.Sub);
|
||
|
ilgen.Emit (OpCodes.Callvirt, typeof (string).GetMethod ("get_Chars"));
|
||
|
ilgen.Emit (OpCodes.Call, GetMethod ("IsWordChar", ref mi_is_word_char));
|
||
|
ilgen.Emit (negate ? OpCodes.Bne_Un : OpCodes.Beq, frame.label_fail);
|
||
|
ilgen.Emit (OpCodes.Br, l_match);
|
||
|
|
||
|
ilgen.MarkLabel (l_match);
|
||
|
|
||
|
pc++;
|
||
|
break;
|
||
|
}
|
||
|
case RxOp.Bitmap:
|
||
|
case RxOp.UnicodeBitmap: {
|
||
|
OpFlags flags = (OpFlags)op_flags [pc];
|
||
|
bool negate = (flags & OpFlags.Negate) > 0;
|
||
|
bool ignore = (flags & OpFlags.IgnoreCase) > 0;
|
||
|
bool reverse = (flags & OpFlags.RightToLeft) > 0;
|
||
|
bool unicode = (op == RxOp.UnicodeBitmap);
|
||
|
|
||
|
//if (strpos < string_end) {
|
||
|
Label l1 = ilgen.DefineLabel ();
|
||
|
Label l2 = ilgen.DefineLabel ();
|
||
|
Label l_match = ilgen.DefineLabel ();
|
||
|
if (reverse) {
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4_0);
|
||
|
ilgen.Emit (OpCodes.Ble, l1);
|
||
|
} else {
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Ldfld, fi_string_end);
|
||
|
ilgen.Emit (OpCodes.Bge, l1);
|
||
|
}
|
||
|
// int c = str [strpos];
|
||
|
LocalBuilder local_c = ilgen.DeclareLocal (typeof (int));
|
||
|
if (ignore)
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_textinfo);
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Ldfld, fi_str);
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
if (reverse) {
|
||
|
ilgen.Emit (OpCodes.Ldc_I4_1);
|
||
|
ilgen.Emit (OpCodes.Sub);
|
||
|
}
|
||
|
ilgen.Emit (OpCodes.Callvirt, typeof (string).GetMethod ("get_Chars"));
|
||
|
ilgen.Emit (OpCodes.Conv_I4);
|
||
|
if (ignore)
|
||
|
ilgen.Emit (OpCodes.Callvirt, typeof (TextInfo).GetMethod ("ToLower", new Type [] { typeof (char) }));
|
||
|
// c -= program [pc + 1];
|
||
|
if (unicode) {
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, ReadShort (program, pc + 1));
|
||
|
ilgen.Emit (OpCodes.Sub);
|
||
|
ilgen.Emit (OpCodes.Stloc, local_c);
|
||
|
length = ReadShort (program, pc + 3);
|
||
|
pc += 5;
|
||
|
} else {
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, (int)program [pc + 1]);
|
||
|
ilgen.Emit (OpCodes.Sub);
|
||
|
ilgen.Emit (OpCodes.Stloc, local_c);
|
||
|
length = program [pc + 2];
|
||
|
pc += 3;
|
||
|
}
|
||
|
// if (c < 0 || c >= (length << 3))
|
||
|
// return false;
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_c);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4_0);
|
||
|
ilgen.Emit (OpCodes.Blt, negate ? l_match : frame.label_fail);
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_c);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, length << 3);
|
||
|
ilgen.Emit (OpCodes.Bge, negate ? l_match : frame.label_fail);
|
||
|
|
||
|
// Optimized version for small bitmaps
|
||
|
if (length <= 4) {
|
||
|
uint bitmap = program [pc];
|
||
|
|
||
|
if (length > 1)
|
||
|
bitmap |= ((uint)program [pc + 1] << 8);
|
||
|
if (length > 2)
|
||
|
bitmap |= ((uint)program [pc + 2] << 16);
|
||
|
if (length > 3)
|
||
|
bitmap |= ((uint)program [pc + 3] << 24);
|
||
|
|
||
|
//if ((bitmap >> c) & 1)
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, bitmap);
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_c);
|
||
|
ilgen.Emit (OpCodes.Shr_Un);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4_1);
|
||
|
ilgen.Emit (OpCodes.And);
|
||
|
ilgen.Emit (negate ? OpCodes.Brtrue : OpCodes.Brfalse, l1);
|
||
|
} else {
|
||
|
// if ((program [pc + (c >> 3)] & (1 << (c & 0x7))) != 0) {
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Ldfld, fi_program);
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_c);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4_3);
|
||
|
ilgen.Emit (OpCodes.Shr);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, pc);
|
||
|
ilgen.Emit (OpCodes.Add);
|
||
|
ilgen.Emit (OpCodes.Ldelem_I1);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4_1);
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_c);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, 7);
|
||
|
ilgen.Emit (OpCodes.And);
|
||
|
ilgen.Emit (OpCodes.Shl);
|
||
|
ilgen.Emit (OpCodes.And);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4_0);
|
||
|
ilgen.Emit (negate ? OpCodes.Bne_Un : OpCodes.Beq, l1);
|
||
|
}
|
||
|
ilgen.MarkLabel (l_match);
|
||
|
if (!no_bump) {
|
||
|
// strpos++ / strpos--;
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4_1);
|
||
|
if (reverse)
|
||
|
ilgen.Emit (OpCodes.Sub);
|
||
|
else
|
||
|
ilgen.Emit (OpCodes.Add);
|
||
|
ilgen.Emit (OpCodes.Starg, 1);
|
||
|
}
|
||
|
// continue;
|
||
|
ilgen.Emit (OpCodes.Br, l2);
|
||
|
// }
|
||
|
//}
|
||
|
//return false;
|
||
|
ilgen.MarkLabel (l1);
|
||
|
ilgen.Emit (OpCodes.Br, frame.label_fail);
|
||
|
|
||
|
ilgen.MarkLabel (l2);
|
||
|
|
||
|
pc += length;
|
||
|
break;
|
||
|
}
|
||
|
case RxOp.String:
|
||
|
case RxOp.UnicodeString: {
|
||
|
OpFlags flags = (OpFlags)op_flags [pc];
|
||
|
bool ignore = (flags & OpFlags.IgnoreCase) > 0;
|
||
|
bool reverse = (flags & OpFlags.RightToLeft) > 0;
|
||
|
bool unicode = (op == RxOp.UnicodeString);
|
||
|
|
||
|
if (unicode) {
|
||
|
start = pc + 3;
|
||
|
length = ReadShort (program, pc + 1);
|
||
|
} else {
|
||
|
start = pc + 2;
|
||
|
length = program [pc + 1];
|
||
|
}
|
||
|
//if (strpos + length > string_end)
|
||
|
// return false;
|
||
|
if (reverse) {
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, length);
|
||
|
ilgen.Emit (OpCodes.Blt, frame.label_fail);
|
||
|
} else {
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, length);
|
||
|
ilgen.Emit (OpCodes.Add);
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Ldfld, fi_string_end);
|
||
|
ilgen.Emit (OpCodes.Bgt, frame.label_fail);
|
||
|
}
|
||
|
|
||
|
/* Avoid unsafe code in Moonlight build */
|
||
|
#if false && !NET_2_1
|
||
|
// FIXME:
|
||
|
if (reverse || unicode)
|
||
|
throw new NotImplementedException ();
|
||
|
int i;
|
||
|
LocalBuilder local_strptr = ilgen.DeclareLocal (typeof (char).MakePointerType ());
|
||
|
// char *strptr = &str.start_char + strpos
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Ldfld, fi_str);
|
||
|
ilgen.Emit (OpCodes.Ldflda, typeof (String).GetField ("start_char", BindingFlags.Instance|BindingFlags.NonPublic));
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4_1);
|
||
|
ilgen.Emit (OpCodes.Shl);
|
||
|
ilgen.Emit (OpCodes.Add);
|
||
|
ilgen.Emit (OpCodes.Stloc, local_strptr);
|
||
|
|
||
|
end = start + length;
|
||
|
for (i = 0; i < length; ++i) {
|
||
|
// if (*(strptr + i) != program [start + i])
|
||
|
// return false;
|
||
|
if (ignore)
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_textinfo);
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_strptr);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, i * 2);
|
||
|
ilgen.Emit (OpCodes.Add);
|
||
|
ilgen.Emit (OpCodes.Ldind_I2);
|
||
|
if (ignore)
|
||
|
ilgen.Emit (OpCodes.Callvirt, typeof (TextInfo).GetMethod ("ToLower", new Type [] { typeof (char) }));
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, (int)program [start + i]);
|
||
|
ilgen.Emit (OpCodes.Bne_Un, frame.label_fail);
|
||
|
}
|
||
|
|
||
|
// strpos += length
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, length);
|
||
|
ilgen.Emit (OpCodes.Add);
|
||
|
ilgen.Emit (OpCodes.Starg, 1);
|
||
|
|
||
|
#else
|
||
|
// Allocate a local for 'str' to save an indirection
|
||
|
LocalBuilder local_str = ilgen.DeclareLocal (typeof (string));
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Ldfld, fi_str);
|
||
|
ilgen.Emit (OpCodes.Stloc, local_str);
|
||
|
|
||
|
if (reverse) {
|
||
|
// strpos -= length;
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, length);
|
||
|
ilgen.Emit (OpCodes.Sub);
|
||
|
ilgen.Emit (OpCodes.Starg, 1);
|
||
|
}
|
||
|
|
||
|
// FIXME: Emit a loop for long strings
|
||
|
end = start + (unicode ? length * 2 : length);
|
||
|
while (start < end) {
|
||
|
//if (str [strpos] != program [start])
|
||
|
// return false;
|
||
|
if (ignore)
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_textinfo);
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_str);
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Callvirt, typeof (string).GetMethod ("get_Chars"));
|
||
|
if (ignore)
|
||
|
ilgen.Emit (OpCodes.Callvirt, typeof (TextInfo).GetMethod ("ToLower", new Type [] { typeof (char) }));
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, unicode ? ReadShort (program, start) : (int)program [start]);
|
||
|
ilgen.Emit (OpCodes.Bne_Un, frame.label_fail);
|
||
|
//strpos++;
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4_1);
|
||
|
ilgen.Emit (OpCodes.Add);
|
||
|
ilgen.Emit (OpCodes.Starg, 1);
|
||
|
|
||
|
if (unicode)
|
||
|
start += 2;
|
||
|
else
|
||
|
start ++;
|
||
|
}
|
||
|
|
||
|
if (reverse) {
|
||
|
// strpos -= length;
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, length);
|
||
|
ilgen.Emit (OpCodes.Sub);
|
||
|
ilgen.Emit (OpCodes.Starg, 1);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
pc = end;
|
||
|
break;
|
||
|
}
|
||
|
case RxOp.OpenGroup: {
|
||
|
//Open (program [pc + 1] | (program [pc + 2] << 8), strpos);
|
||
|
int group_id = ReadShort (program, pc + 1);
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, group_id);
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Call, GetMethod ("Open", ref mi_open));
|
||
|
|
||
|
pc += 3;
|
||
|
break;
|
||
|
}
|
||
|
case RxOp.CloseGroup: {
|
||
|
//Close (program [pc + 1] | (program [pc + 2] << 8), strpos);
|
||
|
int group_id = ReadShort (program, pc + 1);
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, group_id);
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Call, GetMethod ("Close", ref mi_close));
|
||
|
|
||
|
pc += 3;
|
||
|
break;
|
||
|
}
|
||
|
case RxOp.Jump: {
|
||
|
int target_pc = pc + ReadShort (program, pc + 1);
|
||
|
if (target_pc > end_pc)
|
||
|
/*
|
||
|
* This breaks the our code generation logic, see
|
||
|
* https://bugzilla.novell.com/show_bug.cgi?id=466151
|
||
|
* for an example.
|
||
|
*/
|
||
|
return null;
|
||
|
if (trace_compile)
|
||
|
Console.WriteLine ("\tjump target: {0}", target_pc);
|
||
|
if (labels == null)
|
||
|
labels = new Dictionary <int, Label> ();
|
||
|
Label l = CreateLabelForPC (ilgen, target_pc);
|
||
|
ilgen.Emit (OpCodes.Br, l);
|
||
|
pc += 3;
|
||
|
break;
|
||
|
}
|
||
|
case RxOp.Test: {
|
||
|
int target1 = pc + ReadShort (program, pc + 1);
|
||
|
int target2 = pc + ReadShort (program, pc + 3);
|
||
|
|
||
|
if (trace_compile)
|
||
|
Console.WriteLine ("\temitting <test_expr>");
|
||
|
|
||
|
// old_stros = strpos;
|
||
|
LocalBuilder local_old_strpos = ilgen.DeclareLocal (typeof (int));
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Stloc, local_old_strpos);
|
||
|
|
||
|
Frame new_frame = new Frame (ilgen);
|
||
|
m = EmitEvalMethodBody (m, ilgen, new_frame, program, pc + 5, target1 < target2 ? target1 : target2, false, false, out pc);
|
||
|
if (m == null)
|
||
|
return null;
|
||
|
|
||
|
if (trace_compile) {
|
||
|
Console.WriteLine ("\temitted <test_expr>");
|
||
|
Console.WriteLine ("\ttarget1 = {0}", target1);
|
||
|
Console.WriteLine ("\ttarget2 = {0}", target2);
|
||
|
}
|
||
|
|
||
|
Label l1 = CreateLabelForPC (ilgen, target1);
|
||
|
Label l2 = CreateLabelForPC (ilgen, target2);
|
||
|
|
||
|
// Pass
|
||
|
ilgen.MarkLabel (new_frame.label_pass);
|
||
|
// strpos = old_strpos;
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_old_strpos);
|
||
|
ilgen.Emit (OpCodes.Starg, 1);
|
||
|
ilgen.Emit (OpCodes.Br, l1);
|
||
|
|
||
|
// Fail
|
||
|
ilgen.MarkLabel (new_frame.label_fail);
|
||
|
// strpos = old_strpos;
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_old_strpos);
|
||
|
ilgen.Emit (OpCodes.Starg, 1);
|
||
|
ilgen.Emit (OpCodes.Br, l2);
|
||
|
|
||
|
// Continue at pc, which should equal to target1
|
||
|
break;
|
||
|
}
|
||
|
case RxOp.SubExpression: {
|
||
|
int target = pc + ReadShort (program, pc + 1);
|
||
|
|
||
|
if (trace_compile)
|
||
|
Console.WriteLine ("\temitting <sub_expr>");
|
||
|
|
||
|
// old_stros = strpos;
|
||
|
LocalBuilder local_old_strpos = ilgen.DeclareLocal (typeof (int));
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Stloc, local_old_strpos);
|
||
|
|
||
|
Frame new_frame = new Frame (ilgen);
|
||
|
m = EmitEvalMethodBody (m, ilgen, new_frame, program, pc + 3, target, false, false, out pc);
|
||
|
if (m == null)
|
||
|
return null;
|
||
|
|
||
|
if (trace_compile) {
|
||
|
Console.WriteLine ("\temitted <sub_expr>");
|
||
|
Console.WriteLine ("\ttarget = {0}", target);
|
||
|
}
|
||
|
|
||
|
Label l1 = CreateLabelForPC (ilgen, target);
|
||
|
|
||
|
// Pass
|
||
|
ilgen.MarkLabel (new_frame.label_pass);
|
||
|
ilgen.Emit (OpCodes.Br, l1);
|
||
|
|
||
|
// Fail
|
||
|
ilgen.MarkLabel (new_frame.label_fail);
|
||
|
// strpos = old_strpos;
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_old_strpos);
|
||
|
ilgen.Emit (OpCodes.Starg, 1);
|
||
|
ilgen.Emit (OpCodes.Br, frame.label_fail);
|
||
|
|
||
|
// Continue at pc, which should equal to target
|
||
|
break;
|
||
|
}
|
||
|
case RxOp.TestCharGroup: {
|
||
|
int char_group_end = pc + ReadShort (program, pc + 1);
|
||
|
pc += 3;
|
||
|
|
||
|
Label label_match = ilgen.DefineLabel ();
|
||
|
|
||
|
/* Determine the negate/reverse flags by examining the first op */
|
||
|
OpFlags flags = (OpFlags)op_flags [pc];
|
||
|
|
||
|
/* Determine whenever this is a negated character class */
|
||
|
/* If it is, then the conditions are ANDed together, not ORed */
|
||
|
bool revert = (flags & OpFlags.Negate) > 0;
|
||
|
bool reverse = (flags & OpFlags.RightToLeft) > 0;
|
||
|
|
||
|
/*
|
||
|
* Generate code for all the matching ops in the group
|
||
|
*/
|
||
|
while (pc < char_group_end) {
|
||
|
Frame new_frame = new Frame (ilgen);
|
||
|
m = EmitEvalMethodBody (m, ilgen, new_frame, program, pc, Int32.MaxValue, true, true, out pc);
|
||
|
if (m == null)
|
||
|
return null;
|
||
|
|
||
|
if (!revert) {
|
||
|
// Pass
|
||
|
ilgen.MarkLabel (new_frame.label_pass);
|
||
|
ilgen.Emit (OpCodes.Br, label_match);
|
||
|
|
||
|
// Fail
|
||
|
// Just fall through to the next test
|
||
|
ilgen.MarkLabel (new_frame.label_fail);
|
||
|
} else {
|
||
|
// Pass
|
||
|
// Just fall through to the next test
|
||
|
ilgen.MarkLabel (new_frame.label_pass);
|
||
|
Label l2 = ilgen.DefineLabel ();
|
||
|
ilgen.Emit (OpCodes.Br, l2);
|
||
|
|
||
|
// Fail
|
||
|
// Fail completely
|
||
|
ilgen.MarkLabel (new_frame.label_fail);
|
||
|
ilgen.Emit (OpCodes.Br, frame.label_fail);
|
||
|
|
||
|
ilgen.MarkLabel (l2);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (revert) {
|
||
|
/* Success */
|
||
|
ilgen.Emit (OpCodes.Br, label_match);
|
||
|
} else {
|
||
|
// If we reached here, all the matching ops have failed
|
||
|
ilgen.Emit (OpCodes.Br, frame.label_fail);
|
||
|
}
|
||
|
|
||
|
ilgen.MarkLabel (label_match);
|
||
|
|
||
|
// strpos++ / strpos--;
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4_1);
|
||
|
if (reverse)
|
||
|
ilgen.Emit (OpCodes.Sub);
|
||
|
else
|
||
|
ilgen.Emit (OpCodes.Add);
|
||
|
ilgen.Emit (OpCodes.Starg, 1);
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
case RxOp.FastRepeat:
|
||
|
case RxOp.FastRepeatLazy: {
|
||
|
/*
|
||
|
* A FastRepeat is a simplified version of Repeat which does
|
||
|
* not contain another repeat inside, so backtracking is
|
||
|
* easier.
|
||
|
* FIXME: Implement faster backtracking versions for
|
||
|
* simple inner exceptions like chars/strings.
|
||
|
*/
|
||
|
bool lazy = program [pc] == (byte)RxOp.FastRepeatLazy;
|
||
|
int tail = pc + ReadShort (program, pc + 1);
|
||
|
start = ReadInt (program, pc + 3);
|
||
|
end = ReadInt (program, pc + 7);
|
||
|
//Console.WriteLine ("min: {0}, max: {1} tail: {2}", start, end, tail);
|
||
|
|
||
|
// deep = null;
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Ldnull);
|
||
|
ilgen.Emit (OpCodes.Stfld, fi_deep);
|
||
|
|
||
|
LocalBuilder local_length = ilgen.DeclareLocal (typeof (int));
|
||
|
|
||
|
ilgen.Emit (OpCodes.Ldc_I4_0);
|
||
|
ilgen.Emit (OpCodes.Stloc, local_length);
|
||
|
|
||
|
LocalBuilder local_old_strpos = ilgen.DeclareLocal (typeof (int));
|
||
|
|
||
|
// First match at least 'start' items
|
||
|
if (start > 0) {
|
||
|
//for (length = 0; length < start; ++length) {
|
||
|
Label l_loop_footer = ilgen.DefineLabel ();
|
||
|
ilgen.Emit (OpCodes.Br, l_loop_footer);
|
||
|
Label l_loop_body = ilgen.DefineLabel ();
|
||
|
ilgen.MarkLabel (l_loop_body);
|
||
|
|
||
|
// int old_strpos = strpos;
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Stloc, local_old_strpos);
|
||
|
|
||
|
// if (!EvalByteCode (pc + 11, strpos, ref res))
|
||
|
Frame new_frame = new Frame (ilgen);
|
||
|
m = EmitEvalMethodBody (m, ilgen, new_frame, program, pc + 11, tail, false, false, out out_pc);
|
||
|
if (m == null)
|
||
|
return null;
|
||
|
|
||
|
// Fail
|
||
|
// return false;
|
||
|
ilgen.MarkLabel (new_frame.label_fail);
|
||
|
ilgen.Emit (OpCodes.Br, frame.label_fail);
|
||
|
|
||
|
// Pass
|
||
|
ilgen.MarkLabel (new_frame.label_pass);
|
||
|
// length++
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_length);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4_1);
|
||
|
ilgen.Emit (OpCodes.Add);
|
||
|
ilgen.Emit (OpCodes.Stloc, local_length);
|
||
|
// Loop footer
|
||
|
ilgen.MarkLabel (l_loop_footer);
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_length);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, start);
|
||
|
ilgen.Emit (OpCodes.Blt, l_loop_body);
|
||
|
}
|
||
|
|
||
|
if (lazy) {
|
||
|
Label l_loop_footer = ilgen.DefineLabel ();
|
||
|
//while (true) {
|
||
|
ilgen.Emit (OpCodes.Br, l_loop_footer);
|
||
|
Label l_loop_body = ilgen.DefineLabel ();
|
||
|
ilgen.MarkLabel (l_loop_body);
|
||
|
// Match the tail
|
||
|
// int cp = Checkpoint ();
|
||
|
LocalBuilder local_cp = ilgen.DeclareLocal (typeof (int));
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Call, GetMethod ("Checkpoint", ref mi_checkpoint));
|
||
|
ilgen.Emit (OpCodes.Stloc, local_cp);
|
||
|
|
||
|
// int old_strpos = strpos;
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Stloc, local_old_strpos);
|
||
|
|
||
|
// if (EvalByteCode (tail, strpos, ref res)) {
|
||
|
Frame new_frame = new Frame (ilgen);
|
||
|
m = EmitEvalMethodBody (m, ilgen, new_frame, program, tail, end_pc, false, false, out out_pc);
|
||
|
if (m == null)
|
||
|
return null;
|
||
|
|
||
|
// Success:
|
||
|
ilgen.MarkLabel (new_frame.label_pass);
|
||
|
// return true;
|
||
|
ilgen.Emit (OpCodes.Br, frame.label_pass);
|
||
|
|
||
|
// Fail:
|
||
|
ilgen.MarkLabel (new_frame.label_fail);
|
||
|
// Backtrack (cp);
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_cp);
|
||
|
ilgen.Emit (OpCodes.Call, GetMethod ("Backtrack", ref mi_backtrack));
|
||
|
// strpos = old_strpos;
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_old_strpos);
|
||
|
ilgen.Emit (OpCodes.Starg, 1);
|
||
|
|
||
|
//if (length >= end)
|
||
|
// return false;
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_length);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, end);
|
||
|
ilgen.Emit (OpCodes.Bge, frame.label_fail);
|
||
|
|
||
|
// Match an item
|
||
|
//if (!EvalByteCode (pc + 11, strpos, ref res))
|
||
|
new_frame = new Frame (ilgen);
|
||
|
m = EmitEvalMethodBody (m, ilgen, new_frame, program, pc + 11, tail, false, false, out out_pc);
|
||
|
if (m == null)
|
||
|
return null;
|
||
|
|
||
|
// Success:
|
||
|
ilgen.MarkLabel (new_frame.label_pass);
|
||
|
// length ++;
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_length);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4_1);
|
||
|
ilgen.Emit (OpCodes.Add);
|
||
|
ilgen.Emit (OpCodes.Stloc, local_length);
|
||
|
ilgen.Emit (OpCodes.Br, l_loop_body);
|
||
|
|
||
|
// Fail:
|
||
|
ilgen.MarkLabel (new_frame.label_fail);
|
||
|
// return false;
|
||
|
ilgen.Emit (OpCodes.Br, frame.label_fail);
|
||
|
|
||
|
// Loop footer
|
||
|
ilgen.MarkLabel (l_loop_footer);
|
||
|
ilgen.Emit (OpCodes.Br, l_loop_body);
|
||
|
} else {
|
||
|
// Then match as many items as possible, recording
|
||
|
// backtracking information
|
||
|
|
||
|
//int old_stack_size = stack.Count;
|
||
|
LocalBuilder local_old_stack_size = ilgen.DeclareLocal (typeof (int));
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Ldflda, fi_stack);
|
||
|
ilgen.Emit (OpCodes.Call, GetMethod (typeof (RxInterpreter.IntStack), "get_Count", ref mi_stack_get_count));
|
||
|
ilgen.Emit (OpCodes.Stloc, local_old_stack_size);
|
||
|
//while (length < end) {
|
||
|
Label l_loop_footer = ilgen.DefineLabel ();
|
||
|
ilgen.Emit (OpCodes.Br, l_loop_footer);
|
||
|
Label l_loop_body = ilgen.DefineLabel ();
|
||
|
ilgen.MarkLabel (l_loop_body);
|
||
|
// int cp = Checkpoint ();
|
||
|
LocalBuilder local_cp = ilgen.DeclareLocal (typeof (int));
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Call, GetMethod ("Checkpoint", ref mi_checkpoint));
|
||
|
ilgen.Emit (OpCodes.Stloc, local_cp);
|
||
|
|
||
|
// int old_strpos = strpos;
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Stloc, local_old_strpos);
|
||
|
|
||
|
// if (!EvalByteCode (pc + 11, strpos, ref res)) {
|
||
|
Frame new_frame = new Frame (ilgen);
|
||
|
m = EmitEvalMethodBody (m, ilgen, new_frame, program, pc + 11, tail, false, false, out out_pc);
|
||
|
if (m == null)
|
||
|
return null;
|
||
|
|
||
|
// Fail:
|
||
|
ilgen.MarkLabel (new_frame.label_fail);
|
||
|
// strpos = old_strpos
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_old_strpos);
|
||
|
ilgen.Emit (OpCodes.Starg, 1);
|
||
|
// Backtrack (cp);
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_cp);
|
||
|
ilgen.Emit (OpCodes.Call, GetMethod ("Backtrack", ref mi_backtrack));
|
||
|
|
||
|
// break;
|
||
|
Label l_after_loop = ilgen.DefineLabel ();
|
||
|
ilgen.Emit (OpCodes.Br, l_after_loop);
|
||
|
|
||
|
// Success:
|
||
|
ilgen.MarkLabel (new_frame.label_pass);
|
||
|
|
||
|
//stack.Push (cp);
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Ldflda, fi_stack);
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_cp);
|
||
|
ilgen.Emit (OpCodes.Call, GetMethod (typeof (RxInterpreter.IntStack), "Push", ref mi_stack_push));
|
||
|
//stack.Push (strpos);
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Ldflda, fi_stack);
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_old_strpos);
|
||
|
ilgen.Emit (OpCodes.Call, GetMethod (typeof (RxInterpreter.IntStack), "Push", ref mi_stack_push));
|
||
|
// length++
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_length);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4_1);
|
||
|
ilgen.Emit (OpCodes.Add);
|
||
|
ilgen.Emit (OpCodes.Stloc, local_length);
|
||
|
// Loop footer
|
||
|
ilgen.MarkLabel (l_loop_footer);
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_length);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, end);
|
||
|
ilgen.Emit (OpCodes.Blt, l_loop_body);
|
||
|
|
||
|
ilgen.MarkLabel (l_after_loop);
|
||
|
|
||
|
// Then, match the tail, backtracking as necessary.
|
||
|
|
||
|
//while (true) {
|
||
|
l_loop_footer = ilgen.DefineLabel ();
|
||
|
ilgen.Emit (OpCodes.Br, l_loop_footer);
|
||
|
l_loop_body = ilgen.DefineLabel ();
|
||
|
ilgen.MarkLabel (l_loop_body);
|
||
|
|
||
|
if (RxInterpreter.trace_rx) {
|
||
|
ilgen.Emit (OpCodes.Ldstr, "matching tail at: {0}");
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Box, typeof (int));
|
||
|
ilgen.Emit (OpCodes.Call, typeof (Console).GetMethod ("WriteLine", new Type [] { typeof (string), typeof (object) }));
|
||
|
}
|
||
|
|
||
|
// if (EvalByteCode (tail, strpos, ref res)) {
|
||
|
new_frame = new Frame (ilgen);
|
||
|
m = EmitEvalMethodBody (m, ilgen, new_frame, program, tail, end_pc, false, false, out out_pc);
|
||
|
if (m == null)
|
||
|
return null;
|
||
|
|
||
|
// Success:
|
||
|
ilgen.MarkLabel (new_frame.label_pass);
|
||
|
|
||
|
if (RxInterpreter.trace_rx) {
|
||
|
ilgen.Emit (OpCodes.Ldstr, "tail matched at: {0}");
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Box, typeof (int));
|
||
|
ilgen.Emit (OpCodes.Call, typeof (Console).GetMethod ("WriteLine", new Type [] { typeof (string), typeof (object) }));
|
||
|
}
|
||
|
|
||
|
// stack.Count = old_stack_size;
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Ldflda, fi_stack);
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_old_stack_size);
|
||
|
ilgen.Emit (OpCodes.Call, GetMethod (typeof (RxInterpreter.IntStack), "set_Count", ref mi_stack_set_count));
|
||
|
// return true;
|
||
|
ilgen.Emit (OpCodes.Br, frame.label_pass);
|
||
|
|
||
|
// Fail:
|
||
|
ilgen.MarkLabel (new_frame.label_fail);
|
||
|
|
||
|
if (RxInterpreter.trace_rx) {
|
||
|
ilgen.Emit (OpCodes.Ldstr, "tail failed to match at: {0}");
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Box, typeof (int));
|
||
|
ilgen.Emit (OpCodes.Call, typeof (Console).GetMethod ("WriteLine", new Type [] { typeof (string), typeof (object) }));
|
||
|
}
|
||
|
|
||
|
// if (stack.Count == old_stack_size)
|
||
|
// return false;
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Ldflda, fi_stack);
|
||
|
ilgen.Emit (OpCodes.Call, GetMethod (typeof (RxInterpreter.IntStack), "get_Count", ref mi_stack_get_count));
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_old_stack_size);
|
||
|
ilgen.Emit (OpCodes.Beq, frame.label_fail);
|
||
|
|
||
|
// Backtrack
|
||
|
//strpos = stack.Pop ();
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Ldflda, fi_stack);
|
||
|
ilgen.Emit (OpCodes.Call, GetMethod (typeof (RxInterpreter.IntStack), "Pop", ref mi_stack_pop));
|
||
|
ilgen.Emit (OpCodes.Starg, 1);
|
||
|
//Backtrack (stack.Pop ());
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Ldflda, fi_stack);
|
||
|
ilgen.Emit (OpCodes.Call, GetMethod (typeof (RxInterpreter.IntStack), "Pop", ref mi_stack_pop));
|
||
|
ilgen.Emit (OpCodes.Call, GetMethod ("Backtrack", ref mi_backtrack));
|
||
|
|
||
|
if (RxInterpreter.trace_rx) {
|
||
|
//Console.WriteLine ("backtracking to: {0}", strpos);
|
||
|
ilgen.Emit (OpCodes.Ldstr, "backtracking to: {0}");
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Box, typeof (int));
|
||
|
ilgen.Emit (OpCodes.Call, typeof (Console).GetMethod ("WriteLine", new Type [] { typeof (string), typeof (object) }));
|
||
|
}
|
||
|
|
||
|
// Loop footer
|
||
|
ilgen.MarkLabel (l_loop_footer);
|
||
|
ilgen.Emit (OpCodes.Br, l_loop_body);
|
||
|
}
|
||
|
|
||
|
// We already processed the tail
|
||
|
pc = out_pc;
|
||
|
goto End;
|
||
|
}
|
||
|
|
||
|
case RxOp.CategoryAny:
|
||
|
case RxOp.CategoryAnySingleline:
|
||
|
case RxOp.CategoryWord:
|
||
|
case RxOp.CategoryDigit:
|
||
|
case RxOp.CategoryWhiteSpace:
|
||
|
case RxOp.CategoryEcmaWord:
|
||
|
case RxOp.CategoryEcmaWhiteSpace:
|
||
|
case RxOp.CategoryUnicodeSpecials:
|
||
|
case RxOp.CategoryUnicode: {
|
||
|
OpFlags flags = (OpFlags)op_flags [pc];
|
||
|
bool negate = (flags & OpFlags.Negate) > 0;
|
||
|
bool reverse = (flags & OpFlags.RightToLeft) > 0;
|
||
|
|
||
|
//if (strpos < string_end) {
|
||
|
Label l_nomatch = ilgen.DefineLabel ();
|
||
|
if (reverse) {
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4_0);
|
||
|
ilgen.Emit (OpCodes.Ble, l_nomatch);
|
||
|
} else {
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Ldfld, fi_string_end);
|
||
|
ilgen.Emit (OpCodes.Bge, l_nomatch);
|
||
|
}
|
||
|
|
||
|
// int c = str [strpos];
|
||
|
LocalBuilder local_c = ilgen.DeclareLocal (typeof (char));
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Ldfld, fi_str);
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
if (reverse) {
|
||
|
ilgen.Emit (OpCodes.Ldc_I4_1);
|
||
|
ilgen.Emit (OpCodes.Sub);
|
||
|
}
|
||
|
ilgen.Emit (OpCodes.Callvirt, typeof (string).GetMethod ("get_Chars"));
|
||
|
ilgen.Emit (OpCodes.Stloc, local_c);
|
||
|
|
||
|
Label l_match = ilgen.DefineLabel ();
|
||
|
|
||
|
Label l_true, l_false;
|
||
|
|
||
|
l_true = negate ? l_nomatch : l_match;
|
||
|
l_false = negate ? l_match : l_nomatch;
|
||
|
|
||
|
switch (op) {
|
||
|
case RxOp.CategoryAny:
|
||
|
// if (str [strpos] != '\n') {
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_c);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, (int)'\n');
|
||
|
ilgen.Emit (OpCodes.Bne_Un, l_true);
|
||
|
break;
|
||
|
case RxOp.CategoryAnySingleline:
|
||
|
ilgen.Emit (OpCodes.Br, l_true);
|
||
|
break;
|
||
|
case RxOp.CategoryWord:
|
||
|
// if (Char.IsLetterOrDigit (c) || Char.GetUnicodeCategory (c) == UnicodeCategory.ConnectorPunctuation) {
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_c);
|
||
|
ilgen.Emit (OpCodes.Call, typeof (Char).GetMethod ("IsLetterOrDigit", new Type [] { typeof (char) }));
|
||
|
ilgen.Emit (OpCodes.Brtrue, l_true);
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_c);
|
||
|
ilgen.Emit (OpCodes.Call, typeof (Char).GetMethod ("GetUnicodeCategory", new Type [] { typeof (char) }));
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, (int)UnicodeCategory.ConnectorPunctuation);
|
||
|
ilgen.Emit (OpCodes.Beq, l_true);
|
||
|
break;
|
||
|
case RxOp.CategoryDigit:
|
||
|
// if (Char.IsDigit (c)) {
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_c);
|
||
|
ilgen.Emit (OpCodes.Call, typeof (Char).GetMethod ("IsDigit", new Type [] { typeof (char) }));
|
||
|
ilgen.Emit (OpCodes.Brtrue, l_true);
|
||
|
break;
|
||
|
case RxOp.CategoryWhiteSpace:
|
||
|
// if (Char.IsWhiteSpace (c)) {
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_c);
|
||
|
ilgen.Emit (OpCodes.Call, typeof (Char).GetMethod ("IsWhiteSpace", new Type [] { typeof (char) }));
|
||
|
ilgen.Emit (OpCodes.Brtrue, l_true);
|
||
|
break;
|
||
|
case RxOp.CategoryEcmaWord:
|
||
|
// if ('a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || '0' <= c && c <= '9' || c == '_') {
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_c);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, (int)'a' - 1);
|
||
|
ilgen.Emit (OpCodes.Cgt);
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_c);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, (int)'z' + 1);
|
||
|
ilgen.Emit (OpCodes.Clt);
|
||
|
ilgen.Emit (OpCodes.And);
|
||
|
ilgen.Emit (OpCodes.Brtrue, l_true);
|
||
|
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_c);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, (int)'A' - 1);
|
||
|
ilgen.Emit (OpCodes.Cgt);
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_c);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, (int)'Z' + 1);
|
||
|
ilgen.Emit (OpCodes.Clt);
|
||
|
ilgen.Emit (OpCodes.And);
|
||
|
ilgen.Emit (OpCodes.Brtrue, l_true);
|
||
|
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_c);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, (int)'0' - 1);
|
||
|
ilgen.Emit (OpCodes.Cgt);
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_c);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, (int)'9' + 1);
|
||
|
ilgen.Emit (OpCodes.Clt);
|
||
|
ilgen.Emit (OpCodes.And);
|
||
|
ilgen.Emit (OpCodes.Brtrue, l_true);
|
||
|
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_c);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, (int)'_');
|
||
|
ilgen.Emit (OpCodes.Beq, l_true);
|
||
|
break;
|
||
|
case RxOp.CategoryEcmaWhiteSpace:
|
||
|
// if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v') {
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_c);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, (int)' ');
|
||
|
ilgen.Emit (OpCodes.Beq, l_true);
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_c);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, (int)'\t');
|
||
|
ilgen.Emit (OpCodes.Beq, l_true);
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_c);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, (int)'\n');
|
||
|
ilgen.Emit (OpCodes.Beq, l_true);
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_c);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, (int)'\r');
|
||
|
ilgen.Emit (OpCodes.Beq, l_true);
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_c);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, (int)'\f');
|
||
|
ilgen.Emit (OpCodes.Beq, l_true);
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_c);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, (int)'\v');
|
||
|
ilgen.Emit (OpCodes.Beq, l_true);
|
||
|
break;
|
||
|
case RxOp.CategoryUnicodeSpecials:
|
||
|
// if ('\uFEFF' <= c && c <= '\uFEFF' || '\uFFF0' <= c && c <= '\uFFFD') {
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_c);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, (int)'\uFEFF' - 1);
|
||
|
ilgen.Emit (OpCodes.Cgt);
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_c);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, (int)'\uFEFF' + 1);
|
||
|
ilgen.Emit (OpCodes.Clt);
|
||
|
ilgen.Emit (OpCodes.And);
|
||
|
ilgen.Emit (OpCodes.Brtrue, l_true);
|
||
|
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_c);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, (int)'\uFFF0' - 1);
|
||
|
ilgen.Emit (OpCodes.Cgt);
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_c);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, (int)'\uFFFD' + 1);
|
||
|
ilgen.Emit (OpCodes.Clt);
|
||
|
ilgen.Emit (OpCodes.And);
|
||
|
ilgen.Emit (OpCodes.Brtrue, l_true);
|
||
|
break;
|
||
|
case RxOp.CategoryUnicode:
|
||
|
// if (Char.GetUnicodeCategory (c) == (UnicodeCategory)program [pc + 1]) {
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_c);
|
||
|
ilgen.Emit (OpCodes.Call, typeof (Char).GetMethod ("GetUnicodeCategory", new Type [] { typeof (char) }));
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, (int)program [pc + 1]);
|
||
|
ilgen.Emit (OpCodes.Beq, l_true);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
ilgen.Emit (OpCodes.Br, l_false);
|
||
|
|
||
|
ilgen.MarkLabel (l_match);
|
||
|
|
||
|
// strpos++;
|
||
|
if (!no_bump) {
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4_1);
|
||
|
if (reverse)
|
||
|
ilgen.Emit (OpCodes.Sub);
|
||
|
else
|
||
|
ilgen.Emit (OpCodes.Add);
|
||
|
ilgen.Emit (OpCodes.Starg, 1);
|
||
|
}
|
||
|
// }
|
||
|
Label l2 = ilgen.DefineLabel ();
|
||
|
ilgen.Emit (OpCodes.Br, l2);
|
||
|
//}
|
||
|
ilgen.MarkLabel (l_nomatch);
|
||
|
//return false;
|
||
|
ilgen.Emit (OpCodes.Br, frame.label_fail);
|
||
|
|
||
|
ilgen.MarkLabel (l2);
|
||
|
|
||
|
if (op == RxOp.CategoryUnicode)
|
||
|
pc += 2;
|
||
|
else
|
||
|
pc++;
|
||
|
break;
|
||
|
}
|
||
|
case RxOp.Reference: {
|
||
|
OpFlags flags = (OpFlags)op_flags [pc];
|
||
|
bool ignore = (flags & OpFlags.IgnoreCase) > 0;
|
||
|
bool reverse = (flags & OpFlags.RightToLeft) > 0;
|
||
|
|
||
|
//length = GetLastDefined (program [pc + 1] | ((int)program [pc + 2] << 8));
|
||
|
LocalBuilder loc_length = ilgen.DeclareLocal (typeof (int));
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4, ReadShort (program, pc + 1));
|
||
|
ilgen.Emit (OpCodes.Call, GetMethod (typeof (RxInterpreter), "GetLastDefined", ref mi_get_last_defined));
|
||
|
ilgen.Emit (OpCodes.Stloc, loc_length);
|
||
|
//if (length < 0)
|
||
|
// return false;
|
||
|
ilgen.Emit (OpCodes.Ldloc, loc_length);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4_0);
|
||
|
ilgen.Emit (OpCodes.Blt, frame.label_fail);
|
||
|
//start = marks [length].Index;
|
||
|
LocalBuilder loc_start = ilgen.DeclareLocal (typeof (int));
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Ldfld, fi_marks);
|
||
|
ilgen.Emit (OpCodes.Ldloc, loc_length);
|
||
|
ilgen.Emit (OpCodes.Ldelema, typeof (Mark));
|
||
|
ilgen.Emit (OpCodes.Call, GetMethod (typeof (Mark), "get_Index", ref mi_mark_get_index));
|
||
|
ilgen.Emit (OpCodes.Stloc, loc_start);
|
||
|
// length = marks [length].Length;
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Ldfld, fi_marks);
|
||
|
ilgen.Emit (OpCodes.Ldloc, loc_length);
|
||
|
ilgen.Emit (OpCodes.Ldelema, typeof (Mark));
|
||
|
ilgen.Emit (OpCodes.Call, GetMethod (typeof (Mark), "get_Length", ref mi_mark_get_length));
|
||
|
ilgen.Emit (OpCodes.Stloc, loc_length);
|
||
|
if (reverse) {
|
||
|
//ptr -= length;
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Ldloc, loc_length);
|
||
|
ilgen.Emit (OpCodes.Sub);
|
||
|
ilgen.Emit (OpCodes.Starg, 1);
|
||
|
//if (ptr < 0)
|
||
|
//goto Fail;
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4_0);
|
||
|
ilgen.Emit (OpCodes.Blt, frame.label_fail);
|
||
|
} else {
|
||
|
//if (strpos + length > string_end)
|
||
|
// return false;
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Ldloc, loc_length);
|
||
|
ilgen.Emit (OpCodes.Add);
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Ldfld, fi_string_end);
|
||
|
ilgen.Emit (OpCodes.Bgt, frame.label_fail);
|
||
|
}
|
||
|
|
||
|
LocalBuilder local_str = ilgen.DeclareLocal (typeof (string));
|
||
|
ilgen.Emit (OpCodes.Ldarg_0);
|
||
|
ilgen.Emit (OpCodes.Ldfld, fi_str);
|
||
|
ilgen.Emit (OpCodes.Stloc, local_str);
|
||
|
|
||
|
// end = start + length;
|
||
|
LocalBuilder loc_end = ilgen.DeclareLocal (typeof (int));
|
||
|
ilgen.Emit (OpCodes.Ldloc, loc_start);
|
||
|
ilgen.Emit (OpCodes.Ldloc, loc_length);
|
||
|
ilgen.Emit (OpCodes.Add);
|
||
|
ilgen.Emit (OpCodes.Stloc, loc_end);
|
||
|
//for (; start < end; ++start) {
|
||
|
Label l_loop_footer = ilgen.DefineLabel ();
|
||
|
ilgen.Emit (OpCodes.Br, l_loop_footer);
|
||
|
Label l_loop_body = ilgen.DefineLabel ();
|
||
|
ilgen.MarkLabel (l_loop_body);
|
||
|
//if (str [strpos] != str [start])
|
||
|
//return false;
|
||
|
if (ignore)
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_textinfo);
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_str);
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Callvirt, typeof (string).GetMethod ("get_Chars"));
|
||
|
if (ignore)
|
||
|
ilgen.Emit (OpCodes.Callvirt, typeof (TextInfo).GetMethod ("ToLower", new Type [] { typeof (char) }));
|
||
|
if (ignore)
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_textinfo);
|
||
|
ilgen.Emit (OpCodes.Ldloc, local_str);
|
||
|
ilgen.Emit (OpCodes.Ldloc, loc_start);
|
||
|
ilgen.Emit (OpCodes.Callvirt, typeof (string).GetMethod ("get_Chars"));
|
||
|
if (ignore)
|
||
|
ilgen.Emit (OpCodes.Callvirt, typeof (TextInfo).GetMethod ("ToLower", new Type [] { typeof (char) }));
|
||
|
ilgen.Emit (OpCodes.Bne_Un, frame.label_fail);
|
||
|
// strpos++;
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4_1);
|
||
|
ilgen.Emit (OpCodes.Add);
|
||
|
ilgen.Emit (OpCodes.Starg, 1);
|
||
|
// start++
|
||
|
ilgen.Emit (OpCodes.Ldloc, loc_start);
|
||
|
ilgen.Emit (OpCodes.Ldc_I4_1);
|
||
|
ilgen.Emit (OpCodes.Add);
|
||
|
ilgen.Emit (OpCodes.Stloc, loc_start);
|
||
|
// Loop footer
|
||
|
ilgen.MarkLabel (l_loop_footer);
|
||
|
ilgen.Emit (OpCodes.Ldloc, loc_start);
|
||
|
ilgen.Emit (OpCodes.Ldloc, loc_end);
|
||
|
ilgen.Emit (OpCodes.Blt, l_loop_body);
|
||
|
|
||
|
if (reverse) {
|
||
|
//ptr -= length;
|
||
|
ilgen.Emit (OpCodes.Ldarg_1);
|
||
|
ilgen.Emit (OpCodes.Ldloc, loc_length);
|
||
|
ilgen.Emit (OpCodes.Sub);
|
||
|
ilgen.Emit (OpCodes.Starg, 1);
|
||
|
}
|
||
|
|
||
|
pc += 3;
|
||
|
break;
|
||
|
}
|
||
|
case RxOp.Repeat:
|
||
|
case RxOp.RepeatLazy:
|
||
|
case RxOp.IfDefined:
|
||
|
// FIXME:
|
||
|
if (RxInterpreter.trace_rx || trace_compile)
|
||
|
Console.WriteLine ("Opcode " + op + " not supported.");
|
||
|
return null;
|
||
|
default:
|
||
|
throw new NotImplementedException ("Opcode '" + op + "' not supported by the regex->IL compiler.");
|
||
|
}
|
||
|
|
||
|
if (one_op)
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
End:
|
||
|
|
||
|
out_pc = pc;
|
||
|
|
||
|
return m;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|