From f9e5386824baf499c8efce2edbc28e478a07c7d9 Mon Sep 17 00:00:00 2001 From: Baudin999 Date: Fri, 27 Dec 2019 09:37:39 +0100 Subject: [PATCH] Still working on the file system and the file system tests --- .../{ => File}/BaseApplicationTest.cs | 9 +- .../{ => File}/CreateModuleInProject.cs | 2 +- .../{ => File}/DeleteModuleInproject.cs | 2 +- .../{ => File}/ModuleStream_tests.cs | 0 .../{ => File}/MoveModuleInProject.cs | 4 +- .../{ => File}/ParseMultipleFiles.cs | 0 .../{ => File}/ProjectFileWatcher.cs | 2 +- .../{ => File}/SchoolExampleTest.cs | 4 +- .../InMemoryTests/BasicInMemoryTest.cs | 41 ++++++ ApplicationTests/MappingTest/SimpleMapping.cs | 81 ++++++++++++ CLI/AssetHelpers.cs | 46 +++++++ CLI/Commands/Commands.Watch.cs | 1 + CLI/Controllers/JsonDataController.cs | 2 +- CLI/Controllers/ModuleController.cs | 2 +- CLI/WebServer.cs | 46 +------ .../{Module.cs => FileProject/FileModule.cs} | 50 +++---- .../{ => FileProject}/FileProject.Network.cs | 8 +- .../{ => FileProject}/FileProject.Watch.cs | 9 +- Project/{ => FileProject}/FileProject.cs | 117 +++++++--------- Project/FileSystems/FileSystem.cs | 125 ++++++++++++++++++ Project/FileSystems/IFileSystem.cs | 19 +++ Project/FileSystems/MemorySystem.cs | 58 ++++++++ Project/IModule.cs | 33 +++++ Project/IO.cs | 79 ----------- Project/IProject.cs | 14 +- Project/MemoryProject/MemoryModule.cs | 102 ++++++++++++++ Project/MemoryProject/MemoryProject.cs | 90 +++++++++++++ Project/ModuleStream.cs | 34 +++++ Project/ProjectContext.cs | 15 ++- Project/ProjectFilesWatcher.cs | 14 +- Project/Transpiler.cs | 4 +- 31 files changed, 756 insertions(+), 257 deletions(-) rename ApplicationTests/{ => File}/BaseApplicationTest.cs (87%) rename ApplicationTests/{ => File}/CreateModuleInProject.cs (91%) rename ApplicationTests/{ => File}/DeleteModuleInproject.cs (94%) rename ApplicationTests/{ => File}/ModuleStream_tests.cs (100%) rename ApplicationTests/{ => File}/MoveModuleInProject.cs (93%) rename ApplicationTests/{ => File}/ParseMultipleFiles.cs (100%) rename ApplicationTests/{ => File}/ProjectFileWatcher.cs (92%) rename ApplicationTests/{ => File}/SchoolExampleTest.cs (99%) create mode 100644 ApplicationTests/InMemoryTests/BasicInMemoryTest.cs create mode 100644 ApplicationTests/MappingTest/SimpleMapping.cs create mode 100644 CLI/AssetHelpers.cs rename Project/{Module.cs => FileProject/FileModule.cs} (79%) rename Project/{ => FileProject}/FileProject.Network.cs (97%) rename Project/{ => FileProject}/FileProject.Watch.cs (90%) rename Project/{ => FileProject}/FileProject.cs (60%) create mode 100644 Project/FileSystems/FileSystem.cs create mode 100644 Project/FileSystems/IFileSystem.cs create mode 100644 Project/FileSystems/MemorySystem.cs create mode 100644 Project/IModule.cs delete mode 100644 Project/IO.cs create mode 100644 Project/MemoryProject/MemoryModule.cs create mode 100644 Project/MemoryProject/MemoryProject.cs diff --git a/ApplicationTests/BaseApplicationTest.cs b/ApplicationTests/File/BaseApplicationTest.cs similarity index 87% rename from ApplicationTests/BaseApplicationTest.cs rename to ApplicationTests/File/BaseApplicationTest.cs index f173082..4c2e9a5 100644 --- a/ApplicationTests/BaseApplicationTest.cs +++ b/ApplicationTests/File/BaseApplicationTest.cs @@ -1,7 +1,6 @@ -using System; +using Project; +using System; using System.IO; -using System.Threading.Tasks; -using Project; using Xunit.Abstractions; namespace ApplicationTests @@ -10,7 +9,7 @@ public class BaseFileWatcherTest : IDisposable { protected readonly ITestOutputHelper output; protected readonly string dir; - protected readonly FileProject project; + protected readonly IProject project; public BaseFileWatcherTest(ITestOutputHelper _output, string _dir) { @@ -22,7 +21,7 @@ public BaseFileWatcherTest(ITestOutputHelper _output, string _dir) } Directory.CreateDirectory(dir); output = _output; - project = new FileProject(dir); + project = ProjectContext.Init(dir); project.Watch(); } diff --git a/ApplicationTests/CreateModuleInProject.cs b/ApplicationTests/File/CreateModuleInProject.cs similarity index 91% rename from ApplicationTests/CreateModuleInProject.cs rename to ApplicationTests/File/CreateModuleInProject.cs index 5c6a32a..68cbd63 100644 --- a/ApplicationTests/CreateModuleInProject.cs +++ b/ApplicationTests/File/CreateModuleInProject.cs @@ -18,7 +18,7 @@ public async Task CreateModule() { try { - var module = await project.CreateModule("Test"); + var module = await project.CreateModule("Test", "Code"); Assert.True(File.Exists(path("Test.car"))); Assert.NotNull(module); Assert.Equal("Test", module.Name); diff --git a/ApplicationTests/DeleteModuleInproject.cs b/ApplicationTests/File/DeleteModuleInproject.cs similarity index 94% rename from ApplicationTests/DeleteModuleInproject.cs rename to ApplicationTests/File/DeleteModuleInproject.cs index 8fa9cd5..69d092f 100644 --- a/ApplicationTests/DeleteModuleInproject.cs +++ b/ApplicationTests/File/DeleteModuleInproject.cs @@ -18,7 +18,7 @@ public async Task DeleteModule() { try { - var module = await project.CreateModule("Test"); + var module = await project.CreateModule("Test", ""); var filePath = module.FilePath.Clone().ToString(); var outPath = module.OutPath.Clone().ToString(); diff --git a/ApplicationTests/ModuleStream_tests.cs b/ApplicationTests/File/ModuleStream_tests.cs similarity index 100% rename from ApplicationTests/ModuleStream_tests.cs rename to ApplicationTests/File/ModuleStream_tests.cs diff --git a/ApplicationTests/MoveModuleInProject.cs b/ApplicationTests/File/MoveModuleInProject.cs similarity index 93% rename from ApplicationTests/MoveModuleInProject.cs rename to ApplicationTests/File/MoveModuleInProject.cs index a60e2bd..a35f288 100644 --- a/ApplicationTests/MoveModuleInProject.cs +++ b/ApplicationTests/File/MoveModuleInProject.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; using System.Threading.Tasks; using Xunit; @@ -15,7 +15,7 @@ public async Task MoveModule() { try { - var module = await project.CreateModule("Test"); + var module = await project.CreateModule("Test", ""); Assert.True(File.Exists(path("Test.car"))); Assert.NotNull(module); Assert.Equal("Test", module.Name); diff --git a/ApplicationTests/ParseMultipleFiles.cs b/ApplicationTests/File/ParseMultipleFiles.cs similarity index 100% rename from ApplicationTests/ParseMultipleFiles.cs rename to ApplicationTests/File/ParseMultipleFiles.cs diff --git a/ApplicationTests/ProjectFileWatcher.cs b/ApplicationTests/File/ProjectFileWatcher.cs similarity index 92% rename from ApplicationTests/ProjectFileWatcher.cs rename to ApplicationTests/File/ProjectFileWatcher.cs index 15b3b60..e422d15 100644 --- a/ApplicationTests/ProjectFileWatcher.cs +++ b/ApplicationTests/File/ProjectFileWatcher.cs @@ -19,7 +19,7 @@ public async Task CreateModule() { try { - await project.CreateModule("Test"); + await project.CreateModule("Test", ""); Assert.True(File.Exists(path("Test.car"))); } catch (Exception ex) diff --git a/ApplicationTests/SchoolExampleTest.cs b/ApplicationTests/File/SchoolExampleTest.cs similarity index 99% rename from ApplicationTests/SchoolExampleTest.cs rename to ApplicationTests/File/SchoolExampleTest.cs index 8b75484..7bb7e98 100644 --- a/ApplicationTests/SchoolExampleTest.cs +++ b/ApplicationTests/File/SchoolExampleTest.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; using System.Threading.Tasks; using Xunit; @@ -75,7 +75,7 @@ public async Task AddStudentModule() { Console.WriteLine("STARTED: AddStudentModule"); await Init(); - var studentModule = await project.CreateModule("Student"); + var studentModule = await project.CreateModule("Student", ""); var schoolModule = project.FindModule("School"); Assert.NotNull(studentModule); diff --git a/ApplicationTests/InMemoryTests/BasicInMemoryTest.cs b/ApplicationTests/InMemoryTests/BasicInMemoryTest.cs new file mode 100644 index 0000000..183c745 --- /dev/null +++ b/ApplicationTests/InMemoryTests/BasicInMemoryTest.cs @@ -0,0 +1,41 @@ +using Project; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace ApplicationTests.InMemoryTests +{ + public class BasicInMemoryTest + { + [Fact] + public async Task CreateInMemoryProject() + + { + IProject project = ProjectContext.InitInMemory(); + IModule module = await project.CreateModule("Test", ""); + Assert.NotNull(module); + IModule test = project.FindModule("Test"); + Assert.NotNull(test); + } + + [Fact] + public async Task CreateALotOfModules() + + { + IProject project = ProjectContext.InitInMemory(); + IModule test = await project.CreateModule("Test", ""); + IModule other = await project.CreateModule("Other", ""); + IModule something = await project.CreateModule("Something", ""); + IModule foo = await project.CreateModule("Foo", ""); + IModule bar = await project.CreateModule("Bar", ""); + + var instance = Project.FileSystems.MemorySystem.Instance; + + Assert.NotNull(test); + + + } + } +} diff --git a/ApplicationTests/MappingTest/SimpleMapping.cs b/ApplicationTests/MappingTest/SimpleMapping.cs new file mode 100644 index 0000000..938ecf5 --- /dev/null +++ b/ApplicationTests/MappingTest/SimpleMapping.cs @@ -0,0 +1,81 @@ +using Compiler; +using Compiler.AST; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Xunit; + +namespace ApplicationTests.MappingTest +{ + public class SimpleMapping + { + [Fact] + public void NameMapper() + { + var code = @" +type Person = + FirstName: Name; +alias Name = String; +"; + + var g = new ASTGenerator(code); + var nameMapper = new NameMapper(); + var names = nameMapper.Map(new NameVisitor(g)); + var result = nameMapper.Process(); + + Assert.NotNull(result); + Assert.Equal(@"Person +FirstName +Name", result); + } + } + + public class NameMapper : IMapper + { + public IEnumerable Result { get; private set; } + + public IEnumerable Map(VisitorBase visitor) where U : IEnumerable + { + this.Result = visitor.Start().SelectMany(i => i); + return this.Result; + } + + public string Process() + { + + return String.Join(Environment.NewLine, Result); + } + } + + public class NameVisitor : VisitorDefault> + { + public NameVisitor(ASTGenerator generator) : base(generator) { } + + public override IEnumerable VisitASTTypeField(ASTTypeField astTypeField) + { + yield return astTypeField.Name; + } + + public override IEnumerable VisitASTType(ASTType astType) + { + yield return astType.Name; + foreach (var field in astType.Fields) + { + yield return Visit(field).First(); + } + } + + public override IEnumerable VisitASTAlias(ASTAlias astAlias) + { + yield return astAlias.Name; + } + } + + public interface IMapper + { + IEnumerable Result { get; } + IEnumerable Map(VisitorBase visitor) where U: IEnumerable; + string Process(); + } +} diff --git a/CLI/AssetHelpers.cs b/CLI/AssetHelpers.cs new file mode 100644 index 0000000..2bf6fc6 --- /dev/null +++ b/CLI/AssetHelpers.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Text; + +namespace CLI +{ + internal static class AssetHelpers + { + internal static string ReadAsset(string name) + { + var assembly = Assembly.GetExecutingAssembly(); + var resourceName = "CLI.Assets." + name; + + using (var stream = assembly.GetManifestResourceStream(resourceName)) + { + if (!(stream is null)) + { + using (var reader = new StreamReader(stream)) + { + var result = reader.ReadToEnd(); + reader.Close(); + reader.Dispose(); + return result; + } + } + else + { + return ""; + } + } + } + + internal static void WriteAsset(string path, string content) + { + File.WriteAllText(path, content); + } + + internal static void ReadAndWriteAsset(string assetName, string outPath) + { + var outName = System.IO.Path.GetFullPath(assetName, outPath); + AssetHelpers.WriteAsset(outName, AssetHelpers.ReadAsset(assetName)); + } + } +} diff --git a/CLI/Commands/Commands.Watch.cs b/CLI/Commands/Commands.Watch.cs index 48d694a..f5e4970 100644 --- a/CLI/Commands/Commands.Watch.cs +++ b/CLI/Commands/Commands.Watch.cs @@ -37,6 +37,7 @@ public static void CreateWatchCommand(CommandLineApplication app) true => fileOption.Value() }; ProjectContext.Init(directory); + //ProjectContext.InitInMemory(); var project = ProjectContext.Instance; if (project != null) { diff --git a/CLI/Controllers/JsonDataController.cs b/CLI/Controllers/JsonDataController.cs index d3098b0..d092f16 100644 --- a/CLI/Controllers/JsonDataController.cs +++ b/CLI/Controllers/JsonDataController.cs @@ -14,7 +14,7 @@ namespace CLI.Controllers { public class JsonDataController : ControllerBase { - private Module? Module; + private IModule? Module; [HttpGet("/api/data/{module}/{type}")] public IActionResult GetData(string module, string type, [FromQuery]bool list) diff --git a/CLI/Controllers/ModuleController.cs b/CLI/Controllers/ModuleController.cs index 4b0cb49..a882cf2 100644 --- a/CLI/Controllers/ModuleController.cs +++ b/CLI/Controllers/ModuleController.cs @@ -32,7 +32,7 @@ public async Task CreateModuleAsync(string name) var project = ProjectContext.Instance; if (project != null) { - Module? module = await project.CreateModule(name, null); + IModule? module = await project.CreateModule(name, null); if (module is null) return BadRequest($"Failed to created module {name}."); return Ok(new List { diff --git a/CLI/WebServer.cs b/CLI/WebServer.cs index 9367be3..3d863fb 100644 --- a/CLI/WebServer.cs +++ b/CLI/WebServer.cs @@ -18,7 +18,7 @@ public static Task Start(string rootPath) { var portNumber = ProjectContext.Instance?.CarConfig?.PortNumber ?? "5000"; RootPath = rootPath; - Task.Run(async () => + Task.Run(async () => { await Task.Delay(1500); WebServer.OpenBrowser($"http://localhost:{portNumber}/index.html"); @@ -53,7 +53,7 @@ public static Task Start(string rootPath) }); } - public static void OpenBrowser(string url) + private static void OpenBrowser(string url) { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { @@ -76,47 +76,11 @@ public static void OpenBrowser(string url) private static void CreateAssets(string outPath) { - Helpers.ReadAndWriteAsset("mermaid.min.js", outPath); - Helpers.ReadAndWriteAsset("mermaid.min.js.map", outPath); + AssetHelpers.ReadAndWriteAsset("mermaid.min.js", outPath); + AssetHelpers.ReadAndWriteAsset("mermaid.min.js.map", outPath); } - private static class Helpers - { - public static string ReadAsset(string name) - { - var assembly = Assembly.GetExecutingAssembly(); - var resourceName = "CLI.Assets." + name; - - using (var stream = assembly.GetManifestResourceStream(resourceName)) - { - if (!(stream is null)) - { - using (var reader = new StreamReader(stream)) - { - var result = reader.ReadToEnd(); - reader.Close(); - reader.Dispose(); - return result; - } - } - else - { - return ""; - } - } - } - - public static void WriteAsset(string path, string content) - { - File.WriteAllText(path, content); - } - - public static void ReadAndWriteAsset(string assetName, string outPath) - { - var outName = System.IO.Path.GetFullPath(assetName, outPath); - Helpers.WriteAsset(outName, Helpers.ReadAsset(assetName)); - } - } + } diff --git a/Project/Module.cs b/Project/FileProject/FileModule.cs similarity index 79% rename from Project/Module.cs rename to Project/FileProject/FileModule.cs index 165102c..ecfbb93 100644 --- a/Project/Module.cs +++ b/Project/FileProject/FileModule.cs @@ -1,22 +1,23 @@ -using System; +using Compiler; +using Compiler.AST; +using Mapper.Application; +using Project.FileSystems; +using System; using System.Collections.Generic; using System.IO; using System.Linq; -using Compiler; -using Compiler.AST; -using Mapper.Application; -using Configuration; using System.Threading.Tasks; -namespace Project +namespace Project.FileProject { - public class Module : IDisposable + public class FileModule : IModule, IDisposable { + private IFileSystem FileSystem { get; } public string Name { get; } public string FilePath { get; } public string BasePath { get; } public string OutPath { get; } - public FileProject Project { get; } + public IProject Project { get; } public List ReferencedModules { get; private set; } = new List(); public Transpiler Transpiler { get; private set; } public ASTGenerator Generator { get; private set; } @@ -40,7 +41,7 @@ public string Code { if (Generator.Code == String.Empty) { - return IO.ReadFileText(this.FilePath); + return FileSystem.ReadFileText(this.FilePath); } else { @@ -60,8 +61,9 @@ public Descriptor ToDescriptor(string description) }; } - public Module(string path, string basePath, FileProject project) + public FileModule(string path, string basePath, IProject project) { + this.FileSystem = ProjectContext.FileSystem ?? throw new Exception("FileSystem does not exist"); this.FilePath = path; this.BasePath = basePath; this.Name = CreateModuleName(); @@ -74,7 +76,7 @@ public Module(string path, string basePath, FileProject project) public void Parse() { - var code = IO.ReadFileText(this.FilePath) + Environment.NewLine; + var code = FileSystem.ReadFileText(this.FilePath) + Environment.NewLine; this.Generator = new ASTGenerator(code, this.Name); this.LastParsed = DateTime.Now; this.Transpiler = new Transpiler(this.Generator, this.Project); @@ -89,12 +91,12 @@ public async Task SaveModuleOutput(bool decend = true, bool suppressMessage = fa { Console.WriteLine($"Perfectly parsed: {Name}"); } - await IO.SaveFile("Model.xsd", this.OutPath, Transpiler.XsdToString()); - await IO.SaveFile("index.html", this.OutPath, Transpiler.HtmlToString()); + await FileSystem.SaveFile("Model.xsd", this.OutPath, Transpiler.XsdToString()); + await FileSystem.SaveFile("index.html", this.OutPath, Transpiler.HtmlToString()); foreach (var (key, value) in Transpiler.JsonToString()) { - await IO.SaveFile(key, this.OutPath, value); + await FileSystem.SaveFile(key, this.OutPath, value); } // Trickery to not parse infinately... @@ -117,24 +119,12 @@ public async Task SaveModuleOutput(bool decend = true, bool suppressMessage = fa } } public async Task Clean() - { - try - { - if (OutPath != null && Directory.Exists(OutPath)) - { - //Directory.Delete(OutPath, true); - await Task.Run(() => Directory.Delete(OutPath, true)); - } - } - catch (Exception ex) - { - Console.WriteLine("Error Cleaning Module " + this.Name); - Console.WriteLine(ex.Message); - } + { + await FileSystem.DeleteDirectory(OutPath); } public async Task SaveCode(string code) { - await IO.SaveFile(this.FilePath, code); + await FileSystem.SaveFile(this.FilePath, code); } public IEnumerable GetDescriptions() @@ -146,7 +136,7 @@ public IEnumerable GetDescriptions() private string CreateModuleName() { - return Module.FromPathToName(this.FilePath, this.BasePath); + return FileModule.FromPathToName(this.FilePath, this.BasePath); } public override string ToString() diff --git a/Project/FileProject.Network.cs b/Project/FileProject/FileProject.Network.cs similarity index 97% rename from Project/FileProject.Network.cs rename to Project/FileProject/FileProject.Network.cs index 65439f6..c2131d8 100644 --- a/Project/FileProject.Network.cs +++ b/Project/FileProject/FileProject.Network.cs @@ -1,11 +1,9 @@ -using System; +using Compiler.AST; +using Project.Models; using System.Collections.Generic; using System.Linq; -using Project.Models; -using Compiler; -using Compiler.AST; -namespace Project +namespace Project.File { public partial class FileProject { diff --git a/Project/FileProject.Watch.cs b/Project/FileProject/FileProject.Watch.cs similarity index 90% rename from Project/FileProject.Watch.cs rename to Project/FileProject/FileProject.Watch.cs index 39a5ca0..53e0fd2 100644 --- a/Project/FileProject.Watch.cs +++ b/Project/FileProject/FileProject.Watch.cs @@ -1,7 +1,8 @@ -using System; +using Project.FileProject; +using System; using System.Linq; -namespace Project +namespace Project.File { public partial class FileProject { @@ -21,7 +22,7 @@ public void Watch() var module = this.Modules.FirstOrDefault(m => m.Name == msm.ModuleName); if (module is null) { - module = new Module(msm.FileFullPath, this.BasePath, this); + module = new FileModule(msm.FileFullPath, this.BasePath, this); Modules.Add(module); } @@ -57,7 +58,7 @@ public void Watch() await moduleOld.Clean(); Modules.Remove(moduleOld); } - var module = new Module(msm.FileFullPath, this.BasePath, this); + var module = new FileModule(msm.FileFullPath, this.BasePath, this); Modules.Add(module); module.Parse(); await module.SaveModuleOutput(); diff --git a/Project/FileProject.cs b/Project/FileProject/FileProject.cs similarity index 60% rename from Project/FileProject.cs rename to Project/FileProject/FileProject.cs index ea910bc..4aa997c 100644 --- a/Project/FileProject.cs +++ b/Project/FileProject/FileProject.cs @@ -1,18 +1,19 @@ -using System; +using Compiler.AST; +using Configuration; +using Project.FileProject; +using Project.FileSystems; +using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.Linq; using System.Threading.Tasks; -using Compiler.AST; -using Configuration; -namespace Project +namespace Project.File { public partial class FileProject : IProject, IDisposable { - - //public static FileProject? Current { get; private set; } + private IFileSystem FileSystem { get; } /// /// The Directory in which the project is created. This is the root path, @@ -30,9 +31,9 @@ public partial class FileProject : IProject, IDisposable /// public string ConfigPath { get; } = ""; - public CarConfig CarConfig { get; private set; } - public ObservableCollection Modules { get; } = new ObservableCollection(); + + public ObservableCollection Modules { get; } = new ObservableCollection(); public FileProject(string path) { @@ -40,18 +41,22 @@ public FileProject(string path) this.OutPath = Path.GetFullPath($"out", this.BasePath); this.ConfigPath = Path.GetFullPath("zdragon.json", this.BasePath); this.CarConfig = CarConfig.Load(this.ConfigPath); + this.FileSystem = ProjectContext.FileSystem ?? throw new Exception("FileSystem does not exist"); - Directory.CreateDirectory(OutPath); + this.FileSystem.CreateDirectory(OutPath); + this.InitializeModules(); + } - var allfiles = Directory.GetFiles(path, "*.car", SearchOption.AllDirectories); - foreach (var file in allfiles) + public async void InitializeModules() + { + await this.FileSystem.GetAllFiles(this.BasePath, "*.car", file => { - var module = new Module(file, path, this); + var module = new FileModule(file, this.BasePath, this); Modules.Add(module); module.Parse(); - } + }); - Task.Run(ParseAllModules); + _ = Task.Run(ParseAllModules); } public async void ParseAllModules() @@ -65,33 +70,39 @@ public async void ParseAllModules() await Task.WhenAll(awaitables); } - public Task CreateModule(string name, string? code = null) + public Task CreateModule(string name, string? code = null) { - var tcs = new TaskCompletionSource(); + var tcs = new TaskCompletionSource(); try { var fileName = String.Join("/", name.Split(".").Select(UppercaseFirst)) + ".car"; var modulePath = Path.GetFullPath(fileName, this.BasePath); var directoryName = Path.GetDirectoryName(modulePath); - //Directory.CreateDirectory(directoryName); if (this.ProjectWatcher != null) { - Task.Factory.StartNew(() => - { - Action cleanup = () => { }; - var listenerName = "CREATEOR: " + DateTime.Now + new Random().Next(1000000).ToString(); - cleanup = this.ProjectWatcher.ModuleStream.Subscribe(listenerName, MessageType.ModuleChanged, msm => + //Task.Factory.StartNew(() => + //{ + this.ProjectWatcher.ModuleStream.Once(msm => { var module = this.FindModule(msm.ModuleName); if (module != null) tcs.TrySetResult(module); else tcs.TrySetException(new Exception("Failed to create the Module")); - Task.Run(cleanup); }); - }); + + //Action cleanup = () => { }; + //var listenerName = "CREATEOR: " + DateTime.Now + new Random().Next(1000000).ToString(); + //cleanup = this.ProjectWatcher.ModuleStream.Subscribe(listenerName, MessageType.ModuleChanged, msm => + //{ + // var module = this.FindModule(msm.ModuleName); + // if (module != null) tcs.TrySetResult(module); + // else tcs.TrySetException(new Exception("Failed to create the Module")); + // Task.Run(cleanup); + //}); + //}); } - _ = IO.SaveFile(modulePath, directoryName, $"# {name}"); + _ = FileSystem.SaveFile(modulePath, directoryName, $"# {name}"); } catch (Exception ex) { @@ -101,7 +112,7 @@ public Task CreateModule(string name, string? code = null) return tcs.Task; } - public async Task MoveModule(string oldName, string newName) + public async Task MoveModule(string oldName, string newName) { var fileName = String.Join("/", newName.Split(".").Select(UppercaseFirst)) + ".car"; var modulePath = Path.GetFullPath(fileName, this.BasePath); @@ -123,7 +134,7 @@ public List GetAstForModule(string moduleName) return module?.AST ?? new List(); } - public Module? FindModule(string name) + public IModule? FindModule(string name) { return this.Modules.FirstOrDefault(m => m.Name == name); } @@ -138,19 +149,23 @@ public async Task DeleteModule(string moduleName) if (this.ProjectWatcher != null) { - await Task.Factory.StartNew(() => - { - Action cleanup = () => { }; - var listenerName = "CREATEOR: " + DateTime.Now + new Random().Next(1000000).ToString(); - cleanup = this.ProjectWatcher.ModuleStream.Subscribe(listenerName, MessageType.ModuleDeleted, msm => + //await Task.Factory.StartNew(() => + //{ + this.ProjectWatcher.ModuleStream.Once(msm => { tcs.TrySetResult(true); - Task.Run(cleanup); }); - }); + // Action cleanup = () => { }; + //var listenerName = "CREATEOR: " + DateTime.Now + new Random().Next(1000000).ToString(); + //cleanup = this.ProjectWatcher.ModuleStream.Subscribe(listenerName, MessageType.ModuleDeleted, msm => + //{ + // tcs.TrySetResult(true); + // Task.Run(cleanup); + //}); + //}); } - await IO.DeleteFile(module.FilePath); + await FileSystem.DeleteFile(module.FilePath); } catch (Exception ex) { @@ -164,42 +179,12 @@ public void Dispose() Console.WriteLine("Disposing of the project..."); ProjectContext.Destruct(); this.ProjectWatcher?.Dispose(); - foreach (Module m in this.Modules) + foreach (FileModule m in this.Modules) { m.Dispose(); } } - // private void CreateNewModuleFile(string modulePath, string moduleName, string? code = null) - // { - // try - // { - // using (var fs = new FileStream(modulePath, FileMode.CreateNew, FileAccess.Write, FileShare.ReadWrite)) - // { - // using (var sr = new StreamWriter(fs)) - // { - // sr.Write(code ?? $@" - //# {moduleName} - - //Have fun with your module! - //"); - // sr.Flush(); - // sr.Close(); - // sr.Dispose(); - // } - // fs.Close(); - // fs.Dispose(); - - // } - // } - // catch (IOException ioe) - // { - // Console.WriteLine("ReadModuleText: Caught Exception reading file [{0}]", ioe); - // throw ioe; - // } - // } - - static string UppercaseFirst(string s) { // Check for empty string. diff --git a/Project/FileSystems/FileSystem.cs b/Project/FileSystems/FileSystem.cs new file mode 100644 index 0000000..afdca0c --- /dev/null +++ b/Project/FileSystems/FileSystem.cs @@ -0,0 +1,125 @@ +using System; +using System.IO; +using System.Threading.Tasks; + +namespace Project.FileSystems +{ + public class FileSystem : IFileSystem + { + public string ReadFileText(string filePath) + { + try + { + if (!System.IO.File.Exists(filePath)) return ""; + using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + using (var sr = new StreamReader(fs)) + { + return sr.ReadToEnd(); + } + } + } + catch (IOException ioe) + { + Console.WriteLine("ReadModuleText: Caught Exception reading file [{0}]", ioe); + throw ioe; + } + } + + public async Task SaveFile(string filePath, string source) + { + try + { + using (var fs = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite)) + { + using (var sr = new StreamWriter(fs)) + { + await sr.WriteAsync(source); + sr.Flush(); + sr.Close(); + sr.Dispose(); + } + fs.Close(); + fs.Dispose(); + } + } + catch (IOException ioe) + { + Console.WriteLine("ReadModuleText: Caught Exception reading file [{0}]", ioe); + throw ioe; + } + } + + public async Task SaveFile(string fileName, string directoryName, string source) + { + try + { + var fullPath = Path.Combine(directoryName, fileName); + Directory.CreateDirectory(directoryName); + await SaveFile(fullPath, source); + } + catch (IOException ioe) + { + Console.WriteLine("ReadModuleText: Caught Exception reading file [{0}]", ioe); + throw ioe; + } + } + + public async Task DeleteFile(string fileName) + { + try + { + await Task.Run(() => System.IO.File.Delete(fileName)); + } + catch (Exception ex) + { + Console.WriteLine("Failed to delete file: " + fileName); + Console.WriteLine(ex.Message); + } + } + + public async Task DeleteDirectory(string path) + { + try + { + + await Task.Run(() => Directory.Delete(path, true)); + } + catch (Exception ex) + { + Console.WriteLine("Failed to delete directory: " + path); + Console.WriteLine(ex.Message); + } + } + + public async Task CreateDirectory(string path) + { + try + { + await Task.Run(() => Directory.CreateDirectory(path)); + } + catch (Exception ex) + { + Console.WriteLine("Failed to create directory: " + path); + Console.WriteLine(ex.Message); + } + } + + public async Task GetAllFiles(string path, string extension, Action handler) + { + try + { + var allfiles = await Task.Run(() => Directory.GetFiles(path, extension, SearchOption.AllDirectories)); + foreach (var file in allfiles) + { + handler(file); + } + } + catch (Exception ex) + { + Console.WriteLine("Failed to get all files: " + path); + Console.WriteLine(ex.Message); + } + } + } +} diff --git a/Project/FileSystems/IFileSystem.cs b/Project/FileSystems/IFileSystem.cs new file mode 100644 index 0000000..913ae06 --- /dev/null +++ b/Project/FileSystems/IFileSystem.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; + +namespace Project.FileSystems +{ + public interface IFileSystem + { + string ReadFileText(string filePath); + Task SaveFile(string filePath, string source); + Task SaveFile(string fileName, string directoryName, string source); + Task DeleteFile(string fileName); + Task DeleteDirectory(string path); + Task CreateDirectory(string path); + Task GetAllFiles(string path, string extension, Action handler); + } + +} diff --git a/Project/FileSystems/MemorySystem.cs b/Project/FileSystems/MemorySystem.cs new file mode 100644 index 0000000..143d79d --- /dev/null +++ b/Project/FileSystems/MemorySystem.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Project.FileSystems +{ + public class MemorySystem : IFileSystem + { + private static readonly Lazy> lazy = + new Lazy>(() => new Dictionary()); + + public static Dictionary Instance { get { return lazy.Value; } } + + public string ReadFileText(string filePath) + { + if (!Instance.ContainsKey(filePath)) return ""; + return Instance[filePath]; + } + + public async Task SaveFile(string filePath, string source) + { + await Task.Run(() => Instance[filePath] = source); + } + + public async Task SaveFile(string fileName, string directoryName, string source) + { + await Task.Run(() => Instance[fileName] = source); + } + + public async Task DeleteFile(string fileName) + { + await Task.Run(() => Instance.Remove(fileName)); + } + + public async Task DeleteDirectory(string directoryName) + { + var removals = Instance.Keys.Where(k => k.StartsWith(directoryName)).Select(k => Task.Run(() => Instance.Remove(k))); + await Task.WhenAll(removals.ToList()); + } + + public async Task CreateDirectory(string directoryName) + { + await Task.Run(() => { }); + } + + public async Task GetAllFiles(string path, string extension, Action handler) + { + await Task.Run(() => + { + Instance.Keys.Where(k => k.EndsWith(extension)).ToList().ForEach(handler); + }); + } + } + + + +} diff --git a/Project/IModule.cs b/Project/IModule.cs new file mode 100644 index 0000000..c8499ec --- /dev/null +++ b/Project/IModule.cs @@ -0,0 +1,33 @@ +using Compiler; +using Compiler.AST; +using Mapper.Application; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Project +{ + public interface IModule : IDisposable + { + string Name { get; } + string FilePath { get; } + string BasePath { get; } + string OutPath { get; } + IProject Project { get; } + List ReferencedModules { get; } + Transpiler Transpiler { get; } + ASTGenerator Generator { get; } + DateTime LastParsed { get; } + List AST { get; } + string Code { get; } + Task SaveCode(string code); + void Parse(); + Task SaveModuleOutput(bool decend = true, bool suppressMessage = false); + Task Clean(); + + // Refactor these two out + + Descriptor ToDescriptor(string description); + IEnumerable GetDescriptions(); + } +} diff --git a/Project/IO.cs b/Project/IO.cs deleted file mode 100644 index 8edb7a6..0000000 --- a/Project/IO.cs +++ /dev/null @@ -1,79 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Threading.Tasks; -using System.Text; - -namespace Project -{ - internal static class IO - { - internal static string ReadFileText(string filePath) - { - try - { - if (!File.Exists(filePath)) return ""; - using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) - { - using (var sr = new StreamReader(fs)) - { - return sr.ReadToEnd(); - } - } - } - catch (IOException ioe) - { - Console.WriteLine("ReadModuleText: Caught Exception reading file [{0}]", ioe); - throw ioe; - } - } - - internal static async Task SaveFile(string filePath, string source) - { - try - { - using (var fs = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite)) - { - using (var sr = new StreamWriter(fs)) - { - await sr.WriteAsync(source); - sr.Flush(); - sr.Close(); - sr.Dispose(); - } - fs.Close(); - fs.Dispose(); - } - } - catch (IOException ioe) - { - Console.WriteLine("ReadModuleText: Caught Exception reading file [{0}]", ioe); - throw ioe; - } - } - - internal static async Task SaveFile(string fileName, string directoryName, string source) - { - try - { - Directory.CreateDirectory(directoryName); - await SaveFile(fileName, source); - } - catch (IOException ioe) - { - Console.WriteLine("ReadModuleText: Caught Exception reading file [{0}]", ioe); - throw ioe; - } - } - - internal static async Task DeleteFile(string fileName) - { - await Task.Run(() => File.Delete(fileName)); - } - - internal static async Task DeleteDirectory(string directoryName) - { - await Task.Run(() => Directory.Delete(directoryName, true)); - } - } -} diff --git a/Project/IProject.cs b/Project/IProject.cs index 54b360c..68948a8 100644 --- a/Project/IProject.cs +++ b/Project/IProject.cs @@ -14,28 +14,28 @@ public interface IProject : IDisposable /// The Directory in which the project is created. This is the root path, /// the path where the zdragon.json file is located. /// - public string BasePath { get; } + string BasePath { get; } /// /// Where the assets, the results are published to. /// - public string OutPath { get; } + string OutPath { get; } /// /// Where the zdragon.json file is located. /// - public string ConfigPath { get; } + string ConfigPath { get; } CarConfig CarConfig { get; } - ObservableCollection Modules { get; } + ObservableCollection Modules { get; } - Task CreateModule(string moduleName, string? code); + Task CreateModule(string moduleName, string? code); - Task MoveModule(string oldName, string newName); + Task MoveModule(string oldName, string newName); - Module? FindModule(string moduleName); + IModule? FindModule(string moduleName); Task DeleteModule(string moduleName); diff --git a/Project/MemoryProject/MemoryModule.cs b/Project/MemoryProject/MemoryModule.cs new file mode 100644 index 0000000..9375665 --- /dev/null +++ b/Project/MemoryProject/MemoryModule.cs @@ -0,0 +1,102 @@ +using Compiler; +using Compiler.AST; +using Mapper.Application; +using Project.FileSystems; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Project.MemoryProject +{ + public class MemoryModule : IModule + { + private IFileSystem FileSystem { get; } + public string Name { get; } + + public string FilePath { get; } + + public string BasePath => ""; + + public string OutPath { get; } + + public IProject Project { get; } + + public List ReferencedModules { get; private set; } + + public Transpiler Transpiler { get; private set; } + + public ASTGenerator Generator { get; private set; } + + public DateTime LastParsed { get; private set; } + + public List AST => Generator.AST; + + public List Errors => Generator.Errors; + + public string Code { get; } + + public MemoryModule(string moduleName, string code) + { + this.FileSystem = ProjectContext.FileSystem ?? throw new Exception("Invalid File System"); + this.Name = moduleName; + this.Code = code; + this.FilePath = $"{moduleName}.car"; + this.OutPath = $"out/{moduleName}"; + this.Project = ProjectContext.Instance ?? throw new Exception("Project not set"); + + this.Generator = new ASTGenerator(code, moduleName); + this.Transpiler = new Transpiler(this.Generator, this.Project); + this.LastParsed = DateTime.Now; + + this.ReferencedModules = new List(); + } + + public void Parse() + { + var code = this.FileSystem.ReadFileText(this.FilePath) + Environment.NewLine; + this.Generator = new ASTGenerator(code, this.Name); + this.LastParsed = DateTime.Now; + this.Transpiler = new Transpiler(this.Generator, this.Project); + + this.ReferencedModules = Generator.AST.FindAll(n => n is ASTImport).Select(i => ((ASTImport)i).ModuleName).ToList(); + } + + public async Task SaveCode(string code) + { + await this.FileSystem.SaveFile(this.FilePath, code); + } + + public Task SaveModuleOutput(bool decend = true, bool suppressMessage = false) + { + throw new NotImplementedException(); + } + + public async Task Clean() + { + await Task.Run(() => { }); + + } + + public Descriptor ToDescriptor(string description) + { + return new Descriptor + { + Description = description, + Name = this.Name, + Module = this.Name + }; + } + + public IEnumerable GetDescriptions() + { + return new List(); + } + + public void Dispose() + { + // nothing to dispose + } + } +} diff --git a/Project/MemoryProject/MemoryProject.cs b/Project/MemoryProject/MemoryProject.cs new file mode 100644 index 0000000..926d28e --- /dev/null +++ b/Project/MemoryProject/MemoryProject.cs @@ -0,0 +1,90 @@ +using Compiler.AST; +using Configuration; +using Project.FileSystems; +using Project.Models; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Project.MemoryProject +{ + public class MemoryProject : IProject + { + private IFileSystem FileSystem { get; } + public string BasePath => ""; + + public string OutPath => "out/"; + + public string ConfigPath => "zdragon.json"; + + public CarConfig CarConfig { get; } + public ObservableCollection Modules { get; } + + public MemoryProject() + { + this.FileSystem = ProjectContext.FileSystem ?? throw new Exception("Invalid file system"); + this.CarConfig = new CarConfig(); + this.Modules = new ObservableCollection(); + } + + + public async Task CreateModule(string moduleName, string? code) + { + var source = code ?? $"# {moduleName}"; + var module = new MemoryModule(moduleName, source); + await this.FileSystem.SaveFile(module.FilePath, source); + this.Modules.Add(module); + return module; + } + + public async Task DeleteModule(string moduleName) + { + var module = FindModule(moduleName); + if (module != null) + { + this.Modules.Remove(module); + module.Dispose(); + } + + return await Task.FromResult(true); + } + + public void Dispose() + { + // nothing to dispose + } + + public IModule? FindModule(string moduleName) + { + return this.Modules.FirstOrDefault(m => m.Name == moduleName); + } + + public List GetAstForModule(string moduleName) + { + return FindModule(moduleName)?.AST ?? new List(); + } + + public Topology GetTopology(bool includeDetails) + { + throw new NotImplementedException(); + } + + public Task MoveModule(string oldName, string newName) + { + throw new NotImplementedException(); + } + + public void ParseAllModules() + { + throw new NotImplementedException(); + } + + public void Watch() + { + throw new NotImplementedException(); + } + } +} diff --git a/Project/ModuleStream.cs b/Project/ModuleStream.cs index 0d12a78..4fc7e7d 100644 --- a/Project/ModuleStream.cs +++ b/Project/ModuleStream.cs @@ -10,11 +10,13 @@ public class ModuleStream : IDisposable private Subject moduleMessageSubject; private IDictionary subscribers; + private List> once; public ModuleStream() { moduleMessageSubject = new Subject(); subscribers = new Dictionary(); + once = new List>(); } public void Dispose() @@ -28,6 +30,8 @@ public void Dispose() { subscriber.Value.Dispose(); } + + once = new List>(); } @@ -38,6 +42,13 @@ public void Dispose() public void Publish(ModuleStreamMessage moduleMessage) { moduleMessageSubject.OnNext(moduleMessage); + + + foreach (var action in once) + { + action(moduleMessage); + } + once = new List>(); } @@ -87,7 +98,30 @@ public Action Subscribe(string subscriberName, MessageType messageType, Action { }; } + public void Once(Action action) + { + once.Add(action); + } + + + private static string key() + { + var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + var stringChars = new char[8]; + var random = new Random(); + + for (int i = 0; i < stringChars.Length; i++) + { + stringChars[i] = chars[random.Next(chars.Length)]; + } + + var finalString = new String(stringChars); + return finalString; + } + } + + } diff --git a/Project/ProjectContext.cs b/Project/ProjectContext.cs index 8e96aa5..aee4cbb 100644 --- a/Project/ProjectContext.cs +++ b/Project/ProjectContext.cs @@ -1,13 +1,24 @@ -using System; +using Project.File; +using Project.FileSystems; + namespace Project { public class ProjectContext { public static IProject? Instance { get; private set; } + public static IFileSystem? FileSystem { get; private set; } public static IProject Init(string dir) { - Instance = new FileProject(dir); + FileSystem = new FileSystem(); + Instance = new Project.File.FileProject(dir); + return Instance; + } + + public static IProject InitInMemory() + { + FileSystem = new MemorySystem(); + Instance = new Project.MemoryProject.MemoryProject(); return Instance; } diff --git a/Project/ProjectFilesWatcher.cs b/Project/ProjectFilesWatcher.cs index f73b0fc..07d4d17 100644 --- a/Project/ProjectFilesWatcher.cs +++ b/Project/ProjectFilesWatcher.cs @@ -1,4 +1,5 @@ -using System; +using Project.FileProject; +using System; using System.IO; namespace Project @@ -22,8 +23,7 @@ public void Start() IncludeSubdirectories = true, Path = this.BasePath, - // Watch for changes in LastAccess and LastWrite times, and - // the renaming of files or directories. + // Watch for changes in FileName and LastWrite NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName, // Only watch text files. @@ -46,7 +46,7 @@ private void OnChanged(object source, FileSystemEventArgs e) try { ModuleStream.Publish(new ModuleStreamMessage( - Module.FromPathToName(e.FullPath, this.BasePath), + FileModule.FromPathToName(e.FullPath, this.BasePath), e.FullPath, MessageType.ModuleChanged )); @@ -78,7 +78,7 @@ private void OnDelete(object source, FileSystemEventArgs e) try { ModuleStream.Publish(new ModuleStreamMessage( - Module.FromPathToName(e.FullPath, this.BasePath), + FileModule.FromPathToName(e.FullPath, this.BasePath), e.FullPath, MessageType.ModuleDeleted )); @@ -94,9 +94,9 @@ private void OnRenamed(object source, RenamedEventArgs e) try { ModuleStream.Publish(new ModuleStreamMessage( - Module.FromPathToName(e.FullPath, this.BasePath), + FileModule.FromPathToName(e.FullPath, this.BasePath), e.FullPath, - Module.FromPathToName(e.OldFullPath, this.BasePath), + FileModule.FromPathToName(e.OldFullPath, this.BasePath), e.OldFullPath, MessageType.ModuleMoved )); diff --git a/Project/Transpiler.cs b/Project/Transpiler.cs index 1972599..e037477 100644 --- a/Project/Transpiler.cs +++ b/Project/Transpiler.cs @@ -16,7 +16,7 @@ public class Transpiler { private IEnumerable Descriptions = Enumerable.Empty(); - public FileProject Project { get; } + public IProject Project { get; } public string Code => Generator.Code; public ASTGenerator Generator { get; } public XSDMapper XsdMapper { get; private set; } @@ -28,7 +28,7 @@ public class Transpiler public List AST => Imports.Concat(Generator.AST).ToList(); - public Transpiler(ASTGenerator generator, FileProject project) + public Transpiler(ASTGenerator generator, IProject project) { this.Generator = generator; this.Project = project;