context: 
  /*
   *  MetaphysicsIndustries.Solus
   *  Copyright (C) 2006-2021 Metaphysics Industries, Inc., Richard Sartor
   *
   *  This library is free software; you can redistribute it and/or
   *  modify it under the terms of the GNU Lesser General Public
   *  License as published by the Free Software Foundation; either
   *  version 3 of the License, or (at your option) any later version.
   *
   *  This library is distributed in the hope that it will be useful,
   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   *  Lesser General Public License for more details.
   *
   *  You should have received a copy of the GNU Lesser General Public
   *  License along with this library; if not, write to the Free Software
   *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
   *  USA
   *
   */
  
  using System;
  using System.Collections.Generic;
  using System.Linq;
  using MetaphysicsIndustries.Giza;
  using MetaphysicsIndustries.Solus.Commands;
  using MetaphysicsIndustries.Solus.Exceptions;
  using MetaphysicsIndustries.Solus.Expressions;
  using MetaphysicsIndustries.Solus.Functions;
  using MetaphysicsIndustries.Solus.Transformers;
  using Expression = MetaphysicsIndustries.Solus.Expressions.Expression;
  
  namespace MetaphysicsIndustries.Solus
  {
      public class SolusParser
      {
          protected SolusGrammar _grammar;
          protected Parser _parser;
          protected Parser _parserC;
          protected Spanner _numberSpanner;
  
          public SolusParser()
              : this(new SolusGrammar())
          {
          }
  
          protected SolusParser(SolusGrammar grammar)
          {
              _grammar = grammar;
              _parser = new Parser(_grammar.def_expr);
              _parserC = new Parser(_grammar.def_commands);
              _numberSpanner = new Spanner(_grammar.def_float_002D_number);
          }
  
          public Expression GetExpression(string input, bool cleanup = false)
          {
              var errors = new List<Error>();
  
              var spans = _parser.Parse(input.ToCharacterSource(), errors);
  
              if (errors.ContainsNonWarnings())
              {
                  var error = errors.GetFirstNonWarning();
                  throw new ParseException(-1, error.Description);
              }
  
              if (spans.Length < 1)
              {
                  throw new ParseException(-1,
                      "There were more no valid parses of the input.");
              }
  
              if (spans.Length > 1)
              {
                  throw new ParseException(-1,
                      "There were more than one valid parses of the input.");
              }
  
              var span = spans[0];
  
              var expr = GetExpressionFromExpr(span);
  
              if (cleanup)
              {
                  var ct = new CleanUpTransformer();
                  expr = ct.CleanUp(expr);
              }
  
              return expr;
          }
  
          public ICommandData[] GetCommands(string input,
              CommandSet commandSet = null)
          {
              var errors = new List<Error>();
              var spans = _parserC.Parse(input.ToCharacterSource(), errors);
              if (errors.ContainsNonWarnings())
              {
                  var error = errors.GetFirstNonWarning();
                  throw new ParseException(-1, error.Description);
              }
  
              if (spans.Length < 1)
                  throw new ParseException(-1,
                      "There were more no valid parses of the input.");
              if (spans.Length > 1)
                  throw new ParseException(-1,
                      "There were more than one valid parses of the input.");
              var span = spans[0];
  
              var commands = new List<ICommandData>();
              foreach (var sub in span.Subspans)
              {
                  if (sub.DefRef != _grammar.def_command) continue;
                  commands.Add(GetCommandFromCommand(sub, commandSet));
              }
  
              return commands.ToArray();
          }
  
          Dictionary<Function, int> _operatorPrecedence = new Dictionary<Function, int>()
          {
              {AdditionOperation.Value, 120},
              {MultiplicationOperation.Value, 130},
              {DivisionOperation.Value, 130},
              {ModularDivision.Value, 130},
              {ExponentOperation.Value, 135},
              {BitwiseAndOperation.Value, 100},
              {BitwiseOrOperation.Value, 80},
          };
  
          ICommandData GetCommandFromCommand(Span span, CommandSet commandSet)
          {
              var sub = span.Subspans[0];
              var def = sub.DefRef;
  
              if (def == _grammar.def_delete_002D_command)
                  return GetDeleteCommandFromSpan(sub);
              if (def == _grammar.def_func_002D_assign_002D_command)
                  return GetFuncAssignCommandFromSpan(sub);
              if (def == _grammar.def_help_002D_command)
                  return GetHelpCommandFromSpan(sub, commandSet);
              if (def == _grammar.def_var_002D_assign_002D_command)
                  return GetVarAssignCommandFromSpan(sub);
              if (def == _grammar.def_vars_002D_command)
                  return GetVarsCommandFromSpan(sub);
  
              throw new ParseException(-1,
                  $"Unknown command, \"{def}\"");
          }
  
          ICommandData GetDeleteCommandFromSpan(Span span)
          {
              var names = span.Subspans.Skip(1).
                  Select(sub => sub.Subspans[0].Value);
              return new DeleteCommandData(names);
          }
  
          ICommandData GetFuncAssignCommandFromSpan(Span span)
          {
              var funcname = span.Subspans[0].Value;
  
              var args = span.Subspans.Skip(1).
                  Where(sub => sub.DefRef == _grammar.def_identifier).
                  Select(sub => sub.Value).
                  ToArray();
  
              // create the function, with no expr
              var func = new UserDefinedFunction(funcname, args, null);
  
              // read the expr. this order of things allows for recursion
              var expr = GetExpressionFromExpr(span.Subspans.Last());
              func.Expression = expr;
  
              return new FuncAssignCommandData(func);
          }
  
          ICommandData GetHelpCommandFromSpan(Span span, CommandSet commandSet)
          {
              var topic = "help";
              if (span.Subspans.Count >= 2)
                  topic = span.Subspans[1].Value;
              return new HelpCommandData(topic);
          }
  
          ICommandData GetVarAssignCommandFromSpan(Span span)
          {
              var varname = span.Subspans[0].Subspans[0].Value;
              var expr = GetExpressionFromExpr(span.Subspans[2]);
              return new VarAssignCommandData(varname, expr);
          }
  
          ICommandData GetVarsCommandFromSpan(Span span)
          {
              return new VarsCommandData();
          }
  
          public Expression GetExpressionFromExpr(Span span)
          {
              var subexprs = new List<Expression>();
              var operators = new List<Operation>();
              var operset = new HashSet<Operation>();
  
              subexprs.Add(GetExpressionFromSubexpr(span.Subspans[0]));
  
              int i;
              for (i = 1; i < span.Subspans.Count; i += 2)
              {
                  Operation op;
                  Expression arg = GetExpressionFromSubexpr(
                      span.Subspans[i + 1]);
                  if (span.Subspans[i].Value == "-")
                  {
                      op = AdditionOperation.Value;
                      arg = new FunctionCall(NegationOperation.Value, arg);
                  }
                  else
                  {
                      op = GetOperationFromBinop(span.Subspans[i]);
                  }
  
                  operators.Add(op);
                  subexprs.Add(arg);
                  operset.Add(op);
              }
  
              var sortedOperset = new List<Operation>(operset);
              sortedOperset.Sort((x, y) =>
                  -_operatorPrecedence[x].CompareTo(_operatorPrecedence[y]));
  
              foreach (var op in sortedOperset)
              {
                  var indexes = Enumerable.Range(0, operators.Count).Where(ix => operators[ix] == op).ToList();
                  var ranges = new List<Tuple<int, int>>();
                  int start = indexes[0];
                  for (i = 1; i < indexes.Count; i++)
                  {
                      if (indexes[i] != indexes[i - 1] + 1)
                      {
                          ranges.Add(new Tuple<int, int>(start, indexes[i - 1]));
                          start = indexes[i];
                      }
                  }
  
                  ranges.Add(new Tuple<int, int>(start, indexes[indexes.Count - 1]));
  
                  Queue<Expression> newExpressions = new Queue<Expression>();
                  foreach (var range in ranges)
                  {
                      int first = range.Item1;
                      int last = range.Item2;
  
                      if (op.IsAssociative &&
                          op.IsCommutative)
                      {
                          int count = last - first + 1;
                          var fcargs = subexprs.GetRange(first, count + 1);
                          FunctionCall fcall = new FunctionCall(op, fcargs);
                          newExpressions.Enqueue(fcall);
                      }
                      else
                      {
                          var leftarg = subexprs[first];
                          for (i = first; i <= last; i++)
                          {
                              var rightarg = subexprs[i + 1];
                              var fcall = new FunctionCall(op, leftarg, rightarg);
                              leftarg = fcall;
                          }
  
                          newExpressions.Enqueue(leftarg);
                      }
                  }
  
                  int adjust = 0;
                  foreach (var range in ranges)
                  {
                      int first = range.Item1 - adjust;
                      int last = range.Item2 - adjust;
  
                      int count = last - first + 1;
  
                      operators.RemoveRange(first, count);
                      subexprs.RemoveRange(first, count + 1);
                      subexprs.Insert(first, newExpressions.Dequeue());
  
                      adjust += count;
                  }
              }
  
              if (subexprs.Count != 1)
              {
                  throw new InvalidOperationException(
                      $"The expressions should have been combined " +
                      $"into a single tree, but for some reason, there are " +
                      $"{subexprs.Count} separate expressions.");
              }
  
              return subexprs[0];
          }
  
          public Expression GetExpressionFromSubexpr(Span span)
          {
              var sub = span.Subspans[0];
  
              return GetExpressionFromSubexprPart(sub);
          }
  
          public Expression GetExpressionFromSubexprPart(Span span)
          {
              var defref = span.DefRef;
              if (defref == _grammar.def_paren)
                  return GetExpressionFromExpr(span.Subspans[1]);
              if (defref == _grammar.def_number)
                  return GetLiteralFromNumber(span);
              if (defref == _grammar.def_string)
                  return GetLiteralFromString(span);
              if (defref == _grammar.def_unary_002D_op)
                  return GetExpressionFromUnaryop(span);
              if (defref == _grammar.def_varref)
                  return GetVariableAccessFromVarref(span);
              if (defref == _grammar.def_array_002D_literal)
                  return GetTensorExpressionFromArrayLiteral(span);
              if (defref == _grammar.
                  def_function_002D_call_002D_or_002D_component_002D_access)
                  return GetExpressionFromFunctionCallOrComponentAccess(
                      span);
              throw new ParseException(-1,
                  $"Unknown subexpression, \"{defref}\"");
          }
  
          public Operation GetOperationFromBinop(Span span)
          {
              if (span.Value == "+")
              {
                  return AdditionOperation.Value;
              }
              else if (span.Value == "-")
              {
                  // NOTE: the "-" case is already covered by
                  // GetExpressionFromExpr
                  return null;
              }
              else if (span.Value == "*")
              {
                  return MultiplicationOperation.Value;
              }
              else if (span.Value == "/")
              {
                  return DivisionOperation.Value;
              }
              else if (span.Value == "%")
              {
                  return ModularDivision.Value;
              }
              else if (span.Value == "^")
              {
                  return ExponentOperation.Value;
              }
              else if (span.Value == "&")
              {
                  return BitwiseAndOperation.Value;
              }
              else if (span.Value == "|")
              {
                  return BitwiseOrOperation.Value;
              }
  
gt:             if (span.Value == "=")