State machine searches for specific placeholders in strings

Posted by Timsoft on Thu, 27 Jun 2019 20:14:34 +0200

Question: xz[[cenvENVzxcENV] [fffff] dsbgENV[fecccccc]nqe W3]NBENV[]ZXC
There are three variable areas in this string, which need to be decomposed by the state machine to find variables.

In order to use state machine, it is necessary to define the number of states of the system and the transition process and conditions between states.

In this case, a total of six states are defined
common char: common string
env: Variable content
over: state machine termination (string length)
maybe: may belong to a variable
Maybe Over: Possibly variables follow.
envOver: Variable termination.

The direct transition relationship between States is shown in Figure 1.

Code:
State Machine Definition:

  public delegate Tuple<bool, T> HandleType<T>(IState<T> current, IState<T> previous, T originData, int currentIndex);

    public interface IState<T>
    {
        string Name { get; set; }
        string Value { get; set; }

        //The latter state u
        IList<IState<T>> Nexts { get; set; }
        //Judging how states migrate
        Func<IState<T>, T, int, IState<T>> Selector { get; set; }

    }
    public interface IContext<T> : IEnumerator<T>, IEnumerable<T>
    {
        /// <summary>
        /// Initial value
        /// </summary>
        T OriginData { get; set; }
        /// <summary>
        /// Used to customize write processing during state machine changes
        /// </summary>
        IDictionary<Tuple<IState<T>, IState<T>>, HandleType<T>> Handles { get; set; }
        /// <summary>
        /// Current status
        /// </summary>
        IState<T> CurrentState { get; set; }
        /// <summary>
        /// Preceding Processing 
        /// </summary>
        /// <param name="next"></param>
        /// <returns></returns>
        bool transition(IState<T> next);
    }

    public class State : IState<string>
    {
        /// <summary>
        /// State type
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// Values used to save this state process
        /// </summary>
        public string Value { get; set; }
        IList<IState<string>> IState<string>.Nexts { get; set; }
        public Func<IState<string>, string, int, IState<string>> Selector { get; set; }
    }

    public class Context<T> : IContext<T>
    {
        public Context()
        {
            Handles = new Dictionary<Tuple<IState<T>, IState<T>>, HandleType<T>>();
        }
        public T OriginData { get; set; }
        private int originDataIndex { get; set; }
        T currentData;
        public IDictionary<Tuple<IState<T>, IState<T>>, HandleType<T>> Handles { get; set; }
        public IState<T> CurrentState { get; set; }
        T IEnumerator<T>.Current { get { return currentData; } }
        object IEnumerator.Current { get { return currentData; } }
        bool IContext<T>.transition(IState<T> next)
        {
            IContext<T> context = this as IContext<T>;
            if (context.CurrentState == null || context.CurrentState.Nexts.Contains(next))
            {
                var key = Tuple.Create(context.CurrentState, next);
                if (context.Handles.ContainsKey(key) && context.Handles[key] != null)
                {
                    var result = context.Handles[key](context.CurrentState, next, OriginData, originDataIndex);
                    if (!result.Item1)
                    {
                        currentData = result.Item2;
                        return false;
                    }
                    else
                    {
                        currentData = result.Item2;
                    }
                }
                context.CurrentState = next;
                return true;
            }
            return false;
        }
        bool IEnumerator.MoveNext()
        {
            IContext<T> context = this as IContext<T>;
            IState<T> current = context.CurrentState;
            if (current == null)
                throw new Exception("Initial state must be set");
            if (context.CurrentState.Selector != null)
            {
                IState<T> next = context.CurrentState.Selector(context.CurrentState, OriginData, originDataIndex);
                var success = context.transition(next);
                originDataIndex++;
                return success;
            }
            return false;
        }
        void IEnumerator.Reset()
        {
            originDataIndex = 0;
        }
        #region IDisposable Support
        private bool disposedValue = false; // To detect redundant calls
        protected virtual void Dispose(bool disposing)
        {
            if (!disposedValue)
            {
                if (disposing)
                {
                    // TODO: Release the managed state (managed object).
                }
                // TODO: Release unmanaged resources (unmanaged objects) and replace finalizers in the following.
                // TODO: Set the large field to null.
                disposedValue = true;
            }
        }
        void IDisposable.Dispose()
        {
            // Do not change this code. Put the cleanup code into the above Dispose(bool disposing).
            Dispose(true);
            // TODO: If you replace the finalizer in the above content, uncomment the following line.
            // GC.SuppressFinalize(this);
        }
        IEnumerator<T> IEnumerable<T>.GetEnumerator()
        {
            return this;
        }
        IEnumerator IEnumerable.GetEnumerator()
        {
            return this;
        }
        #endregion
    }

State Machine State Definition Machine Transition Relation

            //Save the value and corresponding state when the state changes
            var dic = new List<Tuple<string, string>>();

            //Define six state instances
            IState<string> common = new State() { Name = "common char" };
            IState<string> env = new State() { Name = "env" };
            IState<string> over = new State() { Name = "over" };
            IState<string> maybe = new State() { Name = "maybe" };
            IState<string> maybeOver = new State() { Name = "maybeOver" };
            IState<string> envOver = new State() { Name = "envOver" };

            //Initialization state machine
            IContext<string> stringState = new Context<string> { CurrentState = common };
            stringState.OriginData = "";
            //Additional processing
            HandleType<string> attachResolve = (IState<string> previous, IState<string> current, string originData, int currentIndex) =>
            {
                current.Value += originData[currentIndex];
                return new Tuple<bool, string>(true, current.Value);
            };
            //Coverage processing
            HandleType<string> setResolve = (IState<string> previous, IState<string> current, string originData, int currentIndex) =>
            {
                current.Value = originData[currentIndex].ToString();
                dic.Add(new Tuple<string, string>(previous.Name, previous.Value));
                return new Tuple<bool, string>(true, current.Value);
            };
            //over takes the value of the previous state
            HandleType<string> preResolve = (IState<string> previous, IState<string> current, string originData, int currentIndex) =>
            {
                dic.Add(new Tuple<string, string>(previous.Name, previous.Value));
                return new Tuple<bool, string>(true, previous.Value);
            };
            //common char State Transition and Processing Method under Different Transition Conditions
            common.Nexts = new List<IState<string>>() { common, over, maybe };
            common.Selector = (state, originData, currentIndex) =>
            {
                if (currentIndex >= originData.Length)
                {
                    return over;
                }
                var ch = originData[currentIndex];
                if (ch == 'E')
                {
                    return maybe;
                }
                return common;
            };

            stringState.Handles.Add(Tuple.Create(common, common), attachResolve);
            stringState.Handles.Add(Tuple.Create(common, over), preResolve);
            stringState.Handles.Add(Tuple.Create(common, maybe), setResolve);
            //env state transition and processing methods under different transition conditions
            env.Nexts = new List<IState<string>>() { envOver, over, env };
            env.Selector = (state, originData, currentIndex) =>
            {
                if (currentIndex >= originData.Length)
                {
                    return over;
                }
                var ch = originData[currentIndex];
                if (ch == ']') return envOver; else return env;
            };

            stringState.Handles.Add(Tuple.Create(env, envOver), setResolve);
            stringState.Handles.Add(Tuple.Create(env, over), preResolve);
            stringState.Handles.Add(Tuple.Create(env, env), attachResolve);


            // maybe state transition and processing methods under different transition conditions
            maybe.Nexts = new List<IState<string>>() { maybe, common, env, over, maybeOver };
            maybe.Selector = (state, originData, currentIndex) =>
            {
                if (currentIndex >= originData.Length)
                {
                    return over;
                }
                var ch = originData[currentIndex];
                if (state.Value == "E" && ch == 'N') return maybe;
                if (state.Value == "EN" && ch == 'V') return maybe;
                if (state.Value == "ENV" && ch == '[')
                {
                    return maybeOver;
                }
                return common;
            };
            stringState.Handles.Add(Tuple.Create(maybe, maybe), attachResolve);
            stringState.Handles.Add(Tuple.Create(maybe, common), setResolve);
            stringState.Handles.Add(Tuple.Create(maybe, env), setResolve);
            stringState.Handles.Add(Tuple.Create(maybe, over), preResolve);
            stringState.Handles.Add(Tuple.Create(maybe, maybeOver), setResolve);

            // Maybe Over State Transition and Processing Method under Different Transition Conditions
            maybeOver.Nexts = new List<IState<string>>() { env, envOver, over };
            maybeOver.Selector = (state, originData, currentIndex) =>
            {
                var ch = originData[currentIndex];
                if (currentIndex >= originData.Length)
                {
                    return over;
                }
                if (ch == ']') return envOver;
                return env;
            };
            stringState.Handles.Add(Tuple.Create(maybeOver, env), setResolve);
            stringState.Handles.Add(Tuple.Create(maybeOver, over), preResolve);

            // envOver State Transition and Processing Method under Different Transition Conditions
            envOver.Nexts = new List<IState<string>>() { common, maybe, over };
            envOver.Selector = (state, originData, currentIndex) =>
            {
                if (currentIndex >= originData.Length)
                {
                    return over;
                }
                var ch = originData[currentIndex];
                if (ch == 'E') return maybe;
                return common;
            };
            stringState.Handles.Add(Tuple.Create(envOver, common), setResolve);
            stringState.Handles.Add(Tuple.Create(envOver, maybe), setResolve);
            stringState.Handles.Add(Tuple.Create(envOver, over), preResolve);

            // over State Transition and Processing Method under Different Transition Conditions
            over.Nexts = new List<IState<string>>() { };
            stringState.Handles.Add(Tuple.Create(over, maybe), preResolve);
            stringState.Handles.Add(Tuple.Create(over, common), preResolve);
            stringState.Handles.Add(Tuple.Create(over, env), preResolve);
            stringState.Handles.Add(Tuple.Create(over, envOver), preResolve);
            stringState.Handles.Add(Tuple.Create(over, maybeOver), preResolve);

 

