/// <summary>
        /// Get ready to run by all the cuts that are listed on the command line.
        /// </summary>
        /// <param name="context"></param>
        public override void EnterObjectSpecNameAndCutList(FinalStatePatternParser.ObjectSpecNameAndCutListContext context)
        {
            // Cache the FSO for processing
            _current_fso = Convert(context.object_name());
            _current_criteria.Push(new List<ISelectionCriteriaBase>());

            base.EnterObjectSpecNameAndCutList(context);
        }
        /// <summary>
        /// We are doing a stand alone cut on a single line; get things setup to track what happens on exit.
        /// </summary>
        /// <param name="context"></param>
        public override void EnterStandalone_cut(FinalStatePatternParser.Standalone_cutContext context)
        {
            _current_fso = null; // Should already be the case!
            _current_criteria.Push(new List<ISelectionCriteriaBase>());

            // Do the rest.
            base.EnterStandalone_cut(context);
        }
        /// <summary>
        /// Return a DFS parsed from the text. Throw if error during parsing.
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        public static DetectorFinalState Parse(this string text)
        {
            // Lex.
            var input = new AntlrInputStream(text);
            var lexer = new FinalStatePatternLexer(input);
            CommonTokenStream tokens = new CommonTokenStream(lexer);

            // Parse, capture errors.
            var parser = new FinalStatePatternParser(tokens);

            var errors = new ErrorRecorder();

            parser.AddErrorListener(errors);

            var tree = parser.top_level();

            if (errors.Errors.Count > 0)
            {
                string msg = "";
                foreach (var e in errors.Errors)
                {
                    msg += e;
                }
                throw new ArgumentException(msg);
            }

            // Convert to DFS
            var traverser = new FinalStatePatternListener();
            var walker    = new ParseTreeWalker();

            walker.Walk(traverser, tree);

            // And collect the DFS

            return(traverser.BuildDFS());
        }
        /// <summary>
        /// Convert a cut_name to a IValueBase
        /// </summary>
        /// <param name="func"></param>
        /// <returns></returns>
        private IValueBase Convert(FinalStatePatternParser.Cut_nameContext func)
        {
            // Defaults, depending on context
            // This is what happens when the same code is used for multiple rules. :(
            var fso = _current_fso;
            if (fso == null)
            {
                fso = _current_cut;
            }

            // See if the FSO was explicitly defined.
            if (func.object_name() != null)
            {
                fso = Convert(func.object_name(), AllowedFSODefinitionReference.kAsDefinitionOrReference);
            }

            if (fso == null)
            {
                throw new ArgumentException(string.Format("Unable to figure out what object this value is referring to: {0}", func.NAME().GetText()));
            }

            // It is possible to have a name like ETMiss as a stand-alone name.
            // However, then the FSO must be defined.
            // (see above)... And we really need to double check this.
            // TODO: Alias mechanism so ETMiss => ETMiss.ET or similar?
            // Or we can use MET or something like that - so most physicists will
            // end up doing the "right thing".
            var name = func.NAME() == null ? "" : func.NAME().GetText();

            return new SinglePhysicalQuantity()
            {
                PhysicalQantity = name,
                RefersToObject = fso
            };
        }
 /// <summary>
 /// When we enter a function argument list processing, make sure to specify what is needed.
 /// </summary>
 /// <param name="context"></param>
 public override void EnterFunction(FinalStatePatternParser.FunctionContext context)
 {
     _current_cut = new FinalStateObject() { Name = string.Format("FuncArg{0}", context.NAME().GetText()) };
     _current_criteria.Push(new List<ISelectionCriteriaBase>());
     base.EnterFunction(context);
 }
        /// <summary>
        /// When an object is defined, and a bunch of cuts are further required, we show up here.
        /// </summary>
        /// <param name="context"></param>
        public override void ExitObjectSpecNameAndCutList(FinalStatePatternParser.ObjectSpecNameAndCutListContext context)
        {
            // Pull the selection criteria out

            TopLevelCriteria.AddRange(_current_criteria.Pop());

            // Any down level processing.
            base.ExitObjectSpecNameAndCutList(context);
        }
 /// <summary>
 /// Given a cut name, see if there are any references to various FSO's.
 /// </summary>
 /// <param name="cut_nameContext"></param>
 /// <returns></returns>
 private IEnumerable<FinalStateObject> ExtractFSOReferences(FinalStatePatternParser.Cut_nameContext cut_nameContext)
 {
     if (cut_nameContext.object_name() != null)
     {
         yield return Convert(cut_nameContext.object_name());
     }
     else if (cut_nameContext.NAME() != null)
     {
         var nm = cut_nameContext.NAME().GetText();
         var f = FSOs.Where(fs => fs.Name == nm).FirstOrDefault();
         if (f != null)
         {
             yield return f;
         }
     }
 }
 /// <summary>
 /// Extract FSO references
 /// </summary>
 /// <param name="cutContext"></param>
 /// <returns></returns>
 private IEnumerable<FinalStateObject> ExtractFSOReferences(FinalStatePatternParser.CutContext cutContext)
 {
     return _current_criteria.Peek()
         .SelectMany(sc => ExtractFSOReferences(sc));
 }
        /// <summary>
        /// Convert a function
        /// </summary>
        /// <param name="functionContext"></param>
        /// <returns></returns>
        private IValueBase Convert(FinalStatePatternParser.FunctionContext functionContext)
        {
            // All the arguments are either on the stack as selection criteria or buried deep.
            var allObjectsInContext =
                functionContext.function_arg()
                .SelectMany(fa => ExtractFSOReferences(fa));
            var allObjectsOnStack =
                _current_criteria.Peek()
                .SelectMany(fa => ExtractFSOReferences(fa));
            var seenObjectNames = new HashSet<FinalStateObject>();
            foreach (var anObject in allObjectsInContext.Concat(allObjectsOnStack))
            {
                seenObjectNames.Add(anObject);
            }

            var textItems =
                functionContext
                .function_arg()
                .Select(a => a.GetText());
            string arglist = "";
            foreach (var at in textItems)
            {
                if (arglist.Length > 0)
                    arglist += ", ";
                arglist += at;
            }

            return new FunctionPhysicalQuantity()
            {
                Name = functionContext.NAME().GetText(),
                ArgumentList = arglist,
                RefersToObjects = seenObjectNames.ToArray()
            };
        }
 /// TODO: This extraction code is horrible, and is already discovered by parsing. We should be able to just
 /// AVOID IT. Make the parser better when we learn how to do it.
 /// <summary>
 /// Given an argument, see if there are any FSO's in there.
 /// </summary>
 /// <param name="fa"></param>
 /// <returns></returns>
 private IEnumerable<FinalStateObject> ExtractFSOReferences(FinalStatePatternParser.Function_argContext fa)
 {
     if (fa.cut() != null)
     {
         return ExtractFSOReferences(fa.cut());
     }
     else if (fa.cut_name() != null)
     {
         return ExtractFSOReferences(fa.cut_name());
     }
     else
     {
         throw new InvalidOperationException("Unable to extra information from a function argument.");
     }
 }
        /// <summary>
        /// We see a binary cut, so put it on the list.
        /// </summary>
        /// <param name="context"></param>
        public override void ExitCutBinary(FinalStatePatternParser.CutBinaryContext context)
        {
            // If we are processing an argument, it isn't a criteria
            var c = Convert(context);
            _current_criteria.Peek().Add(c);

            // And off we go
            base.ExitCutBinary(context);
        }
 /// <summary>
 /// When we leave the function, clean up so we don't accidentally re-use the
 /// function context.
 /// </summary>
 /// <param name="context"></param>
 public override void ExitFunction(FinalStatePatternParser.FunctionContext context)
 {
     _current_cut = null;
     _parsed_functions.Enqueue(Convert(context));
     _current_criteria.Pop();
     base.ExitFunction(context);
 }
        /// <summary>
        /// Given an object name context, extract a FSO.
        /// </summary>
        /// <param name="objNameContext"></param>
        private FinalStateObject Convert(FinalStatePatternParser.Object_nameContext objNameContext, AllowedFSODefinitionReference refType = AllowedFSODefinitionReference.kAsDefinitionOnly)
        {
            var fso_name = objNameContext.NAME().GetText();

            string fso_base_definition = null;
            if (objNameContext.base_definition() != null)
            {
                fso_base_definition = objNameContext.base_definition().GetText();
            }

            var oldFSO = FSOs.Where(f => f.Name == fso_name).FirstOrDefault();
            if (oldFSO != null)
            {
                if (refType == AllowedFSODefinitionReference.kAsDefinitionOnly && oldFSO.BaseDefinition != fso_base_definition)
                {
                    throw new ArgumentOutOfRangeException(string.Format("Object {0} was defined with two base definitions ({1} and {2})", fso_name, fso_base_definition, oldFSO.BaseDefinition));
                }
                return oldFSO;
            }
            else
            {
                var new_fso = new FinalStateObject() { Name = fso_name, BaseDefinition = fso_base_definition };
                FSOs.Add(new_fso);
                return new_fso;
            }
        }
 /// <summary>
 /// Convert a argument to a cut (a single "term") into a IValueBase
 /// </summary>
 /// <param name="cut_argContext"></param>
 /// <returns></returns>
 private IValueBase Convert(FinalStatePatternParser.Cut_argContext cut_argContext)
 {
     if (cut_argContext.cut_name() != null)
     {
         return Convert(cut_argContext.cut_name());
     }
     else if (cut_argContext.cut_number() != null)
     {
         return Convert(cut_argContext.cut_number());
     }
     else if (cut_argContext.function() != null)
     {
         // Should have already been parsed.
         return _parsed_functions.Dequeue();
     }
     else
     {
         throw new InvalidOperationException();
     }
 }
 private SelectionCriteria Convert(FinalStatePatternParser.CutBinaryContext context)
 {
     var c = new SelectionCriteria();
     c.BinaryRelation = context.BINARY_OP().GetText();
     c.FirstArgument = Convert(context.cut_arg()[0]);
     c.SecondArgument = Convert(context.cut_arg()[1]);
     return c;
 }
        /// <summary>
        /// Done processing the cuts, put them into our criteria list.
        /// </summary>
        /// <param name="context"></param>
        public override void ExitStandalone_cut(FinalStatePatternParser.Standalone_cutContext context)
        {
            TopLevelCriteria.AddRange(_current_criteria.Pop());

            // Continue.
            base.ExitStandalone_cut(context);
        }
        /// <summary>
        /// A new object specification line has been built. Record it and add it to our overall list.
        /// No cuts are applied at this time in the grammar parsing.
        /// </summary>
        /// <param name="context"></param>
        public override void ExitObjectSpecNameOnly(FinalStatePatternParser.ObjectSpecNameOnlyContext context)
        {
            Convert(context.object_name());

            // Go on to what we were doing previously.
            base.ExitObjectSpecNameOnly(context);
        }
 /// <summary>
 /// Convert a cut number to a IValueBase
 /// </summary>
 /// <param name="cut_numberContext"></param>
 /// <returns></returns>
 private IValueBase Convert(FinalStatePatternParser.Cut_numberContext cut_numberContext)
 {
     return new PhysicalValue()
     {
         Number = double.Parse(cut_numberContext.NUMBER().GetText()),
         Unit = cut_numberContext.unit() == null ? null : cut_numberContext.unit().GetText()
     };
 }
        /// <summary>
        /// The user has given us a range cut
        /// </summary>
        /// <param name="context"></param>
        public override void ExitCutRange(FinalStatePatternParser.CutRangeContext context)
        {
            var c1 = new SelectionCriteria();
            var c2 = new SelectionCriteria();

            c1.BinaryRelation = context.BINARY_OP(0).GetText();
            c2.BinaryRelation = context.BINARY_OP(1).GetText();

            c1.FirstArgument = Convert(context.cut_number(0));
            c1.SecondArgument = Convert(context.cut_name());

            c2.FirstArgument = Convert(context.cut_name());
            c2.SecondArgument = Convert(context.cut_number(1));

            _current_criteria.Peek().Add(c1);
            _current_criteria.Peek().Add(c2);

            base.ExitCutRange(context);
        }