private static bool CapsInfer(Interpreter interpreter, Source source, Stringe tagname, TagArg[] args)
 {
     // TODO: Make capsinfer properly infer "first" capitalization given multiple sentences. Currently, it mistakes it for "word" mode.
     var words = Regex.Matches(args[0].GetString(), @"\w+").OfType<Match>().Select(m => m.Value).ToArray();
     int wCount = words.Length;
     int uCount = 0;
     int fwCount = 0;
     bool firstCharIsUpper = false;
     for (int i = 0; i < wCount; i++)
     {
         if (words[i].All(Char.IsUpper))
         {
             uCount++;
         }
         if (Char.IsUpper(words[i][0]))
         {
             fwCount++;
             if (i == 0) firstCharIsUpper = true;
         }
     }
     if (uCount == wCount)
     {
         interpreter.CurrentState.Output.SetCaps(Capitalization.Upper);
     }
     else if (wCount > 1 && fwCount == wCount)
     {
         interpreter.CurrentState.Output.SetCaps(Capitalization.Word);
     }
     else if (firstCharIsUpper)
     {
         interpreter.CurrentState.Output.SetCaps(Capitalization.First);
     }
     return false;
 }
 private static bool Extern(Interpreter interpreter, Source source, Stringe tagname, TagArg[] args)
 {
     var name = args[0].GetString();
     var result = interpreter.Engine.Hooks.Call(name);
     if (result == null)
     {
         throw new ProcessusException(source, tagname, "A hook with the name '" + name + "' does not exist.");
     }
     interpreter.Print(result);
     return false;
 }
 private static bool Separator(Interpreter interpreter, Source source, Stringe tagName, TagArg[] args)
 {
     interpreter.NextAttribs.Separator = args[0].GetTokens();
     return false;
 }
        private static bool Repeat(Interpreter interpreter, Source source, Stringe tagName, TagArg[] args)
        {
            var reps = args[0].GetString().ToLower().Trim();
            if (reps == "each")
            {
                interpreter.NextAttribs.Repetitons = Repeater.Each;
                return false;
            }

            int num;
            if (!Int32.TryParse(reps, out num))
            {
                throw new ProcessusException(source, tagName, "Invalid repetition value '" + reps + "' - must be a number.");
            }
            if (num < 0)
            {
                throw new ProcessusException(source, tagName, "Repetition value cannot be negative.");
            }

            interpreter.NextAttribs.Repetitons = num;
            return false;
        }
 private static bool Chance(Interpreter interpreter, Source source, Stringe tagname, TagArg[] args)
 {
     int a;
     if (!Int32.TryParse(args[0].GetString(), out a))
     {
         throw new ProcessusException(source, tagname, "Invalid chance number.");
     }
     interpreter.SetChance(a);
     return false;
 }
 private static bool Number(Interpreter interpreter, Source source, Stringe tagName, TagArg[] args)
 {
     int a, b;
     if (!Int32.TryParse(args[0].GetString(), out a) || !Int32.TryParse(args[1].GetString(), out b))
     {
         throw new ProcessusException(source, tagName, "Range values could not be parsed. They must be numbers.");
     }
     interpreter.Print(interpreter.FormatNumber(interpreter.RNG.Next(a, b + 1)));
     return false;
 }
 private static bool Desync(Interpreter interpreter, Source source, Stringe tagname, TagArg[] args)
 {
     interpreter.Desync();
     return false;
 }
 private static bool Sync(Interpreter interpreter, Source source, Stringe tagname, TagArg[] args)
 {
     var typeStr = args[1].GetString();
     SyncType type;
     if (!Enum.TryParse(typeStr, true, out type))
     {
         throw new ProcessusException(source, tagname, "Invalid synchronizer type: '" + typeStr + "'");
     }
     interpreter.Sync(args[0].GetString(), type);
     return false;
 }
 private static bool Reset(Interpreter interpreter, Source source, Stringe tagName, TagArg[] args)
 {
     interpreter.Reset(args[0].GetString());
     return false;
 }
 private static bool NumFmt(Interpreter interpreter, Source source, Stringe tagname, TagArg[] args)
 {
     NumberFormat fmt;
     var fmtstr = args[0].GetString();
     if (!Enum.TryParse(Util.NameToCamel(fmtstr), out fmt))
     {
         throw new ProcessusException(source, tagname, "Invalid number format '" + fmtstr + "'");
     }
     interpreter.NumberFormat = fmt;
     return false;
 }
 private static bool RepNum(Interpreter interpreter, Source source, Stringe tagname, TagArg[] args)
 {
     if (interpreter.CurrentRepeater == null) throw new ProcessusException(source, tagname, "No active repeaters.");
     interpreter.Print(interpreter.FormatNumber(interpreter.CurrentRepeater.Index + 1));
     return false;
 }
        private static bool Nth(Interpreter interpreter, Source source, Stringe tagname, TagArg[] args)
        {
            int offset, interval;
            if (!Int32.TryParse(args[0].GetString(), out interval))
            {
                throw new ProcessusException(source, tagname, "Invalid interval value.");
            }

            if (interval <= 0)
            {
                throw new ProcessusException(source, tagname, "Interval must be greater than zero.");
            }

            if (!Int32.TryParse(args[1].GetString(), out offset))
            {
                throw new ProcessusException(source, tagname, "Invalid offset value.");
            }

            if (interpreter.CurrentRepeater == null || !interpreter.CurrentRepeater.Nth(offset, interval)) return false;
            interpreter.PushState(State.CreateDerivedDistinct(source, args[2].GetTokens(), interpreter, interpreter.CurrentState.Output));
            return true;
        }
 private static bool Alt(Interpreter interpreter, Source source, Stringe tagname, TagArg[] args)
 {
     var testState = State.CreateDerivedDistinct(source, args[0].GetTokens(), interpreter,
         interpreter.CurrentState.Output);
     testState.AddPostBlueprint(new AltBlueprint(interpreter, testState, args[1].GetTokens()));
     interpreter.PushState(testState);
     return true;
 }
 private static bool ReplaceMatch(Interpreter interpreter, Source source, Stringe tagname, TagArg[] args)
 {
     interpreter.Print(interpreter.GetMatchString());
     return false;
 }
        private static bool Arg(Interpreter interpreter, Source source, Stringe tagname, TagArg[] args)
        {
            if (!interpreter.SubArgStack.Any())
                throw new ProcessusException(source, tagname, "Tried to access arguments outside of a subroutine body.");

            TagArg arg;
            var argName = args[0].GetString().Trim();
            if (!interpreter.SubArgStack.Peek().TryGetValue(argName, out arg))
                throw new ProcessusException(source, tagname, "Could not find argument '" + argName + "'.");

            // Argument is string
            if (arg.Type == TagArgType.Result)
            {
                interpreter.Print(arg.GetString());
                return false;
            }
            
            // Argument is tokens
            interpreter.PushState(State.CreateDerivedShared(source, arg.GetTokens(), interpreter));
            return true;
        }
 public bool Invoke(Interpreter interpreter, Source source, Stringe tagName, TagArg[] args)
 {
     return _func(interpreter, source, tagName, args);
 }
 private static bool Out(Interpreter interpreter, Source source, Stringe tagname, TagArg[] args)
 {
     ChannelVisibility cv;
     var cv_str = args[1].GetString();
     if (!Enum.TryParse(Util.NameToCamel(cv_str), out cv))
     {
         throw new ProcessusException(source, tagname, "Invalid channel visibility option '" + cv_str + "'");
     }
     interpreter.CurrentState.Output.PushChannel(args[0].GetString(), cv);
     return false;
 }
 private static bool First(Interpreter interpreter, Source source, Stringe tagname, TagArg[] args)
 {
     if (interpreter.CurrentRepeater == null || !interpreter.CurrentRepeater.IsFirst) return false;
     interpreter.PushState(State.CreateDerivedDistinct(source, args[0].GetTokens(), interpreter, interpreter.CurrentState.Output));
     return true;
 }
 private static bool Caps(Interpreter interpreter, Source source, Stringe tagname, TagArg[] args)
 {
     Capitalization caps;
     var caps_str = args[0].GetString();
     if (!Enum.TryParse(Util.NameToCamel(caps_str), out caps))
     {
         throw new ProcessusException(source, tagname, "Invalid capitalization format '" + caps_str + "'");
     }
     interpreter.CurrentState.Output.SetCaps(caps);
     return false;
 }
 private static bool Pin(Interpreter interpreter, Source source, Stringe tagname, TagArg[] args)
 {
     interpreter.Pin(args[0].GetString());
     return false;
 }
 private static bool Close(Interpreter interpreter, Source source, Stringe tagname, TagArg[] args)
 {
     interpreter.CurrentState.Output.PopChannel(args[0].GetString());
     return false;
 }