static CodeAttributeArgumentCollection _ParseCustomAttributeArguments(_PC pc) { var result = new CodeAttributeArgumentCollection(); if (ST.lparen != pc.SymbolId) { return(result); } if (!pc.Advance()) { throw new ArgumentException("Unterminated argument list", "input"); } var named = false; while (ST.rparen != pc.SymbolId) { var arg = new CodeAttributeArgument(); if (ST.identifier == pc.SymbolId) { var s = pc.Value; var pc2 = pc.GetLookAhead(); pc2.EnsureStarted(); pc2.Advance(); _SkipComments(pc2); if (ST.eq == pc2.SymbolId) { pc.Advance(); _SkipComments(pc); pc.Advance(); arg.Name = s; arg.Value = _ParseExpression(pc); result.Add(arg); named = true; continue; } } if (named) { throw new ArgumentException("Named custom attribute arguments must follow the unnamed arguments.", "input"); } var exp = _ParseExpression(pc); _SkipComments(pc); arg.Value = exp; result.Add(arg); if (ST.comma == pc.SymbolId) { if (!pc.Advance()) { throw new ArgumentException("Unterminated argument list.", "input"); } } } if (ST.rparen != pc.SymbolId) { throw new ArgumentException("Unterminated argument list.", "input"); } pc.Advance(); return(result); }
static CodeCompileUnit _ParseCompileUnit(_PC pc) { var l = pc.Line; var c = pc.Column; var p = pc.Position; var result = new CodeCompileUnit().Mark(l, c, p); var ns = new CodeNamespace().Mark(l, c, p); result.Namespaces.Add(ns); while (ST.directive == pc.SymbolId || ST.lineComment == pc.SymbolId || ST.blockComment == pc.SymbolId) { switch (pc.SymbolId) { case ST.directive: var d = _ParseDirective(pc) as CodeDirective; if (null != d) { result.StartDirectives.Add(d); } break; case ST.blockComment: ns.Comments.Add(_ParseCommentStatement(pc)); break; case ST.lineComment: ns.Comments.Add(_ParseCommentStatement(pc, true)); break; } } while (ST.usingKeyword == pc.SymbolId) { while (ST.directive == pc.SymbolId || ST.lineComment == pc.SymbolId || ST.blockComment == pc.SymbolId) { pc.Advance(false); } var l2 = pc.Line; var c2 = pc.Column; var p2 = pc.Position; pc.Advance(); var nsi = new CodeNamespaceImport(_ParseNamespaceName(pc)).SetLoc(l2, c2, p2); if (ST.semi != pc.SymbolId) { pc.Error("Expecting ; in using declaration"); } pc.Advance(false); ns.Imports.Add(nsi); } while (ST.lbracket == pc.SymbolId) { var pc2 = pc.GetLookAhead(true); pc2.Advance(); if (ST.assemblyKeyword != pc2.SymbolId) { break; } result.AssemblyCustomAttributes.AddRange(_ParseAttributeGroup(pc, false).Value); } while (!pc.IsEnded) { var startDirs = new CodeDirectiveCollection(); var comments = new CodeCommentStatementCollection(); CodeLinePragma lp = null; while (ST.directive == pc.SymbolId || ST.lineComment == pc.SymbolId || ST.blockComment == pc.SymbolId) { switch (pc.SymbolId) { case ST.directive: var d = _ParseDirective(pc); var llp = d as CodeLinePragma; if (null != llp) { lp = llp; } else if (null != d) { startDirs.Add(d as CodeDirective); } break; case ST.blockComment: comments.Add(_ParseCommentStatement(pc)); break; case ST.lineComment: comments.Add(_ParseCommentStatement(pc, true)); break; } } if (ST.namespaceKeyword == pc.SymbolId) { var nns = _ParseNamespace(pc); nns.Comments.AddRange(comments); result.Namespaces.Add(nns); } else { var t = _ParseTypeDecl(pc, false, pc.Line, pc.Column, pc.Position, null); t.Comments.AddRange(comments); t.StartDirectives.AddRange(startDirs); t.LinePragma = lp; ns.Types.Add(t); } } return(result); }
static CodeNamespace _ParseNamespace(_PC pc) { var l = pc.Line; var c = pc.Column; var p = pc.Position; var result = new CodeNamespace().Mark(l, c, p); while (ST.lineComment == pc.SymbolId || ST.blockComment == pc.SymbolId || ST.directive == pc.SymbolId) { if (ST.directive != pc.SymbolId) { result.Comments.Add(_ParseCommentStatement(pc, true)); } } if (ST.namespaceKeyword != pc.SymbolId) { pc.Error("Expecting namespace"); } pc.Advance(); result.Name = _ParseNamespaceName(pc); if (ST.lbrace != pc.SymbolId) { pc.Error("Expecing { in namespace declaration"); } pc.Advance(false); if (ST.directive == pc.SymbolId || ST.lineComment == pc.SymbolId || ST.blockComment == pc.SymbolId) { var pc2 = pc.GetLookAhead(true); if (ST.usingKeyword == pc2.SymbolId) { pc.Advance(); } } while (ST.usingKeyword == pc.SymbolId) { while (ST.directive == pc.SymbolId || ST.lineComment == pc.SymbolId || ST.blockComment == pc.SymbolId) { pc.Advance(false); } var l2 = pc.Line; var c2 = pc.Column; var p2 = pc.Position; pc.Advance(); var nsi = new CodeNamespaceImport(_ParseNamespaceName(pc)).SetLoc(l2, c2, p2); if (ST.semi != pc.SymbolId) { pc.Error("Expecting ; in using declaration"); } pc.Advance(false); result.Imports.Add(nsi); } while (ST.rbrace != pc.SymbolId) { result.Types.Add(_ParseTypeDecl(pc, false, pc.Line, pc.Column, pc.Position, null)); } if (ST.rbrace != pc.SymbolId) { pc.Error("Unterminated namespace declaration", l, c, p); } pc.Advance(false); return(result); }
static CodeStatement _ParseStatement(_PC pc, bool includeComments = false) { #region Preamble CodeLinePragma lp = null; var startDirs = new CodeDirectiveCollection(); while (ST.directive == pc.SymbolId) { var d = _ParseDirective(pc); if (null != d) { var clp = d as CodeLinePragma; if (null != clp) { lp = clp; } else { startDirs.Add(d as CodeDirective); } } while (!includeComments && ST.lineComment == pc.SymbolId || ST.blockComment == pc.SymbolId) { pc.Advance(false); } } CodeStatement stmt = null; if (includeComments && (ST.lineComment == pc.SymbolId || ST.blockComment == pc.SymbolId)) { stmt = _ParseCommentStatement(pc); stmt.StartDirectives.AddRange(startDirs); if (null != lp) { stmt.LinePragma = lp; } } else { while (ST.lineComment == pc.SymbolId || ST.blockComment == pc.SymbolId) { pc.Advance(false); } } #endregion Preamble var l = pc.Line; var c = pc.Column; var p = pc.Position; // if we got here we've parsed our start directives and this isn't a comment statement if (null == stmt) { _PC pc2 = null; switch (pc.SymbolId) { case ST.semi: // can't do much with empty statements pc.Advance(); stmt = new CodeSnippetStatement().SetLoc(l, c, p); break; case ST.gotoKeyword: pc.Advance(); if (ST.identifier != pc.SymbolId) { pc.Error("Expecting label identifier in goto statement"); } stmt = new CodeGotoStatement(pc.Value).SetLoc(l, c, p); if (ST.semi != pc.SymbolId) { pc.Error("Expecting ; in goto statement"); } pc.Advance(); break; case ST.returnKeyword: pc.Advance(); if (ST.semi != pc.SymbolId) { stmt = new CodeMethodReturnStatement(_ParseExpression(pc)).Mark(l, c, p); } else { stmt = new CodeMethodReturnStatement().SetLoc(l, c, p); } if (ST.semi != pc.SymbolId) { pc.Error("Expecting ; in return statement"); } pc.Advance(); break; case ST.throwKeyword: pc.Advance(); var expr = _ParseExpression(pc); stmt = new CodeThrowExceptionStatement(expr).Mark(l, c, p); if (ST.semi != pc.SymbolId) { pc.Error("Expecting ; in throw statement"); } pc.Advance(); break; case ST.ifKeyword: stmt = _ParseIfStatement(pc); break; case ST.whileKeyword: stmt = _ParseWhileStatement(pc); break; case ST.forKeyword: stmt = _ParseForStatement(pc); break; case ST.tryKeyword: stmt = _ParseTryCatchFinallyStatement(pc); break; case ST.varType: stmt = _ParseVariableDeclarationStatement(pc); break; default: // possibly a var decl, a label statement, or an expression statement if (ST.identifier == pc.SymbolId) { pc2 = pc.GetLookAhead(true); pc2.Advance(); if (ST.colon == pc2.SymbolId) // label { var lbl = pc2.Value; pc.Advance(); stmt = new CodeLabeledStatement(lbl, new CodeSnippetStatement().SetLoc(l, c, p)).SetLoc(l, c, p); pc2 = null; break; } } pc2 = null; pc2 = pc.GetLookAhead(true); pc2.ResetAdvanceCount(); var advc = 0; try { // possibly a var decl stmt = _ParseVariableDeclarationStatement(pc2); advc = pc2.AdvanceCount; while (advc > 0) { pc.Advance(false); --advc; } break; } catch (SlangSyntaxException sx) { try { pc.ResetAdvanceCount(); expr = _ParseExpression(pc); if (ST.semi != pc.SymbolId) { pc.Error("Expecting ; in expression statement"); } pc.Advance(); var bo = expr as CodeBinaryOperatorExpression; if (null != bo && CodeBinaryOperatorType.Assign == bo.Operator) { var ur = bo.UserData.Contains("slang:unresolved"); stmt = new CodeAssignStatement(bo.Left, bo.Right).Mark(l, c, p, ur); } else { stmt = new CodeExpressionStatement(expr).Mark(l, c, p); } break; } catch (SlangSyntaxException sx2) { if (pc.AdvanceCount > advc) { throw sx2; } throw sx; } } } } #region Post stmt.StartDirectives.AddRange(startDirs); if (null != lp) { stmt.LinePragma = lp; } while (!includeComments && ST.lineComment == pc.SymbolId || ST.blockComment == pc.SymbolId) { pc.Advance(false); } while (ST.directive == pc.SymbolId && pc.Value.StartsWith("#end", StringComparison.InvariantCulture)) { stmt.EndDirectives.Add(_ParseDirective(pc) as CodeDirective); while (!includeComments && ST.lineComment == pc.SymbolId || ST.blockComment == pc.SymbolId) { pc.Advance(false); } } #endregion Post return(stmt); }
static IList <KeyValuePair <string, CodeAttributeDeclaration> > _ParseCustomAttributeGroup(_PC pc) { // expects to be on [ pc.Advance(); _SkipComments(pc); if (pc.IsEnded) { throw new ArgumentException("Unterminated custom attribute declaration group"); } var result = new List <KeyValuePair <string, CodeAttributeDeclaration> >(); var target = pc.Value; var hasTarget = false; var pc2 = pc.GetLookAhead(); pc2.EnsureStarted(); pc2.Advance(); _SkipComments(pc2); if (ST.colon == pc2.SymbolId) { hasTarget = true; pc.Advance(); _SkipComments(pc); pc.Advance(); _SkipComments(pc); if (pc.IsEnded) { throw new ArgumentException("Unterminated custom attribute declaration group", "input"); } } while (ST.rbracket != pc.SymbolId) { var attr = _ParseCustomAttribute(pc); _SkipComments(pc); if (!hasTarget) { result.Add(new KeyValuePair <string, CodeAttributeDeclaration>(null, attr)); } else { result.Add(new KeyValuePair <string, CodeAttributeDeclaration>(target, attr)); } if (pc.IsEnded) { throw new ArgumentException("Unterminated custom attribute declaration group", "input"); } if (ST.comma == pc.SymbolId) { pc.Advance(); _SkipComments(pc); if (pc.IsEnded) { throw new ArgumentException("Unterminated custom attribute declaration group", "input"); } if (ST.rbracket == pc.SymbolId) { throw new ArgumentException("Unexpected comma found in attribute declaration group", "input"); } } } if (ST.rbracket != pc.SymbolId) { throw new ArgumentException("Invalid custom attribute declaration", "input"); } pc.Advance(); _SkipComments(pc); if (0 == result.Count) { throw new ArgumentException("Attribute groups must not be empty.", "input"); } return(result); }
static KeyValuePair <CodeTypeReference, string> _ParsePrivateImplementationType(_PC pc) { // i really hate this routine _SkipComments(pc); CodeTypeReference ptr = null; string name = null; _PC pc2 = pc.GetLookAhead(); pc2.EnsureStarted(); try { ptr = _ParseTypeRef(pc2, true); } catch { ptr = null; } if (ptr != null) { if (ST.dot == pc2.SymbolId) // we didn't finish parsing the next field. This is probably what we wanted to begin with so yay { _ParseTypeRef(pc); pc.Advance(); _SkipComments(pc); if (pc.IsEnded) { throw new ArgumentException("Unterminated private member declaration"); } var s = pc.Value; pc.Advance(); return(new KeyValuePair <CodeTypeReference, string>(ptr, s)); } // HACK: it will have parsed the name as part of the typeref, so we just trim it off. var idx = ptr.BaseType.LastIndexOfAny(new char[] { '.', '+' }); if (0 > idx) { pc.Advance(); if (ST.dot != pc.SymbolId) { // the entire thing is the name. There is no private implementation type if (0 < ptr.TypeArguments.Count) { throw new ArgumentException("Missing member name on private member declaration", "input"); } return(new KeyValuePair <CodeTypeReference, string>(null, ptr.BaseType)); } else // this is probably a "this" { pc.Advance(); _SkipComments(pc); if (ST.keyword == pc.SymbolId && "this" == pc.Value) { pc.Advance(); return(new KeyValuePair <CodeTypeReference, string>(ptr, "this")); } throw new ArgumentException("Illegal private member implementation type.", "input"); } } name = ptr.BaseType.Substring(idx + 1); ptr.BaseType = ptr.BaseType.Substring(0, idx); _ParseTypeRef(pc, false); // advance return(new KeyValuePair <CodeTypeReference, string>(ptr, name)); } var n = pc.Value; pc.Advance(); return(new KeyValuePair <CodeTypeReference, string>(null, n)); //throw new ArgumentException("Expecting identifier on private member declaration", "input"); }
static CodeIterationStatement _ParseForStatement(_PC pc) { // expects to be on for if (!pc.Advance()) { throw new ArgumentException("Unterminated for statement", "input"); } _SkipComments(pc); if (ST.lparen != pc.SymbolId || !pc.Advance()) { throw new ArgumentException("Unterminated for statement", "input"); } _SkipComments(pc); CodeStatement init = null; if (pc.IsEnded) { throw new ArgumentException("Unterminated for statement", "input"); } if (ST.semi != pc.SymbolId) { var pc2 = pc.GetLookAhead(); pc2.EnsureStarted(); try { init = _ParseVariableDeclaration(pc2); } catch { init = null; } } if (null != init) { _ParseVariableDeclaration(pc); _SkipComments(pc); } else { _SkipComments(pc); if (ST.semi != pc.SymbolId) { var e = _ParseExpression(pc); var bbo = e as CodeBinaryOperatorExpression; if (null == e) { throw new NotImplementedException("Expression in init statement was null"); } if (null != bbo && CodeBinaryOperatorType.Assign == bbo.Operator) { init = new CodeAssignStatement(bbo.Left, bbo.Right); } else { init = new CodeExpressionStatement(e); } _SkipComments(pc); if (ST.semi != pc.SymbolId) { throw new ArgumentException("Invalid init statement in for statement", "input"); } if (pc.IsEnded) { throw new ArgumentException("Unterminated for statement", "input"); } } } if (null == init) { if (ST.semi != pc.SymbolId) { throw new ArgumentException("Invalid for statement", "input"); } pc.Advance(); _SkipComments(pc); } if (pc.IsEnded) { throw new ArgumentException("Unterminated for statement", "input"); } CodeExpression test = null; if (ST.semi != pc.SymbolId) { test = _ParseExpression(pc); _SkipComments(pc); if (ST.semi != pc.SymbolId) { throw new ArgumentException("Invalid test expression in for statement", "input"); } if (!pc.Advance()) { throw new ArgumentException("Unterminated for statement", "input"); } } _SkipComments(pc); if (pc.IsEnded) { throw new ArgumentException("Unterminated for statement", "input"); } CodeExpression inc = null; if (ST.rparen != pc.SymbolId) { inc = _ParseExpression(pc); _SkipComments(pc); } if (ST.rparen != pc.SymbolId) { throw new ArgumentNullException("Invalid incremement statement in for loop"); } if (!pc.Advance()) { throw new ArgumentException("Unterminated for statement", "input"); } _SkipComments(pc); if (pc.IsEnded) { throw new ArgumentException("Unterminated for statement", "input"); } var bo = inc as CodeBinaryOperatorExpression; CodeStatement incs = null; if (null != inc) { if (null != bo && CodeBinaryOperatorType.Assign == bo.Operator) { incs = new CodeAssignStatement(bo.Left, bo.Right); } else { incs = new CodeExpressionStatement(inc); } } if (null == init) { init = new CodeSnippetStatement(); } if (null == incs) { incs = new CodeSnippetStatement(); } if (null == test) { test = new CodeSnippetExpression(); } var result = new CodeIterationStatement(init, test, incs); if (ST.lbrace == pc.SymbolId) { if (!pc.Advance()) { throw new ArgumentException("Unterminated for statement", "input"); } while (!pc.IsEnded && ST.rbrace != pc.SymbolId) { result.Statements.Add(_ParseStatement(pc, true)); } if (ST.rbrace != pc.SymbolId) { throw new ArgumentException("Unterminated for statement", "input"); } pc.Advance(); _SkipComments(pc); if (pc.IsEnded) { return(result); } } else { result.Statements.Add(_ParseStatement(pc)); } _SkipComments(pc); return(result); }
static CodeStatement _ParseStatement(_PC pc, bool includeComments = false) { if (includeComments && (ST.lineComment == pc.SymbolId || ST.blockComment == pc.SymbolId)) { return(_ParseCommentStatement(pc)); } _SkipComments(pc); var pc2 = pc.GetLookAhead(); pc2.EnsureStarted(); CodeVariableDeclarationStatement vs = null; try { vs = _ParseVariableDeclaration(pc2); } catch { vs = null; } if (null != vs) { // advance _ParseVariableDeclaration(pc); return(vs); } pc2 = pc.GetLookAhead(); pc2.EnsureStarted(); CodeExpression e; try { e = _ParseExpression(pc2); } catch { e = null; } if (null != e) { _SkipComments(pc2); if (ST.semi == pc2.SymbolId) { pc2.Advance(); _ParseExpression(pc); _SkipComments(pc); pc.Advance(); // c# treats a=1; as an expression-statement using an assign expression, linguistically // so that's how we parsed it. However, CodeDOM has a special case object for this // called CodeAssignStatement. For maximum language portability, we don't want to rely // on assign expressions when we don't have to as they can get weird. So what we do is // whenever we parse one of these we detect it and turn it into an assign statement var bo = e as CodeBinaryOperatorExpression; if (null != bo && CodeBinaryOperatorType.Assign == bo.Operator) { return(new CodeAssignStatement(bo.Left, bo.Right)); } return(new CodeExpressionStatement(e)); } else if (ST.addAssign == pc2.SymbolId || ST.subAssign == pc2.SymbolId) { bool isAttach = ST.addAssign == pc2.SymbolId; _ParseExpression(pc); _SkipComments(pc); pc.Advance(); pc2.Advance(); _SkipComments(pc); var le = _ParseExpression(pc); _SkipComments(pc); if (pc.IsEnded) { throw new ArgumentException("Unterminated statement. Expecting ;", "input"); } pc.Advance(); var v = e as CodeVariableReferenceExpression; CodeEventReferenceExpression er = null; if (null != v) { er = new CodeEventReferenceExpression(null, v.VariableName); } else { var f = e as CodeFieldReferenceExpression; if (null != f) { er = new CodeEventReferenceExpression(f.TargetObject, f.FieldName); } } if (null == er) { throw new ArgumentNullException("The attach/remove target does not refer to a valid event", "input"); } er.UserData.Add("slang:unresolved", true); return(isAttach ? new CodeAttachEventStatement(er, le) as CodeStatement : new CodeRemoveEventStatement(er, le)); } } switch (pc.SymbolId) { case ST.keyword: switch (pc.Value) { case "if": return(_ParseIfStatement(pc)); case "goto": return(_ParseGotoStatement(pc)); case "for": return(_ParseForStatement(pc)); case "while": return(_ParseWhileStatement(pc)); case "return": return(_ParseReturnStatement(pc)); case "throw": return(_ParseThrowStatement(pc)); case "try": return(_ParseTryCatchFinallyStatement(pc)); case "var": case "bool": case "char": case "string": case "sbyte": case "byte": case "short": case "ushort": case "int": case "uint": case "long": case "ulong": case "float": case "double": case "decimal": return(_ParseVariableDeclaration(pc)); default: throw new NotSupportedException(string.Format("The keyword {0} is not supported", pc.Value)); } case ST.identifier: // we already know it isn't an expression var s = pc.Value; pc2 = pc.GetLookAhead(); pc2.EnsureStarted(); pc2.Advance(); if (ST.colon == pc2.SymbolId) { // CodeDOM for some reason wants us to attach a statement to a label. // we don't like that for a number of reasons so we don't do it. var ls = new CodeLabeledStatement(pc.Value); pc.Advance(); _SkipComments(pc); if (pc.IsEnded || ST.colon != pc.SymbolId) { throw new ArgumentException("Unterminated label. Expecting :", "input"); } pc.Advance(); return(ls); } throw new NotImplementedException("Not finished"); default: throw new ArgumentException(string.Format("Unexpected token {0} found statement.", pc.Value), "input"); } }
static CodeTypeReference _ParseTypeRef(_PC pc, bool notArrayPart = false, bool once = false) { _SkipComments(pc); if (pc.IsEnded) { throw new ArgumentException("Expecting a type reference", "input"); } _PC pc2; // for lookahead var isIntrinsic = false; var result = new CodeTypeReference(); var first = true; while (!pc.IsEnded) { var s = pc.Value; if (first) { if (ST.keyword == pc.SymbolId) { s = _TranslateIntrinsicType(s); isIntrinsic = true; } else if (ST.identifier != pc.SymbolId) { throw new ArgumentException("An identifier was expected", "input"); } result.BaseType = s; } else { if (ST.identifier != pc.SymbolId) { throw new ArgumentException("An identifier was expected", "input"); } result.BaseType = string.Concat(result.BaseType, "+", s); } pc.Advance(); _SkipComments(pc); if (pc.IsEnded) { return(result); } if (!first || !isIntrinsic) { _SkipComments(pc); while (ST.dot == pc.SymbolId) { // this might be a nested type but we can't know that yet result.UserData["slang:unresolved"] = true; var bt = string.Concat(result.BaseType, "."); pc2 = pc.GetLookAhead(); pc2.EnsureStarted(); pc2.Advance(); _SkipComments(pc2); if (ST.identifier != pc2.SymbolId) { return(result); } pc.Advance(); _SkipComments(pc); result.BaseType = string.Concat(bt, pc.Value); if (!pc.Advance()) { return(result); } } } if (ST.lt == pc.SymbolId) // generic type parameters follow { var c = result.TypeArguments.Count; result = _ParseTypeGenerics(pc, result); // HACK: Microsoft's CodeTypeReference object doesn't handle nested type references properly // in the case of generics so we have to fix it up. We just manually add the type argument count // to the end of a nested type, since it's not done in the CodeTypeReference internals like it // is supposed to be. It works properly however, on the outermost type (first=true) so we don't // touch it until we start nesting // TODO: Frankly, if they ever fix this (they won't) it will break this code (which is why they won't) // but we could check here to see if it has been fixed before we "operate". I didn't bother, but // it would make things more robust in the case of Microsoft ever fixing anything. if (!first && result.TypeArguments.Count > c) { result.BaseType = string.Concat(result.BaseType, "`", result.TypeArguments.Count - c); } } _SkipComments(pc); if (!notArrayPart) { if (ST.lbracket == pc.SymbolId) { result = _ParseArrayTypeModifiers(result, pc); } _SkipComments(pc); } if (once || ST.dot != pc.SymbolId) { break; } pc2 = pc.GetLookAhead(); pc2.EnsureStarted(); pc2.Advance(); _SkipComments(pc2); if (ST.identifier != pc2.SymbolId) { return(result); } pc.Advance(); _SkipComments(pc); if (pc.IsEnded) { throw new ArgumentException("Unterminated type reference", "input"); } first = false; } return(result); }