private static void parseEventBody(CsEvent ev, TokenJar tok, ref int i) { tok[i].Assert("{"); i++; ev.Methods = new List<CsSimpleMethod>(); while (!tok[i].IsBuiltin("}")) { var startIndex = tok[i].StartIndex; var cAttribs = new List<CsCustomAttributeGroup>(); while (tok[i].IsBuiltin("[")) cAttribs.Add(parseCustomAttributeGroup(tok, ref i)); var m = new CsSimpleMethod { StartIndex = startIndex, CustomAttributes = cAttribs }; parseModifiers(m, tok, ref i); if (!tok[i].IsIdentifier("add") && !tok[i].IsIdentifier("remove")) throw new ParseException("'add' or 'remove' expected.", tok[i].StartIndex, ev); m.Type = tok[i].TokenStr == "add" ? MethodType.Add : MethodType.Remove; if (ev.Methods.Any(me => me.Type == m.Type)) { if (m.Type == MethodType.Add) throw new ParseException("An 'add' method has already been defined for this event.", tok[i].StartIndex, ev); else throw new ParseException("A 'remove' method has already been defined for this event.", tok[i].StartIndex, ev); } ev.Methods.Add(m); i++; if (tok[i].IsBuiltin("{")) { try { m.Body = parseBlock(tok, ref i); } catch (ParseException e) { if (e.IncompleteResult is CsBlock) m.Body = (CsBlock) e.IncompleteResult; throw new ParseException(e.Message, e.Index, ev, e); } } else if (tok[i].IsBuiltin(";")) i++; else throw new ParseException("'{' or ';' expected.", tok[i].StartIndex, ev); m.EndIndex = tok[i - 1].EndIndex; } i++; }
private static object parseMemberDeclaration(TokenJar tok, ref int i, bool returnAssemblyAndModuleCustomAttributes) { var startIndex = tok[i].StartIndex; var customAttribs = new List<CsCustomAttributeGroup>(); while (tok[i].IsBuiltin("[")) { var k = i; var attr = parseCustomAttributeGroup(tok, ref i); if (returnAssemblyAndModuleCustomAttributes && (attr.Location == CustomAttributeLocation.Assembly || attr.Location == CustomAttributeLocation.Module)) { if (customAttribs.Count > 0) throw new ParseException(@"Assembly or module custom attribute not allowed after other custom attributes.", tok[k].StartIndex); return attr; } customAttribs.Add(attr); } if (tok[i].IsBuiltin("using")) { if (customAttribs.Count > 0) throw new ParseException("'using' directives cannot have custom attributes.", startIndex); return parseUsingDeclaration(tok, ref i); } if (tok[i].IsBuiltin("namespace")) { if (customAttribs.Count > 0) throw new ParseException("Namespaces cannot have custom attributes.", startIndex); return parseNamespace(tok, ref i); } var j = i; var modifiers = new[] { "abstract", "const", "extern", "internal", "new", "override", "partial", "private", "protected", "public", "readonly", "sealed", "static", "unsafe", "virtual", "volatile" }; while ((tok[j].Type == TokenType.Builtin || tok[j].Type == TokenType.Identifier) && modifiers.Contains(tok[j].TokenStr)) j++; if (tok[j].IsBuiltin("class") || tok[j].IsBuiltin("struct") || tok[j].IsBuiltin("interface")) { return parseClassStructOrInterfaceDeclaration(tok, ref i, customAttribs, j); } else if (tok[j].IsBuiltin("delegate")) { return parseDelegateDeclaration(tok, ref i, customAttribs, j); } else if (tok[j].IsBuiltin("enum")) { return parseEnumDeclaration(tok, ref i, customAttribs, j); } else if (tok[j].Type == TokenType.Identifier && tok[j + 1].IsBuiltin("(")) { return parseConstructorDeclaration(tok, ref i, customAttribs, j); } else if (tok[j].IsBuiltin("~") && tok[j + 1].Type == TokenType.Identifier && tok[j + 2].IsBuiltin("(")) { return parseDestructorDeclaration(tok, ref i, customAttribs, j); } else if (tok[j].IsBuiltin("implicit") || tok[j].IsBuiltin("explicit")) { return parseCastOperatorOverloadDeclaration(tok, ref i, customAttribs, j); } else if (tok[j].IsBuiltin("operator")) { throw new ParseException("You must specify either 'implicit operator', 'explicit operator', or a return type before the 'operator' keyword.", tok[j].StartIndex); } else { var afterModifiers = j; // It could be a field, a method, an operator overload, a property or an event var isEvent = false; if (tok[j].IsBuiltin("event")) { isEvent = true; j++; } var prevIndex = tok[j].StartIndex; CsTypeName type; try { type = parseTypeName(tok, ref j, typeIdentifierFlags.AllowKeywords | typeIdentifierFlags.AllowNullablesAndPointers | typeIdentifierFlags.AllowArrays).Item1; } catch (ParseException e) { if (e.Index == prevIndex) throw new ParseException("'class', 'struct', 'interface', 'enum', 'delegate', 'event', constructor or destructor declaration, 'implicit operator', 'explicit operator', or type expected.", tok[j].StartIndex); throw; } if (tok[j].IsBuiltin("this") && !isEvent) { // Indexed property var prop = new CsIndexedProperty { StartIndex = startIndex, Type = type, CustomAttributes = customAttribs }; parseModifiers(prop, tok, ref i); if (i != afterModifiers) throw new ParseException("The modifier '{0}' is not valid for indexed properties.".Fmt(tok[i].TokenStr), startIndex); i = j + 1; if (!tok[i].IsBuiltin("[")) throw new ParseException("'[' expected.", tok[j].StartIndex); prop.Parameters = parseParameterList(tok, ref i); parsePropertyBody(prop, tok, ref i); prop.EndIndex = tok[i - 1].EndIndex; return prop; } if (tok[j].IsBuiltin("operator") && !isEvent) return parseOperatorOverloadDeclaration(tok, ref i, customAttribs, j, afterModifiers, type); string name = tok[j].Identifier(isEvent ? "Identifier expected." : "Identifier, 'this' or 'operator' expected."); j++; if (tok[j].IsBuiltin("{")) { if (isEvent) { var evnt = new CsEvent { StartIndex = startIndex, Type = type, NamesAndInitializers = new List<CsNameAndExpression> { new CsNameAndExpression { StartIndex = tok[afterModifiers].StartIndex, EndIndex = tok[afterModifiers].EndIndex, Name = name } }, CustomAttributes = customAttribs }; parseModifiers(evnt, tok, ref i); if (i != afterModifiers) throw new ParseException("The modifier '{0}' is not valid for events.".Fmt(tok[i].TokenStr), startIndex); i = j; parseEventBody(evnt, tok, ref i); evnt.EndIndex = tok[i - 1].EndIndex; return evnt; } else { // It’s a property var prop = new CsProperty { StartIndex = startIndex, Type = type, Name = name, CustomAttributes = customAttribs }; parseModifiers(prop, tok, ref i); if (i != afterModifiers) throw new ParseException("The modifier '{0}' is not valid for properties.".Fmt(tok[i].TokenStr), startIndex); i = j; parsePropertyBody(prop, tok, ref i); prop.EndIndex = tok[i - 1].EndIndex; return prop; } } else if (tok[j].IsBuiltin("=") || tok[j].IsBuiltin(";") || tok[j].IsBuiltin(",")) { return parseFieldOrEventDeclaration(isEvent, tok, ref i, customAttribs, afterModifiers, j, type, null, name); } else if (tok[j].IsBuiltin("(") || tok[j].IsBuiltin("<") || tok[j].IsBuiltin(".")) { // Could still be a method, event, property or indexer return parseMemberDeclarationComplexCase(tok, ref i, customAttribs, j, afterModifiers, isEvent, type, name, startIndex); } else { if (isEvent) throw new ParseException("'=', ',', ';' or '{' expected.", tok[j].StartIndex); else throw new ParseException("For a field, '=', ',' or ';' expected. For a method, '(' or '<' expected. For a property, '{' expected.", tok[j].StartIndex); } } }
private static CsMember parseMemberDeclarationComplexCase(TokenJar tok, ref int i, List<CsCustomAttributeGroup> customAttribs, int j, int afterModifiers, bool isEvent, CsTypeName type, string name, int startIndex) { // If it's "(", it's a method. // If it's "<", it may be a generic method, or it may be a property, event or (possibly generic) method that explicitly implements an interface member from a generic interface. // If it's ".", it is a property, event or (possibly generic) method that explicitly implements an interface member. List<CsGenericTypeParameter> genericTypeParameters = null; CsConcreteTypeName implementsFrom = null; if (tok[j].IsBuiltin("<") || tok[j].IsBuiltin(".")) { // In the case that this might be an explicit interface implementation, try to parse the interface type. j--; var ty = (CsConcreteTypeName) parseTypeName(tok, ref j, typeIdentifierFlags.Lenient).Item1; if (tok[j].IsBuiltin(".") && tok[j + 1].IsBuiltin("this")) { // It's an explicit implementation for an indexed property var prop = new CsIndexedProperty { StartIndex = startIndex, Type = type, CustomAttributes = customAttribs, ImplementsFrom = ty }; parseModifiers(prop, tok, ref i); if (i != afterModifiers) throw new ParseException("The modifier '{0}' is not valid for indexed properties.".Fmt(tok[i].TokenStr), tok[i].StartIndex); i = j + 2; if (!tok[i].IsBuiltin("[")) throw new ParseException("'[' expected.", tok[j].StartIndex); prop.Parameters = parseParameterList(tok, ref i); parsePropertyBody(prop, tok, ref i); prop.EndIndex = tok[i - 1].EndIndex; return prop; } // Apart from 'this', no builtins (keywords) are allowed if (!(ty.Parts[ty.Parts.Count - 1] is CsNameIdentifier)) throw new ParseException("Identifier expected instead of '{0}'.".Fmt(ty.Parts[ty.Parts.Count - 1]), tok[j - 1].StartIndex); // The following cases are still possible: // ① a generic method declaration without custom attributes, e.g. // void MyMethod<T>() { } // In this case, ‘ty’ contains ‘MyMethod<T>’ as if T were a generic type argument instead of a generic type parameter declaration, and j points at ‘(’. // ② a generic method declaration with custom attributes, e.g. // void MyMethod<[Foo] T>() { } // In this case, ‘ty’ contains just ‘MyMethod’ and j points at ‘<’. // ③ an explicit interface implementation, e.g. // a. void IMyInterface.MyMethod() { } // non-generic method // b. void IMyInterface.MyMethod<T>() { } // generic method without custom attributes // c. void IMyInterface.MyMethod<[Foo] T>() { } // generic method with custom attributes // d. int IMyInterface.MyProperty { get { ... } } // property // e. event EventHandler IMyInterface.MyEvent { add { ... } remove { ... } } // event // The interface could be anything, including namespaces, nested types and generic type arguments // (e.g. MyNamespace.IMyInterface<List<int>.Enumerator>). // In all cases, ‘ty’ contains not just the interface name, but also *at least* the member name and *at most* the member name plus its generic parameters. // Therefore, either way, remove it from ‘ty’ to get the genuine interface name. var lastPart = (CsNameIdentifier) ty.Parts[ty.Parts.Count - 1]; ty.Parts.RemoveAt(ty.Parts.Count - 1); if (lastPart.GenericTypeArguments != null) { // If the last part has generic type “arguments”, then we are in case ① or ③b. // In that case, we need to convert those “arguments” into generic parameters. genericTypeParameters = new List<CsGenericTypeParameter>(); foreach (var g in lastPart.GenericTypeArguments) { var single = g.GetSingleIdentifier(); if (single == null) throw new ParseException("Invalid generic type parameter declaration.", tok[j].StartIndex); genericTypeParameters.Add(new CsGenericTypeParameter { StartIndex = g.StartIndex, EndIndex = g.EndIndex, Name = single }); } } else if (tok[j].IsBuiltin("<")) { // The last part didn’t have generic type arguments, and we are on a “<” token, so we are in case ② or ③c. genericTypeParameters = parseGenericTypeParameterList(tok, ref j); } // The “name” that was passed in was the first token after the return type, so it would have been // set to the first identifier in the interface name, which is of course wrong. Fix it now that we know the real name. name = lastPart.Name; // If “ty” has any parts left, then that’s the interface name, otherwise this wasn’t an explicit interface declaration in the first place. implementsFrom = ty.Parts.Count == 0 ? null : ty; if (tok[j].IsBuiltin("{")) { // Case ③d and ③e: an explicitly-implemented property or an event. // It must be an explicit implementation because normal properties/events are already handled by parseMemberDeclaration(). // Explicitly-implemented events must have a body with add/remove methods; they can’t be events with the fields syntax, e.g. // event EventHandler IMyInterface.MyEvent = null; // ✗ not allowed if (genericTypeParameters != null) throw new ParseException("Properties and events cannot be generic.", tok[j].StartIndex); if (isEvent) { var nameExpr = new CsNameAndExpression { // We are assuming that after the modifiers comes the “event” keyword and after that, the name StartIndex = tok[afterModifiers + 1].StartIndex, EndIndex = tok[afterModifiers + 1].EndIndex, Name = name }; var evnt = new CsEvent { StartIndex = startIndex, Type = type, NamesAndInitializers = new List<CsNameAndExpression> { nameExpr }, CustomAttributes = customAttribs, ImplementsFrom = implementsFrom }; parseModifiers(evnt, tok, ref i); if (i != afterModifiers) throw new ParseException("The modifier '{0}' is not valid for events.".Fmt(tok[i].TokenStr), tok[i].StartIndex); i = j; parseEventBody(evnt, tok, ref i); evnt.EndIndex = tok[i - 1].EndIndex; return evnt; } else { var prop = new CsProperty { StartIndex = startIndex, Type = type, Name = name, CustomAttributes = customAttribs, ImplementsFrom = implementsFrom }; parseModifiers(prop, tok, ref i); if (i != afterModifiers) throw new ParseException("The modifier '{0}' is not valid for properties.".Fmt(tok[i].TokenStr), tok[i].StartIndex); i = j; parsePropertyBody(prop, tok, ref i); prop.EndIndex = tok[i - 1].EndIndex; return prop; } } else if (isEvent && (tok[j].IsBuiltin(";") || tok[j].IsBuiltin("="))) { // It’s an event without a body — not allowed for explicit interface implementations throw new ParseException("Events that explicitly implement an interface event must have add/remove methods.", tok[i].StartIndex); } } // We’ve taken care of explicitly-implemented properties, indexers and events. The only case left is that it must be a method. // implementsFrom and genericTypeParameters are fully populated. CsMethod meth = new CsMethod { StartIndex = startIndex, Type = type, Name = name, CustomAttributes = customAttribs, ImplementsFrom = implementsFrom, GenericTypeParameters = genericTypeParameters }; parseModifiers(meth, tok, ref i); if (i != afterModifiers) throw new ParseException("The modifier '{0}' is not valid for methods.".Fmt(tok[i].TokenStr), tok[i].StartIndex); i = j; if (!tok[i].IsBuiltin("(")) throw new ParseException("'(' expected.", tok[i].StartIndex, meth); try { meth.Parameters = parseParameterList(tok, ref i); } catch (ParseException e) { if (e.IncompleteResult is List<CsParameter>) meth.Parameters = (List<CsParameter>) e.IncompleteResult; throw new ParseException(e.Message, e.Index, meth, e); } if (tok[i].IsIdentifier("where")) { try { meth.GenericTypeConstraints = parseGenericTypeConstraints(tok, ref i); } catch (ParseException e) { if (e.IncompleteResult is Dictionary<string, List<CsGenericTypeConstraint>>) meth.GenericTypeConstraints = (Dictionary<string, List<CsGenericTypeConstraint>>) e.IncompleteResult; throw new ParseException(e.Message, e.Index, meth, e); } } if (tok[i].IsBuiltin(";")) i++; else if (tok[i].IsBuiltin("{")) { try { meth.MethodBody = parseBlock(tok, ref i); } catch (ParseException e) { if (e.IncompleteResult is CsBlock) meth.MethodBody = (CsBlock) e.IncompleteResult; throw new ParseException(e.Message, e.Index, meth, e); } } else throw new ParseException(@"';', '{' or 'where' expected.", tok[i].StartIndex, meth); meth.EndIndex = tok[i - 1].EndIndex; return meth; }
private static CsEventOrField parseFieldOrEventDeclaration(bool isEvent, TokenJar tok, ref int i, List<CsCustomAttributeGroup> customAttribs, int afterModifiers, int afterName, CsTypeName type, CsTypeName implementsFrom, string name) { var startIndex = tok[i].StartIndex; CsEventOrField ret; if (isEvent) ret = new CsEvent { StartIndex = startIndex, Type = type, CustomAttributes = customAttribs, ImplementsFrom = implementsFrom }; else ret = new CsField { StartIndex = startIndex, Type = type, CustomAttributes = customAttribs }; parseModifiers(ret, tok, ref i); if (i != afterModifiers) throw new ParseException("The modifier '{0}' is not valid for {1}.".Fmt(tok[i].TokenStr, isEvent ? "events" : "fields"), tok[i].StartIndex); i = afterName; CsExpression initializer = null; try { if (tok[i].IsBuiltin("=")) { i++; initializer = parseExpression(tok, ref i); } ret.NamesAndInitializers.Add(new CsNameAndExpression { StartIndex = tok[afterName - 1].StartIndex, EndIndex = tok[i - 1].EndIndex, Name = name, Expression = initializer }); while (tok[i].IsBuiltin(",")) { i++; var nameStartIndex = tok[i].StartIndex; name = tok[i].Identifier(); initializer = null; i++; if (tok[i].IsBuiltin("=")) { i++; initializer = parseExpression(tok, ref i); } ret.NamesAndInitializers.Add(new CsNameAndExpression { StartIndex = nameStartIndex, EndIndex = tok[i - 1].EndIndex, Name = name, Expression = initializer }); } } catch (ParseException e) { if (e.IncompleteResult is CsExpression) { ret.NamesAndInitializers.Add(new CsNameAndExpression { StartIndex = tok[i].StartIndex, Name = name, Expression = (CsExpression) e.IncompleteResult }); throw new ParseException(e.Message, e.Index, ret, e); } throw; } if (!tok[i].IsBuiltin(";")) throw new ParseException("'=', ',' or ';' expected.", tok[i].StartIndex, ret); ret.EndIndex = tok[i].EndIndex; i++; return ret; }