aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/Scripting/Tango.Scripting/Parsing
diff options
context:
space:
mode:
authorRoy Ben Shabat <Roy.mail.net@gmail.com>2019-04-09 01:47:48 +0300
committerRoy Ben Shabat <Roy.mail.net@gmail.com>2019-04-09 01:47:48 +0300
commit080f1697e97e13461ec6df4d31c8924d01257a1b (patch)
treeb1fe0285de7bc9bc52e9e2195e66fe022bf8f5b3 /Software/Visual_Studio/Scripting/Tango.Scripting/Parsing
parent1608e69a417bc5e40a607c3958c4a60f19f66f1a (diff)
downloadTango-080f1697e97e13461ec6df4d31c8924d01257a1b.tar.gz
Tango-080f1697e97e13461ec6df4d31c8924d01257a1b.zip
MERGE
Diffstat (limited to 'Software/Visual_Studio/Scripting/Tango.Scripting/Parsing')
-rw-r--r--Software/Visual_Studio/Scripting/Tango.Scripting/Parsing/ScriptParser.cs382
-rw-r--r--Software/Visual_Studio/Scripting/Tango.Scripting/Parsing/ScriptSymbol.cs31
-rw-r--r--Software/Visual_Studio/Scripting/Tango.Scripting/Parsing/ScriptType.cs23
3 files changed, 436 insertions, 0 deletions
diff --git a/Software/Visual_Studio/Scripting/Tango.Scripting/Parsing/ScriptParser.cs b/Software/Visual_Studio/Scripting/Tango.Scripting/Parsing/ScriptParser.cs
new file mode 100644
index 000000000..15760c950
--- /dev/null
+++ b/Software/Visual_Studio/Scripting/Tango.Scripting/Parsing/ScriptParser.cs
@@ -0,0 +1,382 @@
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+
+namespace Tango.Scripting.Parsing
+{
+ public class ScriptParser
+ {
+ private const string fakeScript = "CurrentScript";
+ private string fakeScriptName = "CurrentScript.";
+
+ private String ReplaceFakeScript(String str)
+ {
+ if (str != null)
+ {
+ return str.Replace(fakeScriptName, "").Replace(fakeScript, "");
+ }
+
+ return str;
+ }
+
+ private CompilationUnitSyntax GetRoot(String code)
+ {
+ SyntaxTree tree = CSharpSyntaxTree.ParseText(code);
+ var root = (CompilationUnitSyntax)tree.GetRoot();
+ return root;
+ }
+
+ private SemanticModel GetSemanticModel(String code)
+ {
+ SyntaxTree tree = CSharpSyntaxTree.ParseText(code);
+ CompilationUnitSyntax root = tree.GetCompilationUnitRoot();
+ var compilation = CSharpCompilation.Create("CSharpScript").AddSyntaxTrees(tree);
+ SemanticModel model = compilation.GetSemanticModel(tree);
+ return model;
+ }
+
+ public List<ScriptSymbol> GetContextSymbols(String code, int caretOffset)
+ {
+ var currentNode = GetCaretOffsetNode(code, caretOffset);
+
+ if (currentNode == null) return new List<ScriptSymbol>();
+
+ if (currentNode.Ancestors().OfType<BaseTypeDeclarationSyntax>().Count() == 0)
+ {
+ var usings = GetUsingsFull(code);
+
+ int removedLength = usings.Select(x => x.Length).Sum();
+
+ foreach (var use in usings)
+ {
+ code = code.Replace(use, "");
+ }
+
+ String scriptStart = $"public class {fakeScript}\n{{\n";
+ code = $"{scriptStart}{code}\n}}";
+ caretOffset = caretOffset - removedLength + scriptStart.Length;
+ }
+
+ var model = GetSemanticModel(code);
+ var symbols = model.LookupSymbols(caretOffset).ToList().Where(x => x.Kind == SymbolKind.Property || x.Kind == SymbolKind.Field || x.Kind == SymbolKind.Parameter || x.Kind == SymbolKind.Local || x.Kind == SymbolKind.Method).ToList();
+
+ List<ScriptSymbol> vars = new List<ScriptSymbol>();
+
+ foreach (var symbol in symbols.DistinctBy(x => x.Name))
+ {
+ if (symbol.ContainingSymbol.GetType().Name == "LambdaSymbol")
+ {
+ var invocationNode = currentNode.Ancestors().OfType<InvocationExpressionSyntax>().FirstOrDefault();
+
+ if (invocationNode != null)
+ {
+ var expressionNode = invocationNode.Expression as MemberAccessExpressionSyntax;
+
+ if (expressionNode != null && expressionNode.Name != null)
+ {
+ var name = expressionNode.Name as GenericNameSyntax;
+
+ if (name != null)
+ {
+ var type = name.TypeArgumentList.Arguments.FirstOrDefault()?.ToString();
+
+ vars.Add(new ScriptSymbol()
+ {
+ Name = symbol.Name,
+ Type = ReplaceFakeScript(type),
+ Class = ReplaceFakeScript(symbol.ContainingType?.Name),
+ Kind = symbol.Kind,
+ Accessibility = symbol.DeclaredAccessibility,
+ ContainingNamespace = ReplaceFakeScript(symbol.ContainingNamespace?.Name),
+ Summary = GetSymbolDocumentation(symbol),
+ });
+ }
+ }
+ }
+ }
+ if (symbol.Kind == SymbolKind.Method)
+ {
+ var prop = symbol.GetType().GetProperty("ReturnType");
+
+ if (prop != null)
+ {
+ ScriptSymbol m = new ScriptSymbol()
+ {
+ Name = symbol.Name,
+ Type = ReplaceFakeScript(prop.GetValue(symbol).ToString()),
+ Class = ReplaceFakeScript(symbol.ContainingType?.Name),
+ Kind = symbol.Kind,
+ Accessibility = symbol.DeclaredAccessibility,
+ ContainingNamespace = ReplaceFakeScript(symbol.ContainingNamespace?.Name),
+ Summary = GetSymbolDocumentation(symbol),
+ };
+
+ m.Parameters = GetMethodSymbolParameters(symbol);
+
+ vars.Add(m);
+ }
+ }
+ else
+ {
+ var prop = symbol.GetType().GetProperty("Type");
+
+ if (prop != null)
+ {
+ vars.Add(new ScriptSymbol()
+ {
+ Name = symbol.Name,
+ Type = ReplaceFakeScript(prop.GetValue(symbol).ToString()),
+ Class = ReplaceFakeScript(symbol.ContainingType?.Name),
+ Kind = symbol.Kind,
+ Accessibility = symbol.DeclaredAccessibility,
+ ContainingNamespace = ReplaceFakeScript(symbol.ContainingNamespace?.Name),
+ Summary = GetSymbolDocumentation(symbol),
+ });
+ }
+ }
+ }
+
+ return vars.Where(x => x.Type != "?").ToList();
+ }
+
+ public List<String> GetUsings(String code)
+ {
+ Regex r = new Regex("(using [^;^\n]+;)");
+ var matches = r.Matches(code);
+ return matches.OfType<Match>().Select(x => x.Value.Replace("using ", "").Replace(";", "").Replace("\n", "").Replace("\t", "").Replace("\r", "")).ToList();
+ }
+
+ public List<String> GetUsingsFull(String code)
+ {
+ Regex r = new Regex("(using [^;^\n]+;)");
+ var matches = r.Matches(code);
+ return matches.OfType<Match>().Select(x => x.Value).ToList();
+ }
+
+ public List<ScriptType> GetDeclaredTypes(String code)
+ {
+ List<ScriptType> types = new List<ScriptType>();
+
+ SyntaxTree tree = CSharpSyntaxTree.ParseText(code);
+ CompilationUnitSyntax root = tree.GetCompilationUnitRoot();
+ var compilation = CSharpCompilation.Create("CSharpScript").AddSyntaxTrees(tree);
+ SemanticModel model = compilation.GetSemanticModel(tree);
+
+ foreach (var d in root.DescendantNodes().Where(x => x is ClassDeclarationSyntax || x is EnumDeclarationSyntax || x is InterfaceDeclarationSyntax).OfType<BaseTypeDeclarationSyntax>())
+ {
+ var type = model.GetDeclaredSymbol(d);
+ if (!String.IsNullOrWhiteSpace(type.Name))
+ {
+ ScriptType scriptType = new ScriptType();
+ scriptType.Name = type.Name;
+ scriptType.Kind = type.TypeKind;
+ scriptType.ContainingNamespace = type.ContainingNamespace?.Name;
+ scriptType.Summary = GetNodeDocumentation(d);
+
+
+ foreach (var symbol in d.DescendantNodes().OfType<PropertyDeclarationSyntax>())
+ {
+ var symbolModel = model.GetDeclaredSymbol(symbol);
+
+ scriptType.Symbols.Add(new ScriptSymbol()
+ {
+ Class = scriptType.Name,
+ Accessibility = symbolModel.DeclaredAccessibility,
+ Kind = SymbolKind.Property,
+ Name = symbolModel.Name,
+ Type = symbolModel.Type.ToString(),
+ ContainingNamespace = symbolModel.ContainingNamespace?.Name,
+ Summary = GetNodeDocumentation(symbol),
+ });
+ }
+
+ foreach (var symbol in d.DescendantNodes().OfType<FieldDeclarationSyntax>())
+ {
+ var symbolModel = model.GetDeclaredSymbol(symbol.Declaration.Variables.FirstOrDefault()) as IFieldSymbol;
+
+ if (symbolModel != null)
+ {
+ scriptType.Symbols.Add(new ScriptSymbol()
+ {
+ Class = scriptType.Name,
+ Accessibility = symbolModel.DeclaredAccessibility,
+ Kind = SymbolKind.Field,
+ Name = symbolModel.Name,
+ Type = symbolModel.Type.ToString(),
+ ContainingNamespace = symbolModel.ContainingNamespace?.Name,
+ Summary = GetNodeDocumentation(symbol),
+ });
+ }
+ }
+
+ foreach (var symbol in d.DescendantNodes().OfType<MethodDeclarationSyntax>())
+ {
+ var symbolModel = model.GetDeclaredSymbol(symbol);
+
+ if (symbolModel != null)
+ {
+ ScriptSymbol m = new ScriptSymbol()
+ {
+ Class = scriptType.Name,
+ Accessibility = symbolModel.DeclaredAccessibility,
+ Kind = SymbolKind.Method,
+ Name = symbolModel.Name,
+ Type = symbolModel.ReturnType.ToString(),
+ ContainingNamespace = symbolModel.ContainingNamespace?.Name,
+ Summary = GetNodeDocumentation(symbol),
+ };
+
+ foreach (var p in symbol.DescendantNodes().OfType<ParameterSyntax>())
+ {
+ if (p.Type != null && p.Identifier != null)
+ {
+ m.Parameters.Add(new KeyValuePair<string, string>(p.Type.ToString(), p.Identifier.ToString()));
+ }
+ }
+
+ scriptType.Symbols.Add(m);
+ }
+ }
+
+ types.Add(scriptType);
+ }
+ }
+
+ return types;
+ }
+
+ public ObjectCreationExpressionSyntax GetCurrentConstructionExpression(String code)
+ {
+ SyntaxTree tree = CSharpSyntaxTree.ParseText(code);
+ CompilationUnitSyntax root = tree.GetCompilationUnitRoot();
+ return root.DescendantNodes().OfType<ObjectCreationExpressionSyntax>().FirstOrDefault();
+ }
+
+ public T GetExpressionFirst<T>(String line) where T : CSharpSyntaxNode
+ {
+ SyntaxTree tree = CSharpSyntaxTree.ParseText(line);
+ CompilationUnitSyntax root = tree.GetCompilationUnitRoot();
+ return root.DescendantNodes().OfType<T>().FirstOrDefault();
+ }
+
+ public List<T> GetExpressions<T>(String line) where T : CSharpSyntaxNode
+ {
+ SyntaxTree tree = CSharpSyntaxTree.ParseText(line);
+ CompilationUnitSyntax root = tree.GetCompilationUnitRoot();
+ return root.DescendantNodes().OfType<T>().ToList();
+ }
+
+ public List<T> GetDirectExpressions<T>(SyntaxNode node) where T : CSharpSyntaxNode
+ {
+ return node.DescendantNodes().OfType<T>().ToList();
+ }
+
+ private SyntaxNode GetCaretOffsetNode(String code, int offset)
+ {
+ SyntaxTree tree = CSharpSyntaxTree.ParseText(code);
+ CompilationUnitSyntax root = tree.GetCompilationUnitRoot();
+ return root.DescendantNodes().Where(x => offset >= x.FullSpan.Start && offset <= x.FullSpan.End).OrderBy(x => x.FullSpan.Length).FirstOrDefault();
+ }
+
+ private List<SyntaxNode> GetNodeAncestors(SyntaxNode node)
+ {
+ return node.Ancestors().ToList();
+ }
+
+ private String GetNodeDocumentation(SyntaxNode node)
+ {
+ try
+ {
+ var trivia = node.GetLeadingTrivia().FirstOrDefault(t => t.Kind() == SyntaxKind.SingleLineCommentTrivia);
+
+ if (trivia != null && !String.IsNullOrWhiteSpace(trivia.ToString()))
+ {
+ return trivia.ToString().Replace("//", "");
+ }
+ }
+ catch { }
+
+ return "No documentation.";
+ }
+
+ private String GetSymbolDocumentation(ISymbol symbol)
+ {
+ if (symbol != null)
+ {
+ var prop = symbol.GetType().GetProperty("SyntaxNode");
+
+ if (prop != null)
+ {
+ var node = prop.GetValue(symbol) as SyntaxNode;
+
+ if (node != null)
+ {
+ return GetNodeDocumentation(node.Parent);
+ }
+ }
+ }
+
+ return "No documentation.";
+ }
+
+ private SyntaxNode GetSymbolSyntaxNode(ISymbol symbol)
+ {
+ if (symbol != null)
+ {
+ var prop = symbol.GetType().GetProperty("SyntaxNode");
+
+ if (prop != null)
+ {
+ var node = prop.GetValue(symbol) as SyntaxNode;
+
+ return node;
+ }
+ }
+
+ return null;
+ }
+
+ private List<KeyValuePair<String, String>> GetMethodSymbolParameters(ISymbol symbol)
+ {
+ List<KeyValuePair<String, String>> parameters = new List<KeyValuePair<string, string>>();
+
+ try
+ {
+ var prop = symbol.GetType().GetProperty("Parameters");
+
+ if (prop != null)
+ {
+ var array = prop.GetValue(symbol) as IEnumerable;
+
+ foreach (var item in array)
+ {
+ var type = item.GetType().GetProperty("Type").GetValue(item).ToString();
+ var value = item.GetType().GetProperty("Name").GetValue(item).ToString();
+
+ parameters.Add(new KeyValuePair<string, string>(type, value));
+ }
+ }
+ }
+ catch { }
+
+ return parameters;
+ }
+
+ public String IndentCSharpCode(String code)
+ {
+ var tree = CSharpSyntaxTree.ParseText(code);
+ var root = tree.GetRoot().NormalizeWhitespace();
+ var ret = root.ToFullString();
+ return ret;
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Scripting/Tango.Scripting/Parsing/ScriptSymbol.cs b/Software/Visual_Studio/Scripting/Tango.Scripting/Parsing/ScriptSymbol.cs
new file mode 100644
index 000000000..d6fdaeebf
--- /dev/null
+++ b/Software/Visual_Studio/Scripting/Tango.Scripting/Parsing/ScriptSymbol.cs
@@ -0,0 +1,31 @@
+using Microsoft.CodeAnalysis;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.Scripting.Parsing
+{
+ public class ScriptSymbol
+ {
+ public String Name { get; set; }
+ public String Type { get; set; }
+ public SymbolKind Kind { get; set; }
+ public String Class { get; set; }
+ public Accessibility Accessibility { get; set; }
+ public String ContainingNamespace { get; set; }
+ public List<KeyValuePair<String,String>> Parameters { get; set; }
+ public String Summary { get; set; }
+
+ public ScriptSymbol()
+ {
+ Parameters = new List<KeyValuePair<string, string>>();
+ }
+
+ public override string ToString()
+ {
+ return $"{Kind.ToString()} : {Type} : {Name}";
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Scripting/Tango.Scripting/Parsing/ScriptType.cs b/Software/Visual_Studio/Scripting/Tango.Scripting/Parsing/ScriptType.cs
new file mode 100644
index 000000000..3ca34a85e
--- /dev/null
+++ b/Software/Visual_Studio/Scripting/Tango.Scripting/Parsing/ScriptType.cs
@@ -0,0 +1,23 @@
+using Microsoft.CodeAnalysis;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.Scripting.Parsing
+{
+ public class ScriptType
+ {
+ public String Name { get; set; }
+ public TypeKind Kind { get; set; }
+ public List<ScriptSymbol> Symbols { get; set; }
+ public string ContainingNamespace { get; set; }
+ public String Summary { get; set; }
+
+ public ScriptType()
+ {
+ Symbols = new List<ScriptSymbol>();
+ }
+ }
+}