// The Nova Project by Ken Beckett.
// Copyright (C) 2007-2012 Inevitable Software, all rights reserved.
// Released under the Common Development and Distribution License, CDDL-1.0: http://opensource.org/licenses/cddl1.php
using System.Collections.Generic;
using Nova.Parsing;
using Nova.Rendering;
namespace Nova.CodeDOM
{
/// <summary>
/// Represents a user-overloaded operator.
/// Conversion operators use the derived class <see cref="ConversionOperatorDecl"/>.
/// </summary>
/// <remarks>
/// All overloaded operators must be public and static.
/// Overloaded unary operators have a single parameter which must be of the containing type.
/// Overloaded binary operators have two parameters, at least one of which must be of the containing type.
/// </remarks>
public class OperatorDecl : MethodDecl
{
#region /* FIELDS */
protected string _symbol; // The overloaded operator's symbol (used for rendering)
#endregion
#region /* CONSTRUCTORS */
/// <summary>
/// Create an <see cref="OperatorDecl"/> for the specified operator symbol, return type, and modifiers.
/// </summary>
public OperatorDecl(string symbol, Expression returnType, Modifiers modifiers, CodeObject body, params ParameterDecl[] parameters)
: base(GetOperatorInternalName(symbol, (parameters != null ? parameters.Length : 0)), returnType, modifiers, body, parameters)
{
_symbol = symbol;
}
/// <summary>
/// Create an <see cref="OperatorDecl"/> for the specified operator symbol, return type, and modifiers.
/// </summary>
public OperatorDecl(string symbol, Expression returnType, Modifiers modifiers, params ParameterDecl[] parameters)
: this(symbol, returnType, modifiers, new Block(), parameters)
{ }
#endregion
#region /* PROPERTIES */
/// <summary>
/// The keyword associated with the <see cref="Statement"/>.
/// </summary>
public override string Keyword
{
get { return ParseToken; }
}
/// <summary>
/// The associated operator symbol.
/// </summary>
public string Symbol
{
get { return _symbol; }
}
#endregion
#region /* METHODS */
/// <summary>
/// Create a reference to the <see cref="OperatorDecl"/>.
/// </summary>
/// <param name="isFirstOnLine">True if the reference should be displayed on a new line.</param>
/// <returns>An <see cref="OperatorRef"/>.</returns>
public override SymbolicRef CreateRef(bool isFirstOnLine)
{
return new OperatorRef(this, isFirstOnLine);
}
/// <summary>
/// Get the full name of the <see cref="INamedCodeObject"/>, including any namespace name.
/// </summary>
/// <param name="descriptive">True to display type parameters and method parameters, otherwise false.</param>
public override string GetFullName(bool descriptive)
{
string name = ParseToken + " " + _symbol;
if (descriptive)
name += GetParametersAsString();
if (_parent is TypeDecl)
name = ((TypeDecl)_parent).GetFullName(descriptive) + "." + name;
return name;
}
#endregion
#region /* PARSING */
/// <summary>
/// The token used to parse the code object.
/// </summary>
public const string ParseToken = "operator";
internal static new void AddParsePoints()
{
// Operator declarations are only valid with a TypeDecl parent, but we'll allow any IBlock so that we can
// properly parse them if they accidentally end up at the wrong level (only to flag them as errors).
// This also allows for them to be embedded in a DocCode object.
Parser.AddParsePoint(ParseToken, Parse, typeof(IBlock));
}
/// <summary>
/// Parse an <see cref="OperatorDecl"/>.
/// </summary>
public static new OperatorDecl Parse(Parser parser, CodeObject parent, ParseFlags flags)
{
// Handle conversion operators
if (ModifiersHelpers.IsModifier(parser.LastUnusedTokenText))
return new ConversionOperatorDecl(parser, parent, flags);
// Handle other operators
return new OperatorDecl(parser, parent, true, flags);
}
protected OperatorDecl(Parser parser, CodeObject parent, bool parse, ParseFlags flags)
: base(parser, parent, false, flags)
{
if (parse)
{
parser.NextToken(); // Move past 'operator'
if (parser.TokenText != ParseTokenStart)
{
_symbol = parser.TokenText; // Parse the symbol
parser.NextToken(); // Move past the symbol
}
//else // Parse error if symbol is missing
ParseUnusedType(parser, ref _returnType); // Parse the return type from the Unused list
ParseParameters(parser);
_name = GetOperatorInternalName(_symbol, (_parameters != null ? _parameters.Count : 0));
// Parse any optional attributes and/or modifiers. Do this after the main part of the
// statement has been parsed so that it can be replicated if necessary with different
// modifiers on each part, but before any base-type list or constraints.
ParseModifiersAndAnnotations(parser);
ParseTerminatorOrBody(parser, flags);
}
}
#endregion
#region /* RENDERING */
internal override void AsTextName(CodeWriter writer, RenderFlags flags)
{
writer.Write(_symbol);
}
protected override void AsTextStatement(CodeWriter writer, RenderFlags flags)
{
RenderFlags passFlags = (flags & RenderFlags.PassMask);
if (_returnType != null)
_returnType.AsText(writer, passFlags | RenderFlags.IsPrefix);
UpdateLineCol(writer, flags);
writer.Write(ParseToken + " ");
if (flags.HasFlag(RenderFlags.Description) && _parent is TypeDecl)
{
((TypeDecl)_parent).AsTextName(writer, flags);
writer.Write(Dot.ParseToken);
}
AsTextName(writer, flags);
}
#endregion
#region /* SYMBOL-TO-INTERNAL-NAME MAPPING */
// Dictionaries for looking up operator names from their symbols and vice-versa
private static Dictionary<string, string> _symbolToInternalNameMap;
private static Dictionary<string, string> _internalNameToSymbolMap;
// Data arrays used to initialize the dictionaries above
private static readonly string[,] _unaryMapData =
{
{ Not.ParseToken, Not.InternalName },
{ Complement.ParseToken, Complement.InternalName },
{ Positive.ParseToken, Positive.InternalName },
{ Negative.ParseToken, Negative.InternalName },
{ Increment.ParseToken, Increment.InternalName },
{ Decrement.ParseToken, Decrement.InternalName }
};
private static readonly string[,] _binaryMapData =
{
{ CodeDOM.Add.ParseToken, CodeDOM.Add.InternalName },
{ Subtract.ParseToken, Subtract.InternalName },
{ Multiply.ParseToken, Multiply.InternalName },
{ Divide.ParseToken, Divide.InternalName },
{ Mod.ParseToken, Mod.InternalName },
{ BitwiseAnd.ParseToken, BitwiseAnd.InternalName },
{ BitwiseOr.ParseToken, BitwiseOr.InternalName },
{ BitwiseXor.ParseToken, BitwiseXor.InternalName },
{ Equal.ParseToken, Equal.InternalName },
{ NotEqual.ParseToken, NotEqual.InternalName },
{ LessThan.ParseToken, LessThan.InternalName },
{ GreaterThan.ParseToken, GreaterThan.InternalName },
{ LessThanEqual.ParseToken, LessThanEqual.InternalName },
{ GreaterThanEqual.ParseToken, GreaterThanEqual.InternalName },
{ LeftShift.ParseToken, LeftShift.InternalName },
{ RightShift.ParseToken, RightShift.InternalName }
};
// Determine the internal name for an operator given its symbol and parameter count (1 or 2).
protected static string GetOperatorInternalName(string symbol, int parameterCount)
{
if (_symbolToInternalNameMap == null)
{
// If it hasn't been done yet, build the necessary dictionary
_symbolToInternalNameMap = new Dictionary<string, string>();
for (int i = 0; i < _unaryMapData.GetLength(0); ++i)
_symbolToInternalNameMap.Add(_unaryMapData[i, 0] + "`1", _unaryMapData[i, 1]);
for (int i = 0; i < _binaryMapData.GetLength(0); ++i)
_symbolToInternalNameMap.Add(_binaryMapData[i, 0] + "`2", _binaryMapData[i, 1]);
}
string name;
_symbolToInternalNameMap.TryGetValue(symbol + '`' + parameterCount, out name);
return name ?? symbol;
}
/// <summary>
/// Determine the symbol for an operator given its internal name.
/// </summary>
public static string GetOperatorSymbol(string internalName)
{
if (_internalNameToSymbolMap == null)
{
// If it hasn't been done yet, build the necessary dictionary
_internalNameToSymbolMap = new Dictionary<string, string>();
for (int i = 0; i < _unaryMapData.GetLength(0); ++i)
_internalNameToSymbolMap.Add(_unaryMapData[i, 1], _unaryMapData[i, 0]);
for (int i = 0; i < _binaryMapData.GetLength(0); ++i)
_internalNameToSymbolMap.Add(_binaryMapData[i, 1], _binaryMapData[i, 0]);
}
string symbol;
_internalNameToSymbolMap.TryGetValue(internalName, out symbol);
return symbol;
}
#endregion
}
}