public static LNode of(LNode node, IMessageSink sink) { LNode kind; if (node.ArgCount == 2 && (kind = node.Args[0]).IsId) { if (kind.IsIdNamed(_array)) { return(node.WithArgChanged(0, kind.WithName(S.Array))); } if (kind.IsIdNamed(_opt)) { return(node.WithArgChanged(0, kind.WithName(S.QuestionMark))); } if (kind.IsIdNamed(_ptr)) { return(node.WithArgChanged(0, kind.WithName(S._Pointer))); } } else if (node.ArgCount == 3 && (kind = node.Args[0]).IsIdNamed(_array) && node.Args[1].IsLiteral) { return(node.WithArgs(kind.WithName(S.GetArrayKeyword((int)node.Args[1].Value)), node.Args[2])); } return(null); }
int CountDimensionsIfArrayType(LNode type) { LNode dimsNode; if (type.Calls(S.Of, 2) && (dimsNode = type.Args[0]).IsId) { return(S.CountArrayDimensions(dimsNode.Name)); } return(0); }
public void CsDataTypes() { Stmt("double x;", F.Vars(F.Double, x)); Stmt("int[] x;", F.Vars(F.Of(S.Array, S.Int32), x)); Stmt("long* x;", F.Vars(F.Of(S._Pointer, S.Int64), x)); Stmt("string[][,] x;", F.Vars(F.Of(_(S.Array), F.Of(S.TwoDimensionalArray, S.String)), x)); Stmt("typeof(float*);", F.Call(S.Typeof, F.Of(S._Pointer, S.Single))); Stmt("decimal[,,,] x;", F.Vars(F.Of(S.GetArrayKeyword(4), S.Decimal), x)); Stmt("double? x;", F.Vars(F.Of(S.QuestionMark, S.Double), x)); Stmt("Foo<a.b.c>? x;", F.Vars(F.Of(_(S.QuestionMark), F.Of(Foo, F.Dot(a, b, c))), x)); Stmt("Foo<a?,b.c[,]>[] x;", F.Vars(F.Of(_(S.Array), F.Of(Foo, F.Of(_(S.QuestionMark), a), F.Of(_(S.TwoDimensionalArray), F.Dot(b, c)))), x)); }
private void PrintTypeWithArraySizes(LNode cons) { LNode type = cons.Target; // Called by AutoPrintNewOperator; type is already validated. Debug.Assert(type.Calls(S.Of, 2) && S.IsArrayKeyword(type.Args[0].Name)); // We have to deal with the "constructor arguments" specially. // First of all, the constructor arguments appear inside the // square brackets, which is unusual: int[x + y]. But there's // something much more strange in case of arrays of arrays: the // order of the square brackets must be reversed. If the // constructor argument is 10, an array of two-dimensional // arrays of int is written int[10][,], rather than int[,][10] // which would be easier to handle. int dims = cons.ArgCount, innerDims; LNode elemType = type.Args[1]; var dimStack = InternalList <int> .Empty; while ((innerDims = CountDimensionsIfArrayType(elemType)) != 0) { dimStack.Add(innerDims); elemType = elemType.Args[1]; } PrintType(elemType, EP.Primary.LeftContext(ContinueExpr)); _out.Write('[', true); bool first = true; foreach (var arg in cons.Args) { if (first) { first = false; } else { WriteThenSpace(',', SpaceOpt.AfterComma); } PrintExpr(arg, StartExpr, 0); } _out.Write(']', true); // Write the brackets for the inner array types for (int i = dimStack.Count - 1; i >= 0; i--) { _out.Write(S.GetArrayKeyword(dimStack[i]).Name, true); } }
static Symbol SpecialTypeKind(LNode n, Ambiguity flags, Precedence context) { // detects when notation for special types applies: Foo[], Foo*, Foo? // assumes IsComplexIdentifier() is already known to be true LNode first; if (n.Calls(S.Of, 2) && (first = n.Args[0]).IsId && (flags & Ambiguity.TypeContext) != 0) { var kind = first.Name; if (S.IsArrayKeyword(kind) || kind == S.QuestionMark) { return(kind); } if (kind == S._Pointer && ((flags & Ambiguity.AllowPointer) != 0 || context.Left == StartStmt.Left)) { return(kind); } } return(null); }
public void CsOperatorNew() { Expr("new Foo(x)", F.Call(S.New, F.Call(Foo, x))); Expr("new Foo(x) { a }", F.Call(S.New, F.Call(Foo, x), a)); Expr("new Foo()", F.Call(S.New, F.Call(Foo))); Expr("new Foo { a }", F.Call(S.New, F.Call(Foo), a)); // new Foo() { a } would also be ok Expr("new int[] { a, b }", F.Call(S.New, F.Call(F.Of(S.Array, S.Int32)), a, b)); Expr("new[] { a, b }", F.Call(S.New, F.Call(S.Array), a, b)); Expr("new[] { }", F.Call(S.New, F.Call(S.Array))); Expr("new int[][,] { a }", F.Call(S.New, F.Call(F.Of(_(S.Array), F.Of(S.TwoDimensionalArray, S.Int32))), a)); // This expression is illegal since it requires an initializer list, but it's parsable so should print ok Expr("new int[][,][,,]", F.Call(S.New, F.Call(F.Of(_(S.Array), F.Of(_(S.TwoDimensionalArray), F.Of(S.GetArrayKeyword(3), S.Int32))))), Mode.Both | Mode.ExpectAndDropParserError); Expr("new int[10][,] { a }", F.Call(S.New, F.Call(F.Of(_(S.Array), F.Of(S.TwoDimensionalArray, S.Int32)), F.Literal(10)), a)); Expr("new int[x, x][]", F.Call(S.New, F.Call(F.Of(_(S.TwoDimensionalArray), F.Of(S.Array, S.Int32)), x, x))); Expr("new int[,]", F.Call(S.New, F.Call(F.Of(S.TwoDimensionalArray, S.Int32))), Mode.Both | Mode.ExpectAndDropParserError); Option(Mode.PrintBothParseFirst, "#new(@`[,]`!([Foo] int)());", "new int[,];", F.Call(S.New, F.Call(F.Of(_(S.TwoDimensionalArray), Attr(Foo, F.Int32)))), p => p.DropNonDeclarationAttributes = true); Expr("new { a = 1, b = 2 }", F.Call(S.New, F.Missing, F.Call(S.Assign, a, one), F.Call(S.Assign, b, two))); }
public static bool IsTrivia(this ILNode node) { return(CodeSymbols.IsTriviaSymbol(node.Name)); }
public bool AutoPrintNewOperator(Precedence precedence, Precedence context, Ambiguity flags) { // Prints the new Xyz(...) {...} operator Debug.Assert(_n.Name == S.New); int argCount = _n.ArgCount; if (argCount == 0) { return(false); } bool needParens; Debug.Assert(CanAppearIn(precedence, context, out needParens) && !needParens); LNode cons = _n.Args[0]; LNode type = cons.Target; var consArgs = cons.Args; // There are two basic uses of new: for objects, and for arrays. // In all cases, #new has 1 arg plus optional initializer arguments, // and there's always a list of "constructor args" even if it is empty // (exception: new {...}). // 1. Init an object: 1a. new Foo<Bar>() { ... } <=> #new(Foo<bar>(...), ...) // 1b. new { ... } <=> #new(@``, ...) // 2. Init an array: 2a. new int[] { ... }, <=> #new(int[](), ...) <=> #new(#of(@`[]`, int)(), ...) // 2b. new[,] { ... }. <=> #new(@`[,]`(), ...) // 2c. new int[10,10] { ... }, <=> #new(#of(@`[,]`, int)(10,10), ...) // 2d. new int[10][] { ... }, <=> #new(#of(@`[]`, #of(@`[]`, int))(10), ...) if (HasPAttrs(cons)) { return(false); } if (type == null ? !cons.IsIdNamed(S.Missing) : HasPAttrs(type) || !IsComplexIdentifier(type)) { return(false); } // Okay, we can now be sure that it's printable, but is it an array decl? if (type == null) { // 1b, new {...} _out.Write("new ", true); PrintBracedBlockInNewExpr(); } else if (type != null && type.IsId && S.CountArrayDimensions(type.Name) > 0) // 2b { _out.Write("new", true); _out.Write(type.Name.Name, true); Space(SpaceOpt.Default); PrintBracedBlockInNewExpr(); } else { _out.Write("new ", true); int dims = CountDimensionsIfArrayType(type); if (dims > 0 && cons.Args.Count == dims) { PrintTypeWithArraySizes(cons); } else { // Otherwise we can print the type name without caring if it's an array or not. PrintType(type, EP.Primary.LeftContext(context)); if (cons.ArgCount != 0 || (argCount == 1 && dims == 0)) { PrintArgList(cons, ParenFor.MethodCall, cons.ArgCount, 0, OmitMissingArguments); } } if (_n.Args.Count > 1) { PrintBracedBlockInNewExpr(); } } return(true); }
public bool AutoPrintComplexIdentOperator(Precedence precedence, Precedence context, Ambiguity flags) { // Handles #of and @`.`, including array types int argCount = _n.ArgCount; Symbol name = _n.Name; Debug.Assert((name == S.Of || name == S.Dot) && _n.IsCall); var first = _n.Args[0, null]; if (first == null) { return(false); // no args } bool needParens, needSpecialOfNotation = false; if (!CanAppearIn(precedence, context, out needParens) || needParens) { return(false); // this only happens inside $ operator, e.g. $(a.b) } if (name == S.Dot) { // The trouble with the dot is its high precedence; because of // this, arguments after a dot cannot use prefix notation as a // fallback. For example "@.(a, b(c))" cannot be printed "a.b(c)" // since that means @.(a, b)(c)". The first argument to non- // unary "." can use prefix notation safely though, e.g. // "@.(b(c), a)" can (and must) be printed "b(c).a". if (argCount > 2) { return(false); } if (HasPAttrs(first)) { return(false); } LNode afterDot = _n.Args.Last; // Unary dot is no problem: .(a) is parsed the same as .a, i.e. // the parenthesis are ignored, so we can print an expr like // @`.`(a+b) as .(a+b), but the parser counts parens on binary // dot, so in that case the argument after the dot must not be any // kind of call (except substitution) and must not have attributes, // unless it is in parens. if (argCount == 2 && !afterDot.IsParenthesizedExpr()) { if (HasPAttrs(afterDot)) { return(false); } if (afterDot.IsCall && afterDot.Name != S.Substitute) { return(false); } } else if ((flags & Ambiguity.CastRhs) != 0) { return(false); // cannot print (Foo) @`.`(x) as (Foo) .x } } else if (name == S.Of) { var ici = ICI.Default | ICI.AllowAttrs; if ((flags & Ambiguity.InDefinitionName) != 0) { ici |= ICI.NameDefinition; } if (!IsComplexIdentifier(_n, ici)) { if (IsComplexIdentifier(_n, ici | ICI.AllowAnyExprInOf)) { needSpecialOfNotation = true; } else { return(false); } } } if (name == S.Dot) { if (argCount == 1) { _out.Write('.', true); PrintExpr(first, EP.Substitute); } else { PrintExpr(first, precedence.LeftContext(context), flags & Ambiguity.TypeContext); _out.Write('.', true); PrintExpr(_n.Args[1], precedence.RightContext(context)); } } else if (_n.Name == S.Of) { // Check for special type names such as Foo? or Foo[] Symbol stk = SpecialTypeKind(_n, flags, context); if (stk != null) { if (S.IsArrayKeyword(stk)) { // We do something very strange in case of arrays of arrays: // the order of the square brackets must be reversed when // arrays are nested. For example, an array of two-dimensional // arrays of int is written int[][,], rather than int[,][] // which would be much easier to handle. var stack = InternalList <Symbol> .Empty; var innerType = _n; do { stack.Add(stk); innerType = innerType.Args[1]; } while (S.IsArrayKeyword(stk = SpecialTypeKind(innerType, flags, context) ?? GSymbol.Empty)); PrintType(innerType, EP.Primary.LeftContext(context), (flags & Ambiguity.AllowPointer)); for (int i = 0; i < stack.Count; i++) { _out.Write(stack[i].Name, true); // e.g. [] or [,] } } else { PrintType(_n.Args[1], EP.Primary.LeftContext(context), (flags & Ambiguity.AllowPointer)); _out.Write(stk == S._Pointer ? '*' : '?', true); } return(true); } PrintExpr(first, precedence.LeftContext(context)); _out.Write(needSpecialOfNotation ? "!(" : "<", true); for (int i = 1; i < argCount; i++) { if (i > 1) { WriteThenSpace(',', SpaceOpt.AfterCommaInOf); } PrintType(_n.Args[i], StartExpr, Ambiguity.InOf | Ambiguity.AllowPointer | (flags & Ambiguity.InDefinitionName)); } _out.Write(needSpecialOfNotation ? ')' : '>', true); } else { Debug.Assert(_n.Name == S.Substitute); G.Verify(AutoPrintOperator(ContinueExpr, 0)); } return(true); }