Test:

             Console.WriteLine("Test string");
            string input = "";
            //   "xz[[cenvENVzxcENV[      fffff  ]dsbgENV[fecccccc]nqe  W3]NBENV[]ZXC"
            while (!string.IsNullOrEmpty(input = Console.ReadLine()))
            {
                stringState.OriginData = input;
                foreach (var item in stringState)
                {
                    Console.WriteLine("current state{0}---Current value{1}", stringState.CurrentState.Name, item);
                }
                Console.WriteLine("");
                Console.WriteLine("");
                Console.WriteLine("Extracted Symbol");
                dic.ForEach(a =>
                {
                    Console.WriteLine("String type:" + a.Item1 + "-------------Decomposition value:" + a.Item2);
                });
            }

Test results:

Test string XZ [[cenvENVzxcENV [fffff] dsbgENV [fecccccc] nqe W3] NBENV [] ZXC
 Current status common char -- current value x
 Current status common char -- current value xz
 Current status common char -- current value xz[
Current status common char -- current value xz[[
Current status common char -- current value xz[[c]
Current status common char -- current value xz[[ce]
Current status common char -- current value xz[[cen]
Current status common char -- current value xz[[cenv]
Current state maybe -- current value E
 Current state maybe -- current value EN
 Current state maybe -- current value ENV
 Current status common char -- current value z
 Current status common char -- current value zx
 Current status common char -- current value zxc
 Current state maybe -- current value E
 Current state maybe -- current value EN
 Current state maybe -- current value ENV
 Current status maybe Over -- current value[
Current state env -- current value
 Current state env -- current value
 Current state env -- current value
 Current state env -- current value
 Current state env -- current value
 Current state env -- current value
 Current state env -- current value f
 Current state env -- current value ff
 Current state env -- current value fff
 Current state env -- current value ffff
 Current state env -- current value fffff
 Current state env -- current value fffff
 Current state env -- current value fffff
 Current state envOver -- current value]
Current status common char -- current value d
 Current status common char -- current value ds
 Current status common char -- current value dsb
 Current status common char -- current value dsbg
 Current state maybe -- current value E
 Current state maybe -- current value EN
 Current state maybe -- current value ENV
 Current status maybe Over -- current value[
Current state env -- current value f
 Current state env -- current value fe
 Current state env -- current value fec
 Current state env -- current value fecc
 Current state env -- current value feccc
 Current state env -- current value fecccc
 Current state env -- current value feccccc
 Current state env -- current value fecccccc
 Current state envOver -- current value]
Current status common char -- current value n
 Current status common char -- current value nq
 Current status common char -- current value nqe
 Current status common char -- current value nqe
 Current status common char -- current value nqe
 Current status common char -- current value nqe W
 Current status common char -- current value nqe W3
 Current status common char -- current value nqe W3]
Current status common char -- current value nqe W3]N
 Current status common char -- current value nqe W3]NB
 Current state maybe -- current value E
 Current state maybe -- current value EN
 Current state maybe -- current value ENV
 Current status maybe Over -- current value[
Current state envOver -- current value[
Current status common char -- current value Z
 Current status common char -- current value ZX
 Current status common char -- current value ZXC
 Current status over -- current value ZXC


Extracted Symbol
 String type: common char----------------------------- decomposition value: xz[[cenv]
String type: maybe--------------------------- decomposition value: ENV
 String type: common char--------------------------- decomposition value: zxc
 String type: maybe--------------------------- decomposition value: ENV
 String type: maybeOver--------------------------------- decomposition value:[
String type: env----------------------- decomposition value: fffff
 String type: envOver------------------------- decomposition value:]
String type: common char--------------------------- decomposition value: dsbg
 String type: maybe--------------------------- decomposition value: ENV
 String type: maybeOver--------------------------------- decomposition value:[
String type: env--------------------------- decomposition value: fecccccc
 String type: envOver------------------------- decomposition value:]
String type: common char--------------------------- decomposition value: nqe W3] NB
 String type: maybe--------------------------- decomposition value: ENV
 String type: envOver------------------------- decomposition value:]
String type: common char--------------------------- decomposition value: ZXC