1/*
2 * [The "BSD licence"]
3 * Copyright (c) 2005-2008 Terence Parr
4 * All rights reserved.
5 *
6 * Conversion to C#:
7 * Copyright (c) 2008-2009 Sam Harwell, Pixel Mine, Inc.
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 * 3. The name of the author may not be used to endorse or promote products
19 *    derived from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33namespace Antlr.Runtime.Debug
34{
35    using System.Collections.Generic;
36    using System.Collections.ObjectModel;
37    using Antlr.Runtime.Debug.Misc;
38
39    using Array = System.Array;
40    using CLSCompliantAttribute = System.CLSCompliantAttribute;
41    using Console = System.Console;
42    using DateTime = System.DateTime;
43    using Environment = System.Environment;
44    using Math = System.Math;
45    using StringBuilder = System.Text.StringBuilder;
46
47    /** <summary>Using the debug event interface, track what is happening in the parser
48     *  and record statistics about the runtime.
49     */
50    public class Profiler : BlankDebugEventListener
51    {
52        public static readonly string DataSeparator = "\t";
53        public static readonly string NewLine = Environment.NewLine;
54
55        internal static bool dump = false;
56
57        /** Because I may change the stats, I need to track that for later
58         *  computations to be consistent.
59         */
60        public static readonly string Version = "3";
61        public static readonly string RuntimeStatsFilename = "runtime.stats";
62
63        /** Ack, should not store parser; can't do remote stuff.  Well, we pass
64         *  input stream around too so I guess it's ok.
65         */
66        public DebugParser parser = null;
67
68        // working variables
69
70        [CLSCompliant(false)]
71        protected int ruleLevel = 0;
72        //protected int decisionLevel = 0;
73        protected IToken lastRealTokenTouchedInDecision;
74        protected Dictionary<string, bool> uniqueRules = new Dictionary<string, bool>();
75        protected Stack<string> currentGrammarFileName = new Stack<string>();
76        protected Stack<string> currentRuleName = new Stack<string>();
77        protected Stack<int> currentLine = new Stack<int>();
78        protected Stack<int> currentPos = new Stack<int>();
79
80        // Vector<DecisionStats>
81        //protected Vector decisions = new Vector(200); // need setSize
82        protected DoubleKeyMap<string, int, DecisionDescriptor> decisions = new DoubleKeyMap<string, int, DecisionDescriptor>();
83
84        // Record a DecisionData for each decision we hit while parsing
85        private List<DecisionEvent> decisionEvents = new List<DecisionEvent>();
86        protected Stack<DecisionEvent> decisionStack = new Stack<DecisionEvent>();
87
88        protected int backtrackDepth;
89
90        ProfileStats stats = new ProfileStats();
91
92        public Profiler()
93        {
94        }
95
96        public Profiler(DebugParser parser)
97        {
98            this.parser = parser;
99        }
100
101        public override void EnterRule(string grammarFileName, string ruleName)
102        {
103            //System.out.println("enterRule "+grammarFileName+":"+ruleName);
104            ruleLevel++;
105            stats.numRuleInvocations++;
106            uniqueRules.Add(grammarFileName + ":" + ruleName, true);
107            stats.maxRuleInvocationDepth = Math.Max(stats.maxRuleInvocationDepth, ruleLevel);
108            currentGrammarFileName.Push(grammarFileName);
109            currentRuleName.Push(ruleName);
110        }
111
112        public override void ExitRule(string grammarFileName, string ruleName)
113        {
114            ruleLevel--;
115            currentGrammarFileName.Pop();
116            currentRuleName.Pop();
117        }
118
119        /** Track memoization; this is not part of standard debug interface
120         *  but is triggered by profiling.  Code gen inserts an override
121         *  for this method in the recognizer, which triggers this method.
122         *  Called from alreadyParsedRule().
123         */
124        public virtual void ExamineRuleMemoization(IIntStream input,
125                                           int ruleIndex,
126                                           int stopIndex, // index or MEMO_RULE_UNKNOWN...
127                                           string ruleName)
128        {
129            if (dump)
130                Console.WriteLine("examine memo " + ruleName + " at " + input.Index + ": " + stopIndex);
131            if (stopIndex == BaseRecognizer.MemoRuleUnknown)
132            {
133                //System.out.println("rule "+ruleIndex+" missed @ "+input.index());
134                stats.numMemoizationCacheMisses++;
135                stats.numGuessingRuleInvocations++; // we'll have to enter
136                CurrentDecision().numMemoizationCacheMisses++;
137            }
138            else
139            {
140                // regardless of rule success/failure, if in cache, we have a cache hit
141                //System.out.println("rule "+ruleIndex+" hit @ "+input.index());
142                stats.numMemoizationCacheHits++;
143                CurrentDecision().numMemoizationCacheHits++;
144            }
145        }
146
147        /** Warning: doesn't track success/failure, just unique recording event */
148        public virtual void Memoize(IIntStream input,
149                            int ruleIndex,
150                            int ruleStartIndex,
151                            string ruleName)
152        {
153            // count how many entries go into table
154            if (dump)
155                Console.WriteLine("memoize " + ruleName);
156            stats.numMemoizationCacheEntries++;
157        }
158
159        public override void Location(int line, int pos)
160        {
161            currentLine.Push(line);
162            currentPos.Push(pos);
163        }
164
165        public override void EnterDecision(int decisionNumber, bool couldBacktrack)
166        {
167            lastRealTokenTouchedInDecision = null;
168            stats.numDecisionEvents++;
169            int startingLookaheadIndex = parser.TokenStream.Index;
170            ITokenStream input = parser.TokenStream;
171            if (dump)
172            {
173                Console.WriteLine("enterDecision canBacktrack=" + couldBacktrack + " " + decisionNumber +
174                      " backtrack depth " + backtrackDepth +
175                      " @ " + input.Get(input.Index) +
176                      " rule " + LocationDescription());
177            }
178            string g = currentGrammarFileName.Peek();
179            DecisionDescriptor descriptor = decisions.Get(g, decisionNumber);
180            if (descriptor == null)
181            {
182                descriptor = new DecisionDescriptor();
183                decisions.Put(g, decisionNumber, descriptor);
184                descriptor.decision = decisionNumber;
185                descriptor.fileName = currentGrammarFileName.Peek();
186                descriptor.ruleName = currentRuleName.Peek();
187                descriptor.line = currentLine.Peek();
188                descriptor.pos = currentPos.Peek();
189                descriptor.couldBacktrack = couldBacktrack;
190            }
191            descriptor.n++;
192
193            DecisionEvent d = new DecisionEvent();
194            decisionStack.Push(d);
195            d.decision = descriptor;
196            d.startTime = DateTime.Now;
197            d.startIndex = startingLookaheadIndex;
198        }
199
200        public override void ExitDecision(int decisionNumber)
201        {
202            DecisionEvent d = decisionStack.Pop();
203            d.stopTime = DateTime.Now;
204
205            int lastTokenIndex = lastRealTokenTouchedInDecision.TokenIndex;
206            int numHidden = GetNumberOfHiddenTokens(d.startIndex, lastTokenIndex);
207            int depth = lastTokenIndex - d.startIndex - numHidden + 1; // +1 counts consuming start token as 1
208            d.k = depth;
209            d.decision.maxk = Math.Max(d.decision.maxk, depth);
210
211            if (dump)
212            {
213                Console.WriteLine("exitDecision " + decisionNumber + " in " + d.decision.ruleName +
214                                   " lookahead " + d.k + " max token " + lastRealTokenTouchedInDecision);
215            }
216
217            decisionEvents.Add(d); // done with decision; track all
218        }
219
220        public override void ConsumeToken(IToken token)
221        {
222            if (dump)
223                Console.WriteLine("consume token " + token);
224
225            if (!InDecision)
226            {
227                stats.numTokens++;
228                return;
229            }
230
231            if (lastRealTokenTouchedInDecision == null ||
232                 lastRealTokenTouchedInDecision.TokenIndex < token.TokenIndex)
233            {
234                lastRealTokenTouchedInDecision = token;
235            }
236            DecisionEvent d = CurrentDecision();
237            // compute lookahead depth
238            int thisRefIndex = token.TokenIndex;
239            int numHidden = GetNumberOfHiddenTokens(d.startIndex, thisRefIndex);
240            int depth = thisRefIndex - d.startIndex - numHidden + 1; // +1 counts consuming start token as 1
241            //d.maxk = Math.max(d.maxk, depth);
242            if (dump)
243            {
244                Console.WriteLine("consume " + thisRefIndex + " " + depth + " tokens ahead in " +
245                                   d.decision.ruleName + "-" + d.decision.decision + " start index " + d.startIndex);
246            }
247        }
248
249        /** The parser is in a decision if the decision depth > 0.  This
250         *  works for backtracking also, which can have nested decisions.
251         */
252        public virtual bool InDecision
253        {
254            get
255            {
256                return decisionStack.Count > 0;
257            }
258        }
259
260        public override void ConsumeHiddenToken(IToken token)
261        {
262            //System.out.println("consume hidden token "+token);
263            if (!InDecision)
264                stats.numHiddenTokens++;
265        }
266
267        /** Track refs to lookahead if in a fixed/nonfixed decision.
268         */
269        public override void LT(int i, IToken t)
270        {
271            if (InDecision && i > 0)
272            {
273                DecisionEvent d = CurrentDecision();
274                if (dump)
275                {
276                    Console.WriteLine("LT(" + i + ")=" + t + " index " + t.TokenIndex + " relative to " + d.decision.ruleName + "-" +
277                             d.decision.decision + " start index " + d.startIndex);
278                }
279
280                if (lastRealTokenTouchedInDecision == null ||
281                     lastRealTokenTouchedInDecision.TokenIndex < t.TokenIndex)
282                {
283                    lastRealTokenTouchedInDecision = t;
284                    if (dump)
285                        Console.WriteLine("set last token " + lastRealTokenTouchedInDecision);
286                }
287                // get starting index off stack
288                //			int stackTop = lookaheadStack.size()-1;
289                //			Integer startingIndex = (Integer)lookaheadStack.get(stackTop);
290                //			// compute lookahead depth
291                //			int thisRefIndex = parser.getTokenStream().index();
292                //			int numHidden =
293                //				getNumberOfHiddenTokens(startingIndex.intValue(), thisRefIndex);
294                //			int depth = i + thisRefIndex - startingIndex.intValue() - numHidden;
295                //			/*
296                //			System.out.println("LT("+i+") @ index "+thisRefIndex+" is depth "+depth+
297                //				" max is "+maxLookaheadInCurrentDecision);
298                //			*/
299                //			if ( depth>maxLookaheadInCurrentDecision ) {
300                //				maxLookaheadInCurrentDecision = depth;
301                //			}
302                //			d.maxk = currentDecision()/
303            }
304        }
305
306        /** Track backtracking decisions.  You'll see a fixed or cyclic decision
307         *  and then a backtrack.
308         *
309         * 		enter rule
310         * 		...
311         * 		enter decision
312         * 		LA and possibly consumes (for cyclic DFAs)
313         * 		begin backtrack level
314         * 		mark m
315         * 		rewind m
316         * 		end backtrack level, success
317         * 		exit decision
318         * 		...
319         * 		exit rule
320         */
321        public override void BeginBacktrack(int level)
322        {
323            if (dump)
324                Console.WriteLine("enter backtrack " + level);
325            backtrackDepth++;
326            DecisionEvent e = CurrentDecision();
327            if (e.decision.couldBacktrack)
328            {
329                stats.numBacktrackOccurrences++;
330                e.decision.numBacktrackOccurrences++;
331                e.backtracks = true;
332            }
333        }
334
335        /** Successful or not, track how much lookahead synpreds use */
336        public override void EndBacktrack(int level, bool successful)
337        {
338            if (dump)
339                Console.WriteLine("exit backtrack " + level + ": " + successful);
340            backtrackDepth--;
341        }
342
343        public override void Mark(int i)
344        {
345            if (dump)
346                Console.WriteLine("mark " + i);
347        }
348
349        public override void Rewind(int i)
350        {
351            if (dump)
352                Console.WriteLine("rewind " + i);
353        }
354
355        public override void Rewind()
356        {
357            if (dump)
358                Console.WriteLine("rewind");
359        }
360
361        protected virtual DecisionEvent CurrentDecision()
362        {
363            return decisionStack.Peek();
364        }
365
366        public override void RecognitionException(RecognitionException e)
367        {
368            stats.numReportedErrors++;
369        }
370
371        public override void SemanticPredicate(bool result, string predicate)
372        {
373            stats.numSemanticPredicates++;
374            if (InDecision)
375            {
376                DecisionEvent d = CurrentDecision();
377                d.evalSemPred = true;
378                d.decision.numSemPredEvals++;
379                if (dump)
380                {
381                    Console.WriteLine("eval " + predicate + " in " + d.decision.ruleName + "-" +
382                                       d.decision.decision);
383                }
384            }
385        }
386
387        public override void Terminate()
388        {
389            foreach (DecisionEvent e in decisionEvents)
390            {
391                //System.out.println("decision "+e.decision.decision+": k="+e.k);
392                e.decision.avgk += e.k;
393                stats.avgkPerDecisionEvent += e.k;
394                if (e.backtracks)
395                { // doesn't count gated syn preds on DFA edges
396                    stats.avgkPerBacktrackingDecisionEvent += e.k;
397                }
398            }
399            stats.averageDecisionPercentBacktracks = 0.0f;
400            foreach (DecisionDescriptor d in decisions.Values())
401            {
402                stats.numDecisionsCovered++;
403                d.avgk /= (float)d.n;
404                if (d.couldBacktrack)
405                {
406                    stats.numDecisionsThatPotentiallyBacktrack++;
407                    float percentBacktracks = d.numBacktrackOccurrences / (float)d.n;
408                    //System.out.println("dec "+d.decision+" backtracks "+percentBacktracks*100+"%");
409                    stats.averageDecisionPercentBacktracks += percentBacktracks;
410                }
411                // ignore rules that backtrack along gated DFA edges
412                if (d.numBacktrackOccurrences > 0)
413                {
414                    stats.numDecisionsThatDoBacktrack++;
415                }
416            }
417            stats.averageDecisionPercentBacktracks /= stats.numDecisionsThatPotentiallyBacktrack;
418            stats.averageDecisionPercentBacktracks *= 100; // it's a percentage
419            stats.avgkPerDecisionEvent /= stats.numDecisionEvents;
420            stats.avgkPerBacktrackingDecisionEvent /= (float)stats.numBacktrackOccurrences;
421
422            Console.Error.WriteLine(ToString());
423            Console.Error.WriteLine(GetDecisionStatsDump());
424
425            //		String stats = toNotifyString();
426            //		try {
427            //			Stats.writeReport(RUNTIME_STATS_FILENAME,stats);
428            //		}
429            //		catch (IOException ioe) {
430            //			System.err.println(ioe);
431            //			ioe.printStackTrace(System.err);
432            //		}
433        }
434
435        public virtual void SetParser(DebugParser parser)
436        {
437            this.parser = parser;
438        }
439
440        // R E P O R T I N G
441
442        public virtual string ToNotifyString()
443        {
444            StringBuilder buf = new StringBuilder();
445            buf.Append(Version);
446            buf.Append('\t');
447            buf.Append(parser.GetType().Name);
448            //		buf.Append('\t');
449            //		buf.Append(numRuleInvocations);
450            //		buf.Append('\t');
451            //		buf.Append(maxRuleInvocationDepth);
452            //		buf.Append('\t');
453            //		buf.Append(numFixedDecisions);
454            //		buf.Append('\t');
455            //		buf.Append(Stats.min(decisionMaxFixedLookaheads));
456            //		buf.Append('\t');
457            //		buf.Append(Stats.max(decisionMaxFixedLookaheads));
458            //		buf.Append('\t');
459            //		buf.Append(Stats.avg(decisionMaxFixedLookaheads));
460            //		buf.Append('\t');
461            //		buf.Append(Stats.stddev(decisionMaxFixedLookaheads));
462            //		buf.Append('\t');
463            //		buf.Append(numCyclicDecisions);
464            //		buf.Append('\t');
465            //		buf.Append(Stats.min(decisionMaxCyclicLookaheads));
466            //		buf.Append('\t');
467            //		buf.Append(Stats.max(decisionMaxCyclicLookaheads));
468            //		buf.Append('\t');
469            //		buf.Append(Stats.avg(decisionMaxCyclicLookaheads));
470            //		buf.Append('\t');
471            //		buf.Append(Stats.stddev(decisionMaxCyclicLookaheads));
472            //		buf.Append('\t');
473            //		buf.Append(numBacktrackDecisions);
474            //		buf.Append('\t');
475            //		buf.Append(Stats.min(toArray(decisionMaxSynPredLookaheads)));
476            //		buf.Append('\t');
477            //		buf.Append(Stats.max(toArray(decisionMaxSynPredLookaheads)));
478            //		buf.Append('\t');
479            //		buf.Append(Stats.avg(toArray(decisionMaxSynPredLookaheads)));
480            //		buf.Append('\t');
481            //		buf.Append(Stats.stddev(toArray(decisionMaxSynPredLookaheads)));
482            //		buf.Append('\t');
483            //		buf.Append(numSemanticPredicates);
484            //		buf.Append('\t');
485            //		buf.Append(parser.getTokenStream().size());
486            //		buf.Append('\t');
487            //		buf.Append(numHiddenTokens);
488            //		buf.Append('\t');
489            //		buf.Append(numCharsMatched);
490            //		buf.Append('\t');
491            //		buf.Append(numHiddenCharsMatched);
492            //		buf.Append('\t');
493            //		buf.Append(numberReportedErrors);
494            //		buf.Append('\t');
495            //		buf.Append(numMemoizationCacheHits);
496            //		buf.Append('\t');
497            //		buf.Append(numMemoizationCacheMisses);
498            //		buf.Append('\t');
499            //		buf.Append(numGuessingRuleInvocations);
500            //		buf.Append('\t');
501            //		buf.Append(numMemoizationCacheEntries);
502            return buf.ToString();
503        }
504
505        public override string ToString()
506        {
507            return ToString(GetReport());
508        }
509
510        public virtual ProfileStats GetReport()
511        {
512            //ITokenStream input = parser.TokenStream;
513            //for (int i = 0; i < input.Count && lastRealTokenTouchedInDecision != null && i <= lastRealTokenTouchedInDecision.TokenIndex; i++)
514            //{
515            //    IToken t = input.Get(i);
516            //    if (t.Channel != TokenChannels.Default)
517            //    {
518            //        stats.numHiddenTokens++;
519            //        stats.numHiddenCharsMatched += t.Text.Length;
520            //    }
521            //}
522            stats.Version = Version;
523            stats.name = parser.GetType().Name;
524            stats.numUniqueRulesInvoked = uniqueRules.Count;
525            //stats.numCharsMatched = lastTokenConsumed.getStopIndex() + 1;
526            return stats;
527        }
528
529        public virtual DoubleKeyMap<string, int, DecisionDescriptor> GetDecisionStats()
530        {
531            return decisions;
532        }
533
534        public virtual ReadOnlyCollection<DecisionEvent> DecisionEvents
535        {
536            get
537            {
538                return decisionEvents.AsReadOnly();
539            }
540        }
541
542        public static string ToString(ProfileStats stats)
543        {
544            StringBuilder buf = new StringBuilder();
545            buf.Append("ANTLR Runtime Report; Profile Version ");
546            buf.Append(stats.Version);
547            buf.Append(NewLine);
548            buf.Append("parser name ");
549            buf.Append(stats.name);
550            buf.Append(NewLine);
551            buf.Append("Number of rule invocations ");
552            buf.Append(stats.numRuleInvocations);
553            buf.Append(NewLine);
554            buf.Append("Number of unique rules visited ");
555            buf.Append(stats.numUniqueRulesInvoked);
556            buf.Append(NewLine);
557            buf.Append("Number of decision events ");
558            buf.Append(stats.numDecisionEvents);
559            buf.Append(NewLine);
560            buf.Append("Number of rule invocations while backtracking ");
561            buf.Append(stats.numGuessingRuleInvocations);
562            buf.Append(NewLine);
563            buf.Append("max rule invocation nesting depth ");
564            buf.Append(stats.maxRuleInvocationDepth);
565            buf.Append(NewLine);
566            //		buf.Append("number of fixed lookahead decisions ");
567            //		buf.Append();
568            //		buf.Append(newline);
569            //		buf.Append("min lookahead used in a fixed lookahead decision ");
570            //		buf.Append();
571            //		buf.Append(newline);
572            //		buf.Append("max lookahead used in a fixed lookahead decision ");
573            //		buf.Append();
574            //		buf.Append(newline);
575            //		buf.Append("average lookahead depth used in fixed lookahead decisions ");
576            //		buf.Append();
577            //		buf.Append(newline);
578            //		buf.Append("standard deviation of depth used in fixed lookahead decisions ");
579            //		buf.Append();
580            //		buf.Append(newline);
581            //		buf.Append("number of arbitrary lookahead decisions ");
582            //		buf.Append();
583            //		buf.Append(newline);
584            //		buf.Append("min lookahead used in an arbitrary lookahead decision ");
585            //		buf.Append();
586            //		buf.Append(newline);
587            //		buf.Append("max lookahead used in an arbitrary lookahead decision ");
588            //		buf.Append();
589            //		buf.Append(newline);
590            //		buf.Append("average lookahead depth used in arbitrary lookahead decisions ");
591            //		buf.Append();
592            //		buf.Append(newline);
593            //		buf.Append("standard deviation of depth used in arbitrary lookahead decisions ");
594            //		buf.Append();
595            //		buf.Append(newline);
596            //		buf.Append("number of evaluated syntactic predicates ");
597            //		buf.Append();
598            //		buf.Append(newline);
599            //		buf.Append("min lookahead used in a syntactic predicate ");
600            //		buf.Append();
601            //		buf.Append(newline);
602            //		buf.Append("max lookahead used in a syntactic predicate ");
603            //		buf.Append();
604            //		buf.Append(newline);
605            //		buf.Append("average lookahead depth used in syntactic predicates ");
606            //		buf.Append();
607            //		buf.Append(newline);
608            //		buf.Append("standard deviation of depth used in syntactic predicates ");
609            //		buf.Append();
610            //		buf.Append(newline);
611            buf.Append("rule memoization cache size ");
612            buf.Append(stats.numMemoizationCacheEntries);
613            buf.Append(NewLine);
614            buf.Append("number of rule memoization cache hits ");
615            buf.Append(stats.numMemoizationCacheHits);
616            buf.Append(NewLine);
617            buf.Append("number of rule memoization cache misses ");
618            buf.Append(stats.numMemoizationCacheMisses);
619            buf.Append(NewLine);
620            //		buf.Append("number of evaluated semantic predicates ");
621            //		buf.Append();
622            //		buf.Append(newline);
623            buf.Append("number of tokens ");
624            buf.Append(stats.numTokens);
625            buf.Append(NewLine);
626            buf.Append("number of hidden tokens ");
627            buf.Append(stats.numHiddenTokens);
628            buf.Append(NewLine);
629            buf.Append("number of char ");
630            buf.Append(stats.numCharsMatched);
631            buf.Append(NewLine);
632            buf.Append("number of hidden char ");
633            buf.Append(stats.numHiddenCharsMatched);
634            buf.Append(NewLine);
635            buf.Append("number of syntax errors ");
636            buf.Append(stats.numReportedErrors);
637            buf.Append(NewLine);
638            return buf.ToString();
639        }
640
641        public virtual string GetDecisionStatsDump()
642        {
643            StringBuilder buf = new StringBuilder();
644            buf.Append("location");
645            buf.Append(DataSeparator);
646            buf.Append("n");
647            buf.Append(DataSeparator);
648            buf.Append("avgk");
649            buf.Append(DataSeparator);
650            buf.Append("maxk");
651            buf.Append(DataSeparator);
652            buf.Append("synpred");
653            buf.Append(DataSeparator);
654            buf.Append("sempred");
655            buf.Append(DataSeparator);
656            buf.Append("canbacktrack");
657            buf.Append("\n");
658            foreach (string fileName in decisions.KeySet())
659            {
660                foreach (int d in decisions.KeySet(fileName))
661                {
662                    DecisionDescriptor s = decisions.Get(fileName, d);
663                    buf.Append(s.decision);
664                    buf.Append("@");
665                    buf.Append(LocationDescription(s.fileName, s.ruleName, s.line, s.pos)); // decision number
666                    buf.Append(DataSeparator);
667                    buf.Append(s.n);
668                    buf.Append(DataSeparator);
669                    buf.Append(string.Format("{0}", s.avgk));
670                    buf.Append(DataSeparator);
671                    buf.Append(s.maxk);
672                    buf.Append(DataSeparator);
673                    buf.Append(s.numBacktrackOccurrences);
674                    buf.Append(DataSeparator);
675                    buf.Append(s.numSemPredEvals);
676                    buf.Append(DataSeparator);
677                    buf.Append(s.couldBacktrack ? "1" : "0");
678                    buf.Append(NewLine);
679                }
680            }
681            return buf.ToString();
682        }
683
684        protected virtual int[] Trim(int[] X, int n)
685        {
686            if (n < X.Length)
687            {
688                int[] trimmed = new int[n];
689                Array.Copy(X, 0, trimmed, 0, n);
690                X = trimmed;
691            }
692            return X;
693        }
694
695        /** Get num hidden tokens between i..j inclusive */
696        public virtual int GetNumberOfHiddenTokens(int i, int j)
697        {
698            int n = 0;
699            ITokenStream input = parser.TokenStream;
700            for (int ti = i; ti < input.Count && ti <= j; ti++)
701            {
702                IToken t = input.Get(ti);
703                if (t.Channel != TokenChannels.Default)
704                {
705                    n++;
706                }
707            }
708            return n;
709        }
710
711        protected virtual string LocationDescription()
712        {
713            return LocationDescription(
714                currentGrammarFileName.Peek(),
715                currentRuleName.Peek(),
716                currentLine.Peek(),
717                currentPos.Peek());
718        }
719
720        protected virtual string LocationDescription(string file, string rule, int line, int pos)
721        {
722            return file + ":" + line + ":" + pos + "(" + rule + ")";
723        }
724
725        public class ProfileStats
726        {
727            public string Version;
728            public string name;
729            public int numRuleInvocations;
730            public int numUniqueRulesInvoked;
731            public int numDecisionEvents;
732            public int numDecisionsCovered;
733            public int numDecisionsThatPotentiallyBacktrack;
734            public int numDecisionsThatDoBacktrack;
735            public int maxRuleInvocationDepth;
736            public float avgkPerDecisionEvent;
737            public float avgkPerBacktrackingDecisionEvent;
738            public float averageDecisionPercentBacktracks;
739            public int numBacktrackOccurrences; // doesn't count gated DFA edges
740
741            public int numFixedDecisions;
742            public int minDecisionMaxFixedLookaheads;
743            public int maxDecisionMaxFixedLookaheads;
744            public int avgDecisionMaxFixedLookaheads;
745            public int stddevDecisionMaxFixedLookaheads;
746            public int numCyclicDecisions;
747            public int minDecisionMaxCyclicLookaheads;
748            public int maxDecisionMaxCyclicLookaheads;
749            public int avgDecisionMaxCyclicLookaheads;
750            public int stddevDecisionMaxCyclicLookaheads;
751            //		int Stats.min(toArray(decisionMaxSynPredLookaheads);
752            //		int Stats.max(toArray(decisionMaxSynPredLookaheads);
753            //		int Stats.avg(toArray(decisionMaxSynPredLookaheads);
754            //		int Stats.stddev(toArray(decisionMaxSynPredLookaheads);
755            public int numSemanticPredicates;
756            public int numTokens;
757            public int numHiddenTokens;
758            public int numCharsMatched;
759            public int numHiddenCharsMatched;
760            public int numReportedErrors;
761            public int numMemoizationCacheHits;
762            public int numMemoizationCacheMisses;
763            public int numGuessingRuleInvocations;
764            public int numMemoizationCacheEntries;
765        }
766
767        public class DecisionDescriptor
768        {
769            public int decision;
770            public string fileName;
771            public string ruleName;
772            public int line;
773            public int pos;
774            public bool couldBacktrack;
775
776            public int n;
777            public float avgk; // avg across all decision events
778            public int maxk;
779            public int numBacktrackOccurrences;
780            public int numSemPredEvals;
781        }
782
783        // all about a specific exec of a single decision
784        public class DecisionEvent
785        {
786            public DecisionDescriptor decision;
787            public int startIndex;
788            public int k;
789            public bool backtracks; // doesn't count gated DFA edges
790            public bool evalSemPred;
791            public DateTime startTime;
792            public DateTime stopTime;
793            public int numMemoizationCacheHits;
794            public int numMemoizationCacheMisses;
795        }
796    }
797}
798