* Add first comments
* Finish commenting for Compile and CompileUnit
* Write TODO sections
* Add a few clarifications
* splitted some functions from compile
* Written some methods from Compile to functions
* Update variable names
* Refactor - stage 1
Refactor GetUsesSection and IsPossibleNameSpace
* Refactor - stage 2
Rename a few functions and variables
* Correct an inaccuracy
* Added comments, look through CompileUnit
* Rename a few functions and add new comments
* Split CompileUnit to Subfunctions
Add IsUnitCompiled, IsUnitInPCU, InitializeNewUnit, GetSourceCode, GenSyntaxTree, GenUnitDocumentation, CheckDLLDirectiveOnlyForLibraries, MatchErrorsToBadNodes, CheckIfUnitModule, SetUseDLLForSystemUnits,
CompileInterfaceDependencies, CompileCurrentUnitInterface, GetImplementationUsesSection, CompileImplementationDependencies, CompileCurrentUnitImplementation
* Added some TODOs
* Return uses_unit_in original name
Renaming of syntax tree nodes leads to internal compiler errors
* Extract GenerateILCode method
* Add checking if recompilation needed method
Needs to be discussed and revised
* Add functions for catch blocks in Compile
* Extract building semantic tree method
Creating main function to be moved to another class
* Rename UnitsSortedList
* Edit CompileUnitsFromDelayedList method
* Change a few variable names, make CreateRCFile function and add comments
* Make code more "user-friendly"
* Add TODOs 31.11.23
* Create PrebuildSemanticTreeActionsMethod
* Refactor semantic checks section in initialize new unit method
* Refactor Adding standard units to uses method
* Return file_name and compiler_directives original names to avoid internal compilation errors
* Refactor GetReferences Method
* Initial refactoring of IncludeNamespaces function
* Create three more methods and wrap important code in regions
* Add TODO's
* Resolve merge conflicts
* Add returned value to ConstructSyntaxTree method
* Fix UnitsSortedList NotFoundError in PCUWriter
* Workaround commit
* Update PABCSystem after tests' changes
* Rename syntax trees in some methods
* Delete CurrentSyntaxUnit variable
* Revert unnecessary project files changes
* Squashed commit of the following:
commit f4d7599f1a39252feac97bd5391f46dfdc25f6f4
Author: Владислав Крылов <krylov@sfedu.ru>
Date: Wed Nov 22 11:43:29 2023 +0300
added links to identarranger
commit 3bd5d33e2b2969884e61a3279ff9c353013b57fe
Author: Владислав Крылов <krylov@sfedu.ru>
Date: Wed Nov 22 11:43:03 2023 +0300
Change extension for verybasic to yavb
commit 61294c6e7d405e1c68516cc81d3a109e8a1ee295
Author: MovchanGitHub <92666028+MovchanGitHub@users.noreply.github.com>
Date: Tue Nov 21 09:33:57 2023 +0300
Change Program Example
commit 86971488341ed6bf13c39e13a513d7ac0c51c285
Author: MovchanGitHub <92666028+MovchanGitHub@users.noreply.github.com>
Date: Tue Nov 21 09:28:59 2023 +0300
Add IndentArranger to Compile
commit 2ce50bbbf9db22c4243b247b870f6935ce206d26
Author: MovchanGitHub <92666028+MovchanGitHub@users.noreply.github.com>
Date: Fri Nov 17 10:00:31 2023 +0300
Add Semicolon After Each Statement
commit 796309d340e8d8ba730ff9418fa376f34fb7fd36
Author: MovchanGitHub <92666028+MovchanGitHub@users.noreply.github.com>
Date: Wed Nov 15 18:49:11 2023 +0300
Add Alpha Version of Python-style If-statement
commit 5eb88f946fe8254b4f5c5a56ed0a567b3be51227
Merge: ab4ce5b0 0162b637
Author: Владислав Крылов <krylov@sfedu.ru>
Date: Wed Nov 15 16:54:26 2023 +0300
Merge branch 'IndentArranger' into VeryBasicLanguage
commit ab4ce5b0e32d42004726e3dc45cc0d8a65e53f56
Author: Владислав Крылов <krylov@sfedu.ru>
Date: Wed Nov 15 16:53:51 2023 +0300
Fixes from seminar
commit af74012289d0d08517c13499a2a66cb543fc06d2
Author: Владислав Крылов <krylov@sfedu.ru>
Date: Sun Nov 12 18:57:29 2023 +0300
finally working!
fully implemented compatibility
commit 58a39c313324b468d1eec207ba3f5f6eddf74ef2
Author: Владислав Крылов <krylov@sfedu.ru>
Date: Sun Nov 12 11:58:16 2023 +0300
more compatibility with pabc
Now verybasic statements translate to pascal compiler
added .bat script for autobuilding verybasic
commit 0162b6376b8fe970704f26213bf9f0f670dfb12e
Author: MovchanGitHub <92666028+MovchanGitHub@users.noreply.github.com>
Date: Sun Nov 12 10:06:14 2023 +0300
Update test.txt
commit 1b759ab1cd7ee2ffeb14afff0418ddf0f30b0399
Author: MovchanGitHub <92666028+MovchanGitHub@users.noreply.github.com>
Date: Sun Nov 12 10:05:36 2023 +0300
Add Indent and Unindent Keywords to Generated File
commit cabda7f3985651cff2a8740f1a5bdea8fe7ac892
Author: MovchanGitHub <92666028+MovchanGitHub@users.noreply.github.com>
Date: Fri Nov 10 21:25:52 2023 +0300
Add Generation of Output File
commit 306f033d0176ab559e3622b8505427dbe2e0e203
Author: MovchanGitHub <92666028+MovchanGitHub@users.noreply.github.com>
Date: Thu Nov 9 20:42:18 2023 +0300
Update IndentArranger.sln
commit 60c0ceced1aa3a7d2e33de0facb1126e295f43b5
Author: MovchanGitHub <92666028+MovchanGitHub@users.noreply.github.com>
Date: Thu Nov 9 20:41:22 2023 +0300
Add IndentArranger
commit 1f4556c4573dae154fcc167b41ff6ab9e8122136
Author: Владислав Крылов <krylov@sfedu.ru>
Date: Mon Nov 6 22:48:36 2023 +0300
Made my own VeryBasicParser project
Copied some code from SaushkinParser
Tried to compile it and implement into Pascal
Doesn't work due to grammatik issue
* Squashed commit of the following:
commit 4e73d9ac3ffef68312f06a74dc83afffcf3ccbee
Author: Владислав Крылов <krylov@sfedu.ru>
Date: Wed Nov 22 15:08:10 2023 +0300
Fixed GPPG (i think so at least)
commit e5cfc220828b37fb2f35e565683019716ec3f0ce
Author: Владислав Крылов <krylov@sfedu.ru>
Date: Wed Nov 22 11:44:27 2023 +0300
Update Compiler.cs
commit 4698e2e75a5f3b4f523a8bc7733a50e8abb4fca1
Merge: 22aaf2b2 f4d7599f
Author: Владислав Крылов <krylov@sfedu.ru>
Date: Wed Nov 22 11:43:54 2023 +0300
Merge branch 'IndentArrangerTemp' into VeryBasicLanguage
commit f4d7599f1a39252feac97bd5391f46dfdc25f6f4
Author: Владислав Крылов <krylov@sfedu.ru>
Date: Wed Nov 22 11:43:29 2023 +0300
added links to identarranger
commit 3bd5d33e2b2969884e61a3279ff9c353013b57fe
Author: Владислав Крылов <krylov@sfedu.ru>
Date: Wed Nov 22 11:43:03 2023 +0300
Change extension for verybasic to yavb
commit 22aaf2b2508278a9ffce525ffed52419bf72c23f
Merge: c326174f 61294c6e
Author: Владислав Крылов <krylov@sfedu.ru>
Date: Wed Nov 22 11:26:36 2023 +0300
Merge branch 'IndentArrangerTemp' into VeryBasicLanguage
commit 61294c6e7d405e1c68516cc81d3a109e8a1ee295
Author: MovchanGitHub <92666028+MovchanGitHub@users.noreply.github.com>
Date: Tue Nov 21 09:33:57 2023 +0300
Change Program Example
commit 86971488341ed6bf13c39e13a513d7ac0c51c285
Author: MovchanGitHub <92666028+MovchanGitHub@users.noreply.github.com>
Date: Tue Nov 21 09:28:59 2023 +0300
Add IndentArranger to Compile
commit c326174ff5ecccf9c4f76d58aea4653f047af049
Author: Владислав Крылов <krylov@sfedu.ru>
Date: Sun Nov 19 15:03:19 2023 +0300
Added UniversalParserHelper and GPPG
ShiftReduceParser moved to another project
UniversalParserHelper project added, most of it copied from SaushkinParser
commit 2ce50bbbf9db22c4243b247b870f6935ce206d26
Author: MovchanGitHub <92666028+MovchanGitHub@users.noreply.github.com>
Date: Fri Nov 17 10:00:31 2023 +0300
Add Semicolon After Each Statement
commit 796309d340e8d8ba730ff9418fa376f34fb7fd36
Author: MovchanGitHub <92666028+MovchanGitHub@users.noreply.github.com>
Date: Wed Nov 15 18:49:11 2023 +0300
Add Alpha Version of Python-style If-statement
commit 5eb88f946fe8254b4f5c5a56ed0a567b3be51227
Merge: ab4ce5b0 0162b637
Author: Владислав Крылов <krylov@sfedu.ru>
Date: Wed Nov 15 16:54:26 2023 +0300
Merge branch 'IndentArranger' into VeryBasicLanguage
commit ab4ce5b0e32d42004726e3dc45cc0d8a65e53f56
Author: Владислав Крылов <krylov@sfedu.ru>
Date: Wed Nov 15 16:53:51 2023 +0300
Fixes from seminar
commit af74012289d0d08517c13499a2a66cb543fc06d2
Author: Владислав Крылов <krylov@sfedu.ru>
Date: Sun Nov 12 18:57:29 2023 +0300
finally working!
fully implemented compatibility
commit 58a39c313324b468d1eec207ba3f5f6eddf74ef2
Author: Владислав Крылов <krylov@sfedu.ru>
Date: Sun Nov 12 11:58:16 2023 +0300
more compatibility with pabc
Now verybasic statements translate to pascal compiler
added .bat script for autobuilding verybasic
commit 0162b6376b8fe970704f26213bf9f0f670dfb12e
Author: MovchanGitHub <92666028+MovchanGitHub@users.noreply.github.com>
Date: Sun Nov 12 10:06:14 2023 +0300
Update test.txt
commit 1b759ab1cd7ee2ffeb14afff0418ddf0f30b0399
Author: MovchanGitHub <92666028+MovchanGitHub@users.noreply.github.com>
Date: Sun Nov 12 10:05:36 2023 +0300
Add Indent and Unindent Keywords to Generated File
commit cabda7f3985651cff2a8740f1a5bdea8fe7ac892
Author: MovchanGitHub <92666028+MovchanGitHub@users.noreply.github.com>
Date: Fri Nov 10 21:25:52 2023 +0300
Add Generation of Output File
commit 306f033d0176ab559e3622b8505427dbe2e0e203
Author: MovchanGitHub <92666028+MovchanGitHub@users.noreply.github.com>
Date: Thu Nov 9 20:42:18 2023 +0300
Update IndentArranger.sln
commit 60c0ceced1aa3a7d2e33de0facb1126e295f43b5
Author: MovchanGitHub <92666028+MovchanGitHub@users.noreply.github.com>
Date: Thu Nov 9 20:41:22 2023 +0300
Add IndentArranger
commit 1f4556c4573dae154fcc167b41ff6ab9e8122136
Author: Владислав Крылов <krylov@sfedu.ru>
Date: Mon Nov 6 22:48:36 2023 +0300
Made my own VeryBasicParser project
Copied some code from SaushkinParser
Tried to compile it and implement into Pascal
Doesn't work due to grammatik issue
* Changed GPPG project NET Framework version, added .dll to gitignore
* Adding UniversalParserHelper to project, trying to include VeryBasic
* Managed dependencies and got VeryBasicLanguage to work
* Change extension of a test program
* Rebuild changes
What should be added to .gitignore?
* Fix bug related to err0524_res_unit.pas
* Rebuild Parser
* Change Indent and Unindent tokens
* Add Symbol Table to ParserABC.y
* Change Indent Space Number to 2
* Add While Loop
* Add Some Operations to Parser
* Make Initialization Node at the Beginning of a program
* Create Grammar.txt
* Test program added
* Add ELIF and SyntaxHighlight
* Fix TableSymbol
* Add Division
* Add Method Call
* Add For Loop
* Rename SPython Parser Folder
* Add documented comments for CompileUnit method
* Added Errors to SPython
Added Errors.cs
Removed link to PABCSaushkinParser
Minor fixes
* Create default constructor for SourceContext
* Move null check of currentUnit to upper level in CompileUnit
* Move CreateMainFunction method from Compiler to TreeConverter class
* Add gppg and parserhelper to visualpascalabcnet dependencies
* Updated installer files to include GPPG and UniversalParserHelper
* Rename GPPG to ShiftReduceParser
* Fix ShiftReduceParser project dependencies
* Fix ShiftReduceParserDependencies second iteration
* Workaround commit
* Update PABCSystem after tests' changes
* Refactor ConvertDirectives method
* Get rid of legacy standard modules code
* Return varBeginOffset and beginOffset calculation to Compiler class
Размещение метода в SyntaxTreeToSemanticTreeConverter не целесообразно. В комментарии видимо имелось в виду что-то другое.
* Workaround commit
* Update PABCSystem after tests' changes
* Revert SPython changes
Оставляем только изменения связанные с рефакторингом.
* Update .gitignore
Co-authored-by: Sun Serega <sunserega2@gmail.com>
* Resolve a few Sun Serega treds
* Delete comments in ParsersController.cs
* Replace specific path with path variable in Studio.bat
* Add comment in Studio.bat file and return FileName in CompilerError.cs
* Fix TreeSubsidiary.cs encoding and sectCore.nsh indents
* Return old version of TestRunner.exe
* Rename some variables and polish a few methods
* Uncomment accidentally commented code
* Replace spaces with tabs
* Changed dll name from GPPG
* Revert "Changed dll name from GPPG"
This reverts commit c485cc8cb787809b7e9dfa8a361e75f17ed39893.
* Update .gitignore
* Delete Libraries/ShiftReduceParser.dll
* Delete bin\ShiftReduceParser.dll
* Replace tabs with spaces
* Update encoding in Studio.bat
* Fix bug with PABCrtl excluded files
* Refactor StandardModule class
* Add null checks to make debuging easier
* Delete unnecessary null checks in SymTable.cs
---------
Co-authored-by: Владислав Крылов <krylov@sfedu.ru>
Co-authored-by: MovchanGitHub <92666028+MovchanGitHub@users.noreply.github.com>
Co-authored-by: Sun Serega <sunserega2@gmail.com>
1017 lines
37 KiB
C#
1017 lines
37 KiB
C#
// Gardens Point Parser Generator
|
|
// Copyright (c) Wayne Kelly, QUT 2005-2009
|
|
// (see accompanying GPPGcopyright.rtf)
|
|
#define EXPORT_GPPG
|
|
using System;
|
|
using System.Text;
|
|
using System.Globalization;
|
|
using System.Collections.Generic;
|
|
using System.Runtime.Serialization;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using PascalABCCompiler.SyntaxTree;
|
|
using System.IO;
|
|
|
|
namespace QUT.Gppg
|
|
{
|
|
/// <summary>
|
|
/// Abstract class for GPPG shift-reduce parsers.
|
|
/// Parsers generated by GPPG derive from this base
|
|
/// class, overriding the abstract Initialize() and
|
|
/// DoAction() methods.
|
|
/// </summary>
|
|
/// <typeparam name="TValue">Semantic value type</typeparam>
|
|
/// <typeparam name="TSpan">Location type</typeparam>
|
|
#if EXPORT_GPPG
|
|
public abstract class ShiftReduceParser<TValue, TSpan>
|
|
#else
|
|
internal abstract class ShiftReduceParser<TValue, TSpan>
|
|
#endif
|
|
where TSpan : IMerge<TSpan>, new()
|
|
{
|
|
public AbstractScanner<TValue, TSpan> scanner;
|
|
/// <summary>
|
|
/// The abstract scanner for this parser.
|
|
/// </summary>
|
|
protected AbstractScanner<TValue, TSpan> Scanner {
|
|
get { return scanner; }
|
|
set { scanner = value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Constructor for base class
|
|
/// </summary>
|
|
/// <param name="scanner">Scanner instance for this parser</param>
|
|
protected ShiftReduceParser(AbstractScanner<TValue, TSpan> scanner)
|
|
{
|
|
this.scanner = scanner;
|
|
}
|
|
|
|
// ==============================================================
|
|
// TECHNICAL EXPLANATION.
|
|
// Why the next two fields are not exposed via properties.
|
|
// ==============================================================
|
|
// These fields are of the generic parameter types, and are
|
|
// frequently instantiated as struct types in derived classes.
|
|
// Semantic actions are defined in the derived classes and refer
|
|
// to instance fields of these structs. Is such cases the code
|
|
// "get_CurrentSemanticValue().myField = blah;" will fail since
|
|
// the getter pushes the value of the field, not the reference.
|
|
// So, in the presence of properties, gppg would need to encode
|
|
// such field accesses as ...
|
|
// "tmp = get_CurrentSemanticValue(); // Fetch value
|
|
// tmp.myField = blah; // update
|
|
// set_CurrentSemanticValue(tmp); " // Write update back.
|
|
// There is no issue if TValue is restricted to be a ref type.
|
|
// The same explanation applies to scanner.yylval.
|
|
// ==============================================================
|
|
/// <summary>
|
|
/// The current value of the "$$" symbolic variable in the parser
|
|
/// </summary>
|
|
[SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")]
|
|
protected TValue CurrentSemanticValue;
|
|
|
|
/// <summary>
|
|
/// The current value of the "@$" symbolic variable in the parser
|
|
/// </summary>
|
|
[SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")]
|
|
protected TSpan CurrentLocationSpan;
|
|
|
|
private TSpan LastSpan;
|
|
private int NextToken;
|
|
private State FsaState;
|
|
private bool recovering;
|
|
private int tokensSinceLastError;
|
|
|
|
private PushdownPrefixState<State> StateStack = new PushdownPrefixState<State>();
|
|
private PushdownPrefixState<TValue> valueStack = new PushdownPrefixState<TValue>();
|
|
private PushdownPrefixState<TSpan> locationStack = new PushdownPrefixState<TSpan>();
|
|
|
|
/// <summary>
|
|
/// The stack of semantic value (YYSTYPE) values.
|
|
/// </summary>
|
|
protected PushdownPrefixState<TValue> ValueStack { get { return valueStack; } }
|
|
|
|
/// <summary>
|
|
/// The stack of location value (YYLTYPE) varlues.
|
|
/// </summary>
|
|
protected PushdownPrefixState<TSpan> LocationStack { get { return locationStack; } }
|
|
|
|
private int errorToken;
|
|
private int endOfFileToken;
|
|
private string[] nonTerminals;
|
|
private State[] states;
|
|
private Rule[] rules;
|
|
|
|
/// <summary>
|
|
/// Initialization method to allow derived classes
|
|
/// to insert the rule list into this base class.
|
|
/// </summary>
|
|
/// <param name="rules">The array of Rule objects</param>
|
|
protected void InitRules(Rule[] rules) { this.rules = rules; }
|
|
|
|
/// <summary>
|
|
/// Initialization method to allow derived classes to
|
|
/// insert the states table into this base class.
|
|
/// </summary>
|
|
/// <param name="states">The pre-initialized states table</param>
|
|
protected void InitStates(State[] states) { this.states = states; }
|
|
|
|
/// <summary>
|
|
/// OBSOLETE FOR VERSION 1.4.0
|
|
/// </summary>
|
|
/// <param name="size"></param>
|
|
protected void InitStateTable(int size) { states = new State[size]; }
|
|
|
|
/// <summary>
|
|
/// Initialization method to allow derived classes
|
|
/// to insert the special value for the error and EOF tokens.
|
|
/// </summary>
|
|
/// <param name="err">The error state ordinal</param>
|
|
/// <param name="end">The EOF stat ordinal</param>
|
|
protected void InitSpecialTokens(int err, int end)
|
|
{
|
|
errorToken = err;
|
|
endOfFileToken = end;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initialization method to allow derived classes to
|
|
/// insert the non-terminal symbol names into this base class.
|
|
/// </summary>
|
|
/// <param name="names">Non-terminal symbol names</param>
|
|
protected void InitNonTerminals(string[] names) { nonTerminals = names; }
|
|
|
|
#region YYAbort, YYAccept etcetera.
|
|
[Serializable]
|
|
[SuppressMessage("Microsoft.Design", "CA1064:ExceptionsShouldBePublic")]
|
|
// Reason for FxCop message suppression -
|
|
// This exception cannot escape from the local context
|
|
private class AcceptException : Exception
|
|
{
|
|
internal AcceptException() { }
|
|
protected AcceptException(SerializationInfo i, StreamingContext c) : base(i, c) { }
|
|
}
|
|
[Serializable]
|
|
[SuppressMessage("Microsoft.Design", "CA1064:ExceptionsShouldBePublic")]
|
|
// Reason for FxCop message suppression -
|
|
// This exception cannot escape from the local context
|
|
private class AbortException : Exception
|
|
{
|
|
internal AbortException() { }
|
|
protected AbortException(SerializationInfo i, StreamingContext c) : base(i, c) { }
|
|
}
|
|
[Serializable]
|
|
[SuppressMessage("Microsoft.Design", "CA1064:ExceptionsShouldBePublic")]
|
|
// Reason for FxCop message suppression -
|
|
// This exception cannot escape from the local context
|
|
private class ErrorException : Exception
|
|
{
|
|
internal ErrorException() { }
|
|
protected ErrorException(SerializationInfo i, StreamingContext c) : base(i, c) { }
|
|
}
|
|
|
|
// The following methods are only called from within
|
|
// a semantic action. The thrown exceptions can never
|
|
// propagate outside the ShiftReduceParser class in
|
|
// which they are nested.
|
|
|
|
/// <summary>
|
|
/// Force parser to terminate, returning "true"
|
|
/// </summary>
|
|
protected static void YYAccept() { throw new AcceptException(); }
|
|
|
|
/// <summary>
|
|
/// Force parser to terminate, returning "false"
|
|
/// </summary>
|
|
protected static void YYAbort() { throw new AbortException(); }
|
|
|
|
/// <summary>
|
|
/// Force parser to terminate, returning
|
|
/// "false" if error recovery fails.
|
|
/// </summary>
|
|
protected static void YYError() { throw new ErrorException(); }
|
|
|
|
/// <summary>
|
|
/// Check if parser in error recovery state.
|
|
/// </summary>
|
|
protected bool YYRecovering { get { return recovering; } }
|
|
#endregion
|
|
|
|
/// <summary>
|
|
/// Abstract base method. ShiftReduceParser calls this
|
|
/// to initialize the base class data structures. Concrete
|
|
/// parser classes must override this method.
|
|
/// </summary>
|
|
protected abstract void Initialize();
|
|
|
|
/// <summary>
|
|
/// Main entry point of the Shift-Reduce Parser.
|
|
/// </summary>
|
|
/// <returns>True if parse succeeds, else false for
|
|
/// unrecoverable errors</returns>
|
|
public bool Parse()
|
|
{
|
|
Initialize(); // allow derived classes to instantiate rules, states and nonTerminals
|
|
|
|
NextToken = 0;
|
|
FsaState = states[0];
|
|
|
|
StateStack.Push(FsaState);
|
|
valueStack.Push(CurrentSemanticValue);
|
|
LocationStack.Push(CurrentLocationSpan);
|
|
|
|
while (true)
|
|
{
|
|
#if TRACE_ACTIONS
|
|
Console.Error.WriteLine("Entering state {0} ", FsaState.number);
|
|
#endif
|
|
int action = FsaState.defaultAction;
|
|
|
|
if (FsaState.ParserTable != null)
|
|
{
|
|
if (NextToken == 0)
|
|
{
|
|
#if TRACE_ACTIONS
|
|
Console.Error.Write("Reading a token: ");
|
|
#endif
|
|
// We save the last token span, so that the location span
|
|
// of production right hand sides that begin or end with a
|
|
// nullable production will be correct.
|
|
LastSpan = scanner.yylloc;
|
|
NextToken = scanner.yylex();
|
|
}
|
|
|
|
#if TRACE_ACTIONS
|
|
Console.Error.WriteLine("Next token is {0}", TerminalToString(NextToken));
|
|
#endif
|
|
int tmpAction = 0;
|
|
if (FsaState.ParserTable.TryGetValue(NextToken, out tmpAction))
|
|
action = tmpAction;
|
|
}
|
|
|
|
if (action > 0) // shift
|
|
{
|
|
Shift(action);
|
|
}
|
|
else if (action < 0) // reduce
|
|
{
|
|
try
|
|
{
|
|
Reduce(-action);
|
|
if (action == -1) // accept
|
|
return true;
|
|
}
|
|
catch (Exception x)
|
|
{
|
|
if (x is AbortException)
|
|
return false;
|
|
else if (x is AcceptException)
|
|
return true;
|
|
else if (x is ErrorException && !ErrorRecovery())
|
|
return false;
|
|
else
|
|
throw; // Rethrow x, preserving information.
|
|
|
|
}
|
|
}
|
|
else if (action == 0) // error
|
|
if (!ErrorRecovery())
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private void Shift(int stateIndex)
|
|
{
|
|
#if TRACE_ACTIONS
|
|
Console.Error.Write("Shifting token {0}, ", TerminalToString(NextToken));
|
|
#endif
|
|
FsaState = states[stateIndex];
|
|
|
|
valueStack.Push(scanner.yylval);
|
|
StateStack.Push(FsaState);
|
|
LocationStack.Push(scanner.yylloc);
|
|
|
|
if (recovering)
|
|
{
|
|
if (NextToken != errorToken)
|
|
tokensSinceLastError++;
|
|
|
|
if (tokensSinceLastError > 5)
|
|
recovering = false;
|
|
}
|
|
|
|
if (NextToken != endOfFileToken)
|
|
NextToken = 0;
|
|
}
|
|
|
|
private void Reduce(int ruleNumber)
|
|
{
|
|
#if TRACE_ACTIONS
|
|
DisplayRule(ruleNumber);
|
|
#endif
|
|
Rule rule = rules[ruleNumber];
|
|
//
|
|
// Default actions for unit productions.
|
|
//
|
|
if (rule.RightHandSide.Length == 1)
|
|
{
|
|
CurrentSemanticValue = valueStack.TopElement(); // Default action: $$ = $1;
|
|
CurrentLocationSpan = LocationStack.TopElement(); // Default action "@$ = @1;
|
|
}
|
|
else
|
|
{
|
|
if (rule.RightHandSide.Length == 0)
|
|
{
|
|
// Create a new blank value.
|
|
// Explicit semantic action may mutate this value
|
|
CurrentSemanticValue = default(TValue);
|
|
// The location span for an empty production will start with the
|
|
// beginning of the next lexeme, and end with the finish of the
|
|
// previous lexeme. This gives the correct behaviour when this
|
|
// nonsense value is used in later Merge operations.
|
|
CurrentLocationSpan = (scanner.yylloc != null && LastSpan != null ?
|
|
scanner.yylloc.Merge(LastSpan) :
|
|
default(TSpan));
|
|
}
|
|
else
|
|
{
|
|
// Default action: $$ = $1;
|
|
CurrentSemanticValue = valueStack.TopElement();
|
|
// Default action "@$ = @1.Merge(@N)" for location info.
|
|
TSpan at1 = LocationStack[LocationStack.Depth - rule.RightHandSide.Length];
|
|
TSpan atN = LocationStack[LocationStack.Depth - 1];
|
|
CurrentLocationSpan =
|
|
((at1 != null && atN != null) ? at1.Merge(atN) : default(TSpan));
|
|
}
|
|
}
|
|
|
|
DoAction(ruleNumber);
|
|
|
|
for (int i = 0; i < rule.RightHandSide.Length; i++)
|
|
{
|
|
StateStack.Pop();
|
|
valueStack.Pop();
|
|
LocationStack.Pop();
|
|
}
|
|
|
|
#if TRACE_ACTIONS
|
|
DisplayStack();
|
|
#endif
|
|
FsaState = StateStack.TopElement();
|
|
int fsaState;
|
|
|
|
if (FsaState.Goto.TryGetValue(rule.LeftHandSide, out fsaState))
|
|
FsaState = states[fsaState];
|
|
|
|
StateStack.Push(FsaState);
|
|
valueStack.Push(CurrentSemanticValue);
|
|
LocationStack.Push(CurrentLocationSpan);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Execute the selected action from array.
|
|
/// Must be overriden in derived classes.
|
|
/// </summary>
|
|
/// <param name="actionNumber">Index of the action to perform</param>
|
|
protected abstract void DoAction(int actionNumber);
|
|
|
|
private bool ErrorRecovery()
|
|
{
|
|
bool discard;
|
|
|
|
if (!recovering) // if not recovering from previous error
|
|
ReportError();
|
|
|
|
if (!FindErrorRecoveryState())
|
|
return false;
|
|
//
|
|
// The interim fix for the "looping in error recovery"
|
|
// artifact involved moving the setting of the recovering
|
|
// bool until after invalid tokens have been discarded.
|
|
//
|
|
ShiftErrorToken();
|
|
discard = DiscardInvalidTokens();
|
|
recovering = true;
|
|
tokensSinceLastError = 0;
|
|
return discard;
|
|
}
|
|
|
|
private void ReportError1()
|
|
{
|
|
StringBuilder errorMsg = new StringBuilder();
|
|
errorMsg.AppendFormat("Syntax error, unexpected {0}", TerminalToString(NextToken));
|
|
|
|
if (FsaState.ParserTable.Count < 7)
|
|
{
|
|
bool first = true;
|
|
foreach (int terminal in FsaState.ParserTable.Keys)
|
|
{
|
|
if (first)
|
|
errorMsg.Append(", expecting ");
|
|
else
|
|
errorMsg.Append(", or ");
|
|
|
|
errorMsg.Append(TerminalToString(terminal));
|
|
first = false;
|
|
}
|
|
}
|
|
scanner.yyerror(errorMsg.ToString());
|
|
}
|
|
|
|
private void ReportError()
|
|
{
|
|
object[] args = new object[FsaState.ParserTable.Keys.Count+1];
|
|
args[0] = TerminalToString(NextToken);
|
|
int i=1;
|
|
foreach (int terminal in FsaState.ParserTable.Keys)
|
|
{
|
|
args[i] = TerminalToString(terminal);
|
|
i++;
|
|
}
|
|
scanner.yyerror("",args);
|
|
}
|
|
|
|
private void ShiftErrorToken()
|
|
{
|
|
int old_next = NextToken;
|
|
NextToken = errorToken;
|
|
|
|
Shift(FsaState.ParserTable[NextToken]);
|
|
|
|
#if TRACE_ACTIONS
|
|
Console.Error.WriteLine("Entering state {0} ", FsaState.number);
|
|
#endif
|
|
NextToken = old_next;
|
|
}
|
|
|
|
private bool FindErrorRecoveryState()
|
|
{
|
|
while (true) // pop states until one found that accepts error token
|
|
{
|
|
if (FsaState.ParserTable != null &&
|
|
FsaState.ParserTable.ContainsKey(errorToken) &&
|
|
FsaState.ParserTable[errorToken] > 0) // shift
|
|
return true;
|
|
|
|
#if TRACE_ACTIONS
|
|
Console.Error.WriteLine("Error: popping state {0}", StateStack.TopElement().number);
|
|
#endif
|
|
StateStack.Pop();
|
|
valueStack.Pop();
|
|
LocationStack.Pop();
|
|
|
|
#if TRACE_ACTIONS
|
|
DisplayStack();
|
|
#endif
|
|
if (StateStack.IsEmpty())
|
|
{
|
|
#if TRACE_ACTIONS
|
|
Console.Error.Write("Aborting: didn't find a state that accepts error token");
|
|
#endif
|
|
return false;
|
|
}
|
|
else
|
|
FsaState = StateStack.TopElement();
|
|
}
|
|
}
|
|
|
|
private bool DiscardInvalidTokens()
|
|
{
|
|
|
|
int action = FsaState.defaultAction;
|
|
|
|
if (FsaState.ParserTable != null)
|
|
{
|
|
// Discard tokens until find one that works ...
|
|
while (true)
|
|
{
|
|
if (NextToken == 0)
|
|
{
|
|
#if TRACE_ACTIONS
|
|
Console.Error.Write("Reading a token: ");
|
|
#endif
|
|
NextToken = scanner.yylex();
|
|
}
|
|
|
|
#if TRACE_ACTIONS
|
|
Console.Error.WriteLine("Next token is {0}", TerminalToString(NextToken));
|
|
#endif
|
|
if (NextToken == endOfFileToken)
|
|
return false;
|
|
|
|
if (FsaState.ParserTable.ContainsKey(NextToken))
|
|
action = FsaState.ParserTable[NextToken];
|
|
|
|
if (action != 0)
|
|
return true;
|
|
else
|
|
{
|
|
#if TRACE_ACTIONS
|
|
Console.Error.WriteLine("Error: Discarding {0}", TerminalToString(NextToken));
|
|
#endif
|
|
NextToken = 0;
|
|
}
|
|
}
|
|
}
|
|
else if (recovering && tokensSinceLastError == 0)
|
|
{
|
|
//
|
|
// Boolean recovering is not set until after the first
|
|
// error token has been shifted. Thus if we get back
|
|
// here with recovering set and no tokens read we are
|
|
// looping on the same error recovery action. This
|
|
// happens if current_state.ParserTable is null because
|
|
// the state has an LR(0) reduction, but not all
|
|
// lookahead tokens are valid. This only occurs for
|
|
// error productions that *end* on "error".
|
|
//
|
|
// This action discards tokens one at a time until
|
|
// the looping stops. Another attack would be to always
|
|
// use the LALR(1) table if a production ends on "error"
|
|
//
|
|
#if TRACE_ACTIONS
|
|
Console.Error.WriteLine("Error: panic discard of {0}", TerminalToString(NextToken));
|
|
#endif
|
|
if (NextToken == endOfFileToken)
|
|
return false;
|
|
NextToken = 0;
|
|
return true;
|
|
}
|
|
else
|
|
return true;
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Traditional YACC method. Discards the next input token.
|
|
/// </summary>
|
|
[SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "yyclearin")]
|
|
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "yyclearin")]
|
|
// Reason for FxCop message suppression -
|
|
// This is a traditional name for YACC-like functionality
|
|
protected void yyclearin() { NextToken = 0; }
|
|
|
|
/// <summary>
|
|
/// Tradional YACC method. Clear the "recovering" flag.
|
|
/// </summary>
|
|
[SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "yyerrok")]
|
|
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "yyerrok")]
|
|
// Reason for FxCop message suppression -
|
|
// This is a traditional name for YACC-like functionality
|
|
protected void yyerrok()
|
|
{
|
|
recovering = false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// OBSOLETE FOR VERSION 1.4.0
|
|
/// Method used by derived types to insert new
|
|
/// state instances in the "states" array.
|
|
/// </summary>
|
|
/// <param name="stateNumber">index of the state</param>
|
|
/// <param name="state">data for the state</param>
|
|
protected void AddState(int stateNumber, State state)
|
|
{
|
|
states[stateNumber] = state;
|
|
state.number = stateNumber;
|
|
}
|
|
|
|
private void DisplayStack()
|
|
{
|
|
Console.Error.Write("State now");
|
|
for (int i = 0; i < StateStack.Depth; i++)
|
|
Console.Error.Write(" {0}", StateStack[i].number);
|
|
Console.Error.WriteLine();
|
|
}
|
|
|
|
protected void DisplayRule(int ruleNumber)
|
|
{
|
|
Console.Error.Write("Reducing stack by rule {0}, ", ruleNumber);
|
|
DisplayProduction(rules[ruleNumber]);
|
|
}
|
|
|
|
private void DisplayProduction(Rule rule)
|
|
{
|
|
if (rule.RightHandSide.Length == 0)
|
|
Console.Error.Write("/* empty */ ");
|
|
else
|
|
foreach (int symbol in rule.RightHandSide)
|
|
Console.Error.Write("{0} ", SymbolToString(symbol));
|
|
|
|
Console.Error.WriteLine("-> {0}", SymbolToString(rule.LeftHandSide));
|
|
}
|
|
|
|
protected void DisplayRuleMy(int ruleNumber)
|
|
{
|
|
Console.Error.Write("{0,3}: ", ruleNumber);
|
|
DisplayProductionMy(rules[ruleNumber]);
|
|
}
|
|
|
|
private void DisplayProductionMy(Rule rule)
|
|
{
|
|
Console.Error.Write("{0} ::= ", SymbolToString(rule.LeftHandSide));
|
|
if (rule.RightHandSide.Length == 0)
|
|
Console.Error.Write("/* empty */ ");
|
|
else
|
|
foreach (int symbol in rule.RightHandSide)
|
|
Console.Error.Write("{0} ", SymbolToString(symbol));
|
|
Console.Error.WriteLine();
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Abstract state class naming terminal symbols.
|
|
/// This is overridden by derived classes with the
|
|
/// name (or alias) to be used in error messages.
|
|
/// </summary>
|
|
/// <param name="terminal">The terminal ordinal</param>
|
|
/// <returns></returns>
|
|
protected abstract string TerminalToString(int terminal);
|
|
|
|
private string SymbolToString(int symbol)
|
|
{
|
|
if (symbol < 0)
|
|
return nonTerminals[-symbol-1];
|
|
else
|
|
return TerminalToString(symbol);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Return text representation of argument character
|
|
/// </summary>
|
|
/// <param name="input">The character to convert</param>
|
|
/// <returns>String representation of the character</returns>
|
|
protected static string CharToString(char input)
|
|
{
|
|
switch (input)
|
|
{
|
|
case '\a': return @"'\a'";
|
|
case '\b': return @"'\b'";
|
|
case '\f': return @"'\f'";
|
|
case '\n': return @"'\n'";
|
|
case '\r': return @"'\r'";
|
|
case '\t': return @"'\t'";
|
|
case '\v': return @"'\v'";
|
|
case '\0': return @"'\0'";
|
|
default: return string.Format(CultureInfo.InvariantCulture, "'{0}'", input);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Classes implementing this interface must supply a
|
|
/// method that merges two location objects to return
|
|
/// a new object of the same type.
|
|
/// GPPG-generated parsers have the default location
|
|
/// action equivalent to "@$ = @1.Merge(@N);" where N
|
|
/// is the right-hand-side length of the production.
|
|
/// </summary>
|
|
/// <typeparam name="TSpan">The Location type</typeparam>
|
|
#if EXPORT_GPPG
|
|
public interface IMerge<TSpan>
|
|
#else
|
|
internal interface IMerge<TSpan>
|
|
#endif
|
|
{
|
|
/// <summary>
|
|
/// Interface method that creates a location object from
|
|
/// the current and last object. Typically used to create
|
|
/// a location object extending from the start of the @1
|
|
/// object to the end of the @N object.
|
|
/// </summary>
|
|
/// <param name="last">The lexically last object to merge</param>
|
|
/// <returns>The merged location object</returns>
|
|
TSpan Merge(TSpan last);
|
|
}
|
|
|
|
/// <summary>
|
|
/// This is the default class that carries location
|
|
/// information from the scanner to the parser.
|
|
/// If you don't declare "%YYLTYPE Foo" the parser
|
|
/// will expect to deal with this type.
|
|
/// </summary>
|
|
#if EXPORT_GPPG
|
|
public class LexLocation : IMerge<LexLocation>
|
|
#else
|
|
[SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses")]
|
|
internal class LexLocation : IMerge<LexLocation>
|
|
#endif
|
|
{
|
|
private int startLine; // start line
|
|
private int startColumn; // start column
|
|
private int endLine; // end line
|
|
private int endColumn; // end column
|
|
private string filename;
|
|
|
|
/// <summary>
|
|
/// The line at which the text span starts.
|
|
/// </summary>
|
|
public int StartLine { get { return startLine; } }
|
|
|
|
/// <summary>
|
|
/// The column at which the text span starts.
|
|
/// </summary>
|
|
public int StartColumn { get { return startColumn; } }
|
|
|
|
/// <summary>
|
|
/// The line on which the text span ends.
|
|
/// </summary>
|
|
public int EndLine { get { return endLine; } }
|
|
|
|
/// <summary>
|
|
/// The column of the first character
|
|
/// beyond the end of the text span.
|
|
/// </summary>
|
|
public int EndColumn { get { return endColumn; } }
|
|
|
|
/// <summary>
|
|
/// Default no-arg constructor.
|
|
/// </summary>
|
|
public LexLocation()
|
|
{ }
|
|
|
|
/// <summary>
|
|
/// Constructor for text-span with given start and end.
|
|
/// </summary>
|
|
/// <param name="sl">start line</param>
|
|
/// <param name="sc">start column</param>
|
|
/// <param name="el">end line </param>
|
|
/// <param name="ec">end column</param>
|
|
public LexLocation(int sl, int sc, int el, int ec, string fname = null)
|
|
{ startLine = sl; startColumn = sc; endLine = el; endColumn = ec; filename = fname; }
|
|
|
|
/// <summary>
|
|
/// Create a text location which spans from the
|
|
/// start of "this" to the end of the argument "last"
|
|
/// </summary>
|
|
/// <param name="last">The last location in the result span</param>
|
|
/// <returns>The merged span</returns>
|
|
public LexLocation Merge(LexLocation last)
|
|
{
|
|
string n_file_name = null;
|
|
if ((last.filename == null))
|
|
n_file_name = this.filename;
|
|
else
|
|
if ((this.filename == null))
|
|
n_file_name = last.filename;
|
|
else
|
|
n_file_name = this.filename;
|
|
return new LexLocation(this.startLine, this.startColumn, last.endLine, last.endColumn, n_file_name);
|
|
}
|
|
|
|
public static implicit operator SourceContext(LexLocation loc)
|
|
{
|
|
if (loc == null)
|
|
return null;
|
|
var sc = new SourceContext(loc.startLine, loc.startColumn + 1, loc.endLine, loc.endColumn, loc.filename);
|
|
return sc;
|
|
}
|
|
|
|
public static LexLocation MergeAll(params LexLocation[] locs)
|
|
{
|
|
// первая - корректная
|
|
var l = locs[0];
|
|
for (int i = 1; i < locs.Length; i++)
|
|
{
|
|
var endLine = locs[i].endLine;
|
|
var endColumn = locs[i].endColumn;
|
|
if (endLine > l.endLine || (endLine == l.endLine && endColumn > l.endColumn))
|
|
{
|
|
l.endLine = endLine;
|
|
l.endColumn = endColumn;
|
|
}
|
|
}
|
|
return l;
|
|
}
|
|
|
|
public static LexLocation MergeAll1(params LexLocation[] locs)
|
|
{
|
|
// сливаем все до последней корректной
|
|
var ind = -1;
|
|
for (int i = locs.Length - 1; i >= 0; i--)
|
|
{
|
|
var l = locs[i];
|
|
if (l.endLine > l.startLine || (l.endLine == l.startLine && l.endColumn >= l.startColumn))
|
|
{
|
|
ind = i;
|
|
break;
|
|
}
|
|
}
|
|
return new LexLocation(locs[0].startLine, locs[0].startColumn, locs[ind].endLine, locs[ind].endColumn); ;
|
|
}
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Abstract scanner class that GPPG expects its scanners to
|
|
/// extend.
|
|
/// </summary>
|
|
/// <typeparam name="TValue">Semantic value type YYSTYPE</typeparam>
|
|
/// <typeparam name="TSpan">Source location type YYLTYPE</typeparam>
|
|
#if EXPORT_GPPG
|
|
public abstract class AbstractScanner<TValue, TSpan>
|
|
#else
|
|
internal abstract class AbstractScanner<TValue, TSpan>
|
|
#endif
|
|
where TSpan : IMerge<TSpan>
|
|
{
|
|
/// <summary>
|
|
/// Lexical value optionally set by the scanner. The value
|
|
/// is of the %YYSTYPE type declared in the parser spec.
|
|
/// </summary>
|
|
[SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")]
|
|
[SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "yylval")]
|
|
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "yylval")]
|
|
// Reason for FxCop message suppression -
|
|
// This is a traditional name for YACC-like functionality
|
|
// A field must be declared for this value of parametric type,
|
|
// since it may be instantiated by a value struct. If it were
|
|
// implemented as a property, machine generated code in derived
|
|
// types would not be able to select on the returned value.
|
|
public TValue yylval; // Lexical value: set by scanner
|
|
|
|
/// <summary>
|
|
/// Current scanner location property. The value is of the
|
|
/// type declared by %YYLTYPE in the parser specification.
|
|
/// </summary>
|
|
[SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "yylloc")]
|
|
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "yylloc")]
|
|
// Reason for FxCop message suppression -
|
|
// This is a traditional name for YACC-like functionality
|
|
public virtual TSpan yylloc
|
|
{
|
|
get { return default(TSpan); } // Empty implementation allowing
|
|
set { /* skip */ } // yylloc to be ignored entirely.
|
|
}
|
|
|
|
/// <summary>
|
|
/// Main call point for LEX-like scanners. Returns an int
|
|
/// corresponding to the token recognized by the scanner.
|
|
/// </summary>
|
|
/// <returns>An int corresponding to the token</returns>
|
|
[SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "yylex")]
|
|
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "yylex")]
|
|
// Reason for FxCop message suppression -
|
|
// This is a traditional name for YACC-like functionality
|
|
public abstract int yylex();
|
|
|
|
/// <summary>
|
|
/// Traditional error reporting provided by LEX-like scanners
|
|
/// to their YACC-like clients.
|
|
/// </summary>
|
|
/// <param name="format">Message format string</param>
|
|
/// <param name="args">Optional array of args</param>
|
|
[SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "yyerror")]
|
|
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "yyerror")]
|
|
// Reason for FxCop message suppression -
|
|
// This is a traditional name for YACC-like functionality
|
|
public virtual void yyerror(string format, params object[] args) { }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Encapsulated state for the parser.
|
|
/// Opaque to users, visible to the tool-generated code.
|
|
/// </summary>
|
|
#if EXPORT_GPPG
|
|
public class State
|
|
#else
|
|
internal class State
|
|
#endif
|
|
{
|
|
internal int number;
|
|
internal Dictionary<int, int> ParserTable; // Terminal -> ParseAction
|
|
internal Dictionary<int, int> Goto; // NonTerminal -> State;
|
|
internal int defaultAction; // = 0; // ParseAction
|
|
|
|
/// <summary>
|
|
/// State transition data for this state. Pairs of elements of the
|
|
/// goto array associate symbol ordinals with next state indices.
|
|
/// The actions array is passed to another constructor.
|
|
/// </summary>
|
|
/// <param name="actions">The action list</param>
|
|
/// <param name="goToList">Next state data</param>
|
|
public State(int[] actions, int[] goToList)
|
|
: this(actions)
|
|
{
|
|
Goto = new Dictionary<int, int>();
|
|
for (int i = 0; i < goToList.Length; i += 2)
|
|
Goto.Add(goToList[i], goToList[i + 1]);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Action data for this state. Pairs of elements of the
|
|
/// action array associate action ordinals with each of
|
|
/// those symbols that have actions in the current state.
|
|
/// </summary>
|
|
/// <param name="actions">The action array</param>
|
|
public State(int[] actions)
|
|
{
|
|
ParserTable = new Dictionary<int, int>();
|
|
for (int i = 0; i < actions.Length; i += 2)
|
|
ParserTable.Add(actions[i], actions[i + 1]);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set the default action for this state.
|
|
/// </summary>
|
|
/// <param name="defaultAction">Ordinal of the default action</param>
|
|
public State(int defaultAction)
|
|
{
|
|
this.defaultAction = defaultAction;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set the default action and the state transition table.
|
|
/// </summary>
|
|
/// <param name="defaultAction">The default action</param>
|
|
/// <param name="goToList">Transitions from this state</param>
|
|
public State(int defaultAction, int[] goToList)
|
|
: this(defaultAction)
|
|
{
|
|
Goto = new Dictionary<int, int>();
|
|
for (int i = 0; i < goToList.Length; i += 2)
|
|
Goto.Add(goToList[i], goToList[i + 1]);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Rule representation at runtime.
|
|
/// </summary>
|
|
#if EXPORT_GPPG
|
|
public class Rule
|
|
#else
|
|
internal class Rule
|
|
#endif
|
|
{
|
|
internal int LeftHandSide; // symbol
|
|
internal int[] RightHandSide; // symbols
|
|
|
|
/// <summary>
|
|
/// Rule constructor. This holds the ordinal of
|
|
/// the left hand side symbol, and the list of
|
|
/// right hand side symbols, in lexical order.
|
|
/// </summary>
|
|
/// <param name="left">The LHS non-terminal</param>
|
|
/// <param name="right">The RHS symbols, in lexical order</param>
|
|
public Rule(int left, int[] right)
|
|
{
|
|
this.LeftHandSide = left;
|
|
this.RightHandSide = right;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Stack utility for the shift-reduce parser.
|
|
/// GPPG parsers have three instances:
|
|
/// (1) The parser state stack, T = QUT.Gppg.State,
|
|
/// (2) The semantic value stack, T = TValue,
|
|
/// (3) The location stack, T = TSpan.
|
|
/// </summary>
|
|
/// <typeparam name="T"></typeparam>
|
|
#if EXPORT_GPPG
|
|
public class PushdownPrefixState<T>
|
|
#else
|
|
internal class PushdownPrefixState<T>
|
|
#endif
|
|
{
|
|
// Note that we cannot use the BCL Stack<T> class
|
|
// here as derived types need to index into stacks.
|
|
//
|
|
private T[] array = new T[256];
|
|
private int tos = 0;
|
|
|
|
/// <summary>
|
|
/// Indexer for values of the stack below the top.
|
|
/// </summary>
|
|
/// <param name="index">index of the element, starting from the bottom</param>
|
|
/// <returns>the selected element</returns>
|
|
public T this[int index] { get { return array[index]; } }
|
|
|
|
/// <summary>
|
|
/// The current depth of the stack.
|
|
/// </summary>
|
|
public int Depth { get { return tos; } }
|
|
|
|
internal void Push(T value)
|
|
{
|
|
if (tos >= array.Length)
|
|
{
|
|
T[] newarray = new T[array.Length * 2];
|
|
System.Array.Copy(array, newarray, tos);
|
|
array = newarray;
|
|
}
|
|
array[tos++] = value;
|
|
}
|
|
|
|
internal T Pop()
|
|
{
|
|
T rslt = array[--tos];
|
|
array[tos] = default(T);
|
|
return rslt;
|
|
}
|
|
|
|
internal T TopElement() { return array[tos - 1]; }
|
|
|
|
internal bool IsEmpty() { return tos == 0; }
|
|
}
|
|
} |