📄 parser.cs
字号:
//-----------------------------------------------------------------------------
// Manual Recursive Descent parser
// We can get away with this because the vast majority of
// Blue's syntax is predictive. We always know what we're looking for.
// The only semi-complicated thing is arithmetic expressions.
//
// Look at the ILexer interface to know what the parser has to deal with here.
// The parser can only get the current token (which consumes that token),
// and peek at the next token (which doesn't consume the token).
//
// Generally, by using ILexer.Peek alot, we can see what's coming in the
// token stream and know what to expect. Thus we can delegate to the proper
// parse function.
//-----------------------------------------------------------------------------
using System.Diagnostics;
using System.Collections;
using AST;
//using ErrorLog = Blue.Utilities.ErrorLog;
using Log = Blue.Log;
using IParser = Blue.Public.IParser;
using ILexer = Blue.Public.ILexer;
namespace ManualParser
{
#region Helper classes
#if false
//-----------------------------------------------------------------------------
// Explanation of how we do Expression parsing
//-----------------------------------------------------------------------------
Class to help parse a Left-Associative binary expression
E -> E + T | T
But that can't be parsed recursive descent because it's infinite recursion
So we transform it into:
E -> T1 E'
E' -> + T2 E' | {}
The transformation preserves meaning and can be done recursive descent.
Now this still produces a right-recursive tree, we have to create a left recursive tree.
So when we parse E', we pass in T1 from the parent. And then E' forms a BinaryOp
of T1+T2 and passes that into it's E'.
This builds a left-recursive AST. E' then propagates the tree up through return values.
#endif
#endregion
//-----------------------------------------------------------------------------
// Manual Parser
//-----------------------------------------------------------------------------
/// <summary>
/// The primary parser implementation. Convert a token stream for a source file
/// obtained by an <see cref="Blue.Public.ILexer"/> into a parse tree.
/// Parse errors are handled by throwing <see cref="ManualParser.Parser.ParserErrorException"/>
/// </summary>
public class Parser : IParser
{
#region Construction
//-----------------------------------------------------------------------------
// Initialize with a lexer to provide the token stream
//-----------------------------------------------------------------------------
public Parser(ILexer lexer)
{
m_lexer = lexer;
}
#endregion
protected ILexer m_lexer;
#region Errors
//-----------------------------------------------------------------------------
// When we get a parser error, we want to throw an exception
// This is really just a way to handle control flow within the parser. This
// exception should never get out of the parser.
//-----------------------------------------------------------------------------
/// <summary>
/// ParserErrorException represents parse errors due to bad user-input.
/// </summary>
private class ParserErrorException : ErrorException
{
internal ParserErrorException(Parser.Code c, FileRange location, string s) :
base (c, location, s)
{
// All Parser errors will come through this body.
}
}
//-----------------------------------------------------------------------------
// Error handling
//-----------------------------------------------------------------------------
/// <summary>
/// Syntax error codes specific to Parser
/// </summary>
internal enum Code
{
cUnexpectedToken, // we don't know what we wanted, but we didn't like what we got
cExpectedDiffToken, // we know what we wanted and didn't get it
// cIllegalClassMemberDecl, // We're trying to declare something illegal in our class
cMissingRetType, // something bad about the ctor declaration
cBadLabelDef, // supposed to define a label (really a misuse of a ':')
cAccessorAlreadyDefined, // Property accessor (get | set) already defined
cMissingAccessor, // Property/Indexer must have at least 1 accessor
cNoAbstractCtor, // Constructors can't be abstract
cBadCtorChain, // tried to chain to other than 'this' or 'base'
cNoChainForStaticCtor, // static ctors can't be chained,
cNoBaseChainForStructs, // Struct ctor decls can't chain to a base
cNoDefaultCtorForStructs, // structs can't have a default ctor
cExpectedStatementExp, // Expected a statement expression
cBadForInitializer, // the initializer in the for-loop is bad
cDuplicateModifier, // Modifiers can't be duplicated
cIllegalModifiers, // modifiers on something it shouldn't be on
cNoCtorOnInterface, // Intefaces can't define constructors
cBadModsOnOps, // Overloaded operators must be public & static
cBadParamListOnOps, // Bad parameter list for an overloaded operator
}
// Convenience helper so that we don't have to keep qualifying StdErrorLog.
static private void ThrowError(ParserErrorException e)
{
Blue.Driver.StdErrorLog.ThrowError(e);
}
static private void PrintError(ParserErrorException e)
{
Blue.Driver.StdErrorLog.PrintError(e);
}
// Perhaps the most general syntax error
// We don't know what we wanted, but we didn't like what we got
//protected void ThrowError_UnexpectedToken(Token tokenActual)
ParserErrorException E_UnexpectedToken(Token tokenActual)
{
return new ParserErrorException(
Code.cUnexpectedToken,
tokenActual.Location,
"Unexpected token '"+tokenActual.ToString() + "'"
);
}
// We expected a specific type of token and we got something else
//protected void ThrowError_UnexpectedToken(Token tokenActual, Token.Type typeExpected)
ParserErrorException E_UnexpectedToken(Token tokenActual, Token.Type typeExpected)
{
return new ParserErrorException(
Code.cExpectedDiffToken,
tokenActual.Location,
"Expected '" + typeExpected.ToString() + "', but got token '"+tokenActual.ToString() + "'"
);
}
// We expected one token out of a possible set, but got something different
//protected void ThrowError_UnexpectedToken(Token tokenActual, Token.Type [] arTypeExpected)
ParserErrorException E_UnexpectedToken(Token tokenActual, Token.Type e1, Token.Type e2)
{
return E_UnexpectedToken(tokenActual, new Token.Type[] {e1, e2} );
}
ParserErrorException E_UnexpectedToken(Token tokenActual, Token.Type e1, Token.Type e2, Token.Type e3)
{
return E_UnexpectedToken(tokenActual, new Token.Type[] {e1, e2, e3} );
}
ParserErrorException E_UnexpectedToken(Token tokenActual, Token.Type [] arTypeExpected)
{
string stExpected = "";
foreach(Token.Type t in arTypeExpected)
{
stExpected += t.ToString() + " ";
}
return new ParserErrorException(
Code.cExpectedDiffToken,
tokenActual.Location,
"Expected one of {" + stExpected + "}, but got token '"+tokenActual.ToString() + "'"
);
}
// Bad chain target for a constructor
//protected void ThrowError_BadCtorChain(Identifier idName)
ParserErrorException E_BadCtorChain(Identifier idName)
{
return new ParserErrorException(
Code.cBadCtorChain,
idName.Location,
"Must specify either 'this' or 'base' in constructor chain, not '" + idName.Text + "'"
);
}
// Static constructors can't be chained.
ParserErrorException E_NoChainForStaticCtor(FileRange location)
{
return new ParserErrorException(
Code.cNoChainForStaticCtor,
location,
"Static constructors can't chain to another constructor"
);
}
// fIsGet - true if a 'get' accessor, false if a 'set accessor
// idName - name of the culrprit property
//protected void ThrowError_AccessorAlreadyDefined(Identifier idName, bool fIsGet)
ParserErrorException E_AccessorAlreadyDefined(Identifier idName, bool fIsGet)
{
return new ParserErrorException(
Code.cAccessorAlreadyDefined,
idName.Location,
"Property '" + idName.Text + "' can't have multiple '" + (fIsGet ? "get" : "set") + "' accessors."
);
}
//protected void ThrowError_MissingAccessor(Identifier idName)
ParserErrorException E_MissingAccessor(Identifier idName)
{
return new ParserErrorException(
Code.cMissingAccessor,
idName.Location,
"Property or Indexer '" + idName.Text + "' must have at least 1 accessor."
);
}
ParserErrorException E_NoAbstractCtor(Identifier idName)
//protected void ThrowError_NoAbstractCtor(Identifier idName)
{
return new ParserErrorException(
Code.cNoAbstractCtor,
idName.Location,
"Constructors can't be abstract and must have a body"
);
}
// Check for the corresponding error
protected void CheckError_UnexpectedToken(Token tokenActual, Token.Type [] arTypeExpected)
{
foreach(Token.Type t in arTypeExpected)
{
if (tokenActual.TokenType == t)
return;
}
ThrowError(E_UnexpectedToken(tokenActual, arTypeExpected));
//ThrowError_UnexpectedToken(tokenActual, arTypeExpected);
}
// Check that eActual only has the modifiers from a certain set (eAllowed),
// If not, print an error listing all illegal modifiers
protected void CheckError_LegalModifiers(
FileRange location,
string stTargetHint, // "class", "method", "property", "interface", etc...
AST.Modifiers eActual,
AST.Modifiers eAllowed
)
{
if (eActual != eAllowed)
ThrowError(
new ParserErrorException(
Code.cIllegalModifiers,
location,
"Illegal modifiers on a '" + stTargetHint + "'"
)
);
/*
// Check if Actual has any bits that aren't set in Allowed
AST.Modifiers.EFlags eDiff = eActual.Flags & ~eAllowed.Flags;
// If no difference, then no error
if (eDiff == Modifiers.EFlags.None)
return;
// We do have an error, so print out all flags
AST.Modifiers modDiff = new Modifiers(eDiff);
string st = modDiff.ToString();
ThrowError(
Code.cIllegalModifiers,
location,
"Modifiers '" + st + "' are not allowed on a '" + stTargetHint + "'"
);
*/
}
// Expected a statement expression
//protected void ThrowError_ExpectedStatementExp(FileRange location)
ParserErrorException E_ExpectedStatementExp(FileRange location)
{
return new ParserErrorException(
Code.cExpectedStatementExp,
location,
"Expected a statement expression"
);
}
//protected void ThrowError_BadForLoopInit(FileRange location)
ParserErrorException E_BadForLoopInit(FileRange location)
{
return new ParserErrorException(
Code.cBadForInitializer,
location,
"The initializer in a for-loop must be either an expression-statement or a variable declaration");
}
// The given modifier appears multiple times. Modifiers can only appear once.
/*
protected void ThrowError_DuplicateModifier(FileRange location, AST.Modifiers.EFlags eFlag)
{
ThrowError(
Code.cDuplicateModifier,
location,
"Duplicate '" + eFlag.ToString() + "' modifier.");
}
*/
//protected void ThrowError_NoBaseChainForStructs(FileRange location)
ParserErrorException E_NoBaseChainForStructs(FileRange location)
{
return new ParserErrorException(
Code.cNoBaseChainForStructs,
location,
"Constructors in Struct declarations can't chain to the base class");
}
//protected void ThrowError_NoCtorOnInterface(FileRange location)
ParserErrorException E_NoCtorOnInterface(FileRange location)
{
return new ParserErrorException(
Code.cNoCtorOnInterface,
location,
"Interfaces can't define constructors"
);
}
//protected void ThrowError_NoDefaultCtorForStructs(FileRange location)
ParserErrorException E_NoDefaultCtorForStructs(FileRange location)
{
return new ParserErrorException(
Code.cNoDefaultCtorForStructs,
location,
"Structs can't have default constructors"
);
}
ParserErrorException E_BadModsOnOps(FileRange location)
{
return new ParserErrorException(
Code.cBadModsOnOps,
location,
"Overloaded operators must only have 'public' and 'static' modifiers"
);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -