Created
November 2, 2018 21:45
-
-
Save sailro/1489cb08bac2b74570eccf013a1bb84c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Linq; | |
using System.Reflection; | |
using System.Threading; | |
using Microsoft.CodeAnalysis.CSharp; | |
using Microsoft.CodeAnalysis.Text; | |
namespace SyntaxTree.VisualStudio.Unity.Debugger.Evaluation | |
{ | |
internal static class DebugSyntaxFactory | |
{ | |
// Handle pseudo-variable like $exception or object-ids $1..., that are not parsed with the regular grammar | |
private static CSharpSyntaxNode Parse(string code, string parseMethodName) | |
{ | |
try | |
{ | |
if (!code.Contains("$")) | |
return null; | |
var source = SourceText.From(code); | |
var csSyntaxTypeFormat = $"Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.{{0}}, {typeof(CSharpSyntaxNode).Assembly.FullName}"; | |
var syntaxTypeFormat = $"Microsoft.CodeAnalysis.{{0}}, {typeof(Microsoft.CodeAnalysis.SyntaxTree).Assembly.FullName}"; | |
// ReSharper disable PossibleNullReferenceException, AssignNullToNotNullAttribute | |
var lexerType = Type.GetType(string.Format(csSyntaxTypeFormat, "Lexer")) as TypeInfo; | |
var parserType = Type.GetType(string.Format(csSyntaxTypeFormat, "LanguageParser")) as TypeInfo; | |
// Create lexer, depending on Roslyn version, we have one extra argument (interpolationFollowedByColon) | |
var lexerCtor = lexerType.DeclaredConstructors.FirstOrDefault(c => c.GetParameters().Length >= 3); | |
var lexerCtorArgs = lexerCtor.GetParameters().Length == 3 ? new object[] {source, CSharpParseOptions.Default, false} : new object[] {source, CSharpParseOptions.Default, false, false}; | |
var lexer = lexerCtor.Invoke(lexerCtorArgs) as IDisposable; | |
using (lexer) | |
{ | |
var lexerModeType = Type.GetType(string.Format(csSyntaxTypeFormat, "LexerMode")); | |
var parser = parserType.DeclaredConstructors.First().Invoke(new[] {lexer, null, null, Convert.ChangeType(0x0002, Enum.GetUnderlyingType(lexerModeType)), default(CancellationToken)}) as IDisposable; | |
using (parser) | |
{ | |
var parseExpressionMethod = parserType.GetDeclaredMethod(parseMethodName); | |
var node = parseExpressionMethod.Invoke(parser, new object[0]); | |
var consumeUnexpectedTokensMethod = parserType.GetDeclaredMethod("ConsumeUnexpectedTokens").MakeGenericMethod(node.GetType()); | |
// node we have a correct node using the internal ast | |
node = consumeUnexpectedTokensMethod.Invoke(parser, new[] {node}); | |
// convert to the public ast | |
var greenNodeType = Type.GetType(string.Format(syntaxTypeFormat, "GreenNode")) as TypeInfo; | |
var createRedMethod = greenNodeType.GetMethod("CreateRed", BindingFlags.Public | BindingFlags.Instance, null, new Type[0], null); | |
var csNode = createRedMethod.Invoke(node, new object[0]); | |
var createForDebuggerMethod = typeof(CSharpSyntaxTree).GetMethod("CreateForDebugger", BindingFlags.NonPublic | BindingFlags.Static); | |
var syntaxTree = createForDebuggerMethod.Invoke(null, new[] {csNode, source}) as Microsoft.CodeAnalysis.SyntaxTree; | |
return syntaxTree.GetRoot() as CSharpSyntaxNode; | |
} | |
} | |
// ReSharper restore PossibleNullReferenceException, AssignNullToNotNullAttribute | |
} | |
catch (Exception) | |
{ | |
return null; | |
} | |
} | |
public static CSharpSyntaxNode ParseExpression(string code) | |
{ | |
return Parse(code, nameof(ParseExpression)); | |
} | |
public static CSharpSyntaxNode ParseStatement(string code) | |
{ | |
return Parse(code, nameof(ParseStatement)); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment