// 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;
using Nova.Resolving;
namespace Nova.CodeDOM
{
/// <summary>
/// The NewArray operator is used to create new array instances.
/// </summary>
/// <remarks>
/// Possible formats:
///
/// New Array:
/// new non-array-type[size,...] // Create a new array type
/// new non-array-type[size,...][,...]... // Create a new jagged array type
/// With initializers (init list can be empty) - first format is preferred:
/// new array-type { init } // Create array instance and initialize (init is optional), inferring the sizes
/// new non-array-type[size,...] { init } // Create array and initialize, sizes must match init values
/// new non-array-type[size,...][,...]... { init } // Create jagged array and initialize, sizes must match init values
///
/// In the case of jagged arrays, the first set of array dimensions are arguments to the
/// base ArgumentsOperator class, while the optional sets of brackets without dimensions
/// (array ranks) to the right are actually a part of the Type on the left. For example,
/// "new int[10][]" represents a parameter of "10" and a TypeRef to an "int[]" type.
/// The parsing and display of the type must be split up to allow the "[10]" in the middle.
/// Also, the dimension sizes can be full expressions in addition to literals.
///
/// </remarks>
public class NewArray : NewOperator
{
#region /* CONSTRUCTORS */
/// <summary>
/// Create a <see cref="NewArray"/>.
/// </summary>
/// <param name="type">An expression representing the TypeRef of the elements of the array.</param>
/// <param name="parameters">The array size parameters (if any).</param>
public NewArray(Expression type, params Expression[] parameters)
: base(type, (parameters ?? new Expression[] { null })) // Treat null as a single null parameter
{ }
#endregion
#region /* PROPERTIES */
#endregion
#region /* METHODS */
/// <summary>
/// Determine the type of the parameter for the specified argument index.
/// </summary>
public override TypeRefBase GetParameterType(int argumentIndex)
{
return TypeRef.IntRef;
}
#endregion
#region /* PARSING */
/// <summary>
/// The token used to parse the start of the array ranks.
/// </summary>
public const string ParseTokenStart = TypeRefBase.ParseTokenArrayStart;
/// <summary>
/// The token used to parse the end of the array ranks.
/// </summary>
public const string ParseTokenEnd = TypeRefBase.ParseTokenArrayEnd;
/// <summary>
/// Parse a <see cref="NewArray"/>.
/// </summary>
public NewArray(Parser parser, CodeObject parent)
: base(parser, parent)
{
// Save the starting token for later
Token startingToken = parser.Token;
parser.NextToken(); // Move past 'new'
// Parse the expression representing the TypeRef, but NOT including any array brackets. Any first set of
// following array brackets is parsed below and stored as arguments on the base ArgumentsOperator. Any
// additional sets of brackets for nested arrays are then parsed after that and stored on the TypeRef or
// UnresolvedRef.
Expression expression = Parse(parser, this, false, ParseTokenStart + Initializer.ParseTokenStart, ParseFlags.NoArrays);
SetField(ref _expression, expression, false);
// Parse any parameters (dimension sizes) OR null parameters ([] or [,], etc)
if (parser.TokenText == ParseTokenStart)
ParseArguments(parser, this, ParseTokenStart, ParseTokenEnd, true);
// Parse any trailing empty dimensions (ranks)
if (parser.TokenText == ParseTokenStart)
{
// Add to the existing TypeRef or UnresolvedRef
Expression right = _expression.SkipPrefixes();
if (right is TypeRefBase)
((TypeRefBase)right).ParseArrayRanks(parser);
}
// Set the parent starting token for use by the Initializer to determine proper indentation
parser.ParentStartingToken = startingToken;
// Parse any array initializer
ParseInitializer(parser, this);
}
#endregion
#region /* RESOLVING */
/// <summary>
/// Resolve all child symbolic references, using the specified <see cref="ResolveCategory"/> and <see cref="ResolveFlags"/>.
/// </summary>
public override CodeObject Resolve(ResolveCategory resolveCategory, ResolveFlags flags)
{
if (_initializer != null)
_initializer.Resolve(ResolveCategory.Expression, flags);
base.Resolve(ResolveCategory.Type, flags);
return this;
}
/// <summary>
/// Evaluate the type of the <see cref="Expression"/>.
/// </summary>
/// <returns>The resulting <see cref="TypeRef"/> or <see cref="UnresolvedRef"/>.</returns>
public override TypeRefBase EvaluateType(bool withoutConstants)
{
// Determine the type of the array
TypeRefBase typeRefBase;
if (_expression != null)
{
typeRefBase = _expression.EvaluateType(withoutConstants);
// Set the rank of the top-level array
if (_arguments != null && typeRefBase != null)
{
// Clone the type and add the array rank if none, otherwise insert the top-level rank
typeRefBase = (TypeRefBase)typeRefBase.Clone();
if (typeRefBase.ArrayRanks == null)
typeRefBase.ArrayRanks = new List<int> { _arguments.Count };
else
typeRefBase.ArrayRanks.Insert(0, _arguments.Count);
}
}
else
typeRefBase = _initializer.EvaluateType(withoutConstants);
return typeRefBase;
}
#endregion
#region /* RENDERING */
public override void AsTextExpression(CodeWriter writer, RenderFlags flags)
{
// Suppress empty brackets (they'll be included as a part of the type)
base.AsTextExpression(writer, flags | RenderFlags.NoParensIfEmpty);
}
protected override void AsTextName(CodeWriter writer, RenderFlags flags)
{
UpdateLineCol(writer, flags);
writer.Write(ParseToken);
// Omit any brackets (they will be rendered later)
if (_expression != null)
_expression.AsText(writer, flags | RenderFlags.SuppressBrackets | RenderFlags.PrefixSpace);
}
protected override void AsTextStartArguments(CodeWriter writer, RenderFlags flags)
{
writer.Write(ParseTokenStart);
}
protected override void AsTextEndArguments(CodeWriter writer, RenderFlags flags)
{
if (IsEndFirstOnLine)
writer.WriteLine();
writer.Write(ParseTokenEnd);
}
protected override void AsTextInitializer(CodeWriter writer, RenderFlags flags)
{
// Render any omitted brackets here (after parameters and before any initializer),
// skipping the first set, because they will be rendered as the arguments.
if (_expression != null)
{
TypeRefBase typeRefBase = _expression.EvaluateType();
if (typeRefBase != null)
typeRefBase.AsTextArrayRanks(writer, flags);
}
base.AsTextInitializer(writer, flags);
}
#endregion
}
}