static VList <LNode> AddLineDirectives(VList <LNode> nodes, bool stmtContext, ref int sourceLine_) { int sourceLine = sourceLine_; nodes = nodes.SmartSelect(node => { if (stmtContext && sourceLine > 0 && node.AttrNamed(S.TriviaAppendStatement) == null) { sourceLine++; // printer will print a newline by default } int explicitNewlines = node.Attrs.Count(n => n.IsIdNamed(S.TriviaNewline)); sourceLine += explicitNewlines; // Generate line directive if necessary; to avoid excess // clutter, don't consider emit #line directives within an expression. string lineDirective = null; if (stmtContext || explicitNewlines != 0) { if (node.Range.Source is EmptySourceFile || string.IsNullOrEmpty(node.Range.Source.FileName)) { // synthetic code: no source location if (sourceLine != -1) { sourceLine = -1; lineDirective = "#line default"; } } else { var start = node.Range.Start; if (sourceLine != start.Line) { sourceLine = start.Line; lineDirective = "#line " + start.Line + " " + EcsNodePrinter.PrintString(start.FileName, '"'); } } } int sourceLineWas = sourceLine; if (node.Name.Name.StartsWith("#") && node.ArgCount > 1) { // For some special calls like #if, #while, and #doWhile, // printer might print newlines in places we don't know about, // so erase our knowledge of what the current line is. if (sourceLine > 0) { sourceLine = int.MinValue; } } // Process children node = node.WithAttrs(AddLineDirectives(node.Attrs, false, ref sourceLine)); if (node.IsCall) { node = node.WithArgs(AddLineDirectives(node.Args, node.Calls(S.Braces), ref sourceLine)); } if (sourceLine > 0) { sourceLine += node.GetTrailingTrivia().Count(n => n.IsIdNamed(S.TriviaNewline)); } // Finally, add a line directive if requested. if (lineDirective != null) { var trivia = F.Trivia(S.TriviaCsPPRawText, lineDirective); if (!node.Attrs.Contains(trivia)) { // Trivia tends not to be included in the source range so adding #line // before trivia is generally wrong, while adding #line after attributes // tends to be wrong too. Sigh. Search for a good location to insert... // unless inserting #line default which we can just put at the beginning int insertIndex = 0; if (sourceLineWas > 0) { insertIndex = node.Attrs.IndexWhere(n => n.Range.Start.Line == sourceLineWas && n.Range.Source == node.Range.Source); if (insertIndex == -1) { insertIndex = node.Attrs.Count; } } node = node.WithAttrs(node.Attrs.Insert(insertIndex, trivia)); } } return(node); }); sourceLine_ = sourceLine; return(nodes); }
/// <summary>Expresses an EC# token as a string.</summary> /// <remarks>Note that some Tokens do not contain enough information to /// reconstruct a useful token string, e.g. comment tokens do not store the /// comment but merely contain the location of the comment in the source code. /// For performance reasons, a <see cref="Token"/> does not have a reference /// to its source file, so this method cannot return the original string. /// <para/> /// The results are undefined if the token was not produced by <see cref="EcsLexer"/>. /// </remarks> public static string ToString(Token t) { StringBuilder sb = new StringBuilder(); if (t.Kind == TokenKind.Operator || t.Kind == TokenKind.Assignment || t.Kind == TokenKind.Dot) { if (t.Type() == TT.BQString) { return(EcsNodePrinter.PrintString((t.Value ?? "").ToString(), '`', false)); } string value = (t.Value ?? "(null)").ToString(); return(value); } switch (t.Type()) { case TT.EOF: return("/*EOF*/"); case TT.Spaces: return(" "); case TT.Newline: return("\n"); case TT.SLComment: return("//\n"); case TT.MLComment: return("/**/"); case TT.Shebang: return("#!" + (t.Value ?? "").ToString() + "\n"); case TT.Id: case TT.ContextualKeyword: return(EcsNodePrinter.PrintId(t.Value as Symbol ?? GSymbol.Empty)); case TT.@base: return("base"); case TT.@this: return("this"); case TT.Number: case TT.String: case TT.SQString: case TT.Symbol: case TT.OtherLit: return(EcsNodePrinter.PrintLiteral(t.Value, t.Style)); case TT.Comma: return(","); case TT.Semicolon: return(";"); case TT.LParen: return("("); case TT.RParen: return(")"); case TT.LBrack: return("["); case TT.RBrack: return("]"); case TT.LBrace: return("{"); case TT.RBrace: return("}"); case TT.AttrKeyword: string value = (t.Value ?? "(null)").ToString(); return(value); case TT.TypeKeyword: Symbol valueSym = (t.Value as Symbol) ?? GSymbol.Empty; string result; if (EcsNodePrinter.TypeKeywords.TryGetValue(valueSym, out result)) { return(result); } else { Debug.Fail("Unexpected value for " + t.Type()); return((t.Value ?? "(null)").ToString()); } case TT.@break: return("break"); case TT.@case: return("case"); case TT.@checked: return("checked"); case TT.@class: return("class"); case TT.@continue: return("continue"); case TT.@default: return("default"); case TT.@delegate: return("delegate"); case TT.@do: return("do"); case TT.@enum: return("enum"); case TT.@event: return("event"); case TT.@fixed: return("fixed"); case TT.@for: return("for"); case TT.@foreach: return("foreach"); case TT.@goto: return("goto"); case TT.@if: return("if"); case TT.@interface: return("interface"); case TT.@lock: return("lock"); case TT.@namespace: return("namespace"); case TT.@return: return("return"); case TT.@struct: return("struct"); case TT.@switch: return("switch"); case TT.@throw: return("throw"); case TT.@try: return("try"); case TT.@unchecked: return("unchecked"); case TT.@using: return("using"); case TT.@while: return("while"); case TT.@operator: return("operator"); case TT.@sizeof: return("sizeof"); case TT.@typeof: return("typeof"); case TT.@else: return("else"); case TT.@catch: return("catch"); case TT.@finally: return("finally"); case TT.@in: return("in"); case TT.@as: return("as"); case TT.@is: return("is"); case TT.@new: return("new"); case TT.@out: return("out"); case TT.@stackalloc: return("stackalloc"); case TT.PPif: return("#if"); case TT.PPelse: return("#else"); case TT.PPelif: return("#elif"); case TT.PPendif: return("#endif"); case TT.PPdefine: return("#define"); case TT.PPundef: return("#undef"); case TT.PPwarning: return("#warning" + t.Value); case TT.PPerror: return("#error" + t.Value); case TT.PPnote: return("#note" + t.Value); case TT.PPline: return("#line"); case TT.PPregion: return("#region" + t.Value); case TT.PPendregion: return("#endregion"); case TT.PPpragma: return("#pragma"); case TT.PPignored: return((t.Value ?? "").ToString()); default: return(string.Format("@`unknown token 0x{0:X4}`", t.TypeInt)); } }
/// <summary>Expresses an EC# token as a string.</summary> /// <remarks>Note that some Tokens do not contain enough information to /// reconstruct a useful token string, e.g. comment tokens do not store the /// comment but merely contain the location of the comment in the source code. /// For performance reasons, a <see cref="Token"/> does not have a reference /// to its source file, so this method cannot return the original string. /// <para/> /// The results are undefined if the token was not produced by <see cref="EcsLexer"/>. /// </remarks> public static string ToString(Token t, ICharSource sourceCode) { if (sourceCode != null && t.EndIndex <= sourceCode.Count) { return(sourceCode.Slice(t.StartIndex, t.Length).ToString()); } StringBuilder sb = new StringBuilder(); if (t.Kind == TokenKind.Operator || t.Kind == TokenKind.Assignment || t.Kind == TokenKind.Dot) { if (t.Type() == TT.BQString) { return(EcsNodePrinter.PrintString((t.Value ?? "").ToString(), '`', false)); } string value = (t.Value ?? "(null)").ToString(); return(value); } switch (t.Type()) { case TT.EOF: return("/*EOF*/"); case TT.Spaces: return(" "); case TT.Newline: return("\n"); case TT.SLComment: return("//\n"); case TT.MLComment: return("/**/"); case TT.Shebang: return("#!" + (t.Value ?? "").ToString() + "\n"); case TT.Id: case TT.ContextualKeyword: case TT.LinqKeyword: var mode = (t.Style & NodeStyle.Operator) != 0 ? EcsNodePrinter.IdPrintMode.Operator : (t.Style & NodeStyle.VerbatimId) != 0 ? EcsNodePrinter.IdPrintMode.Verbatim : EcsNodePrinter.IdPrintMode.Normal; return(EcsNodePrinter.PrintId(t.Value as Symbol ?? GSymbol.Empty, mode)); case TT.Base: return("base"); case TT.This: return("this"); case TT.Literal: return(EcsNodePrinter.PrintLiteral(t.Value, t.Style)); case TT.Comma: return(","); case TT.Semicolon: return(";"); case TT.LParen: return("("); case TT.RParen: return(")"); case TT.LBrack: return("["); case TT.RBrack: return("]"); case TT.LBrace: return("{"); case TT.RBrace: return("}"); case TT.AttrKeyword: string value = (t.Value ?? "(null)").ToString(); return(value); case TT.TypeKeyword: Symbol valueSym = (t.Value as Symbol) ?? GSymbol.Empty; string result; if (EcsFacts.TypeKeywords.TryGetValue(valueSym, out result)) { return(result); } else { Debug.Fail("Unexpected value for " + t.Type()); return((t.Value ?? "(null)").ToString()); } case TT.CheckedOrUnchecked: Debug.Assert(LNode.IsSpecialName((Symbol)t.Value)); return(((Symbol)t.Value).Name.Substring(1)); case TT.Break: return("break"); case TT.Case: return("case"); case TT.Class: return("class"); case TT.Continue: return("continue"); case TT.Default: return("default"); case TT.Delegate: return("delegate"); case TT.Do: return("do"); case TT.Enum: return("enum"); case TT.Event: return("event"); case TT.Fixed: return("fixed"); case TT.For: return("for"); case TT.Foreach: return("foreach"); case TT.Goto: return("goto"); case TT.If: return("if"); case TT.Interface: return("interface"); case TT.Lock: return("lock"); case TT.Namespace: return("namespace"); case TT.Return: return("return"); case TT.Struct: return("struct"); case TT.Switch: return("switch"); case TT.Throw: return("throw"); case TT.Try: return("try"); case TT.Using: return("using"); case TT.While: return("while"); case TT.Operator: return("operator"); case TT.Sizeof: return("sizeof"); case TT.Typeof: return("typeof"); case TT.Else: return("else"); case TT.Catch: return("catch"); case TT.Finally: return("finally"); case TT.In: return("in"); case TT.As: return("as"); case TT.Is: return("is"); case TT.New: return("new"); case TT.Out: return("out"); case TT.Stackalloc: return("stackalloc"); case TT.PPif: return("#if"); case TT.PPelse: return("#else"); case TT.PPelif: return("#elif"); case TT.PPendif: return("#endif"); case TT.PPdefine: return("#define"); case TT.PPundef: return("#undef"); case TT.PPwarning: return("#warning" + t.Value); case TT.PPerror: return("#error" + t.Value); case TT.PPnote: return("#note" + t.Value); case TT.PPline: return("#line"); case TT.PPregion: return("#region" + t.Value); case TT.PPendregion: return("#endregion"); case TT.PPpragma: return("#pragma"); case TT.PPignored: return((t.Value ?? "").ToString()); default: return(string.Format("@`unknown token 0x{0:X4}`", t.TypeInt)); } }