crashanalysercmd/PerfToolsSharedLibraries/Engine/SymBuildParsingLib/Parser/Framework/Parser/SymParserBase.cs
author Matti Laitinen <matti.t.laitinen@nokia.com>
Thu, 11 Feb 2010 15:50:58 +0200
changeset 0 818e61de6cd1
permissions -rw-r--r--
Add initial version of Crash Analyser cmdline under EPL

#define SHOW_TOKENS/*
* Copyright (c) 2004-2008 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description: 
*
*/

using System;
using System.Collections;
using System.Threading;
using System.Reflection;
using SymBuildParsingLib.Tree;
using SymBuildParsingLib.Utils;
using SymBuildParsingLib.Grouper;
using SymBuildParsingLib.Lexer;
using SymBuildParsingLib.Token;
using SymBuildParsingLib.Parser.Framework.Workers;
using SymBuildParsingLib.Parser.Framework.Document;
using SymbianTree;

namespace SymBuildParsingLib.Parser.Framework.Parser
{
	public abstract class SymParserBase
	{
		#region Event enumerations
		public enum TEvent
		{
			EEventStaringLexing = 0,
			EEventStaringGrouping,
			EEventParsingComplete
		};
		#endregion

		#region Observer interface
		public delegate void ParserObserver( SymParserBase aParser, TEvent aEvent );
		#endregion

		#region Events
		public event ParserObserver ParserObservers;
		#endregion

		#region Constructors and destructor
		public SymParserBase( SymParserDocument aDocument )
		{
			// Store the document and initialise the document's 
			// parser reference
			iDocument = aDocument;
			iDocument.Context.Parser = this;

			// The lexer reads the tokens from the stream and performs minimal
			// (very basic) grouping.
			iLexer = new SymLexer( iDocument.Context.FileName );

			// We observe the lexer in order to know when all the input data
			// has been consumed from the source file.
			iLexer.LexerObservers += new SymLexer.LexerObserver( HandleLexerEvent );

			// The grouper performs more sophisticated grouping based upon
			// simple C++/C rules. It observes the lexer for tokens.
			iGrouper = new SymGrouper( iLexer );

			// We observe the grouper in order to obtain the grouped tokens
			// which we will parse to form the tree.
			iGrouper.GrouperObservers += new SymGrouper.GrouperObserver( HandleGrouperEvent );

			// Create the thread for the parser
			iWorkerThread = new Thread( new System.Threading.ThreadStart( BuildParserTree ) );
			PrepareWorkerThread();

			// Create any initial required workers
			PrepareInitialWorkers();
		}
		#endregion

		#region API
		public void Parse()
		{
			// Starting the lexer will implicitly start the grouper
			// as the grouper is already awaiting the lexer's output
			// tokens. Tokens will arrive into the iGroupedTokens data
			// member. This call is asynchronous.
			iLexer.Lex();
		}
		#endregion

		#region Clone API
		public SymParserBase CarbonCopy( object[] aArguments )
		{
			// NB. Should use object.Clone with custom IClonable charactersitics?
			System.Type type = this.GetType();
			object objectInstance = Activator.CreateInstance( type, aArguments );
			//
			if	( !(objectInstance is SymParserBase) )
			{
				throw new Exception( "Unexpected object type during Carbon Copy operation" );
			}
			//
			SymParserBase parser = (SymParserBase) objectInstance;
			return parser;
		}
		#endregion

		#region Abstract API
		protected virtual void PrepareInitialWorkers()
		{
		}

		protected virtual void HandleParserToken( SymToken aToken )
		{
			// Try to dish the token out to the workers. Give
			// the token to the highest priority worker initially.
			// Keep trying to offer it until one of the workers
			// consumes it.
			int count = iWorkers.Count;
			for( int i=0; i<count; i++ )
			{
				SymParserWorker worker = (SymParserWorker) iWorkers[ i ];
				SymParserWorker.TTokenConsumptionType consumptionType = worker.OfferToken( aToken );
				//
				if	( consumptionType == SymParserWorker.TTokenConsumptionType.ETokenConsumed )
				{
					break;
				}
			}
		}
		#endregion

		#region Abstract properties
		protected abstract string ParserName { get; }
		#endregion

		#region Properties
		public string FileName
		{
			get { return iLexer.FileName; }
		}

		public SymParserDocument Document
		{
			get { return iDocument; }
		}
		#endregion

		#region Worker related
		public int WorkerCount
		{
			get { return iWorkers.Count; }
		}

		public void QueueWorker( SymParserWorker aWorker )
		{
			// Insert into sorted order
			int count = iWorkers.Count;
			for( int i=0; i<count; i++ )
			{
				SymParserWorker worker = (SymParserWorker) iWorkers[ i ];
				int existingPriority = worker.Priority;
				//
				if	( aWorker.Priority >= existingPriority )
				{
					iWorkers.Insert( i, aWorker );
					return;
				}
			}

			// First entry - just append it...
			iWorkers.Add( aWorker );
		}
		#endregion

		#region Internal Properties
		protected SymTokenContainer GroupedTokens
		{
			get { return iGroupedTokens; }
		}
		#endregion

		#region Internal methods
		private void BuildParserTree()
		{
			// Pull a token from the source buffer
			do
			{
				int tokenCount = 0;
				//
				do
				{
					SymToken token = null;

					// Try to get the head token (if there is one)
					lock( iGroupedTokens )
					{
						tokenCount = iGroupedTokens.Count;
						if	( tokenCount > 0 )
						{
							token = iGroupedTokens.PopHead();
						}
					}

					// Then process it
					if	( token != null )
					{
#if SHOW_TOKENS
						System.Diagnostics.Debug.WriteLine( "BuildParserTree() - next token is: [" + token.Value + "]");
#endif
						HandleParserToken( token );
					}
				} 
				while ( tokenCount > 0 );

				// We've processed all the tokens we had so far... put us to sleep
				//System.Diagnostics.Debug.WriteLine( "Parser.BuildParserTree() - no more tokens - waiting on semaphore..." );
				iSemaphore.Wait();
				//System.Diagnostics.Debug.WriteLine( "Parser.BuildParserTree() - AWAKE!" );
			}
			while( true );

		}

		private void PrepareWorkerThread()
		{
			System.Random randomNumberGen = new Random( System.Environment.TickCount );
			int uniquePostfix = randomNumberGen.Next();
			//
			string workerThreadName = ParserName + uniquePostfix.ToString("d8");
			iWorkerThread.Name = workerThreadName;
			iWorkerThread.IsBackground = true;
			iWorkerThread.Start();
		}
		private void ReportEvent( TEvent aEvent )
		{
			if	( ParserObservers != null )
			{
				ParserObservers( this, aEvent );
			}
		}
		private void NewGroupedTokenArrived( SymToken aToken )
		{
			lock( this )
			{
				iGroupedTokens.Append( aToken );
				//
				if	( iSemaphore.Count == 0 )
				{
					iSemaphore.Signal();
				}
			}
		}
		#endregion

		#region Event handlers
		private void HandleLexerEvent( SymLexer aLexer, SymLexer.TEvent aEvent, SymToken aToken )
		{
			switch( aEvent )
			{
				case SymLexer.TEvent.EEventLexingStarted: 
					ReportEvent( TEvent.EEventStaringLexing );
					break;
				case SymLexer.TEvent.EEventLexingNewLine: 
					break;
				case SymLexer.TEvent.EEventLexingComplete: 
					break;
				default:
					break;
			}
		}

		private void HandleGrouperEvent( SymGrouper aGrouper, SymGrouper.TEvent aEvent, SymToken aGroupedToken )
		{
			switch( aEvent )
			{
				case SymGrouper.TEvent.EEventGroupingStarted: 
					ReportEvent( TEvent.EEventStaringGrouping );
					break;
				case SymGrouper.TEvent.EEventGroupingPaused:
					break;
				case SymGrouper.TEvent.EEventGroupingComplete: 
					ReportEvent( TEvent.EEventParsingComplete );
					break;
				case SymGrouper.TEvent.EEventGroupingTokenReady:
					NewGroupedTokenArrived( aGroupedToken );
					break;
				default:
					break;
			}
		}
		#endregion

		#region Data members
		private readonly Thread iWorkerThread;
		private readonly SymGrouper iGrouper;
		private readonly SymLexer iLexer;
		private readonly SymParserDocument iDocument;
		private SymTokenContainer iGroupedTokens = new SymTokenContainer();
		private ArrayList iWorkers = new ArrayList( 4 );
		private SymSemaphore iSemaphore = new SymSemaphore( 0, 1 );
		#endregion
	}
}