public void TestCounter(int expectedCount, string input) { // Define the state machine var root = new StateSwitchBuilder <char, char, int>(); root.On('\n', i => false); root.On((i, ctx) => false); root.On('\n').Do(i => i + 1).Yield(root); root.Default.Yield(root); // Compile the state machine var emitter = new StateMachineEmitter <char, char>(root, EquatableConditionEmitter <char> .Default); emitter.OnEnter <object>((i, d) => this.output.WriteLine("+")); emitter.OnLeave <object>((i, d) => this.output.WriteLine("-")); var stateExpr = emitter.Emit(); this.output.WriteLine(stateExpr.ToReadableString()); var stateFn = stateExpr.Compile(); // Run the state machine for some input var state = 0; object context = 0; foreach (var ch in input) { stateFn(ch, ref state, ref context); } // The context is now an integer with the number of newlines this.output.WriteLine(context.ToString()); Assert.Equal(expectedCount, (int)context); Assert.True(state >= 0); }
/// <summary>Gets the state ID associated with a builder.</summary> /// <param name="switchState">Builder to look-up.</param> /// <returns>The state ID of the builder.</returns> public int GetIdForBuilder(StateSwitchBuilder <TComparand, TInput> switchState) { if (!this.switchStates.TryGetValue(switchState, out var value)) { value = this.switchStates.Count; this.switchStates.Add(switchState, value); this.switchStateIds.Add(value, switchState); } return(value); }
/// <summary>Constructor.</summary> /// <exception cref="ArgumentNullException">Thrown when one or more required arguments are <c>null</c></exception> /// <exception cref="InvalidOperationException">Thrown when the requested operation is invalid.</exception> /// <param name="root">The root.</param> /// <param name="conditionEmitter">The condition emitter.</param> /// <param name="contextType">Type of the context.</param> public StateMachineEmitter([NotNull] StateSwitchBuilder <TComparand, TInput> root, [NotNull] IConditionEmitter <TComparand, TInput> conditionEmitter, Type contextType) { this.Root = root ?? throw new ArgumentNullException(nameof(root)); this.ConditionEmitter = conditionEmitter ?? throw new ArgumentNullException(nameof(conditionEmitter)); this.InputParameter = Expression.Parameter(typeof(TInput), "input"); this.StateParameter = Expression.Parameter(typeof(int).MakeByRefType(), "state"); this.ContextParameter = Expression.Parameter(contextType ?? typeof(object).MakeByRefType(), "context"); this.StartLabel = Expression.Label("start"); if (this.GetIdForBuilder(this.Root) != 0) { throw new InvalidOperationException("Internal error: Unexpected root ID"); } }
public void ExpressionReplaceTest() { var root = new StateSwitchBuilder <bool, bool, ITestOutputHelper>(); var emitter = new StateMachineEmitter <bool, bool>(root, EquatableConditionEmitter <bool> .Default); root.Default.Do(o => o.WriteLine(((int)root).ToString())); var stateExpr = emitter.Emit(); var stateFn = stateExpr.Compile(); var state = 0; object context = this.output; Assert.False(stateFn(true, ref state, ref context)); }
public void TestMergeOptimization() { // Define the state machine var root = new StateSwitchBuilder <char, char, int>(); root.On('x', i => false).Yield(root); root.On('a').Yield(root); root.On('x').Yield(root); root.On('b').Yield(root); // Compile the state machine var emitter = new StateMachineEmitter <char, char>(root, EquatableConditionEmitter <char> .Default); var stateExpr = emitter.Emit(); this.output.WriteLine(stateExpr.ToReadableString()); }
public void TestRangeSet(bool accept, string input) { var root = new StateSwitchBuilder <RangeSet <char>, char, ITestOutputHelper>(); var emitter = new StateMachineEmitter <RangeSet <char>, char>(root, RangesConditionEmitter <RangeSet <char>, char> .Default); root.OnSequence("test").Do(o => o.WriteLine("Done")).Yield(-2); root.On(new RangeSet <char>(" \t\r\n")).Yield(o => (int)root); var stateExpr = emitter.Emit(); var stateFn = stateExpr.Compile(); var state = 0; object context = this.output; foreach (var ch in input) { this.output.WriteLine(stateFn(ch, ref state, ref context).ToString()); } Assert.Equal(accept, state == -2); }
/// <summary>Constructor.</summary> /// <exception cref="ArgumentNullException">Thrown when one or more required arguments are <c>null</c></exception> /// <exception cref="InvalidOperationException">Thrown when the requested operation is invalid.</exception> /// <param name="root">The root.</param> /// <param name="conditionEmitter">The condition emitter.</param> public StateMachineEmitter([NotNull] StateSwitchBuilder <TComparand, TInput> root, [NotNull] IConditionEmitter <TComparand, TInput> conditionEmitter) : this(root, conditionEmitter, null) { }
public PerformStatic(StateSwitchBuilder <TComparand, TInput, TContext> target, bool yield) { this.Yield = yield; this.Target = target; }
/// <summary>When a given input sequence is matched, consume the input except for the last one.</summary> /// <exception cref="InvalidOperationException">Thrown when the requested operation is invalid.</exception> /// <typeparam name="TInput">Type of the input.</typeparam> /// <typeparam name="TContext">Type of the context.</typeparam> /// <param name="that">The builder to act on.</param> /// <param name="inputs">The inputs.</param> /// <returns>A StatePerformBuilder<TInput,TData></returns> public static StatePerformBuilder <RangeSet <TInput>, TInput, TContext> OnSequence <TInput, TContext>(this StateSwitchBuilder <RangeSet <TInput>, TInput, TContext> that, IEnumerable <TInput> inputs) where TInput : IComparable <TInput> { return(OnSequence(that, inputs.Select(i => new RangeSet <TInput>(i)))); }
/// <summary>When a given input sequence is matched, consume the input except for the last one.</summary> /// <exception cref="InvalidOperationException">Thrown when the requested operation is invalid.</exception> /// <typeparam name="TComparand">Type of the comparand.</typeparam> /// <typeparam name="TInput">Type of the input.</typeparam> /// <typeparam name="TContext">Type of the context.</typeparam> /// <param name="that">The builder to act on.</param> /// <param name="inputs">The inputs.</param> /// <returns>A StatePerformBuilder<TInput,TData></returns> public static StatePerformBuilder <TComparand, TInput, TContext> OnSequence <TComparand, TInput, TContext>(this StateSwitchBuilder <TComparand, TInput, TContext> that, IEnumerable <TComparand> inputs) where TComparand : IEquatable <TComparand> { using (var enumerator = inputs.GetEnumerator()) { if (!enumerator.MoveNext()) { throw new InvalidOperationException("Empty input not allowed"); } var result = that.On(enumerator.Current); while (enumerator.MoveNext()) { result = result.Yield().On(enumerator.Current); } return(result); } }
/// <summary>When a given input is matched, consume it and proceed.</summary> /// <typeparam name="TComparand">Type of the comparand.</typeparam> /// <typeparam name="TInput">Type of the input.</typeparam> /// <typeparam name="TContext">Type of the context.</typeparam> /// <param name="that">The builder to act on.</param> /// <param name="input">The input.</param> /// <returns>A StateSwitchBuilder<TInput,TData></returns> public static StateSwitchBuilder <TComparand, TInput, TContext> Take <TComparand, TInput, TContext>(this StateSwitchBuilder <TComparand, TInput, TContext> that, TComparand input) where TComparand : IEquatable <TComparand> { return(that.On(input).Yield()); }