aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/Scripting/Tango.Scripting.Basic/Project.cs
diff options
context:
space:
mode:
authorVictoria Plitt <Victoria.Plitt@twine-s.com>2020-04-26 08:34:14 +0200
committerVictoria Plitt <Victoria.Plitt@twine-s.com>2020-04-26 08:34:14 +0200
commit9038c50a3a350312c9895240be28b4fb90fbe3b1 (patch)
treecbc8824844340d8eff76f57455fcea8e7a9d6cd6 /Software/Visual_Studio/Scripting/Tango.Scripting.Basic/Project.cs
parentccaf9afcde7a630a7d765fa9874322b5f44e2df7 (diff)
parent8438e5447b6bed6ed97f2e3fda6977ce4bf74d0c (diff)
downloadTango-9038c50a3a350312c9895240be28b4fb90fbe3b1.tar.gz
Tango-9038c50a3a350312c9895240be28b4fb90fbe3b1.zip
Merge branch 'master' of https://twinetfs.visualstudio.com/Tango/_git/Tango
Diffstat (limited to 'Software/Visual_Studio/Scripting/Tango.Scripting.Basic/Project.cs')
-rw-r--r--Software/Visual_Studio/Scripting/Tango.Scripting.Basic/Project.cs211
1 files changed, 211 insertions, 0 deletions
diff --git a/Software/Visual_Studio/Scripting/Tango.Scripting.Basic/Project.cs b/Software/Visual_Studio/Scripting/Tango.Scripting.Basic/Project.cs
new file mode 100644
index 000000000..446e5b529
--- /dev/null
+++ b/Software/Visual_Studio/Scripting/Tango.Scripting.Basic/Project.cs
@@ -0,0 +1,211 @@
+using Microsoft.CodeAnalysis.CSharp.Scripting;
+using Microsoft.CodeAnalysis.Scripting;
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using Tango.Core;
+using Tango.Core.IO;
+using Tango.Scripting.Core;
+using System.IO;
+using Tango.Core.Helpers;
+
+namespace Tango.Scripting.Basic
+{
+ public class Project<T> : ExtendedObject where T : IContext
+ {
+ private String _name;
+ public String Name
+ {
+ get { return _name; }
+ set { _name = value; RaisePropertyChangedAuto(); }
+ }
+
+ private String _description;
+ public String Description
+ {
+ get { return _description; }
+ set { _description = value; RaisePropertyChangedAuto(); }
+ }
+
+ public ApartmentState ApartmentState { get; set; }
+
+ public ObservableCollection<ReferenceAssembly> ReferenceAssemblies { get; set; }
+
+ public ObservableCollection<Script> Scripts { get; set; }
+
+ [JsonIgnore]
+ public ObservableCollection<IScriptSource> AdditionalScripts
+ {
+ get
+ {
+ return Scripts.Where(x => !x.IsEntryPoint).Cast<IScriptSource>().ToObservableCollection();
+ }
+ }
+
+ public Project()
+ {
+ ApartmentState = ApartmentState.MTA;
+
+ ReferenceAssemblies = new ObservableCollection<ReferenceAssembly>();
+
+ Scripts = new ObservableCollection<Script>();
+ Scripts.CollectionChanged += (x, e) => { RaisePropertyChanged(nameof(AdditionalScripts)); };
+ }
+
+ public static TProject New<TProject, TContext>(String name) where TProject : Project<TContext> where TContext : IContext
+ {
+ TProject p = Activator.CreateInstance<TProject>();
+
+ p.Name = name;
+
+ p.ReferenceAssemblies.Add(ReferenceAssembly.FromType(typeof(String)));
+ p.ReferenceAssemblies.Add(ReferenceAssembly.FromType(typeof(Enumerable)));
+ p.ReferenceAssemblies.Add(ReferenceAssembly.FromType(typeof(Form)));
+ return p;
+ }
+
+ public Task<CompilationResult> Compile()
+ {
+ return Task.Factory.StartNew<CompilationResult>(() =>
+ {
+ var result = new CompilationResult();
+ var tempFolder = TemporaryManager.CreateFolder(Name);
+ result.TemporaryProjectPath = tempFolder;
+
+ String mainScriptCode = String.Empty;
+
+ foreach (var script in Scripts)
+ {
+ script.LoadCount = 0;
+ String code = script.Code;
+ String codeFile = Path.Combine(tempFolder, script.Name);
+
+ String loadingString = String.Empty;
+
+ foreach (var file in Scripts.Where(x => !x.IsEntryPoint && script != x).Select(x => Path.Combine(tempFolder, x.Name)))
+ {
+ loadingString = $"#load \"{file}\"\n";
+ script.LoadCount++;
+ }
+
+ code = loadingString + code;
+
+ if (!script.IsEntryPoint)
+ {
+ //In case we use #load
+ //foreach (var match in Regex.Matches(code, "#load \".+\"").OfType<Match>())
+ //{
+ // String line = match.ToString();
+ // var pathMatch = Regex.Match(line, "(?<=\")(.*?)(?=\")");
+ // if (pathMatch.Success)
+ // {
+ // String path = pathMatch.ToString();
+
+ // if (!System.IO.Path.IsPathRooted(path))
+ // {
+ // StringBuilder builder = new StringBuilder(code);
+ // builder.Insert(match.Index + pathMatch.Index, System.IO.Path.GetFullPath(tempFolder + "\\"));
+ // code = builder.ToString();
+ // }
+ // }
+ //}
+ File.WriteAllText(codeFile, code);
+ }
+ else
+ {
+ code += Environment.NewLine + Environment.NewLine + "new Program().OnExecute(GlobalContext);";
+ mainScriptCode = code;
+ }
+ }
+
+ var scriptOptions = ScriptOptions.Default.WithReferences(LoadReferenceAssemblies());
+
+ var s = CSharpScript.Create<object>(mainScriptCode, scriptOptions, typeof(GlobalObject<T>));
+ result.Script = s;
+
+ var compileResults = s.Compile();
+
+ foreach (var error in compileResults.Where(x => x.Severity == Microsoft.CodeAnalysis.DiagnosticSeverity.Error))
+ {
+ CompilationError cError = new CompilationError();
+ cError.File = System.IO.Path.GetFileName(error.Location.SourceTree.FilePath);
+ if (cError.File == String.Empty)
+ {
+ cError.File = Scripts.Single(x => x.IsEntryPoint).Name;
+ }
+ Script errorScript = Scripts.Single(x => x.Name == cError.File);
+ cError.Message = error.GetMessage();
+ cError.Severity = error.Severity;
+ var line = error.Location.GetMappedLineSpan();
+ cError.Line = line.StartLinePosition.Line + 1 - (errorScript != null ? errorScript.LoadCount : 0);
+ cError.Column = line.StartLinePosition.Character + 1;
+ cError.Length = line.EndLinePosition.Character - line.StartLinePosition.Character;
+ result.Errors.Add(cError);
+ }
+
+ return result;
+ });
+ }
+
+ public async Task<ProjectSession<T>> Run(T context)
+ {
+ var result = await Compile();
+
+ if (result.Errors.Count > 0)
+ {
+ throw new InvalidOperationException($"Cannot run project with the following compilation errors:\n{String.Join(Environment.NewLine, result.Errors.Select(x => x.Message))}");
+ }
+
+ Thread scriptThread = null;
+ ProjectSession<T> session = null;
+
+ session = new ProjectSession<T>(this, () =>
+ {
+ scriptThread.Abort();
+ });
+
+ scriptThread = new Thread(() =>
+ {
+ try
+ {
+ var runResult = result.Script.RunAsync(globals: new GlobalObject<T>() { GlobalContext = context }).Result;
+ session.Completed(runResult.ReturnValue);
+ }
+ catch (ThreadAbortException)
+ {
+
+ }
+ catch (Exception ex)
+ {
+ session.Failed(ex.InnerException);
+ }
+ });
+
+ scriptThread.SetApartmentState(ApartmentState);
+ scriptThread.IsBackground = true;
+ scriptThread.Start();
+
+ return session;
+ }
+
+ public List<Assembly> LoadReferenceAssemblies()
+ {
+ List<Assembly> loadedAssemblies = new List<Assembly>();
+
+ foreach (var asm in ReferenceAssemblies)
+ {
+ loadedAssemblies.Add(asm.Load());
+ }
+
+ return loadedAssemblies;
+ }
+ }
+}