aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/Scripting/Tango.Scripting/Parsing/ScriptParser.cs
blob: 15760c95012afca129296f1c5079e6e4da7967e1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Tango.Core;
using Tango.BL.Entities;
using Tango.Logging;
using Tango.MachineStudio.Common;
using Tango.MachineStudio.Common.Authentication;
using Tango.MachineStudio.Common.Modules;
using Tango.MachineStudio.DB;
using Tango.MachineStudio.MachineDesigner;
using Tango.MachineStudio.Stubs;

namespace Tango.MachineStudio.UI.Modules
{
<
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;
        }
    }
}