/* * Copyright (c) 2012-2015, Microsoft Mobile * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package org.juniversal.translator.csharp; import org.eclipse.jdt.core.dom.*; import org.jetbrains.annotations.Nullable; import org.juniversal.translator.core.*; import java.io.Writer; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import static org.juniversal.translator.core.ASTUtil.forEach; import static org.juniversal.translator.core.ASTUtil.isArrayLengthField; import static org.juniversal.translator.core.ASTUtil.isType; // TODO: C# doesn't allow data members and methods to have the same names; add a check for that, so fails at translation // time not build time // TODO: Think about how to handle "transient" in Java public class CSharpSourceFileWriter extends SourceFileWriter { private CSharpTranslator cSharpTranslator; private CSharpContext context; private HashSet<String> cSharpReservedWords; public CSharpSourceFileWriter(CSharpTranslator cSharpTranslator, SourceFile sourceFile, Writer writer) { super(cSharpTranslator, sourceFile, writer); this.cSharpTranslator = cSharpTranslator; this.context = new CSharpContext(); addDeclarationWriters(); addStatementWriters(); addExpressionWriters(); // TODO: Implement this // Simple name addWriter(SimpleName.class, new CSharpASTNodeWriter<SimpleName>(this) { @Override public void write(SimpleName simpleName) { String identifier = simpleName.getIdentifier(); // Escape identifier names which are also reserved words in C# if (getCSharpReservedWords().contains(identifier)) matchAndWrite(identifier, "@" + identifier); else matchAndWrite(identifier); } }); } @Override public CSharpTranslator getTranslator() { return cSharpTranslator; } @Override public CSharpContext getContext() { return context; } /** * Add visitors for class, method, field, and type declarations. */ private void addDeclarationWriters() { // TODO: Implement this // Compilation unit addWriter(CompilationUnit.class, new CompilationUnitWriter(this)); // TODO: Implement this // Javadoc comment addWriter(Javadoc.class, new JavadocCommentWriter(this)); // TODO: Implement this // Type (class/interface) declaration addWriter(TypeDeclaration.class, new TypeDeclarationWriter(this)); // TODO: Implement this // Type (class/interface) declaration addWriter(EnumDeclaration.class, new EnumDeclarationWriter(this)); // TODO: Implement this // Method declaration (which includes implementation) addWriter(MethodDeclaration.class, new MethodDeclarationWriter(this)); // Field declaration addWriter(FieldDeclaration.class, new FieldDeclarationWriter(this)); // Simple type addWriter(SimpleType.class, new SimpleTypeWriter(this)); // TODO: Implement this // Variable declaration fragment addWriter(VariableDeclarationFragment.class, new CSharpASTNodeWriter<VariableDeclarationFragment>(this) { @Override public void write(VariableDeclarationFragment variableDeclarationFragment) { String name = variableDeclarationFragment.getName().getIdentifier(); if (getContext().getTypeMethodNames().contains(name)) throw sourceNotSupported("Class also contains a method with name '" + name + "'; in C#, unlike Java, a class can't have a method and a variable with the same name, so rename one of them"); // TODO: Handle syntax with extra dimensions on array if (variableDeclarationFragment.getExtraDimensions() > 0) throw sourceNotSupported("\"int foo[]\" array type syntax not currently supported; use \"int[] foo\" instead"); writeNode(variableDeclarationFragment.getName()); Expression initializer = variableDeclarationFragment.getInitializer(); if (initializer != null) { copySpaceAndComments(); matchAndWrite("="); copySpaceAndComments(); writeNode(initializer); } } }); // TODO: Implement this // Single variable declaration (used in parameter list, catch clauses, and enhanced for statements) addWriter(SingleVariableDeclaration.class, new CSharpASTNodeWriter<SingleVariableDeclaration>(this) { @Override public void write(SingleVariableDeclaration singleVariableDeclaration) { // TODO: Handle syntax with extra dimensions on array if (singleVariableDeclaration.getExtraDimensions() > 0) throw sourceNotSupported("\"int foo[]\" array type syntax not currently supported; use \"int[] foo\" instead"); List<?> modifiers = singleVariableDeclaration.modifiers(); ensureModifiersJustFinalOrAnnotations(modifiers); skipModifiers(modifiers); Type type = singleVariableDeclaration.getType(); if (singleVariableDeclaration.isVarargs()) { write("params "); writeNode(type); copySpaceAndComments(); // TODO: Think through & handle all cases where a regular type is converted to an array here matchAndWrite("...", "[]"); } else writeNode(type); copySpaceAndComments(); writeNode(singleVariableDeclaration.getName()); // TODO: Handle initializer if (singleVariableDeclaration.getInitializer() != null) throw new JUniversalException("Unexpected initializer present for SingleVariableDeclaration"); } }); // TODO: Implement this // Parameterized type addWriter(ParameterizedType.class, new CSharpASTNodeWriter<ParameterizedType>(this) { @Override public void write(ParameterizedType parameterizedType) { writeNode(parameterizedType.getType()); copySpaceAndComments(); matchAndWrite("<"); writeCommaDelimitedNodes(parameterizedType.typeArguments()); copySpaceAndComments(); matchAndWrite(">"); } }); addWriter(WildcardType.class, new CSharpASTNodeWriter<WildcardType>(this) { @Override public void write(WildcardType wildcardType) { ArrayList<WildcardType> wildcardTypes = getContext().getMethodWildcardTypes(); if (wildcardTypes == null) throw sourceNotSupported("Wildcard types (that is, ?) only supported in method parameters and return types. You may want to change the Java source to use an explicitly named generic type instead of a wildcard here."); writeWildcardTypeSyntheticName(wildcardTypes, wildcardType); setPositionToEndOfNode(wildcardType); } }); // Array type addWriter(ArrayType.class, new CSharpASTNodeWriter<ArrayType>(this) { @Override public void write(ArrayType arrayType) { writeNode(arrayType.getElementType()); forEach(arrayType.dimensions(), (Dimension dimension) -> { copySpaceAndComments(); matchAndWrite("["); copySpaceAndComments(); matchAndWrite("]"); }); } }); // TODO: Implement this // Primitive type addWriter(PrimitiveType.class, new CSharpASTNodeWriter<PrimitiveType>(this) { @Override public void write(PrimitiveType primitiveType) { PrimitiveType.Code code = primitiveType.getPrimitiveTypeCode(); if (code == PrimitiveType.BYTE) matchAndWrite("byte", "sbyte"); else if (code == PrimitiveType.SHORT) matchAndWrite("short"); else if (code == PrimitiveType.CHAR) matchAndWrite("char"); else if (code == PrimitiveType.INT) matchAndWrite("int"); else if (code == PrimitiveType.LONG) matchAndWrite("long"); else if (code == PrimitiveType.FLOAT) matchAndWrite("float"); else if (code == PrimitiveType.DOUBLE) matchAndWrite("double"); else if (code == PrimitiveType.BOOLEAN) matchAndWrite("boolean", "bool"); else if (code == PrimitiveType.VOID) matchAndWrite("void", "void"); else throw invalidAST("Unknown primitive type: " + code); } }); // Array initializer addWriter(ArrayInitializer.class, new CSharpASTNodeWriter<ArrayInitializer>(this) { @Override public void write(ArrayInitializer arrayInitializer) { // TODO: Test more cases here if (arrayInitializer.getParent() instanceof ArrayInitializer) { write("new[] "); /* throw sourceNotSupported( "Nested array initializers, without a 'new' specified, aren't supported in C#. Change the " + "Java source to include a new, a syntax supported by both Java and C#. For instance, " + "change { {1, 2, 3}, {10, 11, 12} ) => { new int[] {1, 2, 3}, new int[] {10, 11, 12} }"); */ } matchAndWrite("{"); // TODO: Check that number of expressions matches array size (I think, as I think C# requires exact number and Java allows less) writeCommaDelimitedNodes(arrayInitializer.expressions()); // TODO: Skip extra trailing commas here copySpaceAndComments(); matchAndWrite("}"); } }); } /** * Add visitors for the different kinds of statements. */ private void addStatementWriters() { // TODO: Implement this // Block addWriter(Block.class, new CSharpASTNodeWriter<Block>(this) { @Override public void write(Block block) { matchAndWrite("{"); writeNodes(block.statements()); copySpaceAndComments(); matchAndWrite("}"); } }); // TODO: Implement this // Empty statement (";") addWriter(EmptyStatement.class, new CSharpASTNodeWriter<EmptyStatement>(this) { @Override public void write(EmptyStatement emptyStatement) { matchAndWrite(";"); } }); // TODO: Implement this // Expression statement addWriter(ExpressionStatement.class, new CSharpASTNodeWriter<ExpressionStatement>(this) { @Override public void write(ExpressionStatement expressionStatement) { writeNode(expressionStatement.getExpression()); copySpaceAndComments(); matchAndWrite(";"); } }); // If statement addWriter(IfStatement.class, new CSharpASTNodeWriter<IfStatement>(this) { @Override public void write(IfStatement ifStatement) { matchAndWrite("if"); copySpaceAndComments(); matchAndWrite("("); copySpaceAndComments(); writeNode(ifStatement.getExpression()); copySpaceAndComments(); matchAndWrite(")"); copySpaceAndComments(); writeNode(ifStatement.getThenStatement()); Statement elseStatement = ifStatement.getElseStatement(); if (elseStatement != null) { copySpaceAndComments(); matchAndWrite("else"); copySpaceAndComments(); writeNode(elseStatement); } } }); // While statement addWriter(WhileStatement.class, new CSharpASTNodeWriter<WhileStatement>(this) { @Override public void write(WhileStatement whileStatement) { matchAndWrite("while"); copySpaceAndComments(); matchAndWrite("("); copySpaceAndComments(); writeNode(whileStatement.getExpression()); copySpaceAndComments(); matchAndWrite(")"); copySpaceAndComments(); writeNode(whileStatement.getBody()); } }); // Do while statement addWriter(DoStatement.class, new CSharpASTNodeWriter<DoStatement>(this) { @Override public void write(DoStatement doStatement) { matchAndWrite("do"); copySpaceAndComments(); writeNode(doStatement.getBody()); copySpaceAndComments(); matchAndWrite("while"); copySpaceAndComments(); matchAndWrite("("); copySpaceAndComments(); writeNode(doStatement.getExpression()); copySpaceAndComments(); matchAndWrite(")"); copySpaceAndComments(); matchAndWrite(";"); } }); // TODO: Implement this // Continue statement addWriter(ContinueStatement.class, new CSharpASTNodeWriter<ContinueStatement>(this) { @Override public void write(ContinueStatement continueStatement) { if (continueStatement.getLabel() != null) throw sourceNotSupported("continue statement with a label isn't supported as that construct doesn't exist in C++; change the code to not use a label"); matchAndWrite("continue"); copySpaceAndComments(); matchAndWrite(";"); } }); // TODO: Implement this // Break statement addWriter(BreakStatement.class, new CSharpASTNodeWriter<BreakStatement>(this) { @Override public void write(BreakStatement breakStatement) { if (breakStatement.getLabel() != null) throw sourceNotSupported("break statement with a label isn't supported as that construct doesn't exist in C++; change the code to not use a label"); matchAndWrite("break"); copySpaceAndComments(); matchAndWrite(";"); } }); // TODO: Implement this // For statement addWriter(ForStatement.class, new ForStatementWriter(this)); addWriter(EnhancedForStatement.class, new CSharpASTNodeWriter<EnhancedForStatement>(this) { @Override public void write(EnhancedForStatement enhancedForStatement) { matchAndWrite("for", "foreach"); copySpaceAndComments(); matchAndWrite("("); copySpaceAndComments(); writeNode(enhancedForStatement.getParameter()); copySpaceAndComments(); // TODO: Ensure spaces around "in" matchAndWrite(":", "in"); copySpaceAndComments(); writeNode(enhancedForStatement.getExpression()); copySpaceAndComments(); matchAndWrite(")"); copySpaceAndComments(); writeNode(enhancedForStatement.getBody()); } }); // TODO: Implement this // Switch statement addWriter(SwitchStatement.class, new SwitchStatementWriter(this)); // TODO: Implement this // Return statement addWriter(ReturnStatement.class, new CSharpASTNodeWriter<ReturnStatement>(this) { @Override public void write(ReturnStatement returnStatement) { matchAndWrite("return"); Expression expression = returnStatement.getExpression(); if (expression != null) { copySpaceAndComments(); writeNode(returnStatement.getExpression()); } copySpaceAndComments(); matchAndWrite(";"); } }); // Local variable declaration statement addWriter(VariableDeclarationStatement.class, new CSharpASTNodeWriter<VariableDeclarationStatement>(this) { @Override public void write(VariableDeclarationStatement variableDeclarationStatement) { writeVariableDeclaration(variableDeclarationStatement.modifiers(), variableDeclarationStatement.getType(), variableDeclarationStatement.fragments()); copySpaceAndComments(); matchAndWrite(";"); } }); // Try statement addWriter(TryStatement.class, new TryStatementWriter(this)); // TODO: Implement this // Throw statement addWriter(ThrowStatement.class, new CSharpASTNodeWriter<ThrowStatement>(this) { @Override public void write(ThrowStatement throwStatement) { matchAndWrite("throw"); copySpaceAndComments(); writeNode(throwStatement.getExpression()); copySpaceAndComments(); matchAndWrite(";"); } }); // TODO: Implement this // Delegating constructor invocation addWriter(ConstructorInvocation.class, new CSharpASTNodeWriter(this) { @Override public void write(ASTNode node) { throw sourceNotSupported("Delegating constructors aren't currently supported; for now you have to change the code to not use them (e.g. by adding an init method)"); } }); addWriter(AssertStatement.class, new CSharpASTNodeWriter<AssertStatement>(this) { @Override public void write(AssertStatement assertStatement) { matchAndWrite("assert", nativeReference("System.Diagnostics", "Debug.Assert")); write("("); skipSpaceAndComments(); writeNode(assertStatement.getExpression()); @Nullable Expression message = assertStatement.getMessage(); if (message != null) { skipSpaceAndComments(); matchAndWrite(":", ","); copySpaceAndComments(); writeNode(message); copySpaceAndComments(); matchAndWrite(";", ");"); } } }); // TODO: Implement this // Throw statement addWriter(SynchronizedStatement.class, new CSharpASTNodeWriter<SynchronizedStatement>(this) { @Override public void write(SynchronizedStatement synchronizedStatement) { matchAndWrite("synchronized", "lock"); copySpaceAndComments(); matchAndWrite("("); writeNode(synchronizedStatement.getExpression()); copySpaceAndComments(); matchAndWrite(")"); copySpaceAndComments(); writeNode(synchronizedStatement.getBody()); } }); // Static initializer addWriter(Initializer.class, new CSharpASTNodeWriter<Initializer>(this) { @Override public void write(Initializer initializer) { throw sourceNotSupported("Static initializers aren't supported (for one thing, their order of execution isn't fully deterministic); use a static method that initializes on demand instead"); } }); } /** * Add visitors for the different kinds of expressions. */ private void addExpressionWriters() { // TODO: Implement this // Assignment expression addWriter(Assignment.class, new AssignmentWriter(this)); // TODO: Implement this // Method invocation addWriter(MethodInvocation.class, new MethodInvocationWriter(this)); // TODO: Implement this // Super Method invocation addWriter(SuperMethodInvocation.class, new SuperMethodInvocationWriter(this)); // TODO: Implement this // Class instance creation addWriter(ClassInstanceCreation.class, new ClassInstanceCreationWriter(this)); // TODO: Implement this // Array creation addWriter(ArrayCreation.class, new ArrayCreationWriter(this)); // TODO: Implement this // Variable declaration expression (used in a for statement) addWriter(VariableDeclarationExpression.class, new CSharpASTNodeWriter<VariableDeclarationExpression>(this) { @Override public void write(VariableDeclarationExpression variableDeclarationExpression) { writeVariableDeclaration(variableDeclarationExpression.modifiers(), variableDeclarationExpression.getType(), variableDeclarationExpression.fragments()); } }); // TODO: Implement this // Infix expression addWriter(InfixExpression.class, new InfixExpressionWriter(this)); // Prefix expression addWriter(PrefixExpression.class, new CSharpASTNodeWriter<PrefixExpression>(this) { @Override public void write(PrefixExpression prefixExpression) { PrefixExpression.Operator operator = prefixExpression.getOperator(); if (operator == PrefixExpression.Operator.INCREMENT) matchAndWrite("++"); else if (operator == PrefixExpression.Operator.DECREMENT) matchAndWrite("--"); else if (operator == PrefixExpression.Operator.PLUS) matchAndWrite("+"); else if (operator == PrefixExpression.Operator.MINUS) matchAndWrite("-"); else if (operator == PrefixExpression.Operator.COMPLEMENT) matchAndWrite("~"); else if (operator == PrefixExpression.Operator.NOT) matchAndWrite("!"); else throw invalidAST("Unknown prefix operator type: " + operator); copySpaceAndComments(); writeNode(prefixExpression.getOperand()); } }); // Postfix expression addWriter(PostfixExpression.class, new CSharpASTNodeWriter<PostfixExpression>(this) { @Override public void write(PostfixExpression postfixExpression) { writeNode(postfixExpression.getOperand()); // In Swift there can't be any whitespace or comments between a postfix operator & its operand, so // strip it, not copying anything here skipSpaceAndComments(); PostfixExpression.Operator operator = postfixExpression.getOperator(); if (operator == PostfixExpression.Operator.INCREMENT) matchAndWrite("++"); else if (operator == PostfixExpression.Operator.DECREMENT) matchAndWrite("--"); else throw invalidAST("Unknown postfix operator type: " + operator); } }); // instanceof expression addWriter(InstanceofExpression.class, new CSharpASTNodeWriter<InstanceofExpression>(this) { @Override public void write(InstanceofExpression instanceofExpression) { Expression expression = instanceofExpression.getLeftOperand(); writeNode(expression); copySpaceAndComments(); matchAndWrite("instanceof", "is"); copySpaceAndComments(); Type type = instanceofExpression.getRightOperand(); writeNode(type); } }); // conditional expression addWriter(ConditionalExpression.class, new CSharpASTNodeWriter<ConditionalExpression>(this) { @Override public void write(ConditionalExpression conditionalExpression) { writeNode(conditionalExpression.getExpression()); copySpaceAndComments(); matchAndWrite("?"); copySpaceAndComments(); writeNode(conditionalExpression.getThenExpression()); copySpaceAndComments(); matchAndWrite(":"); copySpaceAndComments(); writeNode(conditionalExpression.getElseExpression()); } }); // TODO: Implement this // this addWriter(ThisExpression.class, new CSharpASTNodeWriter<ThisExpression>(this) { @Override public void write(ThisExpression thisExpression) { // TODO: Handle qualified this expressions; probably need to do from parent invoking // node & disallow qualified this accesses if not field reference / method // invocation; it's allowed otherwise in Java but I don't think it does anything // MyClass.this. --> this->MyClass:: if (thisExpression.getQualifier() != null) throw new JUniversalException("Qualified this expression isn't supported yet"); matchAndWrite("this"); } }); // Field access addWriter(FieldAccess.class, new CSharpASTNodeWriter<FieldAccess>(this) { @Override public void write(FieldAccess fieldAccess) { writeNode(fieldAccess.getExpression()); copySpaceAndComments(); matchAndWrite("."); copySpaceAndComments(); if (isArrayLengthField(fieldAccess)) matchAndWrite("length", "Length"); else writeNode(fieldAccess.getName()); } }); // Array access addWriter(ArrayAccess.class, new CSharpASTNodeWriter<ArrayAccess>(this) { @Override public void write(ArrayAccess arrayAccess) { writeNode(arrayAccess.getArray()); copySpaceAndComments(); matchAndWrite("["); copySpaceAndComments(); writeNode(arrayAccess.getIndex()); copySpaceAndComments(); matchAndWrite("]"); } }); // Qualified name addWriter(QualifiedName.class, new CSharpASTNodeWriter<QualifiedName>(this) { @Override public void write(QualifiedName qualifiedName) { // TODO: Figure out the other cases where this can occur & make them all correct writeNode(qualifiedName.getQualifier()); copySpaceAndComments(); matchAndWrite("."); copySpaceAndComments(); if (isArrayLengthField(qualifiedName)) matchAndWrite("length", "Length"); else writeNode(qualifiedName.getName()); } }); // TODO: Implement this // Parenthesized expression addWriter(ParenthesizedExpression.class, new CSharpASTNodeWriter<ParenthesizedExpression>(this) { @Override public void write(ParenthesizedExpression parenthesizedExpression) { matchAndWrite("("); copySpaceAndComments(); writeNode(parenthesizedExpression.getExpression()); copySpaceAndComments(); matchAndWrite(")"); } }); // TODO: Implement this // Cast expression addWriter(CastExpression.class, new CastExpressionWriter(this)); // Number literal addWriter(NumberLiteral.class, new NumberLiteralWriter(this)); // Boolean literal addWriter(BooleanLiteral.class, new CSharpASTNodeWriter<BooleanLiteral>(this) { @Override public void write(BooleanLiteral booleanLiteral) { matchAndWrite(booleanLiteral.booleanValue() ? "true" : "false"); } }); // Character literal addWriter(CharacterLiteral.class, new CSharpASTNodeWriter<CharacterLiteral>(this) { @Override public void write(CharacterLiteral characterLiteral) { // TODO: Map character escape sequences // TODO: Add cast to {byte) or other types if needed, to handle for instance: // byte[] binaryData = new byte[]{'1', '2', '3'}; // Maybe should do that for all expressions, not just literals (do more testing to see) matchAndWrite(characterLiteral.getEscapedValue()); } }); // Null literal addWriter(NullLiteral.class, new CSharpASTNodeWriter(this) { @Override public void write(ASTNode node) { matchAndWrite("null"); } }); // TODO: Implement this // String literal addWriter(StringLiteral.class, new CSharpASTNodeWriter<StringLiteral>(this) { @Override public void write(StringLiteral stringLiteral) { matchAndWrite(stringLiteral.getEscapedValue()); } }); addWriter(TypeLiteral.class, new CSharpASTNodeWriter<TypeLiteral>(this) { @Override public void write(TypeLiteral typeLiteral) { throw sourceNotSupported("Type literals (<type>.class) aren't supported by JUniversal, partially to ease idiomatic translation to C++. For HashMaps of types, consider using string keys instead."); } }); } public HashSet<String> getCSharpReservedWords() { if (cSharpReservedWords == null) cSharpReservedWords = createCSharpReservedWords(); return cSharpReservedWords; } /** * Create a set containing all reserved keywords in C# that aren't reserved words in Java. These keywords are the * ones that should be escaped if used in Java source as an identifier. * * @return hash set containing set of C#-only reserved words */ public HashSet<String> createCSharpReservedWords() { HashSet<String> reservedWords = new HashSet<>(); reservedWords.add("as"); reservedWords.add("base"); reservedWords.add("bool"); reservedWords.add("checked"); reservedWords.add("decimal"); reservedWords.add("delegate"); reservedWords.add("event"); reservedWords.add("explicit"); reservedWords.add("extern"); reservedWords.add("fixed"); reservedWords.add("foreach"); reservedWords.add("implicit"); reservedWords.add("in"); reservedWords.add("internal"); reservedWords.add("is"); reservedWords.add("lock"); reservedWords.add("long"); reservedWords.add("namespace"); reservedWords.add("object"); reservedWords.add("operator"); reservedWords.add("out"); reservedWords.add("override"); reservedWords.add("params"); reservedWords.add("readonly"); reservedWords.add("ref"); reservedWords.add("sbyte"); reservedWords.add("sealed"); reservedWords.add("short"); reservedWords.add("sizeof"); reservedWords.add("stackalloc"); reservedWords.add("string"); reservedWords.add("struct"); reservedWords.add("typeof"); reservedWords.add("uint"); reservedWords.add("ulong"); reservedWords.add("unchecked"); reservedWords.add("unsafe"); reservedWords.add("ushort"); reservedWords.add("using"); reservedWords.add("virtual"); return reservedWords; } }