/// <summary> /// Registers a scanner that scans received strings. /// If a string matches the given pattern, the callback is invoked. /// </summary> /// <param name="pattern"></param> /// <param name="callback"></param> /// <returns></returns> public CommandRegistry AddScanner(RegexString pattern, ScannerDelegate callback) { ThrowIfDisposed(); _queue.AddScanner(pattern, callback); return(this); }
private void PipeThreadCallback(string[] inputs, InputResultDelegate callback, IContextObject ctx) { object output = null; for (int i = 0; i < inputs.Length; i++) { string input = $"{inputs[i]}".Trim(); CommandMetadata metadata; lock (_lock) { //Lock the metadata collection, and grab the first metadata that has a matching executor List <CommandMetadata> metadatas = _metadata.Where(m => m.GetFirstOrDefaultExecutorData(input) != null).ToList(); metadata = metadatas.FirstOrDefault(); } if (metadata == null) { //No command matches, so ignore this entire piped input callback.Invoke(InputResult.Unhandled, null); break; } CommandExecutorData exeData = metadata.GetFirstOrDefaultExecutorData(input); RegexString trigger = exeData.ExecutorAttribute.CommandMatcher; input = trigger.RemoveMatchedString(input); object[] arguments = null; if (output != null) { //If there's output from a previous command, append it to the arguments for this command arguments = new[] { output }; } AbstractParser parser; if (i == inputs.Length - 1) { //We only want the final parsing to invoke the parser callback parser = _registry.GetParser(_registry, input, arguments, metadata, exeData, ctx, callback); } else { parser = _registry.GetParser(_registry, input, arguments, metadata, exeData, ctx, null); } //Threads are joined for synchronous behaviour. Running each command concurrently (and thus potentially out of order) will not work here. Thread thread = parser.GetThread(); thread.Start(); thread.Join(); //Set output so that it's appended to the end of the next input output = parser.Output; } }
internal ScannerData(RegexString pattern, ScannerDelegate callback) { Pattern = pattern; Callback = callback; }
private void ParserThreadCallback() { while (!_tokenSource.Token.IsCancellationRequested) { //Check if the MRE has been set if (!_mre.WaitOne(100)) { continue; } if (!_queue.TryDequeue(out QueueData data)) { _mre.Reset(); continue; } //The vertical bar is a pipe character. inputA | inputB = run command B with the output of command A string[] inputs = data.Input.Split('|'); if (inputs.Length > 1) { //If we have a piped command, send it off to a new thread to be handled, //As each command needs to be handled in order Thread pipeThread = new Thread(() => PipeThreadCallback(inputs, data.Callback, data.Context)); pipeThread.Start(); _mre.Set(); continue; } string input = data.Input; //We don't lower the data because case sensitivity is an option for command matching CommandMetadata metadata; lock (_lock) { //Lock the metadata collection, and grab the first metadata that has a matching executor List <CommandMetadata> metadatas = _metadata.Where(m => m.GetFirstOrDefaultExecutorData(input) != null).ToList(); metadata = metadatas.FirstOrDefault(); } if (metadata == null) { data.Callback?.Invoke(InputResult.Unhandled, null); //No command matches, so ignore this input _mre.Set(); continue; } CommandExecutorData exeData = metadata.GetFirstOrDefaultExecutorData(input); RegexString trigger = exeData.ExecutorAttribute.CommandMatcher; input = trigger.RemoveMatchedString(input).TrimStart(); AbstractParser parser = _registry.GetParser(_registry, input, null, metadata, exeData, data.Context, data.Callback); try { parser.Start(); } finally { //Our job is done, so prepare for the next input _mre.Set(); } } _mre.Dispose(); }
/// <summary> /// Adds a new scanner /// </summary> /// <param name="pattern">The scanner's pattern</param> /// <param name="callback">The scanner's callback</param> public void AddScanner(RegexString pattern, ScannerDelegate callback) { ThrowIfDisposed(); _scanners.Add(new ScannerData(pattern, callback)); }