diff --git a/SwiftBindings.sln b/SwiftBindings.sln new file mode 100644 index 000000000000..0153ec68a445 --- /dev/null +++ b/SwiftBindings.sln @@ -0,0 +1,48 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SyntaxDynamo", "src\SyntaxDynamo\src\SyntaxDynamo.csproj", "{E05ECC4A-7212-4077-AD30-E4183CA3C1AF}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SyntaxDynamo.Tests", "src\SyntaxDynamo\tests\SyntaxDynamo.Tests.csproj", "{0C13F0AC-76F1-4FCB-AF42-9CB910A9471B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {E05ECC4A-7212-4077-AD30-E4183CA3C1AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E05ECC4A-7212-4077-AD30-E4183CA3C1AF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E05ECC4A-7212-4077-AD30-E4183CA3C1AF}.Debug|x64.ActiveCfg = Debug|Any CPU + {E05ECC4A-7212-4077-AD30-E4183CA3C1AF}.Debug|x64.Build.0 = Debug|Any CPU + {E05ECC4A-7212-4077-AD30-E4183CA3C1AF}.Debug|x86.ActiveCfg = Debug|Any CPU + {E05ECC4A-7212-4077-AD30-E4183CA3C1AF}.Debug|x86.Build.0 = Debug|Any CPU + {E05ECC4A-7212-4077-AD30-E4183CA3C1AF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E05ECC4A-7212-4077-AD30-E4183CA3C1AF}.Release|Any CPU.Build.0 = Release|Any CPU + {E05ECC4A-7212-4077-AD30-E4183CA3C1AF}.Release|x64.ActiveCfg = Release|Any CPU + {E05ECC4A-7212-4077-AD30-E4183CA3C1AF}.Release|x64.Build.0 = Release|Any CPU + {E05ECC4A-7212-4077-AD30-E4183CA3C1AF}.Release|x86.ActiveCfg = Release|Any CPU + {E05ECC4A-7212-4077-AD30-E4183CA3C1AF}.Release|x86.Build.0 = Release|Any CPU + {0C13F0AC-76F1-4FCB-AF42-9CB910A9471B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0C13F0AC-76F1-4FCB-AF42-9CB910A9471B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0C13F0AC-76F1-4FCB-AF42-9CB910A9471B}.Debug|x64.ActiveCfg = Debug|Any CPU + {0C13F0AC-76F1-4FCB-AF42-9CB910A9471B}.Debug|x64.Build.0 = Debug|Any CPU + {0C13F0AC-76F1-4FCB-AF42-9CB910A9471B}.Debug|x86.ActiveCfg = Debug|Any CPU + {0C13F0AC-76F1-4FCB-AF42-9CB910A9471B}.Debug|x86.Build.0 = Debug|Any CPU + {0C13F0AC-76F1-4FCB-AF42-9CB910A9471B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0C13F0AC-76F1-4FCB-AF42-9CB910A9471B}.Release|Any CPU.Build.0 = Release|Any CPU + {0C13F0AC-76F1-4FCB-AF42-9CB910A9471B}.Release|x64.ActiveCfg = Release|Any CPU + {0C13F0AC-76F1-4FCB-AF42-9CB910A9471B}.Release|x64.Build.0 = Release|Any CPU + {0C13F0AC-76F1-4FCB-AF42-9CB910A9471B}.Release|x86.ActiveCfg = Release|Any CPU + {0C13F0AC-76F1-4FCB-AF42-9CB910A9471B}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/eng/pipelines/runtimelab.yml b/eng/pipelines/runtimelab.yml index a2d5f7dda0f4..3d3a7752e8c7 100644 --- a/eng/pipelines/runtimelab.yml +++ b/eng/pipelines/runtimelab.yml @@ -49,16 +49,33 @@ stages: - stage: build displayName: Build jobs: - - template: /eng/pipelines/templates/build-job.yml - parameters: - osGroup: OSX - archType: x64 - ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: + - ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: + - template: /eng/pipelines/templates/build-job.yml + parameters: + osGroup: OSX + archType: x64 isOfficialBuild: true runTests: false pool: vmImage: 'macOS-latest' + - ${{ if or(eq(variables['System.TeamProject'], 'public'), in(variables['Build.Reason'], 'PullRequest')) }}: + - template: /eng/pipelines/templates/build-job.yml + parameters: + osGroup: OSX + archType: arm64 + runTests: true + pool: + vmImage: 'macOS-latest' + + - template: /eng/pipelines/templates/build-job.yml + parameters: + osGroup: OSX + archType: x64 + runTests: true + pool: + vmImage: 'macOS-latest' + # Publish and validation steps. Only run in official builds - ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - template: \eng\common\templates\post-build\post-build.yml diff --git a/global.json b/global.json index ef7775d375fe..c5c66e5ff962 100644 --- a/global.json +++ b/global.json @@ -8,6 +8,6 @@ "dotnet": "8.0.100" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.24102.4" + "Microsoft.DotNet.Arcade.Sdk": "8.0.0-beta.22430.3" } } diff --git a/src/SwiftBindings/SwiftBindings.sln b/src/SwiftBindings/SwiftBindings.sln deleted file mode 100644 index 039811c309a5..000000000000 --- a/src/SwiftBindings/SwiftBindings.sln +++ /dev/null @@ -1,48 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26124.0 -MinimumVisualStudioVersion = 15.0.26124.0 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SwiftBindings", "src\SwiftBindings.csproj", "{B7977360-6671-4707-9A1C-1C29D5BE2674}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SwiftBindings.Tests", "tests\SwiftBindings.Tests.csproj", "{CE81B6BD-CCCC-4223-9069-B28435A4A5C1}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {B7977360-6671-4707-9A1C-1C29D5BE2674}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B7977360-6671-4707-9A1C-1C29D5BE2674}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B7977360-6671-4707-9A1C-1C29D5BE2674}.Debug|x64.ActiveCfg = Debug|Any CPU - {B7977360-6671-4707-9A1C-1C29D5BE2674}.Debug|x64.Build.0 = Debug|Any CPU - {B7977360-6671-4707-9A1C-1C29D5BE2674}.Debug|x86.ActiveCfg = Debug|Any CPU - {B7977360-6671-4707-9A1C-1C29D5BE2674}.Debug|x86.Build.0 = Debug|Any CPU - {B7977360-6671-4707-9A1C-1C29D5BE2674}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B7977360-6671-4707-9A1C-1C29D5BE2674}.Release|Any CPU.Build.0 = Release|Any CPU - {B7977360-6671-4707-9A1C-1C29D5BE2674}.Release|x64.ActiveCfg = Release|Any CPU - {B7977360-6671-4707-9A1C-1C29D5BE2674}.Release|x64.Build.0 = Release|Any CPU - {B7977360-6671-4707-9A1C-1C29D5BE2674}.Release|x86.ActiveCfg = Release|Any CPU - {B7977360-6671-4707-9A1C-1C29D5BE2674}.Release|x86.Build.0 = Release|Any CPU - {CE81B6BD-CCCC-4223-9069-B28435A4A5C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CE81B6BD-CCCC-4223-9069-B28435A4A5C1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CE81B6BD-CCCC-4223-9069-B28435A4A5C1}.Debug|x64.ActiveCfg = Debug|Any CPU - {CE81B6BD-CCCC-4223-9069-B28435A4A5C1}.Debug|x64.Build.0 = Debug|Any CPU - {CE81B6BD-CCCC-4223-9069-B28435A4A5C1}.Debug|x86.ActiveCfg = Debug|Any CPU - {CE81B6BD-CCCC-4223-9069-B28435A4A5C1}.Debug|x86.Build.0 = Debug|Any CPU - {CE81B6BD-CCCC-4223-9069-B28435A4A5C1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CE81B6BD-CCCC-4223-9069-B28435A4A5C1}.Release|Any CPU.Build.0 = Release|Any CPU - {CE81B6BD-CCCC-4223-9069-B28435A4A5C1}.Release|x64.ActiveCfg = Release|Any CPU - {CE81B6BD-CCCC-4223-9069-B28435A4A5C1}.Release|x64.Build.0 = Release|Any CPU - {CE81B6BD-CCCC-4223-9069-B28435A4A5C1}.Release|x86.ActiveCfg = Release|Any CPU - {CE81B6BD-CCCC-4223-9069-B28435A4A5C1}.Release|x86.Build.0 = Release|Any CPU - EndGlobalSection -EndGlobal diff --git a/src/SwiftBindings/src/MyClass.cs b/src/SwiftBindings/src/MyClass.cs deleted file mode 100644 index 907d856d3f57..000000000000 --- a/src/SwiftBindings/src/MyClass.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; - -namespace SwiftBindings -{ - public class MyClass - { - public static bool ReturnTrue => true; - public static void Main(string[] args) - { - Console.WriteLine("Hello World!"); - } - } -} diff --git a/src/SwiftBindings/tests/SmokeTests.cs b/src/SwiftBindings/tests/SmokeTests.cs deleted file mode 100644 index db7c93457129..000000000000 --- a/src/SwiftBindings/tests/SmokeTests.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using Xunit; - -namespace SwiftBindings.Tests -{ - public class MyClassTests - { - [Fact] - public void Test1() - { - Assert.True(MyClass.ReturnTrue); - } - } -} diff --git a/src/SwiftBindings/tests/SwiftBindings.Tests.csproj b/src/SwiftBindings/tests/SwiftBindings.Tests.csproj deleted file mode 100644 index b09cc9165c4c..000000000000 --- a/src/SwiftBindings/tests/SwiftBindings.Tests.csproj +++ /dev/null @@ -1,9 +0,0 @@ - - - net8.0 - - - - - - diff --git a/src/SyntaxDynamo/src/CodeElementCollection.cs b/src/SyntaxDynamo/src/CodeElementCollection.cs new file mode 100644 index 000000000000..e51256417970 --- /dev/null +++ b/src/SyntaxDynamo/src/CodeElementCollection.cs @@ -0,0 +1,62 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace SyntaxDynamo +{ + public class CodeElementCollection : List, ICodeElementSet where T : ICodeElement + { + public CodeElementCollection() : base() + { + } + + #region ICodeElem implementation + + public event EventHandler Begin = (s, e) => { }; + + public event EventHandler End = (s, e) => { }; + + public virtual object? BeginWrite(ICodeWriter writer) + { + OnBeginWrite(new WriteEventArgs(writer)); + return null; + } + + protected virtual void OnBeginWrite(WriteEventArgs args) + { + Begin(this, args); + } + + public virtual void Write(ICodeWriter writer, object? o) + { + } + + public virtual void EndWrite(ICodeWriter writer, object? o) + { + OnEndWrite(new WriteEventArgs(writer)); + } + + protected virtual void OnEndWrite(WriteEventArgs args) + { + End.FireInReverse(this, args); + } + + #endregion + + #region ICodeElemSet implementation + + public System.Collections.Generic.IEnumerable Elements + { + get + { + return this.Cast(); + } + } + + #endregion + + } +} diff --git a/src/SyntaxDynamo/src/CodeWriter.cs b/src/SyntaxDynamo/src/CodeWriter.cs new file mode 100644 index 000000000000..67381570d55d --- /dev/null +++ b/src/SyntaxDynamo/src/CodeWriter.cs @@ -0,0 +1,157 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Globalization; +using System.IO; + +namespace SyntaxDynamo +{ + public class CodeWriter : ICodeWriter + { + int charsWrittenThisLine; + bool indentedThisLine; + + const int kSpacesPerIndent = 4; + const int kWrapPoint = 60; + + public CodeWriter(Stream stm) + : this(new StreamWriter(stm)) + { + } + + public CodeWriter(TextWriter tw) + { + ArgumentNullException.ThrowIfNull(tw, nameof(tw)); + TextWriter = tw; + charsWrittenThisLine = 0; + IndentLevel = 0; + IsAtLineStart = true; + } + + public static void WriteToFile(string fileName, ICodeElement element) + { + using (var stm = new FileStream(fileName, FileMode.Create)) + { + CodeWriter writer = new CodeWriter(stm); + element.WriteAll(writer); + writer.TextWriter.Flush(); + } + } + + public static Stream WriteToStream(ICodeElement element) + { + var stm = new MemoryStream(); + var codeWriter = new CodeWriter(stm); + element.WriteAll(codeWriter); + codeWriter.TextWriter.Flush(); + stm.Flush(); + stm.Seek(0, SeekOrigin.Begin); + return stm; + } + + public static string WriteToString(ICodeElement element) + { + using (var reader = new StreamReader(WriteToStream(element))) + return reader.ReadToEnd(); + } + + public TextWriter TextWriter { get; private set; } + + #region ICodeWriter implementation + + public void BeginNewLine(bool prependIndents) + { + Write(Environment.NewLine, false); + if (prependIndents) + WriteIndents(); + IsAtLineStart = true; + } + + public void EndLine() + { + charsWrittenThisLine = 0; + if (indentedThisLine) + { + indentedThisLine = false; + // strictly speaking, this test shouldn't be necessary + if (IndentLevel > 0) + Exdent(); + } + } + + public bool IsAtLineStart { get; private set; } + + public void Write(string code, bool allowSplit) + { + var characterEnum = StringInfo.GetTextElementEnumerator(code); + while (characterEnum.MoveNext()) + { + string c = characterEnum.GetTextElement(); + WriteUnicode(c, allowSplit); + } + } + + public void Write(char c, bool allowSplit) + { + Write(c.ToString(), allowSplit); + } + + void WriteUnicode(string c, bool allowSplit) + { + var info = new StringInfo(c); + if (info.LengthInTextElements > 1) + throw new ArgumentOutOfRangeException(nameof(c), $"Expected a single unicode value but got '{c}'"); + WriteNoBookKeeping(c); + IsAtLineStart = false; + charsWrittenThisLine++; + if (allowSplit && charsWrittenThisLine > kWrapPoint && IsWhiteSpace(c)) + { + if (!indentedThisLine) + { + Indent(); + indentedThisLine = true; + } + WriteNoBookKeeping(Environment.NewLine); + charsWrittenThisLine = 0; + WriteIndents(); + IsAtLineStart = true; + } + } + + bool IsWhiteSpace(string c) + { + return c.Length == 1 && Char.IsWhiteSpace(c[0]); + + } + + void WriteNoBookKeeping(string s) + { + TextWriter.Write(s); + } + + void WriteIndents() + { + int totalSpaces = IndentLevel * kSpacesPerIndent; + // know what's embarrassing? When your indents cause breaks which generates more indents. + for (int i = 0; i < totalSpaces; i++) + Write(" ", false); + } + + public void Indent() + { + IndentLevel++; + } + + public void Exdent() + { + if (IndentLevel == 0) + throw new Exception("IndentLevel is at 0."); + IndentLevel--; + } + + public int IndentLevel { get; private set; } + + #endregion + } +} diff --git a/src/SyntaxDynamo/src/CommaListElementCollection.cs b/src/SyntaxDynamo/src/CommaListElementCollection.cs new file mode 100644 index 000000000000..04d99a13d763 --- /dev/null +++ b/src/SyntaxDynamo/src/CommaListElementCollection.cs @@ -0,0 +1,87 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; + +namespace SyntaxDynamo +{ + public class CommaListElementCollection : List, ICodeElement where T : ICodeElement + { + public CommaListElementCollection() + : this("", "") + { + } + + public CommaListElementCollection(IEnumerable objs) + : this("", "", objs) + { + } + + public CommaListElementCollection(string prefix, string suffix) + : base() + { + ArgumentNullException.ThrowIfNull(prefix, nameof(prefix)); + ArgumentNullException.ThrowIfNull(suffix, nameof(suffix)); + Prefix = prefix; + Suffix = suffix; + } + + public CommaListElementCollection(string prefix, string suffix, IEnumerable objs, bool newlineAfterEach = false) + : this(prefix, suffix) + { + ArgumentNullException.ThrowIfNull(objs, nameof(objs)); + AddRange(objs); + NewlineAfterEach = newlineAfterEach; + } + + public string Prefix { get; private set; } + public string Suffix { get; private set; } + public bool NewlineAfterEach { get; private set; } + + #region ICodeElem implementation + + public event EventHandler Begin = (s, e) => { }; + + public event EventHandler End = (s, e) => { }; + + public object? BeginWrite(ICodeWriter writer) + { + OnBegin(new WriteEventArgs(writer)); + return null; + } + + protected virtual void OnBegin(WriteEventArgs args) + { + Begin(this, args); + } + + public void Write(ICodeWriter writer, object? o) + { + writer.Write(Prefix, true); + for (int i = 0; i < Count; i++) + { + this[i].WriteAll(writer); + if (i < Count - 1) + writer.Write(", ", true); + if (NewlineAfterEach && !writer.IsAtLineStart) + { + writer.BeginNewLine(true); + } + } + writer.Write(Suffix, true); + } + + public void EndWrite(ICodeWriter writer, object? o) + { + OnEnd(new WriteEventArgs(writer)); + } + + protected virtual void OnEnd(WriteEventArgs args) + { + End.FireInReverse(this, args); + } + + #endregion + } +} diff --git a/src/SyntaxDynamo/src/DecoratedCodeElementCollection.cs b/src/SyntaxDynamo/src/DecoratedCodeElementCollection.cs new file mode 100644 index 000000000000..3171c83b5863 --- /dev/null +++ b/src/SyntaxDynamo/src/DecoratedCodeElementCollection.cs @@ -0,0 +1,81 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Collections.Generic; + +namespace SyntaxDynamo +{ + public class DecoratedCodeElementCollection : CodeElementCollection where T : ICodeElement + { + bool startOnOwnLine, endOnOwnLine, indent; + + public DecoratedCodeElementCollection(string startDecoration, string endDecoration, + bool startOnOwnLine, bool endOnOwnLine, bool indent, + IEnumerable? elems) + : base() + { + StartDecoration = startDecoration; + EndDecoration = endDecoration; + this.startOnOwnLine = startOnOwnLine; + this.endOnOwnLine = endOnOwnLine; + this.indent = indent; + if (elems != null) + AddRange(elems); + } + + public DecoratedCodeElementCollection(string startDecoration, string endDecoration, + bool startOnOwnLine, bool endOnOwnLine, bool indent) + : this(startDecoration, endDecoration, startOnOwnLine, endOnOwnLine, indent, null) + { + } + + public string StartDecoration { get; private set; } + public string EndDecoration { get; private set; } + + public override void Write(ICodeWriter writer, object? o) + { + if (StartDecoration != null) + { + if (startOnOwnLine) + writer.BeginNewLine(true); + writer.Write(StartDecoration, true); + if (startOnOwnLine) + writer.EndLine(); + } + if (indent) + writer.Indent(); + } + + public override void EndWrite(ICodeWriter writer, object? o) + { + if (indent) + writer.Exdent(); + if (EndDecoration != null) + { + if (endOnOwnLine) + writer.BeginNewLine(true); + writer.Write(EndDecoration, true); + if (endOnOwnLine) + writer.EndLine(); + } + + base.EndWrite(writer, o); + } + + public override string ToString() + { + return string.Format("{0}{1}{2}", + StartDecoration ?? "", + StartDecoration != null && EndDecoration != null ? "..." : "", + EndDecoration ?? ""); + } + + public static DecoratedCodeElementCollection CBlock(IEnumerable elems) + { + var col = new DecoratedCodeElementCollection("{", "}", true, true, true); + if (elems != null) + col.AddRange(elems); + return col; + } + } +} diff --git a/src/SyntaxDynamo/src/DelegatedCommaListElementCollection.cs b/src/SyntaxDynamo/src/DelegatedCommaListElementCollection.cs new file mode 100644 index 000000000000..843845db6b7e --- /dev/null +++ b/src/SyntaxDynamo/src/DelegatedCommaListElementCollection.cs @@ -0,0 +1,63 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; + +namespace SyntaxDynamo +{ + public class DelegatedCommaListElemCollection : List, ICodeElement where T : ICodeElement + { + Action elementWriter; + public DelegatedCommaListElemCollection(Action elementWriter) + : base() + { + ArgumentNullException.ThrowIfNull(elementWriter, nameof(elementWriter)); + this.elementWriter = elementWriter; + } + + public DelegatedCommaListElemCollection(Action elementWriter, IEnumerable objs) + : base() + { + ArgumentNullException.ThrowIfNull(elementWriter, nameof(elementWriter)); + ArgumentNullException.ThrowIfNull(objs, nameof(objs)); + this.elementWriter = elementWriter; + AddRange(objs); + } + + public event EventHandler Begin = (s, e) => { }; + + public event EventHandler End = (s, e) => { }; + + public object? BeginWrite(ICodeWriter writer) + { + OnBegin(new WriteEventArgs(writer)); + return null; + } + + protected virtual void OnBegin(WriteEventArgs args) + { + Begin(this, args); + } + + public void Write(ICodeWriter writer, object? o) + { + for (int i = 0; i < Count; i++) + { + elementWriter(writer, i, this[i]); + if (i < Count - 1) + writer.Write(", ", true); + } + } + + public void EndWrite(ICodeWriter writer, object? o) + { + OnEnd(new WriteEventArgs(writer)); + } + + protected virtual void OnEnd(WriteEventArgs args) + { + End.FireInReverse(this, args); + } + } +} diff --git a/src/SyntaxDynamo/src/DelegatedSimpleElement.cs b/src/SyntaxDynamo/src/DelegatedSimpleElement.cs new file mode 100644 index 000000000000..354054caa4cd --- /dev/null +++ b/src/SyntaxDynamo/src/DelegatedSimpleElement.cs @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; + +namespace SyntaxDynamo +{ + public abstract class DelegatedSimpleElement : ICodeElement + { + #region ICodeElem implementation + + public event EventHandler Begin = (s, e) => { }; + + public event EventHandler End = (s, e) => { }; + + public object? BeginWrite(ICodeWriter writer) + { + OnBegin(new WriteEventArgs(writer)); + return null; + } + + protected virtual void OnBegin(WriteEventArgs args) + { + Begin(this, args); + } + + public void Write(ICodeWriter writer, object? o) + { + LLWrite(writer, o); + } + + protected abstract void LLWrite(ICodeWriter writer, object? o); + + public void EndWrite(ICodeWriter writer, object? o) + { + OnEnd(new WriteEventArgs(writer)); + } + + protected virtual void OnEnd(WriteEventArgs args) + { + End.FireInReverse(this, args); + } + + #endregion + } + + public class LineBreak : DelegatedSimpleElement + { + protected override void LLWrite(ICodeWriter writer, object? o) + { + writer.EndLine(); + writer.BeginNewLine(true); + } + } +} diff --git a/src/SyntaxDynamo/src/Extensions.cs b/src/SyntaxDynamo/src/Extensions.cs new file mode 100644 index 000000000000..812243f0d4cb --- /dev/null +++ b/src/SyntaxDynamo/src/Extensions.cs @@ -0,0 +1,75 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; + +namespace SyntaxDynamo +{ + public static class Extensions + { + public static void WriteAll(this ICodeElement elem, ICodeWriter writer) + { + object? memento = elem.BeginWrite(writer); + elem.Write(writer, memento); + ICodeElementSet? set = elem as ICodeElementSet; + if (set != null) + { + foreach (ICodeElement sub in set.Elements) + { + sub.WriteAll(writer); + } + } + elem.EndWrite(writer, memento); + } + + public static void FireInReverse(this EventHandler handler, object sender, EventArgs args) where T : EventArgs + { + var dels = handler.GetInvocationList(); + for (int i = dels.Length - 1; i >= 0; i--) + { + dels[i].DynamicInvoke(new object[] { sender, args }); + } + } + + public static IEnumerable Interleave(this IEnumerable contents, T separator, bool includeSeparatorFirst = false) + { + bool first = true; + foreach (T t in contents) + { + if (!first || includeSeparatorFirst) + yield return separator; + first = false; + yield return t; + } + } + + public static IEnumerable BracketInterleave(this IEnumerable contents, T start, T end, T separator, bool includeSeparatorFirst = false) + { + yield return start; + foreach (T t in contents.Interleave(separator, includeSeparatorFirst)) + yield return t; + yield return end; + } + + public static T AttachBefore(this T attacher, ICodeElement attachTo) where T : ICodeElement + { + ArgumentNullException.ThrowIfNull(attachTo, nameof(attachTo)); + attachTo.Begin += (s, eventArgs) => + { + attacher.WriteAll(eventArgs.Writer); + }; + return attacher; + } + + public static T AttachAfter(this T attacher, ICodeElement attachTo) where T : ICodeElement + { + ArgumentNullException.ThrowIfNull(attachTo, nameof(attachTo)); + attachTo.End += (s, eventArgs) => + { + attacher.WriteAll(eventArgs.Writer); + }; + return attacher; + } + } +} diff --git a/src/SyntaxDynamo/src/ICodeElement.cs b/src/SyntaxDynamo/src/ICodeElement.cs new file mode 100644 index 000000000000..f9a06cf22cbb --- /dev/null +++ b/src/SyntaxDynamo/src/ICodeElement.cs @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; + +namespace SyntaxDynamo +{ + public interface ICodeElement + { + // these three methods represent the memento pattern. The object returned is only ever used by + // the ICodeElem. + object? BeginWrite(ICodeWriter writer); + void Write(ICodeWriter writer, object? o); + void EndWrite(ICodeWriter writer, object? o); + + // These events seem redundant, but they are intended for use for non-structural code elements + // such as block comments or #region or #if/#else/#endif + + // Begin should be fired by BeginWrite BEFORE anything is written + event EventHandler Begin; + // Note, when you implement End, it should use GetInvocationList and fire in reverse order. + // End should be fired by EndWrite AFTER anything is written + event EventHandler End; + } +} diff --git a/src/SyntaxDynamo/src/ICodeElementSet.cs b/src/SyntaxDynamo/src/ICodeElementSet.cs new file mode 100644 index 000000000000..3ee50301f0a5 --- /dev/null +++ b/src/SyntaxDynamo/src/ICodeElementSet.cs @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Collections.Generic; + +namespace SyntaxDynamo +{ + public interface ICodeElementSet : ICodeElement + { + IEnumerable Elements { get; } + } +} diff --git a/src/SyntaxDynamo/src/ICodeWriter.cs b/src/SyntaxDynamo/src/ICodeWriter.cs new file mode 100644 index 000000000000..3b51aff75bf6 --- /dev/null +++ b/src/SyntaxDynamo/src/ICodeWriter.cs @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; + +namespace SyntaxDynamo +{ + public interface ICodeWriter + { + void BeginNewLine(bool prependIndents); + void EndLine(); + void Write(char c, bool allowSplit); + void Write(string code, bool allowSplit); + void Indent(); + void Exdent(); + int IndentLevel { get; } + bool IsAtLineStart { get; } + } +} diff --git a/src/SyntaxDynamo/src/LabeledCodeElementCollection.cs b/src/SyntaxDynamo/src/LabeledCodeElementCollection.cs new file mode 100644 index 000000000000..8a3938a3a47c --- /dev/null +++ b/src/SyntaxDynamo/src/LabeledCodeElementCollection.cs @@ -0,0 +1,69 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; + +namespace SyntaxDynamo +{ + public class LabeledCodeElementCollection : ICodeElementSet where T : ICodeElement + { + public LabeledCodeElementCollection(SimpleLineElement label, CodeElementCollection block) + { + ArgumentNullException.ThrowIfNull(label, nameof(label)); + ArgumentNullException.ThrowIfNull(block, nameof(block)); + Label = label; + Block = block; + } + + public SimpleLineElement Label { get; private set; } + public CodeElementCollection Block { get; private set; } + + #region ICodeElem implementation + + public event EventHandler Begin = (s, e) => { }; + + public event EventHandler End = (s, e) => { }; + + public object? BeginWrite(ICodeWriter writer) + { + OnBegin(new WriteEventArgs(writer)); + return null; + } + + protected virtual void OnBegin(WriteEventArgs args) + { + Begin(this, args); + } + + public void Write(ICodeWriter writer, object? o) + { + } + + public void EndWrite(ICodeWriter writer, object? o) + { + OnEnd(new WriteEventArgs(writer)); + } + + protected virtual void OnEnd(WriteEventArgs args) + { + End.FireInReverse(this, args); + } + + #endregion + + #region ICodeElemSet implementation + + public IEnumerable Elements + { + get + { + yield return Label; + yield return Block; + } + } + + #endregion + + } +} diff --git a/src/SyntaxDynamo/src/LineCodeElementCollection.cs b/src/SyntaxDynamo/src/LineCodeElementCollection.cs new file mode 100644 index 000000000000..d7505ab99f0a --- /dev/null +++ b/src/SyntaxDynamo/src/LineCodeElementCollection.cs @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Collections.Generic; + +namespace SyntaxDynamo +{ + public class LineCodeElementCollection : CodeElementCollection where T : ICodeElement + { + bool indent, prependIndents, isSingleLine; + public LineCodeElementCollection(IEnumerable? elems, bool indent, bool prependIndents) + : this(elems, true, indent, prependIndents) + { + } + + public LineCodeElementCollection(bool isSingleLine, bool indent, bool prependIndents) + : this(null, isSingleLine, indent, prependIndents) + { + } + + public LineCodeElementCollection(IEnumerable? elems, bool isSingleLine, bool indent, bool prependIndents) + { + this.isSingleLine = isSingleLine; + this.indent = indent; + this.prependIndents = prependIndents; + if (elems != null) + AddRange(elems); + } + + public override void Write(ICodeWriter writer, object? o) + { + if (isSingleLine) + { + if (indent) + writer.Indent(); + writer.BeginNewLine(prependIndents); + } + } + + protected override void OnEndWrite(WriteEventArgs args) + { + if (isSingleLine) + args.Writer.EndLine(); + base.OnEndWrite(args); + } + + public LineCodeElementCollection And(T elem) + { + Add(elem); + return this; + } + + } +} diff --git a/src/SyntaxDynamo/src/SimpleElement.cs b/src/SyntaxDynamo/src/SimpleElement.cs new file mode 100644 index 000000000000..efdd94f66ee1 --- /dev/null +++ b/src/SyntaxDynamo/src/SimpleElement.cs @@ -0,0 +1,60 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; + +namespace SyntaxDynamo +{ + public class SimpleElement : ICodeElement + { + bool allowSplit; + public SimpleElement(string label, bool allowSplit = false) + { + Label = label; + this.allowSplit = allowSplit; + } + + public string Label { get; private set; } + #region ICodeElem implementation + + public event EventHandler Begin = (s, e) => { }; + + public event EventHandler End = (s, e) => { }; + + public object? BeginWrite(ICodeWriter writer) + { + OnBegin(new WriteEventArgs(writer)); + return null; + } + + protected virtual void OnBegin(WriteEventArgs args) + { + Begin(this, args); + } + + public void Write(ICodeWriter writer, object? o) + { + writer.Write(Label, allowSplit); + } + + public void EndWrite(ICodeWriter writer, object? o) + { + OnEnd(new WriteEventArgs(writer)); + } + + protected virtual void OnEnd(WriteEventArgs args) + { + End.FireInReverse(this, args); + } + + #endregion + + public override string ToString() + { + return Label; + } + + static SimpleElement spacer = new SimpleElement(" ", true); + public static SimpleElement Spacer { get { return spacer; } } + } +} diff --git a/src/SyntaxDynamo/src/SimpleLineElement.cs b/src/SyntaxDynamo/src/SimpleLineElement.cs new file mode 100644 index 000000000000..fc58691fed06 --- /dev/null +++ b/src/SyntaxDynamo/src/SimpleLineElement.cs @@ -0,0 +1,65 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; + +namespace SyntaxDynamo +{ + public class SimpleLineElement : ICodeElement + { + bool indent, prependIndents, allowSplit; + + public SimpleLineElement(string contents, bool indent, bool prependIdents, bool allowSplit) + { + Contents = contents ?? ""; + this.indent = indent; + this.prependIndents = prependIdents; + this.allowSplit = allowSplit; + } + + public string Contents { get; private set; } + + #region ICodeElem implementation + + public event EventHandler Begin = (s, e) => { }; + + public event EventHandler End = (s, e) => { }; + + public object? BeginWrite(ICodeWriter writer) + { + OnBegin(new WriteEventArgs(writer)); + return null; + } + + protected virtual void OnBegin(WriteEventArgs args) + { + Begin(this, args); + } + + public void Write(ICodeWriter writer, object? o) + { + if (indent) + writer.Indent(); + writer.BeginNewLine(prependIndents); + writer.Write(Contents, allowSplit); + writer.EndLine(); + } + + public void EndWrite(ICodeWriter writer, object? o) + { + OnEnd(new WriteEventArgs(writer)); + } + + protected virtual void OnEnd(WriteEventArgs args) + { + End.FireInReverse(this, args); + } + + #endregion + + public override string ToString() + { + return Contents; + } + } +} diff --git a/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSArgument.cs b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSArgument.cs new file mode 100644 index 000000000000..08bc6f9abfe7 --- /dev/null +++ b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSArgument.cs @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace SyntaxDynamo.CSLang +{ + public class CSArgument : DelegatedSimpleElement + { + public CSArgument(ICSExpression expr) + { + ArgumentNullException.ThrowIfNull(expr, nameof(expr)); + Value = expr; + } + + public ICSExpression Value { get; private set; } + + protected override void LLWrite(ICodeWriter writer, object? o) + { + Value.WriteAll(writer); + } + } + + public class CSArgumentList : CommaListElementCollection + { + + public void Add(ICSExpression expr) + { + Add(new CSArgument(expr)); + } + + public static CSArgumentList FromExpressions(params ICSExpression[] exprs) + { + var al = new CSArgumentList(); + foreach (var ex in exprs) + al.Add(new CSArgument(ex)); + return al; + } + } +} diff --git a/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSArray1D.cs b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSArray1D.cs new file mode 100644 index 000000000000..c0005edc3988 --- /dev/null +++ b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSArray1D.cs @@ -0,0 +1,122 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; + +namespace SyntaxDynamo.CSLang +{ + public class CSArray1D : CSBaseExpression + { + public CSArray1D(CSIdentifier name, CommaListElementCollection paramList) + { + ArgumentNullException.ThrowIfNull(name, nameof(name)); + ArgumentNullException.ThrowIfNull(paramList, nameof(paramList)); + Name = name; + Parameters = paramList; + } + + public CSArray1D(string name, params CSBaseExpression[] parameters) + : this(new CSIdentifier(name), new CommaListElementCollection(parameters)) + { + } + + protected override void LLWrite(ICodeWriter writer, object? o) + { + Name.WriteAll(writer); + writer.Write("[", false); + Parameters.WriteAll(writer); + writer.Write("]", false); + } + + public CSIdentifier Name { get; private set; } + public CommaListElementCollection Parameters { get; private set; } + + public static CSArray1D New(CSSimpleType type, bool isStackAlloc, params CSBaseExpression[] parameters) + { + string ID = (isStackAlloc ? "stackalloc " : "new ") + type.Name; + return new CSArray1D(ID, parameters); + } + } + + public class CSArray1DInitialized : CSBaseExpression + { + public CSArray1DInitialized(CSType type, CommaListElementCollection initializers) + { + ArgumentNullException.ThrowIfNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(initializers, nameof(initializers)); + Type = type; + Parameters = initializers; + } + + public CSArray1DInitialized(CSType type, params CSBaseExpression[] parameters) + : this(type, new CommaListElementCollection(parameters)) + { + } + + public CSArray1DInitialized(string typeName, params CSBaseExpression[] parameters) + : this(new CSSimpleType(typeName), new CommaListElementCollection(parameters)) + { + } + + public CSArray1DInitialized(string typeName, IEnumerable parameters) + : this(new CSSimpleType(typeName), new CommaListElementCollection(parameters)) + { + } + + public CSType Type { get; private set; } + public CommaListElementCollection Parameters { get; private set; } + protected override void LLWrite(ICodeWriter writer, object? o) + { + writer.Write("new ", true); + Type.WriteAll(writer); + writer.Write("[", false); + writer.Write("] ", true); + writer.Write("{ ", true); + Parameters.WriteAll(writer); + writer.Write("}", false); + } + + } + + public class CSListInitialized : CSBaseExpression + { + public CSListInitialized(CSType type, CommaListElementCollection initializers) + { + ArgumentNullException.ThrowIfNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(initializers, nameof(initializers)); + Type = type; + Parameters = initializers; + } + + public CSListInitialized(CSType type, params CSBaseExpression[] parameters) + : this(type, new CommaListElementCollection(parameters)) + { + } + + public CSListInitialized(string typeName, params CSBaseExpression[] parameters) + : this(new CSSimpleType(typeName), new CommaListElementCollection(parameters)) + { + } + + public CSListInitialized(string typeName, IEnumerable parameters) + : this(new CSSimpleType(typeName), new CommaListElementCollection(parameters)) + { + } + + public CSType Type { get; private set; } + public CommaListElementCollection Parameters { get; private set; } + protected override void LLWrite(ICodeWriter writer, object? o) + { + writer.Write("new List<", true); + Type.WriteAll(writer); + writer.Write(">", true); + writer.Write("(", false); + writer.Write(") ", true); + writer.Write("{ ", true); + Parameters.WriteAll(writer); + writer.Write("}", false); + } + + } +} diff --git a/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSAssignment.cs b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSAssignment.cs new file mode 100644 index 000000000000..0d78c0643044 --- /dev/null +++ b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSAssignment.cs @@ -0,0 +1,83 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using SyntaxDynamo; + +namespace SyntaxDynamo.CSLang +{ + public class CSAssignment : CSBaseExpression, ICSLineable + { + public CSAssignment(CSBaseExpression lhs, CSAssignmentOperator op, CSBaseExpression rhs) + { + ArgumentNullException.ThrowIfNull(lhs, nameof(lhs)); + ArgumentNullException.ThrowIfNull(rhs, nameof(rhs)); + Target = lhs; + Value = rhs; + Operation = op; + } + + public CSAssignment(string lhs, CSAssignmentOperator op, CSBaseExpression rhs) + : this(new CSIdentifier(lhs), op, rhs) + { + } + + protected override void LLWrite(ICodeWriter writer, object? o) + { + Target.WriteAll(writer); + writer.Write(string.Format(" {0} ", ToAssignmentOpString(Operation)), true); + Value.WriteAll(writer); + } + + public CSBaseExpression Target { get; private set; } + public CSBaseExpression Value { get; private set; } + public CSAssignmentOperator Operation { get; private set; } + + static string ToAssignmentOpString(CSAssignmentOperator op) + { + switch (op) + { + case CSAssignmentOperator.Assign: + return "="; + case CSAssignmentOperator.AddAssign: + return "+="; + case CSAssignmentOperator.SubAssign: + return "-="; + case CSAssignmentOperator.MulAssign: + return "*="; + case CSAssignmentOperator.DivAssign: + return "/="; + case CSAssignmentOperator.ModAssign: + return "%="; + case CSAssignmentOperator.AndAssign: + return "&="; + case CSAssignmentOperator.OrAssign: + return "|="; + case CSAssignmentOperator.XorAssign: + return "^="; + default: + throw new ArgumentOutOfRangeException("op"); + } + } + + public static CSLine Assign(string name, CSAssignmentOperator op, CSBaseExpression value) + { + return new CSLine(new CSAssignment(name, op, value)); + } + + public static CSLine Assign(string name, CSBaseExpression value) + { + return Assign(name, CSAssignmentOperator.Assign, value); + } + + public static CSLine Assign(CSBaseExpression name, CSBaseExpression value) + { + return Assign(name, CSAssignmentOperator.Assign, value); + } + + public static CSLine Assign(CSBaseExpression name, CSAssignmentOperator op, CSBaseExpression value) + { + return new CSLine(new CSAssignment(name, op, value)); + } + } +} diff --git a/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSAttribute.cs b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSAttribute.cs new file mode 100644 index 000000000000..4d00608f8fb9 --- /dev/null +++ b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSAttribute.cs @@ -0,0 +1,100 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Runtime.InteropServices; + +namespace SyntaxDynamo.CSLang +{ + public class CSAttribute : LineCodeElementCollection + { + public CSAttribute(CSIdentifier name, CSArgumentList args, bool isSingleLine = false, bool isReturn = false) + : base(isSingleLine, false, isSingleLine) + { + ArgumentNullException.ThrowIfNull(name, nameof(name)); + Add(new SimpleElement("[")); + if (isReturn) + Add(new SimpleElement("return:")); + Add(name); + if (args != null) + { + Add(new SimpleElement("(")); + Add(args); + Add(new SimpleElement(")")); + } + Add(new SimpleElement("]")); + } + + public CSAttribute(string name, CSArgumentList args) + : this(new CSIdentifier(name), args) + { + } + + public CSAttribute(string name, params ICSExpression[] exprs) + : this(new CSIdentifier(name), CSArgumentList.FromExpressions(exprs)) + { + } + + // DllImport("msvcrt.dll", EntryPoint="puts") + public static CSAttribute DllImport(string dllName, string? entryPoint = null) + { + CSArgumentList args = new CSArgumentList(); + args.Add(CSConstant.Val(dllName)); + if (entryPoint != null) + args.Add(new CSAssignment("EntryPoint", CSAssignmentOperator.Assign, CSConstant.Val(entryPoint))); + return new CSAttribute(new CSIdentifier("DllImport"), args, true); + } + + public static CSAttribute DllImport(CSBaseExpression dllName, string? entryPoint = null) + { + CSArgumentList args = new CSArgumentList(); + args.Add(dllName); + if (entryPoint != null) + args.Add(new CSAssignment("EntryPoint", CSAssignmentOperator.Assign, CSConstant.Val(entryPoint))); + return new CSAttribute(new CSIdentifier("DllImport"), args, true); + } + + public static CSAttribute UnmanagedCallConv(string callconv) + { + CSArgumentList args = new CSArgumentList(); + + args.Add(new CSAssignment("CallConvs", CSAssignmentOperator.Assign, new CSIdentifier(string.Format("new Type[] { typeof({0}) }", callconv)))); + return new CSAttribute(new CSIdentifier("UnmanagedCallConv"), args, true); + } + + static CSAttribute returnMarshalAsI1 = new CSAttribute("return: MarshalAs", new CSIdentifier("UnmanagedType.I1")); + + public static CSAttribute ReturnMarshalAsI1 => returnMarshalAsI1; + + public static CSAttribute FieldOffset(int offset) + { + CSArgumentList args = new CSArgumentList(); + args.Add(CSConstant.Val(offset)); + return new CSAttribute(new CSIdentifier("FieldOffset"), args, true); + } + + public static CSAttribute LayoutKind(LayoutKind layout) + { + CSArgumentList args = new CSArgumentList(); + args.Add(new CSIdentifier(String.Format("LayoutKind.{0}", layout))); + return new CSAttribute(new CSIdentifier("StructLayout"), args, true); + } + + public static CSAttribute FromAttr(Type attribute, CSArgumentList args, bool isSingleLine = false, bool isReturn = false) + { + ArgumentNullException.ThrowIfNull(attribute, nameof(attribute)); + if (!attribute.IsSubclassOf(typeof(Attribute))) + throw new ArgumentException(String.Format("Type {0} is not an Attribute type.", attribute.Name), nameof(attribute)); + var name = attribute.Name.EndsWith("Attribute") ? + attribute.Name.Substring(0, attribute.Name.Length - "Attribute".Length) : attribute.Name; + return new CSAttribute(new CSIdentifier(name), args, isSingleLine, isReturn); + } + + public static CSAttribute MarshalAsFunctionPointer() + { + CSArgumentList list = new CSArgumentList(); + list.Add(new CSArgument(new CSIdentifier("UnmanagedType.FunctionPtr"))); + return FromAttr(typeof(MarshalAsAttribute), list, false); + } + } +} diff --git a/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSBaseExpression.cs b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSBaseExpression.cs new file mode 100644 index 000000000000..eb1115cac859 --- /dev/null +++ b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSBaseExpression.cs @@ -0,0 +1,98 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using SyntaxDynamo; + +namespace SyntaxDynamo.CSLang +{ + public abstract class CSBaseExpression : DelegatedSimpleElement, ICSExpression + { + public CSBaseExpression Dot(CSBaseExpression rhs) + { + return new CSBinaryExpression(CSBinaryOperator.Dot, this, rhs); + } + public static CSBaseExpression operator +(CSBaseExpression lhs, CSBaseExpression rhs) + { + return new CSBinaryExpression(CSBinaryOperator.Add, lhs, rhs); + } + public static CSBaseExpression operator -(CSBaseExpression lhs, CSBaseExpression rhs) + { + return new CSBinaryExpression(CSBinaryOperator.Sub, lhs, rhs); + } + public static CSBaseExpression operator *(CSBaseExpression lhs, CSBaseExpression rhs) + { + return new CSBinaryExpression(CSBinaryOperator.Mul, lhs, rhs); + } + public static CSBaseExpression operator /(CSBaseExpression lhs, CSBaseExpression rhs) + { + return new CSBinaryExpression(CSBinaryOperator.Div, lhs, rhs); + } + public static CSBaseExpression operator %(CSBaseExpression lhs, CSBaseExpression rhs) + { + return new CSBinaryExpression(CSBinaryOperator.Mod, lhs, rhs); + } + public static CSBaseExpression operator <(CSBaseExpression lhs, CSBaseExpression rhs) + { + return new CSBinaryExpression(CSBinaryOperator.Less, lhs, rhs); + } + public static CSBaseExpression operator >(CSBaseExpression lhs, CSBaseExpression rhs) + { + return new CSBinaryExpression(CSBinaryOperator.Greater, lhs, rhs); + } + public static CSBaseExpression operator ==(CSBaseExpression lhs, CSBaseExpression rhs) + { + return new CSBinaryExpression(CSBinaryOperator.Equal, lhs, rhs); + } + public static CSBaseExpression operator !=(CSBaseExpression lhs, CSBaseExpression rhs) + { + return new CSBinaryExpression(CSBinaryOperator.NotEqual, lhs, rhs); + } + public static CSBaseExpression operator <=(CSBaseExpression lhs, CSBaseExpression rhs) + { + return new CSBinaryExpression(CSBinaryOperator.LessEqual, lhs, rhs); + } + public static CSBaseExpression operator >=(CSBaseExpression lhs, CSBaseExpression rhs) + { + return new CSBinaryExpression(CSBinaryOperator.GreaterEqual, lhs, rhs); + } + public static CSBaseExpression operator &(CSBaseExpression lhs, CSBaseExpression rhs) + { + return new CSBinaryExpression(CSBinaryOperator.BitAnd, lhs, rhs); + } + public static CSBaseExpression operator |(CSBaseExpression lhs, CSBaseExpression rhs) + { + return new CSBinaryExpression(CSBinaryOperator.BitOr, lhs, rhs); + } + public static CSBaseExpression operator ^(CSBaseExpression lhs, CSBaseExpression rhs) + { + return new CSBinaryExpression(CSBinaryOperator.BitXor, lhs, rhs); + } + public static CSBaseExpression operator <<(CSBaseExpression lhs, int bits) + { + return new CSBinaryExpression(CSBinaryOperator.LeftShift, lhs, CSConstant.Val(bits)); + } + public static CSBaseExpression operator >>(CSBaseExpression lhs, int bits) + { + return new CSBinaryExpression(CSBinaryOperator.RightShift, lhs, CSConstant.Val(bits)); + } + public override bool Equals(object? obj) + { + if (obj == null || GetType() != obj.GetType()) + { + return false; + } + + // Add your custom equality logic here + + return base.Equals(obj); + } + + public override int GetHashCode() + { + // Add your custom hash code logic here + + return base.GetHashCode(); + } + } +} diff --git a/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSBinaryExpression.cs b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSBinaryExpression.cs new file mode 100644 index 000000000000..e372626d558d --- /dev/null +++ b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSBinaryExpression.cs @@ -0,0 +1,83 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; + +namespace SyntaxDynamo.CSLang +{ + public class CSBinaryExpression : CSBaseExpression + { + public CSBinaryExpression(CSBinaryOperator op, ICSExpression lhs, ICSExpression rhs) + { + ArgumentNullException.ThrowIfNull(lhs, nameof(lhs)); + ArgumentNullException.ThrowIfNull(rhs, nameof(rhs)); + Operation = op; + Left = lhs; + Right = rhs; + } + + protected override void LLWrite(ICodeWriter writer, object? o) + { + Left.WriteAll(writer); + writer.Write(string.Format(Operation == CSBinaryOperator.Dot ? "{0}" : " {0} ", OpToString(Operation)), true); + Right.WriteAll(writer); + } + + public CSBinaryOperator Operation { get; private set; } + public ICSExpression Left { get; private set; } + public ICSExpression Right { get; private set; } + + static string OpToString(CSBinaryOperator op) + { + switch (op) + { + case CSBinaryOperator.Add: + return "+"; + case CSBinaryOperator.Sub: + return "-"; + case CSBinaryOperator.Mul: + return "*"; + case CSBinaryOperator.Div: + return "/"; + case CSBinaryOperator.Mod: + return "%"; + case CSBinaryOperator.And: + return "&&"; + case CSBinaryOperator.Or: + return "||"; + case CSBinaryOperator.Less: + return "<"; + case CSBinaryOperator.Greater: + return ">"; + case CSBinaryOperator.Equal: + return "=="; + case CSBinaryOperator.NotEqual: + return "!="; + case CSBinaryOperator.LessEqual: + return "<="; + case CSBinaryOperator.GreaterEqual: + return ">="; + case CSBinaryOperator.BitAnd: + return "&"; + case CSBinaryOperator.BitOr: + return "|"; + case CSBinaryOperator.BitXor: + return "^"; + case CSBinaryOperator.LeftShift: + return ">>"; + case CSBinaryOperator.RightShift: + return "<<"; + case CSBinaryOperator.Dot: + return "."; + case CSBinaryOperator.Is: + return "is"; + case CSBinaryOperator.As: + return "as"; + case CSBinaryOperator.NullCoalesce: + return "??"; + default: + throw new ArgumentOutOfRangeException("op"); + } + } + } +} diff --git a/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSBinding.cs b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSBinding.cs new file mode 100644 index 000000000000..e49d5881baca --- /dev/null +++ b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSBinding.cs @@ -0,0 +1,82 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Linq; +using System.Collections.Generic; + +namespace SyntaxDynamo.CSLang +{ + public class CSBinding : ICodeElementSet + { + public CSBinding(CSIdentifier name, ICSExpression? val = null, bool onOwnLine = false) + { + Name = name; + Value = val; + OnOwnLine = onOwnLine; + } + + public CSBinding(string name, ICSExpression? val = null, bool onOwnLine = false) + : this(new CSIdentifier(name), val, onOwnLine) + { + } + + public CSIdentifier Name { get; private set; } + public ICSExpression? Value { get; private set; } + public bool OnOwnLine { get; private set; } + + public override string ToString() + { + return string.Format("{0}{1}{2}", + Name.Name, Value == null ? "" : " = ", Value == null ? "" : Value.ToString()); + } + + #region ICodeElem implementation + public event EventHandler Begin = (s, e) => { }; + + public event EventHandler End = (s, e) => { }; + + public object? BeginWrite(ICodeWriter writer) + { + OnBegin(new WriteEventArgs(writer)); + return null; + } + + protected virtual void OnBegin(WriteEventArgs args) + { + Begin(this, args); + if (OnOwnLine) + args.Writer.BeginNewLine(true); + } + + public void Write(ICodeWriter writer, object? o) + { + } + + public void EndWrite(ICodeWriter writer, object? o) + { + OnEnd(new WriteEventArgs(writer)); + } + + protected virtual void OnEnd(WriteEventArgs args) + { + End.FireInReverse(this, args); + } + #endregion + + #region ICodeElemSet implementation + public IEnumerable Elements + { + get + { + yield return Name; + if (Value != null) + { + yield return new SimpleElement(" = ", true); + yield return Value; + } + } + } + #endregion + } +} diff --git a/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSClass.cs b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSClass.cs new file mode 100644 index 000000000000..fe1685cfff5f --- /dev/null +++ b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSClass.cs @@ -0,0 +1,218 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Linq; +using SyntaxDynamo; + +namespace SyntaxDynamo.CSLang +{ + public class CSClass : ICodeElementSet, ICSTopLevelDeclaration + { + public CSClass(CSVisibility vis, CSIdentifier name, IEnumerable? methods = null, + bool isStatic = false, bool isSealed = false) + { + ArgumentNullException.ThrowIfNull(name, nameof(name)); + Visibility = vis; + IsStatic = isStatic; + IsSealed = isSealed; + Name = name; + Inheritance = new CSInheritance(); + Delegates = new List(); + Fields = new List(); + Constructors = new List(); + Methods = new List(); + Properties = new List(); + InnerClasses = new CSClasses(); + InnerEnums = new List(); + StaticConstructor = new CSCodeBlock(); + GenericParams = new CSGenericTypeDeclarationCollection(); + GenericConstraints = new CSGenericConstraintCollection(); + + if (methods != null) + Methods.AddRange(methods); + } + + public CSClass(CSVisibility vis, string name, + IEnumerable? members = null, bool isStatic = false, bool isSealed = false) + : this(vis, new CSIdentifier(name), members, isStatic, isSealed) + { + } + + protected virtual string EntityLabel { get { return "class"; } } + + public CSVisibility Visibility { get; private set; } + public bool IsStatic { get; private set; } + public bool IsSealed { get; private set; } + public CSIdentifier Name { get; private set; } + public CSInheritance Inheritance { get; private set; } + public List Delegates { get; private set; } + public List Fields { get; private set; } + public List Constructors { get; private set; } + public List Methods { get; private set; } + public List Properties { get; private set; } + public CSClasses InnerClasses { get; private set; } + public List InnerEnums { get; private set; } + public CSCodeBlock StaticConstructor { get; private set; } + public CSGenericTypeDeclarationCollection GenericParams { get; private set; } + public CSGenericConstraintCollection GenericConstraints { get; private set; } + + public CSType ToCSType(IEnumerable genericReplacements) + { + List replacements = genericReplacements.ToList(); + if (replacements.Count < GenericParams.Count) + { + replacements.AddRange(GenericParams.Skip(replacements.Count).Select(gen => new CSSimpleType(gen.Name.Name))); + } + CSSimpleType t = new CSSimpleType(Name.Name, false, replacements.ToArray()); + return t; + } + + public CSType ToCSType() + { + return ToCSType(GenericParams.Select(gen => new CSSimpleType(gen.Name.Name))); + } + + #region ICodeElem implementation + + public event EventHandler Begin = (s, e) => { }; + + public event EventHandler End = (s, e) => { }; + + public virtual object? BeginWrite(ICodeWriter writer) + { + OnBeginWrite(new WriteEventArgs(writer)); + return null; + } + + protected virtual void OnBeginWrite(WriteEventArgs args) + { + Begin(this, args); + } + + public virtual void Write(ICodeWriter writer, object? o) + { + } + + public virtual void EndWrite(ICodeWriter writer, object? o) + { + OnEndWrite(new WriteEventArgs(writer)); + } + + protected virtual void OnEndWrite(WriteEventArgs args) + { + End.FireInReverse(this, args); + } + + #endregion + + #region ICodeElemSet implementation + + public IEnumerable Elements + { + get + { + var decl = new LineCodeElementCollection(true, false, true); + if (Visibility != CSVisibility.None) + decl.Add(new SimpleElement(CSMethod.VisibilityToString(Visibility) + " ")); + if (IsStatic) + decl.Add(new SimpleElement("static ", true)); + if (IsSealed) + decl.Add(new SimpleElement("sealed ", true)); + decl.Add(new CSIdentifier(EntityLabel + " ")); + decl.Add(Name); + decl.Add(GenericParams); + if (Inheritance.Count > 0) + { + decl.Add(new SimpleElement(" : ", true)); + decl.Add(Inheritance); + } + if (GenericConstraints.Count > 0) + { + decl.Add(SimpleElement.Spacer); + decl.Add(GenericConstraints); + } + yield return decl; + + var contents = new DecoratedCodeElementCollection("{", "}", + true, true, true); + + contents.AddRange(Delegates); + contents.AddRange(Fields); + + if (StaticConstructor.Count > 0) + { + var m = new CSMethod(CSVisibility.None, CSMethodKind.Static, + null, Name, new CSParameterList(), StaticConstructor); + contents.Add(m); + } + contents.AddRange(Constructors); + contents.AddRange(Methods); + contents.AddRange(Properties); + contents.Add(InnerClasses); + contents.AddRange(InnerEnums); + + yield return contents; + + } + } + + #endregion + } + + public class CSClasses : CodeElementCollection + { + public CSClasses(IEnumerable? classes = null) + : base() + { + if (classes != null) + AddRange(classes); + } + + public CSClasses And(CSClass use) + { + Add(use); + return this; + } + } + + public class CSStruct : CSClass + { + public CSStruct(CSVisibility vis, CSIdentifier name, IEnumerable? methods = null, + bool isStatic = false, bool isSealed = false) + : base(vis, name, methods, isStatic, isSealed) + { + } + + public CSStruct(CSVisibility vis, string name, + IEnumerable? members = null, bool isStatic = false, bool isSealed = false) + : this(vis, new CSIdentifier(name), members, isStatic, isSealed) + { + } + + protected override string EntityLabel + { + get + { + return "struct"; + } + } + } + + public class CSStructs : CodeElementCollection + { + public CSStructs(IEnumerable? structs = null) + : base() + { + if (structs != null) + AddRange(structs); + } + + public CSStructs And(CSStruct st) + { + Add(st); + return this; + } + } +} diff --git a/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSCodeBlock.cs b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSCodeBlock.cs new file mode 100644 index 000000000000..acff4591d330 --- /dev/null +++ b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSCodeBlock.cs @@ -0,0 +1,56 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; + +namespace SyntaxDynamo.CSLang +{ + public class CSCodeBlock : DecoratedCodeElementCollection, ICSStatement + { + public CSCodeBlock() + : this(null) + { + + } + + public CSCodeBlock(IEnumerable? statements) + : this("{", "}", statements) + { + } + + public static CSCodeBlock Create(params ICodeElement[] statements) + { + return new CSCodeBlock(statements); + } + + public CSCodeBlock(string start, string end, IEnumerable? statements) + : base(start, end, true, true, true) + { + if (statements != null) + { + foreach (ICodeElement elem in statements) + { + And(elem); + } + } + } + + public CSCodeBlock And(ICodeElement elem) + { + if (!(elem is ICSStatement)) + throw new ArgumentException($"contents must each be an IStatement, got {elem.GetType()}"); + Add(elem); + return this; + } + } + + public class CSUnsafeCodeBlock : CSCodeBlock + { + public CSUnsafeCodeBlock(IEnumerable statements) + : base("unsafe {", "}", statements) + { + } + } + +} diff --git a/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSComment.cs b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSComment.cs new file mode 100644 index 000000000000..cd152bb04a91 --- /dev/null +++ b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSComment.cs @@ -0,0 +1,61 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; + +namespace SyntaxDynamo.CSLang +{ + public class CSComment : SimpleLineElement + { + public CSComment(string text) + : base(Commentize(text), false, true, false) + { + } + + static string Commentize(string text) + { + text = text ?? ""; + if (text.Contains("\n")) + throw new ArgumentException("Comment text must not contain new line characters.", "text"); + return "// " + text; + } + } + + public class CSCommentBlock : CodeElementCollection + { + public CSCommentBlock(params CSComment[] comments) + { + AddRange(comments); + } + + public CSCommentBlock(params string[] text) + { + ArgumentNullException.ThrowIfNull(text, nameof(text)); + AddRange(Sanitize(text)); + } + + static IEnumerable Sanitize(string[] text) + { + foreach (string s in text) + { + string[] lines = s.Split('\n'); + foreach (string line in lines) + yield return new CSComment(line); + } + } + + public CSCommentBlock And(string text) + { + AddRange(Sanitize(new string[] { text })); + return this; + } + + public CSCommentBlock And(CSComment comment) + { + ArgumentNullException.ThrowIfNull(comment, nameof(comment)); + Add(comment); + return this; + } + } +} diff --git a/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSConditionalCompilation.cs b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSConditionalCompilation.cs new file mode 100644 index 000000000000..dc939eba3e58 --- /dev/null +++ b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSConditionalCompilation.cs @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using SyntaxDynamo; + +namespace SyntaxDynamo.CSLang +{ + public class CSConditionalCompilation : LineCodeElementCollection + { + CSConditionalCompilation(CSIdentifier tag, CSIdentifier? condition) + : base(true, false, false) + { + Add(tag); + if ((object?)condition != null) + { + Add(SimpleElement.Spacer); + Add(condition); + } + } + + static CSConditionalCompilation _else = new CSConditionalCompilation(new CSIdentifier("#else"), null); + public static CSConditionalCompilation Else { get { return _else; } } + static CSConditionalCompilation _endif = new CSConditionalCompilation(new CSIdentifier("#endif"), null); + public static CSConditionalCompilation Endif { get { return _endif; } } + + public static CSConditionalCompilation If(CSIdentifier condition) + { + ArgumentNullException.ThrowIfNull(condition, nameof(condition)); + return new CSConditionalCompilation(new CSIdentifier("#if"), condition); + } + + public static void ProtectWithIfEndif(CSIdentifier condition, ICodeElement elem) + { + var @if = If(condition); + @if.AttachBefore(elem); + Endif.AttachAfter(elem); + } + } +} diff --git a/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSConstant.cs b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSConstant.cs new file mode 100644 index 000000000000..6e28b1f1bcc9 --- /dev/null +++ b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSConstant.cs @@ -0,0 +1,78 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using SyntaxDynamo; +using System.IO; +using System.CodeDom.Compiler; +using System.CodeDom; + +namespace SyntaxDynamo.CSLang +{ + public class CSConstant : CSBaseExpression + { + public CSConstant(string val) + { + ArgumentNullException.ThrowIfNull(val, nameof(val)); + Value = val; + } + + public static explicit operator CSConstant(string val) + { + return new CSConstant(val); + } + + public string Value { get; private set; } + + protected override void LLWrite(ICodeWriter writer, object? o) + { + writer.Write(Value, false); + } + + public static CSConstant Val(byte b) { return new CSConstant(b.ToString()); } + public static CSConstant Val(sbyte sb) { return new CSConstant(sb.ToString()); } + public static CSConstant Val(ushort us) { return new CSConstant(us.ToString()); } + public static CSConstant Val(short s) { return new CSConstant(s.ToString()); } + public static CSConstant Val(uint ui) { return new CSConstant(ui.ToString()); } + public static CSConstant Val(int i) { return new CSConstant(i.ToString()); } + public static CSConstant Val(ulong ul) { return new CSConstant(ul.ToString()); } + public static CSConstant Val(long l) { return new CSConstant(l.ToString()); } + public static CSConstant Val(float f) + { + return new CSConstant(f.ToString() + "f"); + } + public static CSConstant Val(double d) { return new CSConstant(d.ToString()); } + public static CSConstant Val(bool b) { return new CSConstant(b ? "true" : "false"); } + public static CSConstant Val(char c) { return new CSConstant(ToCharLiteral(c)); } + public static CSConstant Val(string s) { return new CSConstant(ToStringLiteral(s)); } + static CSConstant cNull = new CSConstant("null"); + public static CSConstant Null { get { return cNull; } } + static CSConstant cIntPtrZero = new CSConstant("IntPtr.Zero"); + public static CSConstant IntPtrZero { get { return cIntPtrZero; } } + public static CSBaseExpression ValNFloat(double d) { return new CSCastExpression(CSSimpleType.NFloat, Val(d)); } + + static string ToCharLiteral(char c) + { + using (var writer = new StringWriter()) + { + using (var provider = CodeDomProvider.CreateProvider("CSharp")) + { + provider.GenerateCodeFromExpression(new CodePrimitiveExpression(c), writer, new CodeGeneratorOptions()); + return writer.ToString(); + } + } + } + + static string ToStringLiteral(string s) + { + using (var writer = new StringWriter()) + { + using (var provider = CodeDomProvider.CreateProvider("CSharp")) + { + provider.GenerateCodeFromExpression(new CodePrimitiveExpression(s), writer, new CodeGeneratorOptions()); + return writer.ToString(); + } + } + } + } +} diff --git a/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSDelegateTypeDecl.cs b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSDelegateTypeDecl.cs new file mode 100644 index 000000000000..94b9d6326324 --- /dev/null +++ b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSDelegateTypeDecl.cs @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; + +namespace SyntaxDynamo.CSLang +{ + public class CSDelegateTypeDecl : DelegatedSimpleElement, ICSStatement + { + public CSDelegateTypeDecl(CSVisibility vis, CSType type, CSIdentifier name, CSParameterList parms, bool isUnsafe = false) + { + ArgumentNullException.ThrowIfNull(name, nameof(name)); + Visibility = vis; + Type = type != null ? type : CSSimpleType.Void; + Name = name; + Parameters = parms; + IsUnsafe = isUnsafe; + } + + public CSVisibility Visibility { get; private set; } + public CSType Type { get; private set; } + public CSIdentifier Name { get; private set; } + public CSParameterList Parameters { get; private set; } + public bool IsUnsafe { get; private set; } + + protected override void LLWrite(ICodeWriter writer, object? o) + { + writer.BeginNewLine(true); + writer.Write(CSMethod.VisibilityToString(Visibility), false); + if (IsUnsafe) + writer.Write(" unsafe", true); + writer.Write(" delegate ", true); + Type.WriteAll(writer); + writer.Write(' ', true); + Name.WriteAll(writer); + writer.Write('(', true); + Parameters.WriteAll(writer); + writer.Write(')', true); + writer.Write(';', false); + writer.EndLine(); + } + } +} diff --git a/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSEnum.cs b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSEnum.cs new file mode 100644 index 000000000000..14bd0db4cd89 --- /dev/null +++ b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSEnum.cs @@ -0,0 +1,107 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace SyntaxDynamo.CSLang +{ + public class CSEnum : ICodeElementSet, ICSTopLevelDeclaration + { + public CSEnum(CSVisibility vis, CSIdentifier name, CSType optionalType) + { + ArgumentNullException.ThrowIfNull(name, nameof(name)); + Values = new List(); + Name = name; + OptionalType = optionalType; + Visibility = vis; + } + + public CSEnum(CSVisibility vis, string name, CSType optionalType) + : this(vis, new CSIdentifier(name), optionalType) + { + } + + public List Values { get; private set; } + public CSVisibility Visibility { get; private set; } + public CSType OptionalType { get; private set; } + public CSIdentifier Name { get; private set; } + + public CSType ToCSType() + { + return new CSSimpleType(Name.Name); + } + + #region ICodeElem implementation + + public event EventHandler Begin = (s, e) => { }; + + public event EventHandler End = (s, e) => { }; + + public virtual object? BeginWrite(ICodeWriter writer) + { + OnBeginWrite(new WriteEventArgs(writer)); + return null; + } + + protected virtual void OnBeginWrite(WriteEventArgs args) + { + Begin(this, args); + } + + public virtual void Write(ICodeWriter writer, object? o) + { + } + + public virtual void EndWrite(ICodeWriter writer, object? o) + { + OnEndWrite(new WriteEventArgs(writer)); + } + + protected virtual void OnEndWrite(WriteEventArgs args) + { + End.FireInReverse(this, args); + } + + #endregion + + #region ICodeElemSet implementation + + public IEnumerable Elements + { + get + { + var decl = new LineCodeElementCollection(true, false, true); + if (Visibility != CSVisibility.None) + decl.Add(new SimpleElement(CSMethod.VisibilityToString(Visibility) + " ")); + decl.Add(new CSIdentifier("enum" + " ")); + decl.Add(Name); + + if (OptionalType != null) + { + decl.Add(new SimpleElement(" : ", true)); + decl.Add(OptionalType); + } + yield return decl; + + var contents = new DecoratedCodeElementCollection("{", "}", + true, true, true); + + var bindings = new CommaListElementCollection(); + bindings.AddRange(Values); + if (bindings.Count > 0 && !bindings[0].OnOwnLine) + { + bindings[0] = new CSBinding(bindings[0].Name, bindings[0].Value, true); + } + contents.Add(bindings); + + yield return contents; + + } + } + + #endregion + + } +} diff --git a/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSFieldDeclaration.cs b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSFieldDeclaration.cs new file mode 100644 index 000000000000..880b50353b37 --- /dev/null +++ b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSFieldDeclaration.cs @@ -0,0 +1,112 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Linq; +using SyntaxDynamo; + +namespace SyntaxDynamo.CSLang +{ + public class CSVariableDeclaration : LineCodeElementCollection, ICSExpression, ICSLineable + { + public CSVariableDeclaration(CSType type, IEnumerable bindings) + : base(null, false, true) + { + ArgumentNullException.ThrowIfNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(bindings, nameof(bindings)); + Type = type; + And(Type).And(SimpleElement.Spacer); + Bindings = new CommaListElementCollection(bindings); + Add(Bindings); + } + + public CSVariableDeclaration(CSType type, string name, ICSExpression? value = null) + : this(type, new CSIdentifier(name), value) + { + } + + public CSVariableDeclaration(CSType type, CSIdentifier name, ICSExpression? value = null) + : this(type, new CSBinding[] { new CSBinding(name, value) }) + { + } + + public CSType Type { get; private set; } + public CommaListElementCollection Bindings { get; private set; } + + public static CSLine VarLine(CSType type, CSIdentifier name, ICSExpression? value = null) + { + return new CSLine(new CSVariableDeclaration(type, name, value)); + } + + public static CSLine VarLine(CSType type, string name, ICSExpression? value = null) + { + return new CSLine(new CSVariableDeclaration(type, name, value)); + } + + public static CSLine VarLine(string name, ICSExpression value) + { + return new CSLine(new CSVariableDeclaration(CSSimpleType.Var, name, value)); + } + + public static CSLine VarLine(CSIdentifier name, ICSExpression value) + { + return new CSLine(new CSVariableDeclaration(CSSimpleType.Var, name, value)); + } + } + + public class CSFieldDeclaration : CSVariableDeclaration + { + public CSFieldDeclaration(CSType type, IEnumerable bindings, CSVisibility vis = CSVisibility.None, bool isStatic = false, bool isReadonly = false, bool isUnsafe = false) + : base(type, bindings) + { + Visibilty = vis; + IsStatic = isStatic; + IsUnsafe = isUnsafe; + if (isReadonly) + { + this.Insert(0, new SimpleElement("readonly")); + this.Insert(1, SimpleElement.Spacer); + } + if (IsUnsafe) + { + this.Insert(0, new SimpleElement("unsafe")); + this.Insert(1, SimpleElement.Spacer); + } + if (isStatic) + { + this.Insert(0, new SimpleElement("static")); + this.Insert(1, SimpleElement.Spacer); + } + if (vis != CSVisibility.None) + { + this.Insert(0, new SimpleElement(CSMethod.VisibilityToString(vis))); + this.Insert(1, SimpleElement.Spacer); + } + } + + public CSFieldDeclaration(CSType type, string name, ICSExpression? value = null, CSVisibility vis = CSVisibility.None, bool isStatic = false, bool isReadonly = false, bool isUnsafe = false) + : this(type, new CSIdentifier(name), value, vis, isStatic, isReadonly, isUnsafe) + { + } + + public CSFieldDeclaration(CSType type, CSIdentifier name, ICSExpression? value = null, CSVisibility vis = CSVisibility.None, bool isStatic = false, bool isReadOnly = false, bool isUnsafe = false) + : this(type, new CSBinding[] { new CSBinding(name, value) }, vis, isStatic, isReadOnly, isUnsafe) + { + } + + public CSVisibility Visibilty { get; private set; } + public bool IsStatic { get; private set; } + public bool IsUnsafe { get; private set; } + + public static CSLine FieldLine(CSType type, CSIdentifier name, ICSExpression? value = null, CSVisibility vis = CSVisibility.None, bool isStatic = false) + { + return new CSLine(new CSFieldDeclaration(type, name, value, vis, isStatic)); + } + + public static CSLine FieldLine(CSType type, string name, ICSExpression? value = null, CSVisibility vis = CSVisibility.None, bool isStatic = false) + { + return new CSLine(new CSFieldDeclaration(type, name, value, vis, isStatic)); + } + } +} diff --git a/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSFile.cs b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSFile.cs new file mode 100644 index 000000000000..2587b2b2e9bb --- /dev/null +++ b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSFile.cs @@ -0,0 +1,74 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Linq; +using SyntaxDynamo; + +namespace SyntaxDynamo.CSLang +{ + public class CSFile : ICodeElementSet + { + public CSFile(CSUsingPackages use, IEnumerable ns) + { + Using = use ?? new CSUsingPackages(); + ns = ns ?? new CSNamespace[0]; + Namespaces = new CSNamespaceBlock(ns); + } + + public static CSFile Create(CSUsingPackages use, params CSNamespace[] ns) + { + return new CSFile(use, ns); + } + + public CSUsingPackages Using { get; private set; } + public CSNamespaceBlock Namespaces { get; private set; } + + #region ICodeElem implementation + + public event EventHandler Begin = (s, e) => { }; + + public event EventHandler End = (s, e) => { }; + + public object? BeginWrite(ICodeWriter writer) + { + OnBegin(new WriteEventArgs(writer)); + return null; + } + + protected virtual void OnBegin(WriteEventArgs args) + { + Begin(this, args); + } + + public void Write(ICodeWriter writer, object? o) + { + } + + public void EndWrite(ICodeWriter writer, object? o) + { + OnEnd(new WriteEventArgs(writer)); + } + + protected virtual void OnEnd(WriteEventArgs args) + { + End(this, args); + } + + #endregion + + #region ICodeElemSet implementation + + public System.Collections.Generic.IEnumerable Elements + { + get + { + yield return Using; + yield return Namespaces; + } + } + + #endregion + } +} diff --git a/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSFileBasic.cs b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSFileBasic.cs new file mode 100644 index 000000000000..57fc2f050595 --- /dev/null +++ b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSFileBasic.cs @@ -0,0 +1,69 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; + +namespace SyntaxDynamo.CSLang +{ + public class CSFileBasic : ICodeElementSet + { + string nameSpace; + + public CSFileBasic(string nameSpace) + { + ArgumentNullException.ThrowIfNull(nameSpace, nameof(nameSpace)); + this.nameSpace = nameSpace; + Using = new CSUsingPackages(); + Classes = new CSClasses(); + } + + #region ICodeElem implementation + + public event EventHandler Begin = (s, e) => { }; + + public event EventHandler End = (s, e) => { }; + + public CSClasses Classes { get; private set; } + public CSUsingPackages Using { get; private set; } + + public object? BeginWrite(ICodeWriter writer) + { + return null; + } + + public void Write(ICodeWriter writer, object? o) + { + } + + public void EndWrite(ICodeWriter writer, object? o) + { + } + + protected virtual void OnBegin(WriteEventArgs e) + { + Begin(this, e); + } + + protected virtual void OnEnd(WriteEventArgs e) + { + End(this, e); + } + + #endregion + + #region ICodeElemSet implementation + + public System.Collections.Generic.IEnumerable Elements + { + get + { + yield return Using; + CSNamespace ns = new CSNamespace(nameSpace); + ns.Block.AddRange(Classes); + yield return ns; + } + } + + #endregion + } +} diff --git a/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSFixed.cs b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSFixed.cs new file mode 100644 index 000000000000..023be1f6292b --- /dev/null +++ b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSFixed.cs @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; + +namespace SyntaxDynamo.CSLang +{ + public class CSFixedCodeBlock : CSCodeBlock, ICSStatement + { + public CSFixedCodeBlock(CSType type, CSIdentifier ident, CSBaseExpression expr, IEnumerable body) + : base(body) + { + ArgumentNullException.ThrowIfNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(ident, nameof(ident)); + ArgumentNullException.ThrowIfNull(expr, nameof(expr)); + Type = type; + Identifier = ident; + Expr = expr; + } + + public override void Write(ICodeWriter writer, object? o) + { + writer.BeginNewLine(true); + writer.Write("fixed (", true); + Type.Write(writer, o); + writer.Write(' ', false); + Identifier.Write(writer, o); + writer.Write(" = ", true); + Expr.Write(writer, o); + writer.Write(") ", true); + base.Write(writer, o); + } + + public CSType Type { get; private set; } + public CSIdentifier Identifier { get; private set; } + public CSBaseExpression Expr { get; private set; } + } +} diff --git a/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSForEach.cs b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSForEach.cs new file mode 100644 index 000000000000..7719ee0e83b9 --- /dev/null +++ b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSForEach.cs @@ -0,0 +1,67 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +namespace SyntaxDynamo.CSLang +{ + public class CSForEach : CodeElementCollection, ICSStatement + { + + class ForElement : DelegatedSimpleElement, ICSStatement + { + public ForElement(CSType type, CSIdentifier ident, CSBaseExpression expr) + : base() + { + Type = type; + Ident = ident; + Expr = expr; + } + + public CSType Type { get; private set; } + public CSIdentifier Ident { get; private set; } + public CSBaseExpression Expr { get; private set; } + + protected override void LLWrite(ICodeWriter writer, object? o) + { + writer.BeginNewLine(true); + writer.Write("foreach (", false); + if (Type != null) + { + Type.WriteAll(writer); + } + else + { + writer.Write("var", false); + } + SimpleElement.Spacer.WriteAll(writer); + Ident.WriteAll(writer); + writer.Write(" in ", true); + Expr.WriteAll(writer); + writer.Write(")", false); + writer.EndLine(); + } + } + + public CSForEach(CSType type, CSIdentifier ident, CSBaseExpression expr, CSCodeBlock body) + { + ArgumentNullException.ThrowIfNull(ident, nameof(ident)); + ArgumentNullException.ThrowIfNull(expr, nameof(expr)); + Type = type; + Ident = ident; + Expr = expr; + Body = body ?? new CSCodeBlock(); + Add(new ForElement(type, ident, expr)); + Add(Body); + } + + public CSForEach(CSType type, string ident, CSBaseExpression expr, CSCodeBlock body) + : this(type, new CSIdentifier(ident), expr, body) + { + } + + public CSType Type { get; private set; } + public CSIdentifier Ident { get; private set; } + public CSBaseExpression Expr { get; private set; } + public CSCodeBlock Body { get; private set; } + } +} diff --git a/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSFunctionCall.cs b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSFunctionCall.cs new file mode 100644 index 000000000000..c043a6fc58c6 --- /dev/null +++ b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSFunctionCall.cs @@ -0,0 +1,136 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; + +namespace SyntaxDynamo.CSLang +{ + public class CSFunctionCall : CSBaseExpression, ICSLineable + { + public CSFunctionCall(CSIdentifier ident, CommaListElementCollection paramList, bool isConstructor = false) + { + ArgumentNullException.ThrowIfNull(ident, nameof(ident)); + ArgumentNullException.ThrowIfNull(paramList, nameof(paramList)); + Name = ident; + Parameters = paramList; + IsConstructor = isConstructor; + } + + public CSFunctionCall(string identifier, bool isConstructor, params CSBaseExpression[] parameters) + : this(new CSIdentifier(identifier), new CommaListElementCollection(parameters), isConstructor) + { + } + + public static CSFunctionCall Function(string identifier, params CSBaseExpression[] parameters) + { + return new CSFunctionCall(identifier, false, parameters); + } + public static CSLine FunctionLine(string identifier, params CSBaseExpression[] parameters) => new CSLine(Function(identifier, parameters)); + + public static CSFunctionCall Ctor(string identifier, params CSBaseExpression[] parameters) + { + return new CSFunctionCall(identifier, true, parameters); + } + public static CSLine CtorLine(string identifier, params CSBaseExpression[] parameters) => new CSLine(Ctor(identifier, parameters)); + + public static CSLine ConsoleWriteLine(params CSBaseExpression[] parameters) => FunctionLine("Console.WriteLine", parameters); + + protected override void LLWrite(ICodeWriter writer, object? o) + { + if (IsConstructor) + writer.Write("new ", false); + Name.WriteAll(writer); + writer.Write("(", false); + Parameters.WriteAll(writer); + writer.Write(")", false); + } + + public bool IsConstructor { get; private set; } + public CSIdentifier Name { get; private set; } + public CommaListElementCollection Parameters { get; private set; } + + public static CSLine FunctionCallLine(CSIdentifier identifier, params CSBaseExpression[] parameters) + { + return FunctionCallLine(identifier, false, parameters); + } + + public static CSLine FunctionCallLine(CSIdentifier identifier, bool isConstructor, params CSBaseExpression[] parameters) + { + return new CSLine(new CSFunctionCall(identifier, + new CommaListElementCollection(parameters), isConstructor)); + } + + public static CSLine FunctionCallLine(string identifier, params CSBaseExpression[] parameters) + { + return FunctionCallLine(identifier, false, parameters); + } + + public static CSLine FunctionCallLine(string identifier, bool isConstructor, params CSBaseExpression[] parameters) + { + ArgumentNullException.ThrowIfNull(identifier, nameof(identifier)); + return new CSLine(new CSFunctionCall(new CSIdentifier(identifier), + new CommaListElementCollection(parameters), isConstructor)); + } + + static CSIdentifier iNameOf = new CSIdentifier("nameof"); + + public static CSFunctionCall Nameof(CSIdentifier id) + { + return FooOf(iNameOf, id); + } + + public static CSFunctionCall Nameof(string name) + { + return Nameof(new CSIdentifier(name)); + } + + static CSIdentifier iTypeof = new CSIdentifier("typeof"); + + public static CSFunctionCall Typeof(Type t) + { + return Typeof(t.Name); + } + + public static CSFunctionCall Typeof(string t) + { + return FooOf(iTypeof, new CSIdentifier(t)); + } + + public static CSFunctionCall Typeof(CSSimpleType t) + { + return Typeof(t.Name); + } + + static CSIdentifier iSizeof = new CSIdentifier("sizeof"); + + public static CSFunctionCall Sizeof(CSBaseExpression expr) + { + return FooOf(iSizeof, expr); + } + + static CSIdentifier iDefault = new CSIdentifier("default"); + + public static CSFunctionCall Default(Type t) + { + return Default(t.Name); + } + + public static CSFunctionCall Default(string t) + { + return FooOf(iDefault, new CSIdentifier(t)); + } + + public static CSFunctionCall Default(CSSimpleType t) + { + return Default(t.Name); + } + + static CSFunctionCall FooOf(CSIdentifier foo, CSBaseExpression parameter) + { + CommaListElementCollection parms = new CommaListElementCollection(); + parms.Add(parameter); + return new CSFunctionCall(foo, parms, false); + } + } + +} diff --git a/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSGenericConstraint.cs b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSGenericConstraint.cs new file mode 100644 index 000000000000..032508626b3d --- /dev/null +++ b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSGenericConstraint.cs @@ -0,0 +1,91 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; + +namespace SyntaxDynamo.CSLang +{ + public class CSGenericConstraint : DelegatedSimpleElement + { + public CSGenericConstraint(CSIdentifier name, CSIdentifier isA) + { + ArgumentNullException.ThrowIfNull(name, nameof(name)); + ArgumentNullException.ThrowIfNull(isA, nameof(isA)); + Name = name; + IsA = new CommaListElementCollection(); + IsA.Add(isA); + } + + public CSGenericConstraint(CSIdentifier name, IEnumerable multiIs) + { + ArgumentNullException.ThrowIfNull(name, nameof(name)); + Name = name; + IsA = new CommaListElementCollection(); + if (multiIs != null) + IsA.AddRange(multiIs); + } + + public CSIdentifier Name { get; private set; } + public CommaListElementCollection IsA { get; private set; } + + protected override void LLWrite(ICodeWriter writer, object? o) + { + writer.Write("where ", true); + Name.Write(writer, o); + writer.Write(" : ", true); + IsA.WriteAll(writer); + } + } + + public class CSGenericConstraintCollection : List, ICodeElementSet + { + public IEnumerable Elements + { + get + { + bool first = true; + foreach (CSGenericConstraint tc in this) + { + if (!first) + { + yield return new LineBreak(); + } + first = false; + yield return tc; + } + } + } + + public event EventHandler Begin = (s, e) => { }; + + public event EventHandler End = (s, e) => { }; + + public virtual object? BeginWrite(ICodeWriter writer) + { + OnBeginWrite(new WriteEventArgs(writer)); + return null; + } + + protected virtual void OnBeginWrite(WriteEventArgs args) + { + Begin(this, args); + } + + public virtual void Write(ICodeWriter writer, object? o) + { + } + + public virtual void EndWrite(ICodeWriter writer, object? o) + { + OnEndWrite(new WriteEventArgs(writer)); + } + + protected virtual void OnEndWrite(WriteEventArgs args) + { + End.FireInReverse(this, args); + } + + } + +} diff --git a/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSGenericTypeDeclaration.cs b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSGenericTypeDeclaration.cs new file mode 100644 index 000000000000..8609da198d21 --- /dev/null +++ b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSGenericTypeDeclaration.cs @@ -0,0 +1,82 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; + +namespace SyntaxDynamo.CSLang +{ + public class CSGenericTypeDeclaration + { + public CSGenericTypeDeclaration(CSIdentifier name) + { + ArgumentNullException.ThrowIfNull(name, nameof(name)); + Name = name; + } + + public CSIdentifier Name { get; private set; } + } + + public class CSGenericTypeDeclarationCollection : List, ICodeElementSet + { + public CSGenericTypeDeclarationCollection() + : base() + { + } + + public IEnumerable Elements + { + get + { + if (this.Count > 0) + { + yield return new SimpleElement("<"); + bool first = true; + foreach (CSGenericTypeDeclaration decl in this) + { + if (!first) + { + yield return new SimpleElement(",", true); + yield return SimpleElement.Spacer; + } + else + { + first = false; + } + yield return decl.Name; + } + yield return new SimpleElement(">"); + } + } + } + + public event EventHandler Begin = (s, e) => { }; + + public event EventHandler End = (s, e) => { }; + + public virtual object? BeginWrite(ICodeWriter writer) + { + OnBeginWrite(new WriteEventArgs(writer)); + return null; + } + + protected virtual void OnBeginWrite(WriteEventArgs args) + { + Begin(this, args); + } + + public virtual void Write(ICodeWriter writer, object? o) + { + } + + public virtual void EndWrite(ICodeWriter writer, object? o) + { + OnEndWrite(new WriteEventArgs(writer)); + } + + protected virtual void OnEndWrite(WriteEventArgs args) + { + End.FireInReverse(this, args); + } + } +} diff --git a/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSIdentifier.cs b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSIdentifier.cs new file mode 100644 index 000000000000..a6596679d4dc --- /dev/null +++ b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSIdentifier.cs @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using SyntaxDynamo; + +namespace SyntaxDynamo.CSLang +{ + public class CSIdentifier : CSBaseExpression + { + public CSIdentifier(string name) + { + ArgumentNullException.ThrowIfNull(name, nameof(name)); + Name = name; + } + + public static explicit operator CSIdentifier(string name) + { + return new CSIdentifier(name); + } + + protected override void LLWrite(ICodeWriter writer, object? o) + { + writer.Write(Name, false); + } + + public string Name { get; private set; } + + public override string ToString() + { + return Name; + } + + public static CSIdentifier Create(string name) => new CSIdentifier(name); + + static CSIdentifier thisID = new CSIdentifier("this"); + public static CSIdentifier This { get { return thisID; } } + static CSIdentifier baseID = new CSIdentifier("base"); + public static CSIdentifier Base { get { return baseID; } } + } +} diff --git a/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSIfElse.cs b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSIfElse.cs new file mode 100644 index 000000000000..3338fb44ce54 --- /dev/null +++ b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSIfElse.cs @@ -0,0 +1,62 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; + +namespace SyntaxDynamo.CSLang +{ + public class CSIfElse : CodeElementCollection, ICSStatement + { + class CSIfElement : DelegatedSimpleElement, ICSStatement + { + public CSIfElement(CSBaseExpression condition) + : base() + { + Condition = condition; + } + + protected override void LLWrite(ICodeWriter writer, object? o) + { + writer.BeginNewLine(true); + writer.Write("if (", false); + Condition.WriteAll(writer); + writer.Write(")", false); + writer.EndLine(); + } + + public CSBaseExpression Condition { get; private set; } + } + + public CSIfElse(CSBaseExpression condition, CSCodeBlock ifClause, CSCodeBlock? elseClause = null) + : base() + { + ArgumentNullException.ThrowIfNull(condition, nameof(condition)); + ArgumentNullException.ThrowIfNull(ifClause, nameof(ifClause)); + Condition = new CSIfElement(condition); + IfClause = ifClause; + ElseClause = elseClause; + + Add(Condition); + Add(IfClause); + if (ElseClause != null && ElseClause.Count > 0) + { + Add(new SimpleLineElement("else", false, true, false)); + Add(ElseClause); + } + } + + public CSIfElse(CSBaseExpression expr, IEnumerable ifClause, IEnumerable elseClause) + : this(expr, new CSCodeBlock(ifClause), + elseClause != null ? new CSCodeBlock(elseClause) : null) + + { + + } + + public DelegatedSimpleElement Condition { get; private set; } + public CSCodeBlock IfClause { get; private set; } + public CSCodeBlock? ElseClause { get; private set; } + + } +} diff --git a/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSIndexExpression.cs b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSIndexExpression.cs new file mode 100644 index 000000000000..dff95a42f838 --- /dev/null +++ b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSIndexExpression.cs @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; + +namespace SyntaxDynamo.CSLang +{ + public class CSIndexExpression : CSBaseExpression + { + public CSIndexExpression(CSBaseExpression aggregate, CommaListElementCollection paramList, bool addParensAroundAggregate) + { + ArgumentNullException.ThrowIfNull(aggregate, nameof(aggregate)); + ArgumentNullException.ThrowIfNull(paramList, nameof(paramList)); + AddParensAroundAggregate = addParensAroundAggregate; + Aggregate = aggregate; + Parameters = paramList; + } + + public CSIndexExpression(string identifier, bool addParensAroundAggregate, params CSBaseExpression[] parameters) + : this(new CSIdentifier(identifier), new CommaListElementCollection(parameters), addParensAroundAggregate) + { + } + + public CSIndexExpression(CSBaseExpression aggregate, bool addParensAroundAggregate, params CSBaseExpression[] parameters) + : this(aggregate, new CommaListElementCollection(parameters), addParensAroundAggregate) + { + } + + protected override void LLWrite(ICodeWriter writer, object? o) + { + if (AddParensAroundAggregate) + writer.Write('(', false); + Aggregate.WriteAll(writer); + if (AddParensAroundAggregate) + writer.Write(')', false); + writer.Write("[", false); + Parameters.WriteAll(writer); + writer.Write("]", false); + } + + public bool AddParensAroundAggregate { get; private set; } + public CSBaseExpression Aggregate { get; private set; } + public CommaListElementCollection Parameters { get; private set; } + + } +} diff --git a/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSInheritance.cs b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSInheritance.cs new file mode 100644 index 000000000000..fa8bad2f7580 --- /dev/null +++ b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSInheritance.cs @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.IO; + +namespace SyntaxDynamo.CSLang +{ + public class CSInheritance : CommaListElementCollection + { + public CSInheritance(IEnumerable identifiers) + { + if (identifiers != null) + AddRange(identifiers); + } + + public CSInheritance(params string[] identifiers) + : this(identifiers.Select(str => new CSIdentifier(str))) + { + } + + public void Add(Type t) + { + ArgumentNullException.ThrowIfNull(t, "t"); + Add(new CSIdentifier(t.Name)); + } + } +} diff --git a/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSInitializer.cs b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSInitializer.cs new file mode 100644 index 000000000000..cfa46418fe33 --- /dev/null +++ b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSInitializer.cs @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; + +namespace SyntaxDynamo.CSLang +{ + public class CSInitializer : CSBaseExpression + { + public CSInitializer(IEnumerable parameters, bool appendNewlineAfterEach) + { + Parameters = new CommaListElementCollection("", "", parameters, appendNewlineAfterEach); + } + + public CommaListElementCollection Parameters { get; private set; } + + protected override void LLWrite(ICodeWriter writer, object? o) + { + writer.Write("{ ", false); + Parameters.WriteAll(writer); + writer.Write(" }", false); + } + } + + public class CSInitializedType : CSBaseExpression + { + public CSInitializedType(CSFunctionCall call, CSInitializer initializer) + { + ArgumentNullException.ThrowIfNull(call, nameof(call)); + ArgumentNullException.ThrowIfNull(initializer, nameof(initializer)); + Call = call; + Initializer = initializer; + } + + public CSInitializedType(CSFunctionCall call, IEnumerable parameters, bool appendNewlineAfterEach) + : this(call, new CSInitializer(parameters, appendNewlineAfterEach)) + { + } + + public CSFunctionCall Call { get; private set; } + public CSInitializer Initializer { get; private set; } + + protected override void LLWrite(ICodeWriter writer, object? o) + { + Call.WriteAll(writer); + SimpleElement.Spacer.WriteAll(writer); + Initializer.WriteAll(writer); + } + } +} diff --git a/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSInject.cs b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSInject.cs new file mode 100644 index 000000000000..a522a92b4e8f --- /dev/null +++ b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSInject.cs @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace SyntaxDynamo.CSLang +{ + // CSInject is a way to more formalize the notion of code that is just plain easier to + // inject as raw text. It's not strictly necessary, but when you see a CSInject, it will make + // it clear that you're doing something not quite on the up and up. + public class CSInject : CSIdentifier + { + public CSInject(string name) + : base(name) + { + } + + public static explicit operator CSInject(string name) + { + return new CSInject(name); + } + } +} diff --git a/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSInterface.cs b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSInterface.cs new file mode 100644 index 000000000000..5cb27359879a --- /dev/null +++ b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSInterface.cs @@ -0,0 +1,125 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace SyntaxDynamo.CSLang +{ + public class CSInterface : ICodeElementSet, ICSTopLevelDeclaration + { + public CSInterface(CSVisibility vis, CSIdentifier name, + IEnumerable? methods = null) + { + ArgumentNullException.ThrowIfNull(name, nameof(name)); + Visibility = vis; + Name = name; + Inheritance = new CSInheritance(); + Methods = new List(); + Properties = new List(); + GenericParams = new CSGenericTypeDeclarationCollection(); + GenericConstraints = new CSGenericConstraintCollection(); + if (methods != null) + Methods.AddRange(methods); + } + + public CSInterface(CSVisibility vis, string name, IEnumerable? methods = null) + : this(vis, new CSIdentifier(name), methods) + { + } + + public CSType ToCSType(IEnumerable genericReplacements) + { + var replacements = genericReplacements.ToList(); + if (replacements.Count < GenericParams.Count) + { + replacements.AddRange(GenericParams.Skip(replacements.Count).Select(gen => new CSSimpleType(gen.Name.Name))); + } + return new CSSimpleType(Name.Name, false, replacements.ToArray()); + } + + public CSType ToCSType() + { + return ToCSType(GenericParams.Select(gen => new CSSimpleType(gen.Name.Name))); + } + + public CSVisibility Visibility { get; private set; } + public CSIdentifier Name { get; private set; } + public CSInheritance Inheritance { get; private set; } + public List Methods { get; private set; } + public List Properties { get; private set; } + public CSGenericTypeDeclarationCollection GenericParams { get; private set; } + public CSGenericConstraintCollection GenericConstraints { get; private set; } + + #region ICodeElem implementation + public event EventHandler Begin = (s, e) => { }; + + public event EventHandler End = (s, e) => { }; + + public virtual object? BeginWrite(ICodeWriter writer) + { + OnBeginWrite(new WriteEventArgs(writer)); + return null; + } + + protected virtual void OnBeginWrite(WriteEventArgs args) + { + Begin(this, args); + } + + public virtual void Write(ICodeWriter writer, object? o) + { + } + + public virtual void EndWrite(ICodeWriter writer, object? o) + { + OnEndWrite(new WriteEventArgs(writer)); + } + + protected virtual void OnEndWrite(WriteEventArgs args) + { + End.FireInReverse(this, args); + } + + #endregion + + #region ICodeElemSet implementation + + public IEnumerable Elements + { + get + { + var decl = new LineCodeElementCollection(true, false, true); + if (Visibility != CSVisibility.None) + decl.Add(new SimpleElement(CSMethod.VisibilityToString(Visibility) + " ")); + decl.Add(new CSIdentifier("interface ")); + decl.Add(Name); + + decl.Add(GenericParams); + if (Inheritance.Count > 0) + { + decl.Add(new SimpleElement(" : ", true)); + decl.Add(Inheritance); + } + if (GenericConstraints.Count > 0) + { + decl.Add(SimpleElement.Spacer); + decl.Add(GenericConstraints); + } + yield return decl; + + var contents = new DecoratedCodeElementCollection("{", "}", + true, true, true); + + contents.AddRange(Methods); + contents.AddRange(Properties); + + yield return contents; + + } + } + + #endregion + } +} diff --git a/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSLambda.cs b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSLambda.cs new file mode 100644 index 000000000000..bdabf7d746da --- /dev/null +++ b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSLambda.cs @@ -0,0 +1,81 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Linq; +using System.Collections.Generic; + +namespace SyntaxDynamo.CSLang +{ + public class CSLambda : CSBaseExpression + { + public CSLambda(CSParameterList parameters, ICSExpression value) + { + ArgumentNullException.ThrowIfNull(value, nameof(value)); + Parameters = parameters ?? new CSParameterList(); + Value = value; + Body = null; + } + + public CSLambda(CSParameterList parameters, CSCodeBlock body) + { + body = body ?? new CSCodeBlock(); + Parameters = parameters ?? new CSParameterList(); + Value = null; + Body = body; + } + + public CSLambda(ICSExpression value, params string[] parameters) + : this(new CSParameterList(parameters.Select(p => new CSParameter(CSSimpleType.Void, new CSIdentifier(p)))), value) + { + } + + public CSLambda(CSCodeBlock body, params string[] parameters) + : this(new CSParameterList(parameters.Select(p => new CSParameter(CSSimpleType.Void, new CSIdentifier(p)))), body) + { + } + + public CSParameterList Parameters { get; private set; } + public ICSExpression? Value { get; private set; } + public CSCodeBlock? Body { get; private set; } + + #region implemented abstract members of DelegatedSimpleElem + + protected override void LLWrite(ICodeWriter writer, object? o) + { + // hack - Parameters really want types. If you set them to void, we'll consider them to be + // typeless. + bool allVoid = Parameters.All(p => p.CSType == CSSimpleType.Void); + + writer.Write('(', true); + if (allVoid) + { + bool didFirst = false; + foreach (CSParameter p in Parameters) + { + if (didFirst) + { + writer.Write(", ", true); + } + p.Name.WriteAll(writer); + didFirst = true; + } + } + else + { + Parameters.WriteAll(writer); + } + writer.Write(") => ", true); + if (Value != null) + { + Value.WriteAll(writer); + } + else + { + Body?.WriteAll(writer); + } + } + + #endregion + } +} diff --git a/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSLine.cs b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSLine.cs new file mode 100644 index 000000000000..75a7af80c699 --- /dev/null +++ b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSLine.cs @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using SyntaxDynamo; + +namespace SyntaxDynamo.CSLang +{ + public class CSLine : DelegatedSimpleElement, ICSStatement + { + public CSLine(ICodeElement contents, bool addSemicolon = true) + { + ArgumentNullException.ThrowIfNull(contents, nameof(contents)); + Contents = contents; + if (!(contents is ICSLineable) && addSemicolon) + throw new ArgumentException("contents must be ILineable", nameof(contents)); + AddSemicolon = addSemicolon; + } + + protected override void LLWrite(ICodeWriter writer, object? o) + { + writer.BeginNewLine(true); + Contents.WriteAll(writer); + if (AddSemicolon) + writer.Write(';', false); + writer.EndLine(); + } + + public ICodeElement Contents { get; private set; } + public bool AddSemicolon { get; set; } + } +} diff --git a/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSMethod.cs b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSMethod.cs new file mode 100644 index 000000000000..8640544ece58 --- /dev/null +++ b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSMethod.cs @@ -0,0 +1,249 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Linq; +using SyntaxDynamo; + +namespace SyntaxDynamo.CSLang +{ + public class CSMethod : CodeElementCollection + { + public CSMethod(CSVisibility vis, CSMethodKind kind, CSType? type, CSIdentifier name, CSParameterList parms, CSCodeBlock? body) + : this(vis, kind, type, name, parms, null, false, body) + { + + } + public CSMethod(CSVisibility vis, CSMethodKind kind, CSType? type, CSIdentifier name, + CSParameterList parms, CSBaseExpression[]? baseOrThisCallParms, bool callsBase, CSCodeBlock? body, bool isSealed = false, + bool isAsync = false) + { + ArgumentNullException.ThrowIfNull(name, nameof(name)); + ArgumentNullException.ThrowIfNull(parms, nameof(parms)); + GenericParameters = new CSGenericTypeDeclarationCollection(); + GenericConstraints = new CSGenericConstraintCollection(); + Visibility = vis; + Kind = kind; + Type = type; // no throw on null - could be constructor + Name = name; + Parameters = parms; + CallsBase = callsBase; + BaseOrThisCallParameters = baseOrThisCallParms; + + Body = body; // can be null + IsSealed = isSealed; + IsAsync = isAsync; + + LineCodeElementCollection lc = new LineCodeElementCollection(new ICodeElement[0], false, true); + if (vis != CSVisibility.None) + { + lc.And(new SimpleElement(VisibilityToString(vis))).And(SimpleElement.Spacer); + } + + if (isSealed) + { + lc.And(new SimpleElement("sealed")).And(SimpleElement.Spacer); + } + + if (isAsync) + { + lc.And(new SimpleElement("async")).And(SimpleElement.Spacer); + } + + lc.And(new SimpleElement(MethodKindToString(kind))).And(SimpleElement.Spacer); + + if (type != null) + { + lc.And(type).And(SimpleElement.Spacer); + } + + lc.And(name).And(GenericParameters).And(new SimpleElement("(")).And(parms).And(new SimpleElement(")")).And(GenericConstraints); + if (body == null) + { + if (!(kind == CSMethodKind.StaticExtern || kind == CSMethodKind.Interface)) + throw new ArgumentException("Method body is only optional when method kind kind is either StaticExtern or Interface", + nameof(body)); + lc.Add(new SimpleElement(";")); + } + Add(lc); + if (BaseOrThisCallParameters != null) + { + Add(new CSFunctionCall(CallsBase ? ": base" : ": this", false, BaseOrThisCallParameters)); + } + if (body != null) + Add(body); + } + + public CSVisibility Visibility { get; private set; } + public CSMethodKind Kind { get; private set; } + public CSType? Type { get; private set; } + public CSIdentifier Name { get; private set; } + public CSParameterList Parameters { get; private set; } + public bool CallsBase { get; private set; } + public CSBaseExpression[]? BaseOrThisCallParameters { get; private set; } + public CSCodeBlock? Body { get; private set; } + public CSGenericTypeDeclarationCollection GenericParameters { get; private set; } + public CSGenericConstraintCollection GenericConstraints { get; private set; } + public bool IsSealed { get; private set; } + public bool IsAsync { get; private set; } + + public CSMethod AsSealed() + { + var sealedMethod = new CSMethod(Visibility, Kind, Type, Name, Parameters, BaseOrThisCallParameters, CallsBase, Body, true); + return CopyGenerics(this, sealedMethod); + } + + public CSMethod AsOverride() + { + var overrideMethod = new CSMethod(Visibility, CSMethodKind.Override, Type, Name, Parameters, BaseOrThisCallParameters, CallsBase, Body, IsSealed); + return CopyGenerics(this, overrideMethod); + } + + public CSMethod AsPrivate() + { + var privateMethod = new CSMethod(CSVisibility.None, Kind, Type, Name, Parameters, BaseOrThisCallParameters, CallsBase, Body, IsSealed); + return CopyGenerics(this, privateMethod); + } + + public static CSMethod CopyGenerics(CSMethod from, CSMethod to) + { + to.GenericParameters.AddRange(from.GenericParameters); + to.GenericConstraints.AddRange(from.GenericConstraints); + return to; + } + + public static CSMethod RemoveGenerics(CSMethod from) + { + var newMethod = new CSMethod(from.Visibility, from.Kind, from.Type, from.Name, from.Parameters, from.Body); + return newMethod; + } + + public static string VisibilityToString(CSVisibility visibility) + { + switch (visibility) + { + case CSVisibility.None: + return ""; + case CSVisibility.Internal: + return "internal"; + case CSVisibility.Private: + return "private"; + case CSVisibility.Public: + return "public"; + case CSVisibility.Protected: + return "protected"; + default: + throw new ArgumentOutOfRangeException("vis"); + } + } + + public static string MethodKindToString(CSMethodKind kind) + { + switch (kind) + { + case CSMethodKind.None: + case CSMethodKind.Interface: + return ""; + case CSMethodKind.Extern: + return "extern"; + case CSMethodKind.New: + return "new"; + case CSMethodKind.Override: + return "override"; + case CSMethodKind.Static: + return "static"; + case CSMethodKind.StaticExtern: + return "static extern"; + case CSMethodKind.StaticNew: + return "static new"; + case CSMethodKind.Virtual: + return "virtual"; + case CSMethodKind.Abstract: + return "abstract"; + case CSMethodKind.Unsafe: + return "unsafe"; + case CSMethodKind.StaticUnsafe: + return "static unsafe"; + default: + throw new ArgumentOutOfRangeException(nameof(kind)); + } + } + + public static CSMethod PublicMethod(CSType type, string name, CSParameterList parms, CSCodeBlock body) + { + ArgumentNullException.ThrowIfNull(body, nameof(body)); + return new CSMethod(CSVisibility.Public, CSMethodKind.None, type, new CSIdentifier(name), parms, body); + } + + public static CSMethod PublicMethod(CSMethodKind kind, CSType type, string name, CSParameterList parms, CSCodeBlock body) + { + ArgumentNullException.ThrowIfNull(body, nameof(body)); + return new CSMethod(CSVisibility.Public, kind, type, new CSIdentifier(name), parms, body); + } + + public static CSMethod PublicConstructor(string name, CSParameterList parms, CSCodeBlock body) + { + ArgumentNullException.ThrowIfNull(body, nameof(body)); + return new CSMethod(CSVisibility.Public, CSMethodKind.None, null, new CSIdentifier(name), parms, body); + } + + public static CSMethod PublicConstructor(string name, CSParameterList parms, CSCodeBlock body, params CSBaseExpression[] baseParams) + { + ArgumentNullException.ThrowIfNull(body, nameof(body)); + return new CSMethod(CSVisibility.Public, CSMethodKind.None, null, new CSIdentifier(name), parms, + baseParams, true, body); + } + + public static CSMethod PrivateConstructor(string name, CSParameterList parms, CSCodeBlock body) + { + ArgumentNullException.ThrowIfNull(body, nameof(body)); + return new CSMethod(CSVisibility.None, CSMethodKind.None, null, new CSIdentifier(name), parms, body); + } + + public static CSMethod PrivateConstructor(string name, CSParameterList parms, CSCodeBlock body, params CSBaseExpression[] baseParams) + { + ArgumentNullException.ThrowIfNull(body, nameof(body)); + return new CSMethod(CSVisibility.None, CSMethodKind.None, null, new CSIdentifier(name), parms, + baseParams, true, body); + } + + public static CSMethod PInvoke(CSVisibility vis, CSType type, string name, string dllName, string externName, CSParameterList parms) + { + ArgumentNullException.ThrowIfNull(type, nameof(type)); + CSMethod method = new CSMethod(vis, CSMethodKind.StaticExtern, type, + new CSIdentifier(name), parms, null); + + CSAttribute.DllImport(dllName, externName).AttachBefore(method); + + return method; + } + + public static CSMethod PInvoke(CSVisibility vis, CSType type, string name, CSBaseExpression dllName, string externName, CSParameterList parms) + { + ArgumentNullException.ThrowIfNull(type, nameof(type)); + CSMethod method = new CSMethod(vis, CSMethodKind.StaticExtern, type, + new CSIdentifier(name), parms, null); + + CSAttribute.DllImport(dllName, externName).AttachBefore(method); + + return method; + } + + public static CSMethod PublicPInvoke(CSType type, string name, string dllName, string externName, CSParameterList parms) + { + return PInvoke(CSVisibility.Public, type, name, dllName, externName, parms); + } + + public static CSMethod PrivatePInvoke(CSType type, string name, string dllName, string externName, CSParameterList parms) + { + return PInvoke(CSVisibility.None, type, name, dllName, externName, parms); + } + + public static CSMethod InternalPInvoke(CSType type, string name, string dllName, string externName, CSParameterList parms) + { + return PInvoke(CSVisibility.Internal, type, name, dllName, externName, parms); + } + + } +} diff --git a/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSNamespace.cs b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSNamespace.cs new file mode 100644 index 000000000000..6899c4b68b6b --- /dev/null +++ b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSNamespace.cs @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using SyntaxDynamo; +using System.Linq; +using System.Collections.Generic; + +namespace SyntaxDynamo.CSLang +{ + public class CSNamespace : LabeledCodeElementCollection + { + public CSNamespace() + : base(new SimpleLineElement(string.Empty, false, false, false), + new DecoratedCodeElementCollection(string.Empty, string.Empty, false, false, false)) + { + } + + public CSNamespace(string nameSpace) + : base(new SimpleLineElement(string.Format("namespace {0}", nameSpace), + false, true, false), + new DecoratedCodeElementCollection("{", "}", true, true, true)) + { + } + } + + public class CSNamespaceBlock : CodeElementCollection + { + public CSNamespaceBlock(params string[] nameSpaces) + : base() + { + this.AddRange(nameSpaces.Select(s => new CSNamespace(s))); + } + + public CSNamespaceBlock(IEnumerable nameSpaces) + : base() + { + this.AddRange(nameSpaces); + } + + public CSNamespaceBlock And(string s) + { + return And(new CSNamespace(s)); + } + + public CSNamespaceBlock And(CSNamespace ns) + { + ArgumentNullException.ThrowIfNull(ns, nameof(ns)); + Add(ns); + return this; + } + } +} diff --git a/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSParameter.cs b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSParameter.cs new file mode 100644 index 000000000000..d0b08e384e18 --- /dev/null +++ b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSParameter.cs @@ -0,0 +1,117 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Linq; +using SyntaxDynamo; + +namespace SyntaxDynamo.CSLang +{ + public class CSParameter : DelegatedSimpleElement + { + public CSParameter(CSType type, CSIdentifier name, + CSParameterKind parameterKind = CSParameterKind.None, + CSConstant? defaultValue = null) + { + ArgumentNullException.ThrowIfNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(name, nameof(name)); + CSType = type; + Name = name; + ParameterKind = parameterKind; + DefaultValue = defaultValue; + } + + public CSParameter(CSType type, string name, + CSParameterKind parameterKind = CSParameterKind.None, + CSConstant? defaultValue = null) + : this(type, new CSIdentifier(name), parameterKind, defaultValue) + { + } + + public CSParameter(string type, string name, + CSParameterKind parameterKind = CSParameterKind.None, + CSConstant? defaultValue = null) + : this(new CSSimpleType(type), new CSIdentifier(name), parameterKind, defaultValue) + { + } + + protected override void LLWrite(ICodeWriter writer, object? o) + { + if (this.ParameterKind != CSParameterKind.None) + { + writer.Write(ToParameterKindString(this.ParameterKind), false); + writer.Write(' ', false); + } + this.CSType.WriteAll(writer); + writer.Write(' ', true); + Name.WriteAll(writer); + if ((object?)DefaultValue != null) + { + writer.Write(" = ", true); + DefaultValue.WriteAll(writer); + } + } + + static string ToParameterKindString(CSParameterKind parameterKind) + { + switch (parameterKind) + { + case CSParameterKind.None: + return ""; + case CSParameterKind.Out: + return "out"; + case CSParameterKind.Ref: + return "ref"; + case CSParameterKind.This: + return "this"; + case CSParameterKind.Params: + return "params"; + default: + throw new ArgumentOutOfRangeException(nameof(parameterKind), "unexpected parameter kind " + parameterKind.ToString()); + } + } + + public CSType CSType { get; private set; } + public CSIdentifier Name { get; private set; } + public CSConstant? DefaultValue { get; private set; } + public CSParameterKind ParameterKind { get; private set; } + } + + public class CSParameterList : CommaListElementCollection + { + public CSParameterList(IEnumerable? parameters) + : base() + { + if (parameters != null) + AddRange(parameters); + } + public CSParameterList(params CSParameter[] parameters) + : base() + { + if (parameters != null) + AddRange(parameters); + } + + public CSParameterList() : this((IEnumerable?)null) { } + + public CSParameterList(CSParameter parameter) : this(new CSParameter[] { parameter }) { } + + public CSParameterList And(CSParameter parameter) + { + ArgumentNullException.ThrowIfNull(parameter, nameof(parameter)); + Add(parameter); + return this; + } + + public CSParameterList And(string type, string identifier, + CSParameterKind parameterKind = CSParameterKind.None, + CSConstant? defaultValue = null) + { + ArgumentNullException.ThrowIfNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(identifier, nameof(identifier)); + return And(new CSParameter(new CSSimpleType(type), + new CSIdentifier(identifier), parameterKind, defaultValue)); + } + } +} diff --git a/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSParenthesisExpression.cs b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSParenthesisExpression.cs new file mode 100644 index 000000000000..49854ba97270 --- /dev/null +++ b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSParenthesisExpression.cs @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; + +namespace SyntaxDynamo.CSLang +{ + public class CSParenthesisExpression : CSBaseExpression + { + public CSParenthesisExpression(ICSExpression within) + { + ArgumentNullException.ThrowIfNull(within, nameof(within)); + Within = within; + } + + public ICSExpression Within { get; private set; } + + protected override void LLWrite(ICodeWriter writer, object? o) + { + writer.Write('(', true); + Within.WriteAll(writer); + writer.Write(')', true); + } + } + + public class CSCastExpression : CSBaseExpression, ICSLineable + { + public CSCastExpression(string type, ICSExpression toCast) + : this(new CSSimpleType(type), toCast) + { + } + + public CSCastExpression(CSType type, ICSExpression toCast) + { + ArgumentNullException.ThrowIfNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(toCast, nameof(toCast)); + Type = type; + ToCast = toCast; + } + + public CSType Type { get; private set; } + public ICSExpression ToCast { get; private set; } + + protected override void LLWrite(ICodeWriter writer, object? o) + { + writer.Write("(", true); + Type.WriteAll(writer); + writer.Write(')', true); + ToCast.WriteAll(writer); + } + } +} diff --git a/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSProperty.cs b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSProperty.cs new file mode 100644 index 000000000000..bffde0716e1e --- /dev/null +++ b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSProperty.cs @@ -0,0 +1,202 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Text; +using System.Collections.Generic; +using System.Linq; +using SyntaxDynamo; + +namespace SyntaxDynamo.CSLang +{ + public class CSProperty : CodeElementCollection + { + public CSProperty(CSType type, CSMethodKind kind, CSIdentifier name, + CSVisibility getVis, IEnumerable getter, + CSVisibility setVis, IEnumerable setter) + : this(type, kind, name, + getVis, getter != null ? new CSCodeBlock(getter) : null, + setVis, setter != null ? new CSCodeBlock(setter) : null) + { + } + + public CSProperty(CSType type, CSMethodKind kind, CSIdentifier name, + CSVisibility getVis, CSCodeBlock? getter, + CSVisibility setVis, CSCodeBlock? setter) + : this(type, kind, name, getVis, getter, setVis, setter, null) + { + } + + public CSProperty(CSType type, CSMethodKind kind, + CSVisibility getVis, CSCodeBlock getter, + CSVisibility setVis, CSCodeBlock setter, CSParameterList parms) + : this(type, kind, new CSIdentifier("this"), getVis, getter, setVis, setter, + parms) + { + } + + CSProperty(CSType type, CSMethodKind kind, CSIdentifier name, + CSVisibility getVis, CSCodeBlock? getter, + CSVisibility setVis, CSCodeBlock? setter, CSParameterList? parms) + { + ArgumentNullException.ThrowIfNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(name, nameof(name)); + bool unifiedVis = getVis == setVis; + IndexerParameters = parms; + + LineCodeElementCollection decl = new LineCodeElementCollection(null, false, true); + + GetterVisibility = getVis; + SetterVisibility = setVis; + CSVisibility bestVis = (CSVisibility)Math.Min((int)getVis, (int)setVis); + decl.And(new SimpleElement(CSMethod.VisibilityToString(bestVis))).And(SimpleElement.Spacer); + if (kind != CSMethodKind.None) + decl.And(new SimpleElement(CSMethod.MethodKindToString(kind))).And(SimpleElement.Spacer); + + PropType = type; + Name = name; + + decl.And(type).And(SimpleElement.Spacer) + .And(name); + if (parms != null) + { + decl.And(new SimpleElement("[", true)).And(parms).And(new SimpleElement("]")); + } + Add(decl); + + CSCodeBlock cb = new CSCodeBlock(null); + + if (getter != null) + { + Getter = getter; + LineCodeElementCollection getLine = MakeEtter(getVis, "get", unifiedVis, getVis > setVis); + cb.Add(getLine); + if (getter.Count() == 0) + { + getLine.Add(new SimpleElement(";")); + } + else + { + cb.Add(getter); + } + } + if (setter != null) + { + Setter = setter; + LineCodeElementCollection setLine = MakeEtter(setVis, "set", unifiedVis, setVis > getVis); + cb.Add(setLine); + if (setter.Count() == 0) + { + setLine.Add(new SimpleElement(";")); + } + else + { + cb.Add(setter); + } + } + + Add(cb); + } + public CSType PropType { get; private set; } + public CSIdentifier Name { get; private set; } + public CSParameterList? IndexerParameters { get; private set; } + public CSVisibility? GetterVisibility { get; private set; } + public CSVisibility? SetterVisibility { get; private set; } + + public CSCodeBlock? Getter { get; private set; } + + public CSCodeBlock? Setter { get; private set; } + + static LineCodeElementCollection MakeEtter(CSVisibility vis, string getset, + bool unifiedVis, bool moreRestrictiveVis) + { + LineCodeElementCollection getLine = new LineCodeElementCollection(null, false, true); + if (!unifiedVis && vis != CSVisibility.None && moreRestrictiveVis) + getLine.And(new SimpleElement(CSMethod.VisibilityToString(vis))).And(SimpleElement.Spacer); + return getLine.And(new SimpleElement(getset, false)); + } + + public static CSProperty PublicGetSet(CSType type, string name) + { + return new CSProperty(type, CSMethodKind.None, new CSIdentifier(name), + CSVisibility.Public, new CSCodeBlock(), CSVisibility.Public, new CSCodeBlock()); + } + + public static CSProperty PublicGetPrivateSet(CSType type, string name) + { + return new CSProperty(type, CSMethodKind.None, new CSIdentifier(name), + CSVisibility.Public, new CSCodeBlock(), CSVisibility.Private, new CSCodeBlock()); + } + + static CSProperty PublicGetPubPrivSetBacking(CSType type, string name, bool declareField, bool setIsPublic, string? backingFieldName = null) + { + ArgumentNullException.ThrowIfNull(name, nameof(name)); + if (!declareField && backingFieldName == null) + throw new ArgumentException("declareField must be true if there is no supplied field name", nameof(declareField)); + backingFieldName = backingFieldName ?? MassageName(name); + + CSIdentifier backingIdent = new CSIdentifier(backingFieldName); + LineCodeElementCollection getCode = + new LineCodeElementCollection(new ICodeElement[] { CSReturn.ReturnLine(backingIdent) }, false, true); + LineCodeElementCollection setCode = + new LineCodeElementCollection( + new ICodeElement[] { + CSAssignment.Assign (backingFieldName, new CSIdentifier ("value")) + }, false, true); + CSProperty prop = new CSProperty(type, CSMethodKind.None, new CSIdentifier(name), CSVisibility.Public, + new CSCodeBlock(getCode), + (setIsPublic ? CSVisibility.Public : CSVisibility.Private), new CSCodeBlock(setCode)); + if (declareField) + prop.Insert(0, CSFieldDeclaration.FieldLine(type, backingFieldName)); + return prop; + } + + public static CSProperty PublicGetSetBacking(CSType type, string name, bool declareField, string? backingFieldName = null) + { + return PublicGetPubPrivSetBacking(type, name, true, declareField, backingFieldName); + } + + public static CSProperty PublicGetPrivateSetBacking(CSType type, string name, bool declareField, string? backingFieldName = null) + { + return PublicGetPubPrivSetBacking(type, name, false, declareField, backingFieldName); + } + + public static CSProperty PublicGetBacking(CSType type, CSIdentifier name, CSIdentifier backingFieldName, + bool includeBackingFieldDeclaration = false, CSMethodKind methodKind = CSMethodKind.None) + { + ArgumentNullException.ThrowIfNull(name, nameof(name)); + ArgumentNullException.ThrowIfNull(backingFieldName, nameof(backingFieldName)); + LineCodeElementCollection getCode = + new LineCodeElementCollection( + new ICodeElement[] { + CSReturn.ReturnLine (backingFieldName) + }, false, true); + CSProperty prop = new CSProperty(type, methodKind, name, + CSVisibility.Public, new CSCodeBlock(getCode), + CSVisibility.Public, null); + if (includeBackingFieldDeclaration) + prop.Insert(0, CSFieldDeclaration.FieldLine(type, backingFieldName)); + return prop; + } + + public static CSProperty PublicGetBacking(CSType type, string name, string backingFieldName, bool includeBackingFieldDeclaration = false) + { + ArgumentNullException.ThrowIfNull(name, nameof(name)); + ArgumentNullException.ThrowIfNull(backingFieldName, nameof(backingFieldName)); + return PublicGetBacking(type, + new CSIdentifier(name), + new CSIdentifier(backingFieldName), + includeBackingFieldDeclaration); + } + + static string MassageName(string name) + { + StringBuilder sb = new StringBuilder(); + sb.Append("_"); + sb.Append(Char.ToLowerInvariant(name[0])); + if (name.Length > 0) + sb.Append(name.Substring(1)); + return sb.ToString(); + } + } +} diff --git a/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSReturn.cs b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSReturn.cs new file mode 100644 index 000000000000..1756c4faf225 --- /dev/null +++ b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSReturn.cs @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace SyntaxDynamo.CSLang +{ + public class CSReturn : DelegatedSimpleElement, ICSExpression, ICSLineable + { + public CSReturn(ICSExpression expr) + { + Value = expr; + } + + protected override void LLWrite(ICodeWriter writer, object? o) + { + writer.Write("return ", true); + if (Value != null) + Value.WriteAll(writer); + } + + public ICSExpression Value { get; private set; } + + public static CSLine ReturnLine(ICSExpression expr) + { + return new CSLine(new CSReturn(expr)); + } + } +} diff --git a/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSShortCircuit.cs b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSShortCircuit.cs new file mode 100644 index 000000000000..df8584c574cf --- /dev/null +++ b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSShortCircuit.cs @@ -0,0 +1,34 @@ +using System; +namespace SyntaxDynamo.CSLang +{ + public class CSShortCircuit : DelegatedSimpleElement, ICSLineable + { + public CSShortCircuit(CSShortCircuitKind kind) + { + Kind = kind; + } + + public CSShortCircuitKind Kind { get; private set; } + + protected override void LLWrite(ICodeWriter writer, object? o) + { + var keyword = Kind == CSShortCircuitKind.Break ? "break" : "continue"; + writer.Write(keyword, false); + } + + public static CSLine ShortCircuit(CSShortCircuitKind kind) + { + return new CSLine(new CSShortCircuit(kind)); + } + + public static CSLine Continue() + { + return ShortCircuit(CSShortCircuitKind.Continue); + } + + public static CSLine Break() + { + return ShortCircuit(CSShortCircuitKind.Break); + } + } +} diff --git a/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSTernary.cs b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSTernary.cs new file mode 100644 index 000000000000..d9078259b297 --- /dev/null +++ b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSTernary.cs @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; + +namespace SyntaxDynamo.CSLang +{ + public class CSTernary : CSBaseExpression + { + public CSTernary(CSBaseExpression predicate, CSBaseExpression onTrue, CSBaseExpression onFalse, bool addParentheses) + { + ArgumentNullException.ThrowIfNull(predicate, nameof(predicate)); + ArgumentNullException.ThrowIfNull(onTrue, nameof(onTrue)); + ArgumentNullException.ThrowIfNull(onFalse, nameof(onFalse)); + Predicate = predicate; + OnTrue = onTrue; + OnFalse = onFalse; + AddParentheses = addParentheses; + } + public CSBaseExpression Predicate { get; private set; } + public CSBaseExpression OnTrue { get; private set; } + public CSBaseExpression OnFalse { get; private set; } + public bool AddParentheses { get; set; } + + #region implemented abstract members of DelegatedSimpleElem + + protected override void LLWrite(ICodeWriter writer, object? o) + { + if (AddParentheses) + { + writer.Write('(', true); + } + Predicate.WriteAll(writer); + writer.Write(" ? ", true); + OnTrue.WriteAll(writer); + writer.Write(" : ", true); + OnFalse.WriteAll(writer); + if (AddParentheses) + { + writer.Write(')', true); + } + } + + #endregion + } +} diff --git a/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSThrow.cs b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSThrow.cs new file mode 100644 index 000000000000..274d3232b39a --- /dev/null +++ b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSThrow.cs @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; + +namespace SyntaxDynamo.CSLang +{ + public class CSThrow : CSBaseExpression, ICSStatement, ICSLineable + { + public CSThrow(CSBaseExpression expr) + { + ArgumentNullException.ThrowIfNull(expr, nameof(expr)); + Expr = expr; + } + + public CSBaseExpression Expr { get; private set; } + + #region implemented abstract members of DelegatedSimpleElem + + protected override void LLWrite(ICodeWriter writer, object? o) + { + writer.Write("throw ", true); + Expr.WriteAll(writer); + } + + #endregion + + public static CSLine ThrowLine(T exType, CommaListElementCollection args) where T : Exception + { + return new CSLine(new CSThrow(new CSFunctionCall(new CSIdentifier(exType.GetType().Name), args, true))); + } + + public static CSLine ThrowLine(T exType, string message) where T : Exception + { + CommaListElementCollection args = new CommaListElementCollection(); + if (message != null) + args.Add(CSConstant.Val(message)); + return ThrowLine(exType, args); + } + + public static CSLine ThrowLine(T exType, CSBaseExpression expr) where T : Exception + { + ArgumentNullException.ThrowIfNull(expr, nameof(expr)); + CommaListElementCollection args = new CommaListElementCollection(); + args.Add(expr); + return ThrowLine(exType, args); + } + } +} diff --git a/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSTryCatch.cs b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSTryCatch.cs new file mode 100644 index 000000000000..da74a4793bcf --- /dev/null +++ b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSTryCatch.cs @@ -0,0 +1,83 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +namespace SyntaxDynamo.CSLang +{ + public class CSCatch : DelegatedSimpleElement, ICSStatement + { + public CSCatch(CSType? catchType, CSIdentifier? name, CSCodeBlock body) + { + CatchType = catchType; + Name = name; + Body = body ?? new CSCodeBlock(); + } + + public CSCatch(string catchType, string name, CSCodeBlock body) + : this(new CSSimpleType(catchType), name != null ? new CSIdentifier(name) : null, body) + { + } + + public CSCatch(Type catchType, string name, CSCodeBlock body) + : this(new CSSimpleType(catchType), name != null ? new CSIdentifier(name) : null, body) + { + } + + public CSCatch(CSCodeBlock body) + : this((CSType?)null, null, body) + { + } + + public CSType? CatchType { get; private set; } + public CSIdentifier? Name { get; private set; } + public CSCodeBlock Body { get; private set; } + protected override void LLWrite(ICodeWriter writer, object? o) + { + + writer.BeginNewLine(true); + writer.Write("catch ", false); + if (CatchType != null) + { + writer.Write("(", false); + CatchType.WriteAll(writer); + if ((object?)Name != null) + { + SimpleElement.Spacer.WriteAll(writer); + Name?.WriteAll(writer); + } + writer.Write(")", false); + } + writer.EndLine(); + Body.WriteAll(writer); + writer.EndLine(); + } + } + + public class CSTryCatch : CodeElementCollection, ICSStatement + { + public CSTryCatch(CSCodeBlock tryBlock, params CSCatch[] catchBlocks) + { + TryBlock = tryBlock ?? new CSCodeBlock(); + CatchBlocks = new CodeElementCollection(); + CatchBlocks.AddRange(catchBlocks); + + Add(new SimpleElement("try ", true)); + Add(TryBlock); + Add(CatchBlocks); + } + + public CSTryCatch(CSCodeBlock tryBlock, Type catchType, string name, CSCodeBlock catchBlock) + : this(tryBlock, new CSCatch(catchType, name, catchBlock)) + { + } + + public override void Write(ICodeWriter writer, object? o) + { + writer.BeginNewLine(true); + base.Write(writer, o); + } + + public CSCodeBlock TryBlock { get; private set; } + public CodeElementCollection CatchBlocks { get; private set; } + } +} diff --git a/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSType.cs b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSType.cs new file mode 100644 index 000000000000..b1155cfa1edb --- /dev/null +++ b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSType.cs @@ -0,0 +1,277 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using SyntaxDynamo; +using System.IO; +using System.Text; +using System.Linq; +using System.Collections.Generic; + +namespace SyntaxDynamo.CSLang +{ + public abstract class CSType : DelegatedSimpleElement + { + public override string ToString() => CodeWriter.WriteToString(this); + + public abstract CSFunctionCall Typeof(); + public abstract CSFunctionCall Default(); + public abstract CSFunctionCall Ctor(); + + public static CSType Copy(CSType csType) + { + if (csType is CSSimpleType csSimpleType) + { + return new CSSimpleType(csSimpleType); + } + if (csType is CSGenericReferenceType csGen) + { + return new CSGenericReferenceType(csGen); + } + throw new NotImplementedException($"Type {csType.GetType().Name} needs a copy constructor"); + } + } + + public class CSSimpleType : CSType + { + public CSSimpleType(Type t) + : this(t.Name) + { + } + public CSSimpleType(string name) + : this(name, false) + { + } + + public CSSimpleType(CSSimpleType csSimpleType) + : this(csSimpleType.Name, csSimpleType.IsArray) + { + if (csSimpleType.GenericTypes != null) + { + GenericTypes = new CSType[csSimpleType.GenericTypes.Length]; + for (int i = 0; i < GenericTypes.Length; i++) + { + GenericTypes[i] = CSType.Copy(csSimpleType.GenericTypes[i]); + } + } + } + + public CSSimpleType(string name, bool isArray) + { + ArgumentNullException.ThrowIfNull(name, nameof(name)); + hiddenName = name + (isArray ? "[]" : ""); + } + + public static explicit operator CSSimpleType(string name) + { + return new CSSimpleType(name); + } + + public static CSSimpleType CreateArray(string name) + { + return new CSSimpleType(name, true); + } + + public CSSimpleType(string name, bool isArray, params CSType[] genericSpecialization) + { + ArgumentNullException.ThrowIfNull(name, nameof(name)); + IsGeneric = genericSpecialization != null && genericSpecialization.Length > 0; + GenericTypes = genericSpecialization; + GenericTypeName = name; + IsArray = isArray; + } + + public CSSimpleType(string name, bool isArray, params string[] genericSpecialization) + : this(name, isArray, genericSpecialization.Select(s => new CSSimpleType(s)).ToArray()) + { + } + + string? hiddenName; + public string Name + { + get + { + return hiddenName ?? GenerateName(); + } + } + public bool IsGeneric { get; private set; } + public string? GenericTypeName { get; private set; } + public CSType[]? GenericTypes { get; private set; } + public bool IsArray { get; private set; } + public bool IsPointer { get; private set; } + + string GenerateName() + { + StringBuilder sb = new StringBuilder(); + sb.Append(GenericTypeName); + if (GenericTypes != null && GenericTypes.Length > 0) + { + sb.Append("<"); + int i = 0; + foreach (CSType type in GenericTypes) + { + if (i > 0) + sb.Append(", "); + sb.Append(type.ToString()); + i++; + } + sb.Append(">"); + } + if (IsArray) + sb.Append("[]"); + return sb.ToString(); + } + + protected override void LLWrite(ICodeWriter writer, object? o) + { + writer.Write(Name, false); + } + + public override CSFunctionCall Typeof() + { + return new CSFunctionCall("typeof", false, new CSIdentifier(Name)); + } + + public override CSFunctionCall Default() + { + return new CSFunctionCall("default", false, new CSIdentifier(Name)); + } + + public override CSFunctionCall Ctor() + { + return new CSFunctionCall(Name, true); + } + + static CSSimpleType + tBool = new CSSimpleType("bool"), + tChar = new CSSimpleType("char"), + tSbyte = new CSSimpleType("sbyte"), + tShort = new CSSimpleType("short"), + tInt = new CSSimpleType("int"), + tLong = new CSSimpleType("long"), + tFloat = new CSSimpleType("float"), + tByte = new CSSimpleType("byte"), + tUshort = new CSSimpleType("ushort"), + tUint = new CSSimpleType("uint"), + tUlong = new CSSimpleType("ulong"), + tDouble = new CSSimpleType("double"), + tString = new CSSimpleType("string"), + tObject = new CSSimpleType("object"), + tIntPtr = new CSSimpleType("IntPtr"), + tVoid = new CSSimpleType("void"), + tByteStar = new CSSimpleType("byte").Star, + tType = new CSSimpleType("Type"), + tVar = new CSSimpleType("var"), + tNfloat = new CSSimpleType("nfloat"), + tNint = new CSSimpleType("nint"), + tNUint = new CSSimpleType("nuint") + ; + + public CSSimpleType Star + { + get + { + if (Name.EndsWith("[]")) + { + throw new NotImplementedException("Blindly making an array a pointer doesn't do what you think."); + } + else + { + var ptrType = new CSSimpleType(Name + " *", false); + ptrType.IsPointer = true; + return ptrType; + } + } + } + + public static CSSimpleType Bool { get { return tBool; } } + public static CSSimpleType Char { get { return tChar; } } + public static CSSimpleType SByte { get { return tSbyte; } } + public static CSSimpleType Short { get { return tShort; } } + public static CSSimpleType Int { get { return tInt; } } + public static CSSimpleType Long { get { return tLong; } } + public static CSSimpleType Float { get { return tFloat; } } + public static CSSimpleType Byte { get { return tByte; } } + public static CSSimpleType UShort { get { return tUshort; } } + public static CSSimpleType UInt { get { return tUint; } } + public static CSSimpleType ULong { get { return tUlong; } } + public static CSSimpleType Double { get { return tDouble; } } + public static CSSimpleType String { get { return tString; } } + public static CSSimpleType Object { get { return tObject; } } + public static CSSimpleType IntPtr { get { return tIntPtr; } } + public static CSSimpleType Void { get { return tVoid; } } + public static CSSimpleType ByteStar { get { return tByteStar; } } + public static CSSimpleType Type { get { return tType; } } + public static CSSimpleType Var { get { return tVar; } } + public static CSSimpleType NFloat { get { return tNfloat; } } + public static CSSimpleType NInt => tNint; + public static CSSimpleType NUInt => tNUint; + } + + public class CSGenericReferenceType : CSType + { + public CSGenericReferenceType(int depth, int index) + { + Depth = depth; + Index = index; + InterfaceConstraints = new List(); + } + + public CSGenericReferenceType(CSGenericReferenceType csGeneric) + : this(csGeneric.Depth, csGeneric.Index) + { + foreach (var elem in csGeneric.InterfaceConstraints) + { + InterfaceConstraints.Add(CSType.Copy(elem)); + } + ReferenceNamer = csGeneric.ReferenceNamer; + } + + // this doesn't really belong here, but I'm going to need it. + public List InterfaceConstraints { get; private set; } + + public int Depth { get; private set; } + public int Index { get; private set; } + public Func? ReferenceNamer { get; set; } + + protected override void LLWrite(ICodeWriter writer, object? o) + { + writer.Write(Name, true); + } + + public string Name + { + get + { + Func namer = ReferenceNamer ?? DefaultNamer; + return namer(Depth, Index); + } + } + + const string kNames = "TUVWABCDEFGHIJKLMN"; + + public static string DefaultNamer(int depth, int index) + { + if (depth < 0 || depth >= kNames.Length) + throw new ArgumentOutOfRangeException(nameof(depth)); + if (index < 0) + throw new ArgumentOutOfRangeException(nameof(index)); + return String.Format("{0}{1}", kNames[depth], index); + } + + public override CSFunctionCall Typeof() + { + return new CSFunctionCall("typeof", false, new CSIdentifier(Name)); + } + + public override CSFunctionCall Default() + { + return new CSFunctionCall("default", false, new CSIdentifier(Name)); + } + + public override CSFunctionCall Ctor() + { + return new CSFunctionCall(Name, true); + } + } +} diff --git a/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSUnaryExpression.cs b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSUnaryExpression.cs new file mode 100644 index 000000000000..2acfe41f6852 --- /dev/null +++ b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSUnaryExpression.cs @@ -0,0 +1,116 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; + +namespace SyntaxDynamo.CSLang +{ + public class CSUnaryExpression : CSBaseExpression + { + public CSUnaryExpression(CSUnaryOperator op, ICSExpression expr) + { + ArgumentNullException.ThrowIfNull(expr, nameof(expr)); + Operation = op; + Expr = expr; + } + protected override void LLWrite(ICodeWriter writer, object? o) + { + if (IsPostfix(Operation)) + { + Expr.WriteAll(writer); + writer.Write(OperatorToString(Operation), true); + } + else + { + writer.Write(OperatorToString(Operation), true); + Expr.WriteAll(writer); + } + } + + public CSUnaryOperator Operation { get; private set; } + public ICSExpression Expr { get; private set; } + + static string OperatorToString(CSUnaryOperator op) + { + switch (op) + { + case CSUnaryOperator.At: + return "@"; + case CSUnaryOperator.BitNot: + return "~"; + case CSUnaryOperator.Neg: + return "-"; + case CSUnaryOperator.Not: + return "!"; + case CSUnaryOperator.Out: + return "out "; + case CSUnaryOperator.Pos: + return "+"; + case CSUnaryOperator.Ref: + return "ref "; + case CSUnaryOperator.AddressOf: + return "&"; + case CSUnaryOperator.Indirection: + return "*"; + case CSUnaryOperator.Await: + return "await "; + case CSUnaryOperator.PostBang: + return "!"; + case CSUnaryOperator.Question: + return "?"; + default: + throw new ArgumentOutOfRangeException(nameof(op)); + } + } + + static bool IsPostfix(CSUnaryOperator op) + { + return op == CSUnaryOperator.PostBang || op == CSUnaryOperator.Question; + } + + public static CSUnaryExpression AddressOf(ICSExpression expr) + { + return new CSUnaryExpression(CSUnaryOperator.AddressOf, expr); + } + + public static CSUnaryExpression Star(ICSExpression expr) + { + return new CSUnaryExpression(CSUnaryOperator.Indirection, expr); + } + + public static CSUnaryExpression Out(CSIdentifier id) + { + return new CSUnaryExpression(CSUnaryOperator.Out, id); + } + + public static CSUnaryExpression Out(string id) + { + return Out(new CSIdentifier(id)); + } + + public static CSUnaryExpression Ref(CSIdentifier id) + { + return new CSUnaryExpression(CSUnaryOperator.Ref, id); + } + + public static CSUnaryExpression Ref(string id) + { + return Ref(new CSIdentifier(id)); + } + + public static CSUnaryExpression Await(ICSExpression expr) + { + return new CSUnaryExpression(CSUnaryOperator.Await, expr); + } + + public static CSUnaryExpression PostBang(ICSExpression expr) + { + return new CSUnaryExpression(CSUnaryOperator.PostBang, expr); + } + + public static CSUnaryExpression Question(ICSExpression expr) + { + return new CSUnaryExpression(CSUnaryOperator.Question, expr); + } + } +} diff --git a/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSUsing.cs b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSUsing.cs new file mode 100644 index 000000000000..c4c3b0741012 --- /dev/null +++ b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/CSUsing.cs @@ -0,0 +1,64 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Linq; +using System.Collections.Generic; +using SyntaxDynamo; + +namespace SyntaxDynamo.CSLang +{ + public class CSUsing : SimpleLineElement + { + public CSUsing(string package) + : base(string.Format("using {0};", package), false, false, false) + { + Package = package; + } + + public string Package { get; private set; } + } + + public class CSUsingPackages : CodeElementCollection + { + public CSUsingPackages() : base() { } + public CSUsingPackages(params CSUsing[] use) + : this() + { + AddRange(use); + } + public CSUsingPackages(params string[] use) + : this() + { + AddRange(use.Select(s => new CSUsing(s))); + } + + public CSUsingPackages And(CSUsing use) + { + Add(use); + return this; + } + + public CSUsingPackages And(string package) { return And(new CSUsing(package)); } + + public void AddIfNotPresent(string? package, CSIdentifier? protectedBy = null) + { + if (String.IsNullOrEmpty(package)) + return; + CSUsing target = new CSUsing(package); + if (!this.Exists(use => use.Contents == target.Contents)) + { + if ((object?)protectedBy != null) + { + CSConditionalCompilation.ProtectWithIfEndif(protectedBy, target); + } + Add(target); + } + } + + public void AddIfNotPresent(Type t, CSIdentifier? protectedBy = null) + { + AddIfNotPresent(t.Namespace, protectedBy); + } + } +} diff --git a/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/Enums.cs b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/Enums.cs new file mode 100644 index 000000000000..9a1fb38b2d45 --- /dev/null +++ b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/Enums.cs @@ -0,0 +1,100 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace SyntaxDynamo.CSLang +{ + public enum CSVisibility + { + None = 0, + Public, + Internal, + Protected, + Private, + } + + public enum CSMethodKind + { + None = 0, + Static, + StaticExtern, + StaticNew, + Virtual, + Override, + Extern, + New, + Abstract, + Interface, + Unsafe, + StaticUnsafe, + } + + public enum CSBinaryOperator + { + Add = 0, + Sub, + Mul, + Div, + Mod, + And, + Or, + Less, + Greater, + Equal, + NotEqual, + LessEqual, + GreaterEqual, + BitAnd, + BitOr, + BitXor, + LeftShift, + RightShift, + Dot, + Is, + As, + NullCoalesce, + } + + public enum CSUnaryOperator + { + Neg = 0, + Pos, + At, + Not, + BitNot, + Ref, + Out, + AddressOf, + Indirection, + Await, + PostBang, + Question, + } + + public enum CSAssignmentOperator + { + Assign, + AddAssign, + SubAssign, + MulAssign, + DivAssign, + ModAssign, + AndAssign, + OrAssign, + XorAssign + } + + public enum CSParameterKind + { + None, + Ref, + Out, + Params, + This + } + + public enum CSShortCircuitKind + { + Break, + Continue, + } +} diff --git a/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/ICSExpression.cs b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/ICSExpression.cs new file mode 100644 index 000000000000..ce563ba75dd2 --- /dev/null +++ b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/ICSExpression.cs @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using SyntaxDynamo; + +namespace SyntaxDynamo.CSLang +{ + public interface ICSExpression : ICodeElement + { + } + + public interface ICSExpressionList : ICodeElementSet + { + } +} diff --git a/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/ICSLineable.cs b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/ICSLineable.cs new file mode 100644 index 000000000000..6fad9246fd72 --- /dev/null +++ b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/ICSLineable.cs @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; + +namespace SyntaxDynamo.CSLang +{ + public interface ICSLineable + { + } +} diff --git a/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/ICSStatement.cs b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/ICSStatement.cs new file mode 100644 index 000000000000..1ded5a424803 --- /dev/null +++ b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/ICSStatement.cs @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; + +namespace SyntaxDynamo.CSLang +{ + public interface ICSStatement + { + } +} diff --git a/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/ICSTopLevelDeclaration.cs b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/ICSTopLevelDeclaration.cs new file mode 100644 index 000000000000..1f43e8a95161 --- /dev/null +++ b/src/SyntaxDynamo/src/SyntaxDynamo.CSLang/ICSTopLevelDeclaration.cs @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace SyntaxDynamo.CSLang +{ + public interface ICSTopLevelDeclaration : ICodeElement + { + } + + public class CSTopLevelDeclations : CodeElementCollection + { + public CSTopLevelDeclations(params ICSTopLevelDeclaration[] decls) + : base() + { + ArgumentNullException.ThrowIfNull(decls, nameof(decls)); + AddRange(decls); + } + + } +} diff --git a/src/SyntaxDynamo/src/SyntaxDynamo.csproj b/src/SyntaxDynamo/src/SyntaxDynamo.csproj new file mode 100644 index 000000000000..ca1712d0c0cd --- /dev/null +++ b/src/SyntaxDynamo/src/SyntaxDynamo.csproj @@ -0,0 +1,14 @@ + + + + Library + net8.0 + enable + enable + true + + + + + + diff --git a/src/SyntaxDynamo/src/WriteEventArgs.cs b/src/SyntaxDynamo/src/WriteEventArgs.cs new file mode 100644 index 000000000000..4d1dcc000963 --- /dev/null +++ b/src/SyntaxDynamo/src/WriteEventArgs.cs @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; + +namespace SyntaxDynamo +{ + public class WriteEventArgs : EventArgs + { + public WriteEventArgs(ICodeWriter writer) + { + if (writer == null) + throw new ArgumentNullException(nameof(writer)); + Writer = writer; + } + + public ICodeWriter Writer { get; private set; } + } +} diff --git a/src/SyntaxDynamo/tests/SimpleClassTests.cs b/src/SyntaxDynamo/tests/SimpleClassTests.cs new file mode 100644 index 000000000000..6ae1e6690cfb --- /dev/null +++ b/src/SyntaxDynamo/tests/SimpleClassTests.cs @@ -0,0 +1,303 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using Xunit; +using SyntaxDynamo.CSLang; +using System.Reflection; +using TestingUtils; + +namespace SyntaxDynamo.Tests; + +public class SimpleClassTests +{ + public delegate CSClass ClassMutator(CSClass cl); + public delegate CSUsingPackages UsingMutator(CSUsingPackages pkg); + + public static Stream BasicClass(string nameSpace, string className, CSMethod? m, ClassMutator? mutator, UsingMutator? useMutator = null) + { + CSUsingPackages use = new CSUsingPackages("System"); + if(useMutator != null) + use = useMutator(use); + + CSClass cl = new CSClass(CSVisibility.Public, className, m != null ? new CSMethod [] { m } : null); + if(mutator != null) + cl = mutator(cl); + + CSNamespace ns = new CSNamespace(nameSpace); + ns.Block.Add(cl); + + CSFile file = CSFile.Create(use, ns); + return CodeWriter.WriteToStream(file); + } + + [Fact] + public void EmptyClass() + { + using(Stream stm = BasicClass("None", "AClass", null, null)) { + TestHelpers.CompileAndExecute(stm, string.Empty, string.Empty); + } + } + + void DeclExposure(CSVisibility vis) + { + using(Stream stm = BasicClass("None", "AClass", null, cl => { + cl.Fields.Add(CSFieldDeclaration.FieldLine(CSSimpleType.Byte, "b", null, CSVisibility.Public)); + return cl; + })) { + TestHelpers.CompileAndExecute(stm, string.Empty, string.Empty); + } + } + + [Fact] + public void ClassWithSingleDeclAllExposures() + { + foreach(var vis in Enum.GetValues(typeof(CSVisibility))) { + DeclExposure((CSVisibility)vis); + } + } + + void DeclInitExposure(CSVisibility vis) + { + using(Stream stm = BasicClass("None", "AClass", null, cl => { + cl.Fields.Add(CSFieldDeclaration.FieldLine(CSSimpleType.Byte, "b", CSConstant.Val((byte)0), CSVisibility.Public)); + return cl; + })) { + TestHelpers.CompileAndExecute(stm, string.Empty, string.Empty); + } + } + + [Fact] + public void ClassWithSingleDeclInitAllExposures() + { + foreach(var vis in Enum.GetValues(typeof(CSVisibility))) { + DeclInitExposure((CSVisibility)vis); + } + } + + void DeclType(CSType type) + { + if(type == CSSimpleType.Void) + return; + + using(Stream stm = BasicClass("None", "AClass", null, cl => { + cl.Fields.Add(CSFieldDeclaration.FieldLine(type, "b", null, CSVisibility.Public)); + return cl; + })) { + TestHelpers.CompileAndExecute(stm, string.Empty, string.Empty); + } + } + + [Fact] + public void ClassWithSingleDeclAllTypes() + { + foreach(MethodInfo mi in typeof(CSType).GetMethods().Where(mii => mii.IsStatic && mii.IsPublic && + mii.ReturnType == typeof(CSType) && mii.Name != "Copy")) { + CSType? cs = mi.Invoke(null, null) as CSType; + if(cs != null) + DeclType(cs); + } + } + + [Fact] + public void ConstructorTest() + { + using(Stream stm = BasicClass("None", "AClass", null, cl => { + cl.Constructors.Add(CSMethod.PublicConstructor("AClass", new CSParameterList(), new CSCodeBlock())); + return cl; + })) { + TestHelpers.CompileAndExecute(stm, string.Empty, string.Empty); + } + } + + [Fact] + public void ConstructorTestParam() + { + using(Stream stm = BasicClass("None", "AClass", null, cl => { + CSParameterList pl = new CSParameterList().And(new CSParameter(CSSimpleType.Int, new CSIdentifier("x"))); + cl.Constructors.Add(CSMethod.PublicConstructor("AClass", pl, new CSCodeBlock())); + return cl; + })) { + TestHelpers.CompileAndExecute(stm, string.Empty, string.Empty); + } + } + + [Fact] + public void ConstructorTestParamList() + { + using(Stream stm = BasicClass("None", "AClass", null, cl => { + CSParameterList pl = new CSParameterList().And(new CSParameter(CSSimpleType.Int, "x")) + .And(new CSParameter(CSSimpleType.Int, "y")); + cl.Constructors.Add(CSMethod.PublicConstructor("AClass", pl, new CSCodeBlock())); + return cl; + })) { + TestHelpers.CompileAndExecute(stm, string.Empty, string.Empty); + } + } + + [Fact] + public void MethodNoParams() + { + using(Stream stm = BasicClass("None", "AClass", null, cl => { + CSParameterList pl = new CSParameterList(); + CSCodeBlock b = new CSCodeBlock().And(CSReturn.ReturnLine(CSConstant.Val(0))); + cl.Methods.Add(CSMethod.PublicMethod(CSSimpleType.Int, "Foo", pl, b)); + return cl; + })) { + TestHelpers.CompileAndExecute(stm, string.Empty, string.Empty); + } + } + + [Fact] + public void MethodParam() + { + using(Stream stm = BasicClass("None", "AClass", null, cl => { + CSParameterList pl = new CSParameterList().And(new CSParameter(CSSimpleType.Int, "x")); + CSCodeBlock b = new CSCodeBlock().And(CSReturn.ReturnLine(CSConstant.Val(0))); + cl.Methods.Add(CSMethod.PublicMethod(CSSimpleType.Int, "Foo", pl, b)); + return cl; + })) { + TestHelpers.CompileAndExecute(stm, string.Empty, string.Empty); + } + } + + [Fact] + public void MethodParamList() + { + using(Stream stm = BasicClass("None", "AClass", null, cl => { + CSParameterList pl = new CSParameterList().And(new CSParameter(CSSimpleType.Int, "x")) + .And(new CSParameter(CSSimpleType.Int, "y")); + CSCodeBlock b = new CSCodeBlock().And(CSReturn.ReturnLine(CSConstant.Val(0))); + cl.Methods.Add(CSMethod.PublicMethod(CSSimpleType.Int, "Foo", pl, b)); + return cl; + })) { + TestHelpers.CompileAndExecute(stm, string.Empty, string.Empty); + } + } + + [Fact] + public void VirtualMethodNoParams() + { + using(Stream stm = BasicClass("None", "AClass", null, cl => { + CSParameterList pl = new CSParameterList(); + CSCodeBlock b = new CSCodeBlock().And(CSReturn.ReturnLine(CSConstant.Val(0))); + cl.Methods.Add(CSMethod.PublicMethod(CSMethodKind.Virtual, CSSimpleType.Int, "Foo", pl, b)); + return cl; + })) { + TestHelpers.CompileAndExecute(stm, string.Empty, string.Empty); + } + } + + [Fact] + public void VirtualMethodParam() + { + using(Stream stm = BasicClass("None", "AClass", null, cl => { + CSParameterList pl = new CSParameterList().And(new CSParameter(CSSimpleType.Int, "x")); + CSCodeBlock b = new CSCodeBlock().And(CSReturn.ReturnLine(CSConstant.Val(0))); + cl.Methods.Add(CSMethod.PublicMethod(CSMethodKind.Virtual, CSSimpleType.Int, "Foo", pl, b)); + return cl; + })) { + TestHelpers.CompileAndExecute(stm, string.Empty, string.Empty); + } + } + + [Fact] + public void VirtualMethodParamList() + { + using(Stream stm = BasicClass("None", "AClass", null, cl => { + CSParameterList pl = new CSParameterList().And(new CSParameter(CSSimpleType.Int, "x")) + .And(new CSParameter(CSSimpleType.Int, "y")); + CSCodeBlock b = new CSCodeBlock().And(CSReturn.ReturnLine(CSConstant.Val(0))); + cl.Methods.Add(CSMethod.PublicMethod(CSMethodKind.Virtual, CSSimpleType.Int, "Foo", pl, b)); + return cl; + })) { + TestHelpers.CompileAndExecute(stm, string.Empty, string.Empty); + } + } + + [Fact] + public void StaticMethodNoParams() + { + using(Stream stm = BasicClass("None", "AClass", null, cl => { + CSParameterList pl = new CSParameterList(); + CSCodeBlock b = new CSCodeBlock().And(CSReturn.ReturnLine(CSConstant.Val(0))); + cl.Methods.Add(CSMethod.PublicMethod(CSMethodKind.Virtual, CSSimpleType.Int, "Foo", pl, b)); + return cl; + })) { + TestHelpers.CompileAndExecute(stm, string.Empty, string.Empty); + } + } + + [Fact] + public void StaticMethodParam() + { + using(Stream stm = BasicClass("None", "AClass", null, cl => { + CSParameterList pl = new CSParameterList().And(new CSParameter(CSSimpleType.Int, "x")); + CSCodeBlock b = new CSCodeBlock().And(CSReturn.ReturnLine(CSConstant.Val(0))); + cl.Methods.Add(CSMethod.PublicMethod(CSMethodKind.Virtual, CSSimpleType.Int, "Foo", pl, b)); + return cl; + })) { + TestHelpers.CompileAndExecute(stm, string.Empty, string.Empty); + } + } + + [Fact] + public void StaticMethodParamList() + { + using(Stream stm = BasicClass("None", "AClass", null, cl => { + CSParameterList pl = new CSParameterList().And(new CSParameter(CSSimpleType.Int, "x")) + .And(new CSParameter(CSSimpleType.Int, "y")); + CSCodeBlock b = new CSCodeBlock().And(CSReturn.ReturnLine(CSConstant.Val(0))); + cl.Methods.Add(CSMethod.PublicMethod(CSMethodKind.Virtual, CSSimpleType.Int, "Foo", pl, b)); + return cl; + })) { + TestHelpers.CompileAndExecute(stm, string.Empty, string.Empty); + } + } + + [Fact] + public void PublicGetPrivateSetProp() + { + using(Stream stm = BasicClass("None", "AClass", null, cl => { + cl.Properties.Add(CSProperty.PublicGetPrivateSet(CSSimpleType.Int, "Foo")); + return cl; + })) { + TestHelpers.CompileAndExecute(stm, string.Empty, string.Empty); + } + } + + [Fact] + public void PublicGetSetProp() + { + using(Stream stm = BasicClass("None", "AClass", null, cl => { + cl.Properties.Add(CSProperty.PublicGetSet(CSSimpleType.Int, "Foo")); + return cl; + })) { + TestHelpers.CompileAndExecute(stm, string.Empty, string.Empty); + } + } + + [Fact] + public void PublicGetSetBacking() + { + using(Stream stm = BasicClass("None", "AClass", null, cl => { + cl.Properties.Add(CSProperty.PublicGetSetBacking(CSSimpleType.Int, "Foo", true, "_bar")); + return cl; + })) { + TestHelpers.CompileAndExecute(stm, string.Empty, string.Empty); + } + } + + [Fact] + public void Pinvoke() + { + using(Stream stm = BasicClass("None", "AClass", null, cl => { + cl.Methods.Add(CSMethod.PInvoke(CSVisibility.Public, + CSSimpleType.IntPtr, "Walter", "__Internal", "_walter", new CSParameterList())); + return cl; + }, use => { + return use.And(new CSUsing("System.Runtime.InteropServices")); + })) { + TestHelpers.CompileAndExecute(stm, string.Empty, string.Empty); + } + } +} diff --git a/src/SyntaxDynamo/tests/SyntaxDynamo.Tests.csproj b/src/SyntaxDynamo/tests/SyntaxDynamo.Tests.csproj new file mode 100644 index 000000000000..367b314e4c99 --- /dev/null +++ b/src/SyntaxDynamo/tests/SyntaxDynamo.Tests.csproj @@ -0,0 +1,15 @@ + + + + net8.0 + enable + enable + false + true + + + + + + + diff --git a/src/TestingUtils/src/TestingUtils.cs b/src/TestingUtils/src/TestingUtils.cs new file mode 100644 index 000000000000..8425ee899bb7 --- /dev/null +++ b/src/TestingUtils/src/TestingUtils.cs @@ -0,0 +1,86 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.Loader; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Emit; + +namespace TestingUtils +{ + public static class TestHelpers + { + private static int uniqueId = 0; + + public static object? CompileAndExecute(Stream stream, string typeName, string methodName) + { + using (StreamReader reader = new StreamReader(stream)) + { + string fileSourceCode = reader.ReadToEnd(); + var sourceCodes = new[] { fileSourceCode }; + return CompileAndExecute(sourceCodes, typeName, methodName); + } + } + + public static object? CompileAndExecute(string filePath, string sourceCode, string typeName, string methodName) + { + string fileSourceCode = File.ReadAllText(filePath); + var sourceCodes = new[] { fileSourceCode, sourceCode }; + return CompileAndExecute(sourceCodes, typeName, methodName); + } + + private static object? CompileAndExecute(string[] sourceCodes, string typeName, string methodName) + { + OutputKind outputKind = OutputKind.ConsoleApplication; + if (typeName == string.Empty || methodName == string.Empty) + { + outputKind = OutputKind.DynamicallyLinkedLibrary; + } + var options = new CSharpCompilationOptions(outputKind); + var syntaxTrees = sourceCodes.Select(code => CSharpSyntaxTree.ParseText(code)).ToArray(); + var systemRuntimeAssemblyPath = Assembly.Load("System.Runtime").Location; + + var references = new[] + { + MetadataReference.CreateFromFile(typeof(object).Assembly.Location), + MetadataReference.CreateFromFile(typeof(Console).Assembly.Location), + MetadataReference.CreateFromFile(systemRuntimeAssemblyPath), + }; + + var compilation = CSharpCompilation.Create($"CompiledAssembly{uniqueId}", + syntaxTrees: syntaxTrees, + references: references, + options: options); + + string assemblyPath = Path.Combine(Path.GetTempPath(), $"CompiledAssembly{uniqueId++}.dll"); + using (var stream = new FileStream(assemblyPath, FileMode.Create)) + { + EmitResult emitResult = compilation.Emit(stream); + + if (!emitResult.Success) + { + string errorMessage = "Compilation failed:"; + foreach (var diagnostic in emitResult.Diagnostics) + { + errorMessage += $"\n{diagnostic}"; + } + throw new InvalidOperationException(errorMessage); + } + } + + if (outputKind == OutputKind.ConsoleApplication) + { + Assembly compiledAssembly = Assembly.LoadFile(assemblyPath); + Type? targetType = compiledAssembly?.GetType(typeName); + MethodInfo? customMethod = targetType?.GetMethod(methodName); + return customMethod?.Invoke(null, new object[] { }); + } + + return null; + } + } +} diff --git a/src/SwiftBindings/src/SwiftBindings.csproj b/src/TestingUtils/src/TestingUtils.csproj similarity index 52% rename from src/SwiftBindings/src/SwiftBindings.csproj rename to src/TestingUtils/src/TestingUtils.csproj index 91b464afeacc..96578232595d 100644 --- a/src/SwiftBindings/src/SwiftBindings.csproj +++ b/src/TestingUtils/src/TestingUtils.csproj @@ -1,10 +1,14 @@ - Exe + Library net8.0 enable enable + true + + +