├── .gitignore
├── EnchiladaAlgorithm.png
├── FunctionFlow.Strong.CodeGen
├── FunctionFlow.Strong.CodeGen.csproj
└── Program.cs
├── FunctionFlow.Strong
├── FunctionFlow.Strong.csproj
├── Generated-StrongFlowExtensions.cs
├── Generated-StrongFlows.cs
├── IHold.cs
└── StrongFlow.cs
├── FunctionFlow.Test
├── FlowBuilderTests.cs
└── FunctionFlow.Test.csproj
├── FunctionFlow.sln
├── FunctionFlow
├── ExpressionExtensions.cs
├── ExpressionInfo.cs
├── FlowBuilder.cs
├── FlowBuilderExtensions.cs
├── FunctionFlow.csproj
├── IFlowBuilder.cs
├── TypeExtensions.cs
└── WorkItem.cs
├── ReadMe.md
├── icon.png
└── icon.svg
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.suo
8 | *.user
9 | *.userosscache
10 | *.sln.docstates
11 |
12 | # User-specific files (MonoDevelop/Xamarin Studio)
13 | *.userprefs
14 |
15 | # Build results
16 | [Dd]ebug/
17 | [Dd]ebugPublic/
18 | [Rr]elease/
19 | [Rr]eleases/
20 | x64/
21 | x86/
22 | bld/
23 | [Bb]in/
24 | [Oo]bj/
25 | [Ll]og/
26 |
27 | # Visual Studio 2015/2017 cache/options directory
28 | .vs/
29 | # Uncomment if you have tasks that create the project's static files in wwwroot
30 | #wwwroot/
31 |
32 | # Visual Studio 2017 auto generated files
33 | Generated\ Files/
34 |
35 | # MSTest test Results
36 | [Tt]est[Rr]esult*/
37 | [Bb]uild[Ll]og.*
38 |
39 | # NUNIT
40 | *.VisualState.xml
41 | TestResult.xml
42 |
43 | # Build Results of an ATL Project
44 | [Dd]ebugPS/
45 | [Rr]eleasePS/
46 | dlldata.c
47 |
48 | # Benchmark Results
49 | BenchmarkDotNet.Artifacts/
50 |
51 | # .NET Core
52 | project.lock.json
53 | project.fragment.lock.json
54 | artifacts/
55 |
56 | # StyleCop
57 | StyleCopReport.xml
58 |
59 | # Files built by Visual Studio
60 | *_i.c
61 | *_p.c
62 | *_i.h
63 | *.ilk
64 | *.meta
65 | *.obj
66 | *.iobj
67 | *.pch
68 | *.pdb
69 | *.ipdb
70 | *.pgc
71 | *.pgd
72 | *.rsp
73 | *.sbr
74 | *.tlb
75 | *.tli
76 | *.tlh
77 | *.tmp
78 | *.tmp_proj
79 | *.log
80 | *.vspscc
81 | *.vssscc
82 | .builds
83 | *.pidb
84 | *.svclog
85 | *.scc
86 |
87 | # Chutzpah Test files
88 | _Chutzpah*
89 |
90 | # Visual C++ cache files
91 | ipch/
92 | *.aps
93 | *.ncb
94 | *.opendb
95 | *.opensdf
96 | *.sdf
97 | *.cachefile
98 | *.VC.db
99 | *.VC.VC.opendb
100 |
101 | # Visual Studio profiler
102 | *.psess
103 | *.vsp
104 | *.vspx
105 | *.sap
106 |
107 | # Visual Studio Trace Files
108 | *.e2e
109 |
110 | # TFS 2012 Local Workspace
111 | $tf/
112 |
113 | # Guidance Automation Toolkit
114 | *.gpState
115 |
116 | # ReSharper is a .NET coding add-in
117 | _ReSharper*/
118 | *.[Rr]e[Ss]harper
119 | *.DotSettings.user
120 |
121 | # JustCode is a .NET coding add-in
122 | .JustCode
123 |
124 | # TeamCity is a build add-in
125 | _TeamCity*
126 |
127 | # DotCover is a Code Coverage Tool
128 | *.dotCover
129 |
130 | # AxoCover is a Code Coverage Tool
131 | .axoCover/*
132 | !.axoCover/settings.json
133 |
134 | # Visual Studio code coverage results
135 | *.coverage
136 | *.coveragexml
137 |
138 | # NCrunch
139 | _NCrunch_*
140 | .*crunch*.local.xml
141 | nCrunchTemp_*
142 |
143 | # MightyMoose
144 | *.mm.*
145 | AutoTest.Net/
146 |
147 | # Web workbench (sass)
148 | .sass-cache/
149 |
150 | # Installshield output folder
151 | [Ee]xpress/
152 |
153 | # DocProject is a documentation generator add-in
154 | DocProject/buildhelp/
155 | DocProject/Help/*.HxT
156 | DocProject/Help/*.HxC
157 | DocProject/Help/*.hhc
158 | DocProject/Help/*.hhk
159 | DocProject/Help/*.hhp
160 | DocProject/Help/Html2
161 | DocProject/Help/html
162 |
163 | # Click-Once directory
164 | publish/
165 |
166 | # Publish Web Output
167 | *.[Pp]ublish.xml
168 | *.azurePubxml
169 | # Note: Comment the next line if you want to checkin your web deploy settings,
170 | # but database connection strings (with potential passwords) will be unencrypted
171 | *.pubxml
172 | *.publishproj
173 |
174 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
175 | # checkin your Azure Web App publish settings, but sensitive information contained
176 | # in these scripts will be unencrypted
177 | PublishScripts/
178 |
179 | # NuGet Packages
180 | *.nupkg
181 | # The packages folder can be ignored because of Package Restore
182 | **/[Pp]ackages/*
183 | # except build/, which is used as an MSBuild target.
184 | !**/[Pp]ackages/build/
185 | # Uncomment if necessary however generally it will be regenerated when needed
186 | #!**/[Pp]ackages/repositories.config
187 | # NuGet v3's project.json files produces more ignorable files
188 | *.nuget.props
189 | *.nuget.targets
190 |
191 | # Microsoft Azure Build Output
192 | csx/
193 | *.build.csdef
194 |
195 | # Microsoft Azure Emulator
196 | ecf/
197 | rcf/
198 |
199 | # Windows Store app package directories and files
200 | AppPackages/
201 | BundleArtifacts/
202 | Package.StoreAssociation.xml
203 | _pkginfo.txt
204 | *.appx
205 |
206 | # Visual Studio cache files
207 | # files ending in .cache can be ignored
208 | *.[Cc]ache
209 | # but keep track of directories ending in .cache
210 | !*.[Cc]ache/
211 |
212 | # Others
213 | ClientBin/
214 | ~$*
215 | *~
216 | *.dbmdl
217 | *.dbproj.schemaview
218 | *.jfm
219 | *.pfx
220 | *.publishsettings
221 | orleans.codegen.cs
222 |
223 | # Including strong name files can present a security risk
224 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
225 | #*.snk
226 |
227 | # Since there are multiple workflows, uncomment next line to ignore bower_components
228 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
229 | #bower_components/
230 |
231 | # RIA/Silverlight projects
232 | Generated_Code/
233 |
234 | # Backup & report files from converting an old project file
235 | # to a newer Visual Studio version. Backup files are not needed,
236 | # because we have git ;-)
237 | _UpgradeReport_Files/
238 | Backup*/
239 | UpgradeLog*.XML
240 | UpgradeLog*.htm
241 | ServiceFabricBackup/
242 | *.rptproj.bak
243 |
244 | # SQL Server files
245 | *.mdf
246 | *.ldf
247 | *.ndf
248 |
249 | # Business Intelligence projects
250 | *.rdl.data
251 | *.bim.layout
252 | *.bim_*.settings
253 | *.rptproj.rsuser
254 |
255 | # Microsoft Fakes
256 | FakesAssemblies/
257 |
258 | # GhostDoc plugin setting file
259 | *.GhostDoc.xml
260 |
261 | # Node.js Tools for Visual Studio
262 | .ntvs_analysis.dat
263 | node_modules/
264 |
265 | # Visual Studio 6 build log
266 | *.plg
267 |
268 | # Visual Studio 6 workspace options file
269 | *.opt
270 |
271 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
272 | *.vbw
273 |
274 | # Visual Studio LightSwitch build output
275 | **/*.HTMLClient/GeneratedArtifacts
276 | **/*.DesktopClient/GeneratedArtifacts
277 | **/*.DesktopClient/ModelManifest.xml
278 | **/*.Server/GeneratedArtifacts
279 | **/*.Server/ModelManifest.xml
280 | _Pvt_Extensions
281 |
282 | # Paket dependency manager
283 | .paket/paket.exe
284 | paket-files/
285 |
286 | # FAKE - F# Make
287 | .fake/
288 |
289 | # JetBrains Rider
290 | .idea/
291 | *.sln.iml
292 |
293 | # CodeRush
294 | .cr/
295 |
296 | # Python Tools for Visual Studio (PTVS)
297 | __pycache__/
298 | *.pyc
299 |
300 | # Cake - Uncomment if you are using it
301 | # tools/**
302 | # !tools/packages.config
303 |
304 | # Tabs Studio
305 | *.tss
306 |
307 | # Telerik's JustMock configuration file
308 | *.jmconfig
309 |
310 | # BizTalk build output
311 | *.btp.cs
312 | *.btm.cs
313 | *.odx.cs
314 | *.xsd.cs
315 |
316 | # OpenCover UI analysis results
317 | OpenCover/
318 |
319 | # Azure Stream Analytics local run output
320 | ASALocalRun/
321 |
322 | # MSBuild Binary and Structured Log
323 | *.binlog
324 |
325 | # NVidia Nsight GPU debugger configuration file
326 | *.nvuser
327 |
328 | # MFractors (Xamarin productivity tool) working folder
329 | .mfractor/
330 |
--------------------------------------------------------------------------------
/EnchiladaAlgorithm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Prototypist1/FunctionFlow/b85de0e571a2e92c5b4773f4fb5da6b6c09f8abf/EnchiladaAlgorithm.png
--------------------------------------------------------------------------------
/FunctionFlow.Strong.CodeGen/FunctionFlow.Strong.CodeGen.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | netcoreapp3.1
6 | Prototypist.FunctionFlow.Strong.CodeGen
7 | FunctionFlow.Strong.CodeGen
8 |
9 |
10 |
11 | 7.3
12 |
13 |
14 |
15 | 7.3
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/FunctionFlow.Strong.CodeGen/Program.cs:
--------------------------------------------------------------------------------
1 | using Prototypist.FunctionFlow.Strong;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace Prototypist.FunctionFlow.Strong.CodeGen
10 | {
11 |
12 | class Program
13 | {
14 | private const string _FunctionFlow = nameof(Prototypist.FunctionFlow);
15 | private const string _FlowBuilderProp = nameof(Prototypist.FunctionFlow.FlowBuilder);
16 | private const string _StrongFlow = nameof(Prototypist.FunctionFlow.Strong.StrongFlow);
17 | private const string _StrongFlowBase = nameof(Prototypist.FunctionFlow.Strong.StrongFlowBase);
18 | private const string _FlowBuilderType = nameof(Prototypist.FunctionFlow.FlowBuilder);
19 | private const string _Set = nameof(Prototypist.FunctionFlow.FlowBuilder.SetConstant);
20 | private const string _AddStep = nameof(Prototypist.FunctionFlow.FlowBuilder.AddStep);
21 | private const string _AddStepPacked = nameof(Prototypist.FunctionFlow.FlowBuilder.AddStepPacked);
22 | private const string _IHold = nameof(Prototypist.FunctionFlow.Strong.IHold);
23 | private const string _IStrongFlow = nameof(Prototypist.FunctionFlow.Strong.IStrongFlow);
24 | private static readonly string _IHack = Name(typeof(Prototypist.FunctionFlow.Strong.IHack<,>));
25 | private const int MaxOutputSize = 2;
26 | private const int MaxClassSize = 12;
27 |
28 | public class Frog {
29 | public int FlysEaten;
30 |
31 | public Frog(int flysEaten)
32 | {
33 | FlysEaten = flysEaten;
34 | }
35 | }
36 | public class Cat {
37 | public Frog Frog;
38 |
39 | public Cat(Frog frog)
40 | {
41 | Frog = frog ?? throw new ArgumentNullException(nameof(frog));
42 | }
43 | }
44 | public class Pig { }
45 |
46 |
47 | static void Main(string[] args)
48 | {
49 | WriteHolders();
50 | WriteExtensions();
51 | }
52 |
53 | private static void WriteExtensions()
54 | {
55 | var extensions = "";
56 |
57 | extensions += Environment.NewLine;
58 | extensions += $"#region {nameof(Add)}" + Environment.NewLine;
59 |
60 | for (int classSize = 0; classSize < MaxClassSize; classSize++)
61 | {
62 | for (int inputSize = 0; inputSize <= classSize; inputSize++)
63 | {
64 | extensions += Add(classSize, inputSize);
65 | }
66 | }
67 |
68 | extensions += Environment.NewLine;
69 | extensions += "#endregion" + Environment.NewLine;
70 | extensions += Environment.NewLine;
71 | extensions += $"#region {nameof(Update)}" + Environment.NewLine;
72 |
73 | for (int classSize = 1; classSize <= MaxClassSize; classSize++)
74 | {
75 | for (int inputSize = 0; inputSize <= classSize; inputSize++)
76 | {
77 | extensions += Update(classSize, inputSize);
78 | }
79 | }
80 |
81 | extensions += Environment.NewLine;
82 | extensions += "#endregion" + Environment.NewLine;
83 | extensions += Environment.NewLine;
84 | extensions += $"#region {nameof(PackedAdd)}" + Environment.NewLine;
85 |
86 | for (int classSize = 0; classSize < MaxClassSize; classSize++)
87 | {
88 | for (int inputSize = 0; inputSize <= classSize; inputSize++)
89 | {
90 | for (int outputSize = 2; outputSize + classSize <= MaxClassSize && outputSize <= MaxOutputSize; outputSize++)
91 | {
92 | extensions += PackedAdd(classSize, inputSize, outputSize);
93 | }
94 | }
95 | }
96 |
97 | extensions += Environment.NewLine;
98 | extensions += "#endregion" + Environment.NewLine;
99 | extensions += Environment.NewLine;
100 | extensions += $"#region {nameof(PackedUpdate)}" + Environment.NewLine;
101 |
102 | for (int classSize = 2; classSize <= MaxClassSize; classSize++)
103 | {
104 | for (int inputSize = 0; inputSize <= classSize; inputSize++)
105 | {
106 | for (int outputSize = 2; classSize <= MaxClassSize && outputSize <= MaxOutputSize; outputSize++)
107 | {
108 | extensions += PackedUpdate(classSize, inputSize, outputSize);
109 | }
110 | }
111 | }
112 |
113 | extensions += "#endregion" + Environment.NewLine;
114 |
115 | var extensionsText = WrapNameSpace(WrapExtensions(extensions));
116 | File.WriteAllText($"../../../../{_FunctionFlow}.Strong/Generated-{_StrongFlow}Extensions.cs", extensionsText);
117 | }
118 |
119 | private static void WriteHolders()
120 | {
121 | var holders = "";
122 |
123 | holders += Environment.NewLine;
124 | holders += $"#region {nameof(BaseClass)}" + Environment.NewLine;
125 |
126 | for (int classSize = 1; classSize <= MaxClassSize; classSize++)
127 | {
128 | holders += BaseClass(classSize);
129 | }
130 |
131 | holders += Environment.NewLine;
132 | holders += "#endregion" + Environment.NewLine;
133 | holders += Environment.NewLine;
134 | holders += $"#region {nameof(Class)}" + Environment.NewLine;
135 |
136 | for (int classSize = 1; classSize <= MaxClassSize; classSize++)
137 | {
138 | holders += Class(classSize);
139 | }
140 |
141 | holders += Environment.NewLine;
142 | holders += "#endregion" + Environment.NewLine;
143 | holders += Environment.NewLine;
144 | holders += $"#region {nameof(Interface)}" + Environment.NewLine;
145 |
146 | for (int classSize = 1; classSize <= MaxClassSize; classSize++)
147 | {
148 | holders += Interface(classSize);
149 | }
150 |
151 | holders += "#endregion" + Environment.NewLine;
152 |
153 | var holdersText = WrapNameSpace(holders);
154 | File.WriteAllText($"../../../../{_FunctionFlow}.Strong/Generated-{_StrongFlow}s.cs", holdersText);
155 | }
156 |
157 | private static string WrapExtensions(string inner)
158 | {
159 | return $@"
160 | public static class {_StrongFlow}Extensions
161 | {{
162 | {inner}
163 | }}";
164 | }
165 |
166 | private static string WrapNameSpace(string inner)
167 | {
168 | return $@"
169 | using System;
170 | using Prototypist.{_FunctionFlow};
171 |
172 | namespace Prototypist.{_FunctionFlow}.Strong
173 | {{
174 | {inner}
175 | }}";
176 | }
177 |
178 | static string Interface(int n) {
179 | return $@"
180 | public interface {_IStrongFlow}{(n > 0 ? "<" : "")}{GenericTs(n)}{(n > 0 ? ">" : "")} : {_IHack}<{_StrongFlow}{(n > 0 ? "<" : "")}{GenericTs(n)}{(n > 0 ? ">" : "")}, {_StrongFlow}{(n > 0 ? "<" : "")}{GenericTs(n)}{(n > 0 ? ">" : "")}>
181 | {{
182 | }}
183 | ";
184 | }
185 |
186 | static string BaseClass(int n)
187 | {
188 | var typeName = $"{_StrongFlowBase}<{GenericTs(n)}>";
189 |
190 | return $@"
191 | public class {typeName} : {_StrongFlowBase}{(n - 1 > 0 ? "<" : "")}{GenericTs(n - 1)}{(n - 1 > 0 ? ">" : "")}, {_IHold}
192 | {{
193 | public {_StrongFlowBase}({_StrongFlowBase} backing) : base(backing)
194 | {{
195 | }}
196 |
197 | protected {_StrongFlowBase}({_FlowBuilderType} flowBuilder) : base(flowBuilder)
198 | {{
199 | }}
200 | }}
201 | ";
202 | }
203 |
204 | static string Class(int n) {
205 | var baseName = $"{_StrongFlowBase}{(n > 0 ? "<" : "")}{GenericTs(n)}{(n > 0 ? ">" : "")}";
206 | var typeName = $"{_StrongFlow}{(n > 0 ? "<" : "")}{GenericTs(n)}{(n > 0 ? ">" : "")}";
207 |
208 | return $@"
209 | public class {typeName} : {baseName}, {_IStrongFlow}<{GenericTs(n)}>
210 | {{
211 | public {_StrongFlow}({_StrongFlowBase} backing): base(backing) {{
212 | }}
213 | public {_StrongFlow}({Parameters(n)}): base(new {_FlowBuilderType}()) {{
214 | {Set(n)}
215 | }}
216 | }}
217 | ";
218 |
219 | }
220 |
221 |
222 | public static string Add(int n, int funcIn) {
223 | return $@"
224 | public static {_IStrongFlow}<{GenericTs(n)}{(n == 0 ? "" : ", ")}TOut> {nameof(Add)}(this {_IHack}<{_StrongFlow}{(n != 0 ? "<" : "")}{GenericTs(n)}{(n != 0 ? ">" : "")}, T{_StrongFlow}> self, Func<{GenericTIns(funcIn) + (funcIn == 0? "":", ") }TOut> func)
225 | where T{_StrongFlow} : {_StrongFlow}{(n != 0 ? "<":"")}{GenericTs(n)}{(n != 0 ? ">" : "")} {Holds(funcIn)}
226 | {{
227 | self.{_AddStep}(func);
228 | return new {_StrongFlow}<{GenericTs(n)}{(n == 0 ? "" : ", ")}TOut>(({_StrongFlowBase})self);
229 | }}
230 | ";
231 | }
232 |
233 | public static string Update(int n, int funcIn)
234 | {
235 | return $@"
236 | public static {_IStrongFlow}<{GenericTs(n)}> {nameof(Update)}(this {_IHack}<{_StrongFlow}{(n != 0 ? "<" : "")}{GenericTs(n)}{(n != 0 ? ">" : "")}, T{_StrongFlow}> self, Func<{GenericTIns(funcIn) + (funcIn == 0 ? "" : ", ") }TOut> func)
237 | where T{_StrongFlow} : {_StrongFlow}{(n != 0 ? "<" : "")}{GenericTs(n)}{(n != 0 ? ">" : "")} {Holds(funcIn)}, {_IHold}
238 | {{
239 | self.{_AddStep}(func);
240 | return new {_StrongFlow}<{GenericTs(n)}>(({_StrongFlowBase})self);
241 | }}
242 | ";
243 | }
244 |
245 | public static string PackedAdd(int n, int funcIn, int funcOut)
246 | {
247 | return $@"
248 | public static {_IStrongFlow}<{GenericTs(n)}{(n == 0 ? "" : ", ")}{GenericTOuts(funcOut)}> {nameof(PackedAdd)}(this {_IHack}<{_StrongFlow}{(n != 0 ? "<" : "")}{GenericTs(n)}{(n != 0 ? ">" : "")}, T{_StrongFlow}> self, Func<{GenericTIns(funcIn)}{(funcIn == 0 ? "" : ", ")}({GenericTOuts(funcOut)})> func)
249 | where T{_StrongFlow} : {_StrongFlow}{(n != 0 ? "<" : "")}{GenericTs(n)}{(n != 0 ? ">" : "")} {Holds(funcIn)}
250 | {{
251 | self.{_AddStepPacked}<{GenericTOuts(funcOut)}>(func);
252 | return new {_StrongFlow}<{GenericTs(n)}{(n == 0 ? "" : ", ")}{GenericTOuts(funcOut)}>(({_StrongFlowBase})self);
253 | }}
254 | ";
255 | }
256 |
257 | public static string PackedUpdate(int n, int funcIn, int funcOut)
258 | {
259 | return $@"
260 | public static {_IStrongFlow}<{GenericTs(n)}> {nameof(PackedUpdate)}(this {_IHack}<{_StrongFlow}{(n != 0 ? "<" : "")}{GenericTs(n)}{(n != 0 ? ">" : "")}, T{_StrongFlow}> self, Func<{GenericTIns(funcIn)}{(funcIn == 0 ? "" : ", ")}({GenericTOuts(funcOut)})> func)
261 | where T{_StrongFlow} : {_StrongFlow}{(n != 0 ? "<" : "")}{GenericTs(n)}{(n != 0 ? ">" : "")} {Holds(funcIn)} {HoldsOut(funcOut)}
262 | {{
263 | self.{_AddStepPacked}<{GenericTOuts(funcOut)}>(func);
264 | return new {_StrongFlow}<{GenericTs(n)}>(({_StrongFlowBase})self);
265 | }}
266 | ";
267 | }
268 |
269 | private static string Name(Type type)
270 | {
271 | return type.Name.Substring(0, type.Name.LastIndexOf('`'));
272 | }
273 |
274 | private static string Set(int n)
275 | {
276 | var res = "";
277 | for (int i = 0; i < n; i++)
278 | {
279 | res += $" {_Set}(t{i + 1});" + (i != n-1?Environment.NewLine:"");
280 | }
281 | return res;
282 | }
283 |
284 | static string Holds(int n)
285 | {
286 | if (n == 0) { return ""; }
287 | var res = "";
288 |
289 | for (int i = 0; i < n; i++)
290 | {
291 | res += $", {_IHold}";
292 | }
293 | return res;
294 | }
295 |
296 | static string HoldsOut(int n)
297 | {
298 | if (n == 0) { return ""; }
299 | var res = "";
300 |
301 | for (int i = 0; i < n; i++)
302 | {
303 | res += $", {_IHold}";
304 | }
305 | return res;
306 | }
307 |
308 | static string GenericTOuts(int n)
309 | {
310 | var res = "";
311 |
312 | for (int i = 0; i < n; i++)
313 | {
314 | res += $"TOut{i + 1}";
315 | if (i != n - 1)
316 | {
317 | res += ", ";
318 | }
319 | }
320 | return res;
321 | }
322 |
323 | static string GenericTIns(int n)
324 | {
325 | var res = "";
326 |
327 | for (int i = 0; i < n; i++)
328 | {
329 | res += $"TIn{i+1}";
330 | if (i != n -1) {
331 | res += ", ";
332 | }
333 | }
334 | return res;
335 | }
336 |
337 | static string GenericTs(int n) {
338 | var res = "";
339 |
340 | for (int i = 0; i < n; i++)
341 | {
342 | res += $"T{i + 1}";
343 | if (i != n - 1)
344 | {
345 | res += ", ";
346 | }
347 | }
348 | return res;
349 | }
350 |
351 | static string Parameters(int n)
352 | {
353 | var res = "";
354 |
355 | for (int i = 0; i < n; i++)
356 | {
357 | res += $"T{i + 1} t{i+1}";
358 | if (i != n - 1)
359 | {
360 | res += ", ";
361 | }
362 | }
363 | return res;
364 | }
365 |
366 | static string BaseInput(int n)
367 | {
368 | var res = "";
369 |
370 | for (int i = 0; i < n; i++)
371 | {
372 | res += $"t{i + 1}";
373 | if (i != n - 1)
374 | {
375 | res += ", ";
376 | }
377 | }
378 | return res;
379 | }
380 | }
381 | }
382 |
383 |
--------------------------------------------------------------------------------
/FunctionFlow.Strong/FunctionFlow.Strong.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.1
5 | Prototypist.FunctionFlow.Strong
6 | False
7 | Prototypist
8 | Prototypist
9 | "Strongly typed" extensions for function graph
10 | false
11 | Prototypist.pfx
12 | false
13 | 1.0.0.1
14 | 1.0.0.1
15 | 1.0.0.1
16 | FunctionFlow.Strong
17 | git
18 |
19 | https://github.com/Prototypist1/FunctionFlow
20 | https://github.com/Prototypist1/FunctionFlow
21 | 2018 Colin Wielga
22 | FunctionFlow.Strong
23 | Prototypist.FunctionFlow.Strong
24 | -
25 | False
26 | MIT
27 | icon.png
28 |
29 |
30 |
31 | 7.3
32 |
33 |
34 |
35 | 7.3
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | True
45 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/FunctionFlow.Strong/Generated-StrongFlows.cs:
--------------------------------------------------------------------------------
1 |
2 | using System;
3 | using Prototypist.FunctionFlow;
4 |
5 | namespace Prototypist.FunctionFlow.Strong
6 | {
7 |
8 | #region BaseClass
9 |
10 | public class StrongFlowBase : StrongFlowBase, IHold
11 | {
12 | public StrongFlowBase(StrongFlowBase backing) : base(backing)
13 | {
14 | }
15 |
16 | protected StrongFlowBase(FlowBuilder flowBuilder) : base(flowBuilder)
17 | {
18 | }
19 | }
20 |
21 | public class StrongFlowBase : StrongFlowBase, IHold
22 | {
23 | public StrongFlowBase(StrongFlowBase backing) : base(backing)
24 | {
25 | }
26 |
27 | protected StrongFlowBase(FlowBuilder flowBuilder) : base(flowBuilder)
28 | {
29 | }
30 | }
31 |
32 | public class StrongFlowBase : StrongFlowBase, IHold
33 | {
34 | public StrongFlowBase(StrongFlowBase backing) : base(backing)
35 | {
36 | }
37 |
38 | protected StrongFlowBase(FlowBuilder flowBuilder) : base(flowBuilder)
39 | {
40 | }
41 | }
42 |
43 | public class StrongFlowBase : StrongFlowBase, IHold
44 | {
45 | public StrongFlowBase(StrongFlowBase backing) : base(backing)
46 | {
47 | }
48 |
49 | protected StrongFlowBase(FlowBuilder flowBuilder) : base(flowBuilder)
50 | {
51 | }
52 | }
53 |
54 | public class StrongFlowBase : StrongFlowBase, IHold
55 | {
56 | public StrongFlowBase(StrongFlowBase backing) : base(backing)
57 | {
58 | }
59 |
60 | protected StrongFlowBase(FlowBuilder flowBuilder) : base(flowBuilder)
61 | {
62 | }
63 | }
64 |
65 | public class StrongFlowBase : StrongFlowBase, IHold
66 | {
67 | public StrongFlowBase(StrongFlowBase backing) : base(backing)
68 | {
69 | }
70 |
71 | protected StrongFlowBase(FlowBuilder flowBuilder) : base(flowBuilder)
72 | {
73 | }
74 | }
75 |
76 | public class StrongFlowBase : StrongFlowBase, IHold
77 | {
78 | public StrongFlowBase(StrongFlowBase backing) : base(backing)
79 | {
80 | }
81 |
82 | protected StrongFlowBase(FlowBuilder flowBuilder) : base(flowBuilder)
83 | {
84 | }
85 | }
86 |
87 | public class StrongFlowBase : StrongFlowBase, IHold
88 | {
89 | public StrongFlowBase(StrongFlowBase backing) : base(backing)
90 | {
91 | }
92 |
93 | protected StrongFlowBase(FlowBuilder flowBuilder) : base(flowBuilder)
94 | {
95 | }
96 | }
97 |
98 | public class StrongFlowBase : StrongFlowBase, IHold
99 | {
100 | public StrongFlowBase(StrongFlowBase backing) : base(backing)
101 | {
102 | }
103 |
104 | protected StrongFlowBase(FlowBuilder flowBuilder) : base(flowBuilder)
105 | {
106 | }
107 | }
108 |
109 | public class StrongFlowBase : StrongFlowBase, IHold
110 | {
111 | public StrongFlowBase(StrongFlowBase backing) : base(backing)
112 | {
113 | }
114 |
115 | protected StrongFlowBase(FlowBuilder flowBuilder) : base(flowBuilder)
116 | {
117 | }
118 | }
119 |
120 | public class StrongFlowBase : StrongFlowBase, IHold
121 | {
122 | public StrongFlowBase(StrongFlowBase backing) : base(backing)
123 | {
124 | }
125 |
126 | protected StrongFlowBase(FlowBuilder flowBuilder) : base(flowBuilder)
127 | {
128 | }
129 | }
130 |
131 | public class StrongFlowBase : StrongFlowBase, IHold
132 | {
133 | public StrongFlowBase(StrongFlowBase backing) : base(backing)
134 | {
135 | }
136 |
137 | protected StrongFlowBase(FlowBuilder flowBuilder) : base(flowBuilder)
138 | {
139 | }
140 | }
141 | #endregion
142 |
143 | #region Class
144 |
145 | public class StrongFlow : StrongFlowBase, IStrongFlow
146 | {
147 | public StrongFlow(StrongFlowBase backing): base(backing) {
148 | }
149 | public StrongFlow(T1 t1): base(new FlowBuilder()) {
150 | SetConstant(t1);
151 | }
152 | }
153 |
154 | public class StrongFlow : StrongFlowBase, IStrongFlow
155 | {
156 | public StrongFlow(StrongFlowBase backing): base(backing) {
157 | }
158 | public StrongFlow(T1 t1, T2 t2): base(new FlowBuilder()) {
159 | SetConstant(t1);
160 | SetConstant(t2);
161 | }
162 | }
163 |
164 | public class StrongFlow : StrongFlowBase, IStrongFlow
165 | {
166 | public StrongFlow(StrongFlowBase backing): base(backing) {
167 | }
168 | public StrongFlow(T1 t1, T2 t2, T3 t3): base(new FlowBuilder()) {
169 | SetConstant(t1);
170 | SetConstant(t2);
171 | SetConstant(t3);
172 | }
173 | }
174 |
175 | public class StrongFlow : StrongFlowBase, IStrongFlow
176 | {
177 | public StrongFlow(StrongFlowBase backing): base(backing) {
178 | }
179 | public StrongFlow(T1 t1, T2 t2, T3 t3, T4 t4): base(new FlowBuilder()) {
180 | SetConstant(t1);
181 | SetConstant(t2);
182 | SetConstant(t3);
183 | SetConstant(t4);
184 | }
185 | }
186 |
187 | public class StrongFlow : StrongFlowBase, IStrongFlow
188 | {
189 | public StrongFlow(StrongFlowBase backing): base(backing) {
190 | }
191 | public StrongFlow(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5): base(new FlowBuilder()) {
192 | SetConstant(t1);
193 | SetConstant(t2);
194 | SetConstant(t3);
195 | SetConstant(t4);
196 | SetConstant(t5);
197 | }
198 | }
199 |
200 | public class StrongFlow : StrongFlowBase, IStrongFlow
201 | {
202 | public StrongFlow(StrongFlowBase backing): base(backing) {
203 | }
204 | public StrongFlow(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6): base(new FlowBuilder()) {
205 | SetConstant(t1);
206 | SetConstant(t2);
207 | SetConstant(t3);
208 | SetConstant(t4);
209 | SetConstant(t5);
210 | SetConstant(t6);
211 | }
212 | }
213 |
214 | public class StrongFlow : StrongFlowBase, IStrongFlow
215 | {
216 | public StrongFlow(StrongFlowBase backing): base(backing) {
217 | }
218 | public StrongFlow(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7): base(new FlowBuilder()) {
219 | SetConstant(t1);
220 | SetConstant(t2);
221 | SetConstant(t3);
222 | SetConstant(t4);
223 | SetConstant(t5);
224 | SetConstant(t6);
225 | SetConstant(t7);
226 | }
227 | }
228 |
229 | public class StrongFlow : StrongFlowBase, IStrongFlow
230 | {
231 | public StrongFlow(StrongFlowBase backing): base(backing) {
232 | }
233 | public StrongFlow(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8): base(new FlowBuilder()) {
234 | SetConstant(t1);
235 | SetConstant(t2);
236 | SetConstant(t3);
237 | SetConstant(t4);
238 | SetConstant(t5);
239 | SetConstant(t6);
240 | SetConstant(t7);
241 | SetConstant(t8);
242 | }
243 | }
244 |
245 | public class StrongFlow : StrongFlowBase, IStrongFlow
246 | {
247 | public StrongFlow(StrongFlowBase backing): base(backing) {
248 | }
249 | public StrongFlow(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9): base(new FlowBuilder()) {
250 | SetConstant(t1);
251 | SetConstant(t2);
252 | SetConstant(t3);
253 | SetConstant(t4);
254 | SetConstant(t5);
255 | SetConstant(t6);
256 | SetConstant(t7);
257 | SetConstant(t8);
258 | SetConstant(t9);
259 | }
260 | }
261 |
262 | public class StrongFlow : StrongFlowBase, IStrongFlow
263 | {
264 | public StrongFlow(StrongFlowBase backing): base(backing) {
265 | }
266 | public StrongFlow(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9, T10 t10): base(new FlowBuilder()) {
267 | SetConstant(t1);
268 | SetConstant(t2);
269 | SetConstant(t3);
270 | SetConstant(t4);
271 | SetConstant(t5);
272 | SetConstant(t6);
273 | SetConstant(t7);
274 | SetConstant(t8);
275 | SetConstant(t9);
276 | SetConstant(t10);
277 | }
278 | }
279 |
280 | public class StrongFlow : StrongFlowBase, IStrongFlow
281 | {
282 | public StrongFlow(StrongFlowBase backing): base(backing) {
283 | }
284 | public StrongFlow(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9, T10 t10, T11 t11): base(new FlowBuilder()) {
285 | SetConstant(t1);
286 | SetConstant(t2);
287 | SetConstant(t3);
288 | SetConstant(t4);
289 | SetConstant(t5);
290 | SetConstant(t6);
291 | SetConstant(t7);
292 | SetConstant(t8);
293 | SetConstant(t9);
294 | SetConstant(t10);
295 | SetConstant(t11);
296 | }
297 | }
298 |
299 | public class StrongFlow : StrongFlowBase, IStrongFlow
300 | {
301 | public StrongFlow(StrongFlowBase backing): base(backing) {
302 | }
303 | public StrongFlow(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9, T10 t10, T11 t11, T12 t12): base(new FlowBuilder()) {
304 | SetConstant(t1);
305 | SetConstant(t2);
306 | SetConstant(t3);
307 | SetConstant(t4);
308 | SetConstant(t5);
309 | SetConstant(t6);
310 | SetConstant(t7);
311 | SetConstant(t8);
312 | SetConstant(t9);
313 | SetConstant(t10);
314 | SetConstant(t11);
315 | SetConstant(t12);
316 | }
317 | }
318 | #endregion
319 |
320 | #region Interface
321 |
322 | public interface IStrongFlow : IHack, StrongFlow>
323 | {
324 | }
325 |
326 | public interface IStrongFlow : IHack, StrongFlow>
327 | {
328 | }
329 |
330 | public interface IStrongFlow : IHack, StrongFlow>
331 | {
332 | }
333 |
334 | public interface IStrongFlow : IHack, StrongFlow>
335 | {
336 | }
337 |
338 | public interface IStrongFlow : IHack, StrongFlow>
339 | {
340 | }
341 |
342 | public interface IStrongFlow : IHack, StrongFlow>
343 | {
344 | }
345 |
346 | public interface IStrongFlow : IHack, StrongFlow>
347 | {
348 | }
349 |
350 | public interface IStrongFlow : IHack, StrongFlow>
351 | {
352 | }
353 |
354 | public interface IStrongFlow : IHack, StrongFlow>
355 | {
356 | }
357 |
358 | public interface IStrongFlow : IHack, StrongFlow>
359 | {
360 | }
361 |
362 | public interface IStrongFlow : IHack, StrongFlow>
363 | {
364 | }
365 |
366 | public interface IStrongFlow : IHack, StrongFlow>
367 | {
368 | }
369 | #endregion
370 |
371 | }
--------------------------------------------------------------------------------
/FunctionFlow.Strong/IHold.cs:
--------------------------------------------------------------------------------
1 | namespace Prototypist.FunctionFlow.Strong
2 | {
3 |
4 | public interface IHold : IHold
5 | {
6 |
7 | }
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/FunctionFlow.Strong/StrongFlow.cs:
--------------------------------------------------------------------------------
1 |
2 | using System;
3 | using Prototypist.FunctionFlow;
4 |
5 | namespace Prototypist.FunctionFlow.Strong
6 | {
7 |
8 | public class StrongFlow : StrongFlowBase, IStrongFlow
9 | {
10 | public StrongFlow(StrongFlowBase backing):base(backing)
11 | {
12 | }
13 | public StrongFlow() : base(new FlowBuilder())
14 | {
15 | }
16 | }
17 |
18 | // we need two conditions on the extension methods
19 | // we are limited to one condition per type
20 | // so we cature the type twice:
21 | // T1 == T2
22 | public interface IHack: IHold
23 | {
24 | }
25 |
26 | public interface IStrongFlow : IHack{}
27 |
28 | public abstract class StrongFlowBase : IHold
29 | {
30 | public StrongFlowBase(StrongFlowBase backing)
31 | {
32 | this.FlowBuilder = backing?.FlowBuilder ?? throw new ArgumentNullException(nameof(backing));
33 | }
34 |
35 | public StrongFlowBase(FlowBuilder flowBuilder)
36 | {
37 | this.FlowBuilder = flowBuilder ?? throw new ArgumentNullException(nameof(flowBuilder));
38 | }
39 |
40 | protected FlowBuilder FlowBuilder { get; }
41 |
42 | public T Run() {
43 | return FlowBuilder.Run();
44 | }
45 |
46 | public (T1,T2) Run()
47 | {
48 | return FlowBuilder.Run();
49 | }
50 |
51 | public (T1, T2, T3) Run()
52 | {
53 | return FlowBuilder.Run();
54 | }
55 | public (T1, T2, T3, T4) Run()
56 | {
57 | return FlowBuilder.Run();
58 | }
59 | public (T1, T2, T3, T4, T5) Run()
60 | {
61 | return FlowBuilder.Run();
62 | }
63 | public (T1, T2, T3, T4, T5, T6) Run()
64 | {
65 | return FlowBuilder.Run();
66 | }
67 | public (T1, T2, T3, T4, T5, T6, T7) Run()
68 | {
69 | return FlowBuilder.Run();
70 | }
71 |
72 | public void AddStep(Delegate @delegate) {
73 | FlowBuilder.AddStep(@delegate);
74 | }
75 |
76 | public void AddStepPacked(Delegate @delegate)
77 | {
78 | FlowBuilder.AddStepPacked(@delegate);
79 | }
80 | public void AddStepPacked(Delegate @delegate)
81 | {
82 | FlowBuilder.AddStepPacked(@delegate);
83 | }
84 | public void AddStepPacked(Delegate @delegate)
85 | {
86 | FlowBuilder.AddStepPacked(@delegate);
87 | }
88 | public void AddStepPacked(Delegate @delegate)
89 | {
90 | FlowBuilder.AddStepPacked(@delegate);
91 | }
92 | public void AddStepPacked(Delegate @delegate)
93 | {
94 | FlowBuilder.AddStepPacked(@delegate);
95 | }
96 | public void AddStepPacked(Delegate @delegate)
97 | {
98 | FlowBuilder.AddStepPacked(@delegate);
99 | }
100 | public void SetConstant(T t)
101 | {
102 | FlowBuilder.SetConstant(t);
103 | }
104 | }
105 |
106 | public interface IHold {
107 | T Run();
108 | (T1, T2) Run();
109 | (T1, T2, T3) Run();
110 | (T1, T2, T3, T4) Run();
111 | (T1, T2, T3, T4, T5) Run();
112 | (T1, T2, T3, T4, T5, T6) Run();
113 | (T1, T2, T3, T4, T5, T6, T7) Run();
114 |
115 | void AddStep(Delegate @delegate);
116 |
117 | void AddStepPacked(Delegate @delegate);
118 | void AddStepPacked(Delegate @delegate);
119 | void AddStepPacked(Delegate @delegate);
120 | void AddStepPacked(Delegate @delegate);
121 | void AddStepPacked(Delegate @delegate);
122 | void AddStepPacked(Delegate @delegate);
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/FunctionFlow.Test/FlowBuilderTests.cs:
--------------------------------------------------------------------------------
1 |
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Linq.Expressions;
6 | using System.Threading.Tasks;
7 | using Xunit;
8 |
9 | namespace Prototypist.FunctionFlow.Test
10 | {
11 | public class FlowBuilderTests
12 | {
13 |
14 | [Fact]
15 | public void ContainerTest()
16 | {
17 | var container = new TestConainer();
18 | container.Dictionary[typeof(int)] = 5;
19 |
20 | var res = new FlowBuilder().Container(container).Run();
21 |
22 | Assert.Equal(5, res);
23 | }
24 |
25 | [Fact]
26 | public void BlanketTest()
27 | {
28 | var finalInt = 0;
29 | var finalStr = "";
30 |
31 | int inc(int x) => x + 1;
32 | string incStr(string x) => x + "1";
33 |
34 | new FlowBuilder()
35 | .Then(() => 2)
36 | .Then((int i) => i + "")
37 | .Then(inc)
38 | .Then(inc)
39 | .Then(inc)
40 | .Then(inc)
41 | .Then(incStr)
42 | .Then(incStr)
43 | .Then(incStr)
44 | .Then(incStr)
45 | .Then((int x) => finalInt = x)
46 | .Then((string x) => finalStr = x)
47 | .Run();
48 |
49 | Assert.Equal(6, finalInt);
50 | Assert.Equal("21111", finalStr);
51 | }
52 |
53 | [Fact]
54 | public void MultiReturn()
55 | {
56 |
57 | var (finalInt, finalStr) = new FlowBuilder()
58 | .Then(() => 2)
59 | .Then((int i) => i + "")
60 | .Then((int x) => x + 1)
61 | .Then((int x) => x + 1)
62 | .Then((int x) => x + 1)
63 | .Then((int x) => x + 1)
64 | .Then((string x) => x + "1")
65 | .Then((string x) => x + "1")
66 | .Then((string x) => x + "1")
67 | .Then((string x) => x + "1")
68 | .Run();
69 |
70 | Assert.Equal(6, finalInt);
71 | Assert.Equal("21111", finalStr);
72 | }
73 |
74 | [Fact]
75 | public void ParameterTest()
76 | {
77 | var finalInt = 0;
78 |
79 | new FlowBuilder().Then(x => x + 1).Then(x => finalInt = x).Run(10);
80 |
81 | Assert.Equal(11, finalInt);
82 | }
83 |
84 | [Fact]
85 | public void PassThroughTest()
86 | {
87 | Assert.Equal(10, new FlowBuilder().Run(10));
88 | }
89 |
90 | [Fact]
91 | public void SimpleTest()
92 | {
93 | Assert.Equal(1, new FlowBuilder().Then(() => 1).Run());
94 | }
95 |
96 | [Fact]
97 | public void ReturnTest()
98 | {
99 | var res = new FlowBuilder().Then(x =>
100 | x + 1
101 | ).Run(10);
102 |
103 | Assert.Equal(11, res);
104 | }
105 |
106 | [Fact]
107 | public void PackedThen()
108 | {
109 | var (name, value) = new FlowBuilder()
110 | .PackedThen(() => ("Time", 1282368345))
111 | .Run();
112 |
113 | Assert.Equal("Time", name);
114 | Assert.Equal(1282368345, value);
115 | }
116 |
117 | [Fact]
118 | public void SetTest()
119 | {
120 | var res = new FlowBuilder().Set(5).Run();
121 |
122 | Assert.Equal(5, res);
123 | }
124 |
125 | [Fact]
126 | public void ReturnString()
127 | {
128 |
129 | var res = new FlowBuilder().Then(() => "test").Run();
130 |
131 | Assert.Equal("test", res);
132 | }
133 |
134 | [Fact]
135 | public void SourceTest()
136 | {
137 | var res = new FlowBuilder().Source(() => 5 + "").Run(5);
138 |
139 | Assert.Equal("5", res);
140 | }
141 |
142 | [Fact]
143 | public void UnpackTest()
144 | {
145 | var (myInt, myStr) = new FlowBuilder().PackedThen(() => (2, "test")).Run();
146 |
147 | Assert.Equal(2, myInt);
148 | Assert.Equal("test", myStr);
149 |
150 | }
151 |
152 | [Fact]
153 | public void CompileFuncTest()
154 | {
155 | var addOne = new FlowBuilder().Then(x => x + 1).Build>();
156 |
157 | Assert.Equal(11, addOne(10));
158 | Assert.Equal(0, addOne(-1));
159 | }
160 |
161 | [Fact]
162 | public void CompileActionTest()
163 | {
164 | var finalInt = 0;
165 |
166 | var action = new FlowBuilder().Then(x => finalInt = x).Build>();
167 |
168 | action(10);
169 | Assert.Equal(10, finalInt);
170 | action(0);
171 | Assert.Equal(0, finalInt);
172 | }
173 |
174 | [Fact]
175 | public void ParallelCompileActionTest()
176 | {
177 | var function = new FlowBuilder()
178 | .Then((int x) => x + 1)
179 | .Then((int x) => x + 1)
180 | .Then((int x) => x + 1)
181 | .Then((int x) => x + 1).Build>();
182 |
183 | Parallel.For(0, 1000, (i) =>
184 | {
185 | Assert.Equal(i + 4, function(i));
186 | });
187 | }
188 |
189 | [Fact]
190 | public void OutputOrdering()
191 | {
192 | var flow = new FlowBuilder().RunInParallel();
193 | for (int j = 0; j < 100; j++)
194 | {
195 | var i = j;
196 | flow.Then(() => i);
197 | }
198 | Assert.Equal(99, flow.Run());
199 | }
200 |
201 | [Fact]
202 | public void ParallelTest()
203 | {
204 | void wait() => Task.Delay(100).Wait();
205 |
206 | var start = DateTime.Now.Ticks;
207 |
208 | new FlowBuilder()
209 | .RunInParallel()
210 | .Then(wait)
211 | .Then(wait)
212 | .Then(wait)
213 | .Then(wait)
214 | .Run();
215 |
216 | var end = DateTime.Now.Ticks;
217 |
218 | Assert.InRange(TimeSpan.FromTicks(end - start), TimeSpan.FromSeconds(.1), TimeSpan.FromSeconds(.2));
219 |
220 | }
221 |
222 | [Fact]
223 | public void ParallelPairs() {
224 |
225 | Func waitInt= i => {
226 | Task.Delay(100).Wait();
227 | return i + 1;
228 | };
229 |
230 | Func waitString = s => {
231 | Task.Delay(100).Wait();
232 | return s + "1";
233 | };
234 |
235 | var start = DateTime.Now.Ticks;
236 |
237 | var (resInt,resStr)= new FlowBuilder()
238 | .RunInParallel()
239 | .Then(waitInt)
240 | .Then(waitInt)
241 | .Then(waitInt)
242 | .Then(waitString)
243 | .Then(waitString)
244 | .Then(waitString)
245 | .Run(0,"");
246 |
247 | var end = DateTime.Now.Ticks;
248 |
249 | Assert.Equal(3, resInt);
250 | Assert.Equal("111", resStr);
251 | Assert.InRange(TimeSpan.FromTicks(end - start), TimeSpan.FromSeconds(.3), TimeSpan.FromSeconds(.4));
252 | }
253 |
254 | [Fact]
255 | public void ParallelPairsLongShort()
256 | {
257 |
258 | Func waitIntLong = i => {
259 | Task.Delay(100).Wait();
260 | return i + 1;
261 | };
262 |
263 | Func waitIntShort = i => {
264 | return i + 1;
265 | };
266 |
267 | Func waitStringLong = s => {
268 | Task.Delay(100).Wait();
269 | return s + "1";
270 | };
271 |
272 | Func waitStringShort = i => {
273 | return i + "1";
274 | };
275 |
276 | var start = DateTime.Now.Ticks;
277 |
278 | var (resInt, resStr) = new FlowBuilder()
279 | .RunInParallel()
280 | .Then(waitIntLong)
281 | .Then(waitIntShort)
282 | .Then(waitIntLong)
283 | .Then(waitIntShort)
284 | .Then(waitStringShort)
285 | .Then(waitStringLong)
286 | .Then(waitStringShort)
287 | .Then(waitStringLong)
288 | .Run(0, "");
289 |
290 | var end = DateTime.Now.Ticks;
291 |
292 | Assert.Equal(4, resInt);
293 | Assert.Equal("1111", resStr);
294 | Assert.InRange(TimeSpan.FromTicks(end - start), TimeSpan.FromSeconds(.2), TimeSpan.FromSeconds(.3));
295 | }
296 |
297 | [Fact]
298 | public void NoParallelTest()
299 | {
300 | void wait() => Task.Delay(100).Wait();
301 |
302 | var start = DateTime.Now.Ticks;
303 |
304 | new FlowBuilder()
305 | .Then(wait)
306 | .Then(wait)
307 | .Then(wait)
308 | .Run();
309 |
310 | var end = DateTime.Now.Ticks;
311 |
312 | Assert.InRange(TimeSpan.FromTicks(end - start), TimeSpan.FromSeconds(.2), TimeSpan.FromSeconds(.4));
313 |
314 | }
315 |
316 |
317 |
318 | }
319 |
320 |
321 | #region Help
322 |
323 | internal class TestConainer : IContainer
324 | {
325 | public Dictionary Dictionary = new Dictionary();
326 | public T Get()
327 | {
328 | return (T)Dictionary[typeof(T)];
329 | }
330 | }
331 |
332 | #endregion
333 |
334 | }
335 |
--------------------------------------------------------------------------------
/FunctionFlow.Test/FunctionFlow.Test.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 |
6 | false
7 |
8 | Prototypist.FunctionFlow.Test
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | all
18 | runtime; build; native; contentfiles; analyzers; buildtransitive
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/FunctionFlow.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.27004.2005
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FunctionFlow", "FunctionFlow\FunctionFlow.csproj", "{A6283C7F-3DC2-403C-AE47-E5AD924C6A5F}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FunctionFlow.Strong", "FunctionFlow.Strong\FunctionFlow.Strong.csproj", "{C66A1319-5451-4C86-BB8D-75E71D6315C1}"
9 | EndProject
10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FunctionFlow.Strong.CodeGen", "FunctionFlow.Strong.CodeGen\FunctionFlow.Strong.CodeGen.csproj", "{EEE2D861-3015-4143-83E8-73D4EC1F7B51}"
11 | EndProject
12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FunctionFlow.Test", "FunctionFlow.Test\FunctionFlow.Test.csproj", "{8836C40A-27C9-40A2-842D-8945366E86AB}"
13 | EndProject
14 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{5778BB29-8A72-482C-A84E-13258FC7E99F}"
15 | ProjectSection(SolutionItems) = preProject
16 | icon.png = icon.png
17 | icon.svg = icon.svg
18 | ReadMe.md = ReadMe.md
19 | EndProjectSection
20 | EndProject
21 | Global
22 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
23 | Debug|Any CPU = Debug|Any CPU
24 | Debug|ARM = Debug|ARM
25 | Debug|x64 = Debug|x64
26 | Debug|x86 = Debug|x86
27 | Release|Any CPU = Release|Any CPU
28 | Release|ARM = Release|ARM
29 | Release|x64 = Release|x64
30 | Release|x86 = Release|x86
31 | EndGlobalSection
32 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
33 | {A6283C7F-3DC2-403C-AE47-E5AD924C6A5F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
34 | {A6283C7F-3DC2-403C-AE47-E5AD924C6A5F}.Debug|Any CPU.Build.0 = Debug|Any CPU
35 | {A6283C7F-3DC2-403C-AE47-E5AD924C6A5F}.Debug|ARM.ActiveCfg = Debug|Any CPU
36 | {A6283C7F-3DC2-403C-AE47-E5AD924C6A5F}.Debug|ARM.Build.0 = Debug|Any CPU
37 | {A6283C7F-3DC2-403C-AE47-E5AD924C6A5F}.Debug|x64.ActiveCfg = Debug|Any CPU
38 | {A6283C7F-3DC2-403C-AE47-E5AD924C6A5F}.Debug|x64.Build.0 = Debug|Any CPU
39 | {A6283C7F-3DC2-403C-AE47-E5AD924C6A5F}.Debug|x86.ActiveCfg = Debug|Any CPU
40 | {A6283C7F-3DC2-403C-AE47-E5AD924C6A5F}.Debug|x86.Build.0 = Debug|Any CPU
41 | {A6283C7F-3DC2-403C-AE47-E5AD924C6A5F}.Release|Any CPU.ActiveCfg = Release|Any CPU
42 | {A6283C7F-3DC2-403C-AE47-E5AD924C6A5F}.Release|Any CPU.Build.0 = Release|Any CPU
43 | {A6283C7F-3DC2-403C-AE47-E5AD924C6A5F}.Release|ARM.ActiveCfg = Release|Any CPU
44 | {A6283C7F-3DC2-403C-AE47-E5AD924C6A5F}.Release|ARM.Build.0 = Release|Any CPU
45 | {A6283C7F-3DC2-403C-AE47-E5AD924C6A5F}.Release|x64.ActiveCfg = Release|Any CPU
46 | {A6283C7F-3DC2-403C-AE47-E5AD924C6A5F}.Release|x64.Build.0 = Release|Any CPU
47 | {A6283C7F-3DC2-403C-AE47-E5AD924C6A5F}.Release|x86.ActiveCfg = Release|Any CPU
48 | {A6283C7F-3DC2-403C-AE47-E5AD924C6A5F}.Release|x86.Build.0 = Release|Any CPU
49 | {C66A1319-5451-4C86-BB8D-75E71D6315C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
50 | {C66A1319-5451-4C86-BB8D-75E71D6315C1}.Debug|Any CPU.Build.0 = Debug|Any CPU
51 | {C66A1319-5451-4C86-BB8D-75E71D6315C1}.Debug|ARM.ActiveCfg = Debug|Any CPU
52 | {C66A1319-5451-4C86-BB8D-75E71D6315C1}.Debug|ARM.Build.0 = Debug|Any CPU
53 | {C66A1319-5451-4C86-BB8D-75E71D6315C1}.Debug|x64.ActiveCfg = Debug|Any CPU
54 | {C66A1319-5451-4C86-BB8D-75E71D6315C1}.Debug|x64.Build.0 = Debug|Any CPU
55 | {C66A1319-5451-4C86-BB8D-75E71D6315C1}.Debug|x86.ActiveCfg = Debug|Any CPU
56 | {C66A1319-5451-4C86-BB8D-75E71D6315C1}.Debug|x86.Build.0 = Debug|Any CPU
57 | {C66A1319-5451-4C86-BB8D-75E71D6315C1}.Release|Any CPU.ActiveCfg = Release|Any CPU
58 | {C66A1319-5451-4C86-BB8D-75E71D6315C1}.Release|Any CPU.Build.0 = Release|Any CPU
59 | {C66A1319-5451-4C86-BB8D-75E71D6315C1}.Release|ARM.ActiveCfg = Release|Any CPU
60 | {C66A1319-5451-4C86-BB8D-75E71D6315C1}.Release|ARM.Build.0 = Release|Any CPU
61 | {C66A1319-5451-4C86-BB8D-75E71D6315C1}.Release|x64.ActiveCfg = Release|Any CPU
62 | {C66A1319-5451-4C86-BB8D-75E71D6315C1}.Release|x64.Build.0 = Release|Any CPU
63 | {C66A1319-5451-4C86-BB8D-75E71D6315C1}.Release|x86.ActiveCfg = Release|Any CPU
64 | {C66A1319-5451-4C86-BB8D-75E71D6315C1}.Release|x86.Build.0 = Release|Any CPU
65 | {EEE2D861-3015-4143-83E8-73D4EC1F7B51}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
66 | {EEE2D861-3015-4143-83E8-73D4EC1F7B51}.Debug|Any CPU.Build.0 = Debug|Any CPU
67 | {EEE2D861-3015-4143-83E8-73D4EC1F7B51}.Debug|ARM.ActiveCfg = Debug|Any CPU
68 | {EEE2D861-3015-4143-83E8-73D4EC1F7B51}.Debug|ARM.Build.0 = Debug|Any CPU
69 | {EEE2D861-3015-4143-83E8-73D4EC1F7B51}.Debug|x64.ActiveCfg = Debug|Any CPU
70 | {EEE2D861-3015-4143-83E8-73D4EC1F7B51}.Debug|x64.Build.0 = Debug|Any CPU
71 | {EEE2D861-3015-4143-83E8-73D4EC1F7B51}.Debug|x86.ActiveCfg = Debug|Any CPU
72 | {EEE2D861-3015-4143-83E8-73D4EC1F7B51}.Debug|x86.Build.0 = Debug|Any CPU
73 | {EEE2D861-3015-4143-83E8-73D4EC1F7B51}.Release|Any CPU.ActiveCfg = Release|Any CPU
74 | {EEE2D861-3015-4143-83E8-73D4EC1F7B51}.Release|Any CPU.Build.0 = Release|Any CPU
75 | {EEE2D861-3015-4143-83E8-73D4EC1F7B51}.Release|ARM.ActiveCfg = Release|Any CPU
76 | {EEE2D861-3015-4143-83E8-73D4EC1F7B51}.Release|ARM.Build.0 = Release|Any CPU
77 | {EEE2D861-3015-4143-83E8-73D4EC1F7B51}.Release|x64.ActiveCfg = Release|Any CPU
78 | {EEE2D861-3015-4143-83E8-73D4EC1F7B51}.Release|x64.Build.0 = Release|Any CPU
79 | {EEE2D861-3015-4143-83E8-73D4EC1F7B51}.Release|x86.ActiveCfg = Release|Any CPU
80 | {EEE2D861-3015-4143-83E8-73D4EC1F7B51}.Release|x86.Build.0 = Release|Any CPU
81 | {8836C40A-27C9-40A2-842D-8945366E86AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
82 | {8836C40A-27C9-40A2-842D-8945366E86AB}.Debug|Any CPU.Build.0 = Debug|Any CPU
83 | {8836C40A-27C9-40A2-842D-8945366E86AB}.Debug|ARM.ActiveCfg = Debug|Any CPU
84 | {8836C40A-27C9-40A2-842D-8945366E86AB}.Debug|ARM.Build.0 = Debug|Any CPU
85 | {8836C40A-27C9-40A2-842D-8945366E86AB}.Debug|x64.ActiveCfg = Debug|Any CPU
86 | {8836C40A-27C9-40A2-842D-8945366E86AB}.Debug|x64.Build.0 = Debug|Any CPU
87 | {8836C40A-27C9-40A2-842D-8945366E86AB}.Debug|x86.ActiveCfg = Debug|Any CPU
88 | {8836C40A-27C9-40A2-842D-8945366E86AB}.Debug|x86.Build.0 = Debug|Any CPU
89 | {8836C40A-27C9-40A2-842D-8945366E86AB}.Release|Any CPU.ActiveCfg = Release|Any CPU
90 | {8836C40A-27C9-40A2-842D-8945366E86AB}.Release|ARM.ActiveCfg = Release|Any CPU
91 | {8836C40A-27C9-40A2-842D-8945366E86AB}.Release|ARM.Build.0 = Release|Any CPU
92 | {8836C40A-27C9-40A2-842D-8945366E86AB}.Release|x64.ActiveCfg = Release|Any CPU
93 | {8836C40A-27C9-40A2-842D-8945366E86AB}.Release|x64.Build.0 = Release|Any CPU
94 | {8836C40A-27C9-40A2-842D-8945366E86AB}.Release|x86.ActiveCfg = Release|Any CPU
95 | {8836C40A-27C9-40A2-842D-8945366E86AB}.Release|x86.Build.0 = Release|Any CPU
96 | EndGlobalSection
97 | GlobalSection(SolutionProperties) = preSolution
98 | HideSolutionNode = FALSE
99 | EndGlobalSection
100 | GlobalSection(ExtensibilityGlobals) = postSolution
101 | SolutionGuid = {F2A2ABE7-D16A-4A35-ABFD-EBE79D6E35C3}
102 | EndGlobalSection
103 | EndGlobal
104 |
--------------------------------------------------------------------------------
/FunctionFlow/ExpressionExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Linq.Expressions;
2 | using System.Reflection;
3 |
4 | namespace Prototypist.FunctionFlow
5 | {
6 | internal static class ExpressionExtensions{
7 | public static string GetDebugView(this Expression exp)
8 | {
9 | if (exp == null)
10 | return null;
11 |
12 | var propertyInfo = typeof(Expression).GetProperty("DebugView", BindingFlags.Instance | BindingFlags.NonPublic);
13 | return propertyInfo.GetValue(exp) as string;
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/FunctionFlow/ExpressionInfo.cs:
--------------------------------------------------------------------------------
1 |
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Linq.Expressions;
6 |
7 | namespace Prototypist.FunctionFlow
8 | {
9 | public partial class FlowBuilder
10 | {
11 | private class ExpressionInfo
12 | {
13 | public readonly Expression backing;
14 | public readonly Expression[] Inputs;
15 | public Expression Result = null;
16 | public ParameterExpression task = null;
17 | public readonly List waitsOn = new List();
18 | public readonly Type[] returnTypes;
19 | public readonly Type[] ParameterTypes;
20 |
21 | public ExpressionInfo(WorkItem workItem)
22 | {
23 | this.backing = Expression.Constant(workItem.todo) ?? throw new ArgumentNullException(nameof(workItem));
24 | this.returnTypes = workItem.returnTypes;
25 | this.ParameterTypes = workItem.todo.GetType().Parameters();
26 | this.Inputs = new Expression[ParameterTypes.Count()];
27 | }
28 |
29 |
30 | /// returnTypes must not be null
31 | internal bool SameOutput(ExpressionInfo expressionInfo) =>
32 | returnTypes?.SequenceEqual(expressionInfo.returnTypes) ?? false;
33 | internal bool WillWaitFor(ExpressionInfo sourceExpressionInfo) {
34 | if (sourceExpressionInfo == this) {
35 | return true;
36 | }
37 | return waitsOn?.Any(x => x.WillWaitFor(sourceExpressionInfo)) ?? false;
38 | }
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/FunctionFlow/FlowBuilder.cs:
--------------------------------------------------------------------------------
1 |
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Linq.Expressions;
6 | using System.Reflection;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 |
10 | namespace Prototypist.FunctionFlow
11 | {
12 | public interface IContainer
13 | {
14 | T Get();
15 | }
16 |
17 | public partial class FlowBuilder : IFlowBuilder
18 | {
19 | public bool Parallel { get; set; } = false;
20 |
21 | private readonly Dictionary sources = new Dictionary();
22 | private readonly Dictionary constants = new Dictionary();
23 | private Func fallbackSource;
24 | private readonly List todo = new List();
25 |
26 | public FlowBuilder()
27 | {
28 | }
29 |
30 | #region Configure
31 |
32 | public void SetContainer(IContainer container)
33 | {
34 | var containerParameter = Expression.Constant(container);
35 | var get = typeof(IContainer).GetMethods().Where(x => x.Name == "Get" && x.IsGenericMethod).Single();
36 | fallbackSource = (type) => Expression.Call(containerParameter, get.MakeGenericMethod(type));
37 | }
38 |
39 | public void SetConstant(T t)
40 | {
41 | var key = typeof(T);
42 | if (sources.ContainsKey(key)) {
43 | throw new Exception($"Source already set for {key}");
44 | }
45 | constants.Add(key, Expression.Constant(t));
46 | }
47 |
48 | // you can make sources loop pretty easy
49 | // A -> B
50 | // B -> A
51 | // or just
52 | // A -> A
53 | // is it worth reflecting around and blocking loops?
54 | // probably atleast worth throwing in BuildExpression
55 | public void SetSource(T t)
56 | where T : Delegate
57 | {
58 | var key = typeof(T).FuncReturnOrNull();
59 | if (constants.ContainsKey(key))
60 | {
61 | throw new Exception($"Constant already set for {key}");
62 | }
63 | sources.Add(key, t);
64 | }
65 |
66 | public void AddStep(Delegate expression)
67 | {
68 | var returnType = expression.GetType().FuncReturnOrNull();
69 | if (returnType == null)
70 | {
71 | todo.Add(new WorkItem(expression, new Type[0]));
72 | }
73 | else {
74 | todo.Add(new WorkItem(expression, new[] { returnType }));
75 | }
76 | }
77 |
78 | public void AddStepPacked(Delegate expression)
79 | {
80 | todo.Add(new WorkItem(expression, new[] { typeof(T1), typeof(T2) }));
81 | }
82 |
83 | public void AddStepPacked(Delegate expression)
84 | {
85 | todo.Add(new WorkItem(expression, new[] { typeof(T1), typeof(T2), typeof(T3) }));
86 | }
87 |
88 | public void AddStepPacked(Delegate expression)
89 | {
90 | todo.Add(new WorkItem(expression, new[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4) }));
91 | }
92 |
93 | public void AddStepPacked(Delegate expression)
94 | {
95 | todo.Add(new WorkItem(expression, new[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5) }));
96 | }
97 |
98 | public void AddStepPacked(Delegate expression)
99 | {
100 | todo.Add(new WorkItem(expression, new[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6) }));
101 | }
102 |
103 | public void AddStepPacked(Delegate expression)
104 | {
105 | todo.Add(new WorkItem(expression, new[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7) }));
106 | }
107 |
108 | #endregion
109 |
110 | #region Build/Run
111 |
112 | public void Run(params object[] inputs)
113 | {
114 | var parameters = inputs.Select(x => Expression.Parameter(x.GetType())).ToArray();
115 |
116 | BuildExpression(
117 | FromParams(parameters, FromConstants(FromSources(FromContainer()))),
118 | Parallel,
119 | new Type[0],
120 | NoReturn(),
121 | parameters,
122 | todo).Compile().DynamicInvoke(inputs);
123 | }
124 |
125 | public T Run(params object[] inputs)
126 | {
127 | var parameters = inputs.Select(x => Expression.Parameter(x.GetType())).ToArray();
128 |
129 | return (T)(BuildExpression(
130 | FromParams(parameters, FromConstants(FromSources(FromContainer()))),
131 | Parallel,
132 | new[] { typeof(T) },
133 | SingleReturn(typeof(T)),
134 | parameters,
135 | todo).Compile().DynamicInvoke(inputs));
136 | }
137 |
138 | public (T1, T2) Run(params object[] inputs)
139 | {
140 | var parameters = inputs.Select(x => Expression.Parameter(x.GetType())).ToArray();
141 |
142 | return (ValueTuple)(BuildExpression(
143 | FromParams(parameters, FromConstants(FromSources(FromContainer()))),
144 | Parallel,
145 | new[] { typeof(T1), typeof(T2) },
146 | MultiReturn(typeof(ValueTuple)),
147 | parameters,
148 | todo).Compile().DynamicInvoke(inputs));
149 | }
150 |
151 | public (T1, T2, T3) Run(params object[] inputs)
152 | {
153 | var parameters = inputs.Select(x => Expression.Parameter(x.GetType())).ToArray();
154 |
155 | return (ValueTuple)(BuildExpression(
156 | FromParams(parameters, FromConstants(FromSources(FromContainer()))),
157 | Parallel,
158 | new[] { typeof(T1), typeof(T2), typeof(T3) },
159 | MultiReturn(typeof(ValueTuple)),
160 | parameters,
161 | todo).Compile().DynamicInvoke(inputs));
162 | }
163 |
164 | public (T1, T2, T3, T4) Run(params object[] inputs)
165 | {
166 | var parameters = inputs.Select(x => Expression.Parameter(x.GetType())).ToArray();
167 |
168 | return (ValueTuple)(BuildExpression(
169 | FromParams(parameters, FromConstants(FromSources(FromContainer()))),
170 | Parallel,
171 | new[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4) },
172 | MultiReturn(typeof(ValueTuple)),
173 | parameters,
174 | todo).Compile().DynamicInvoke(inputs));
175 | }
176 |
177 | public (T1, T2, T3, T4, T5) Run(params object[] inputs)
178 | {
179 | var parameters = inputs.Select(x => Expression.Parameter(x.GetType())).ToArray();
180 |
181 | return (ValueTuple)(BuildExpression(
182 | FromParams(parameters, FromConstants(FromSources(FromContainer()))),
183 | Parallel,
184 | new[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5) },
185 | MultiReturn(typeof(ValueTuple)),
186 | parameters,
187 | todo).Compile().DynamicInvoke(inputs));
188 | }
189 |
190 | public (T1, T2, T3, T4, T5, T6) Run(params object[] inputs)
191 | {
192 | var parameters = inputs.Select(x => Expression.Parameter(x.GetType())).ToArray();
193 |
194 | return (ValueTuple)(BuildExpression(
195 | FromParams(parameters, FromConstants(FromSources(FromContainer()))),
196 | Parallel,
197 | new[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6) },
198 | MultiReturn(typeof(ValueTuple)),
199 | parameters,
200 | todo).Compile().DynamicInvoke(inputs));
201 | }
202 |
203 | public (T1, T2, T3, T4, T5, T6, T7) Run(params object[] inputs)
204 | {
205 | var parameters = inputs.Select(x => Expression.Parameter(x.GetType())).ToArray();
206 |
207 | return (ValueTuple)(BuildExpression(
208 | FromParams(parameters, FromConstants(FromSources(FromContainer()))),
209 | Parallel,
210 | new[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7) },
211 | MultiReturn(typeof(ValueTuple)),
212 | parameters,
213 | todo).Compile().DynamicInvoke(inputs));
214 | }
215 |
216 | public T Build() where T : Delegate
217 | {
218 | var parameters = typeof(T).Parameters().Select(x => Expression.Parameter(x)).ToArray();
219 |
220 | return (T)BuildExpression(
221 | FromParams(parameters, FromConstants(FromSources(FromContainer()))),
222 | Parallel,
223 | GetReturns(typeof(T)),
224 | typeof(T).FuncReturnOrNull() != null ? SingleReturn(typeof(T).FuncReturnOrNull()) : NoReturn(),
225 | parameters,
226 | todo).Compile();
227 | }
228 |
229 | #endregion
230 |
231 | #region Build/Run Help
232 |
233 | private Func, Expression> FromParams(ParameterExpression[] parameters, Func, Expression> fallback)
234 | {
235 | return (type, searchCurrentContext) =>
236 | {
237 | var res = parameters.SingleOrDefault(x => x.Type == type);
238 | if (res == null)
239 | {
240 | return fallback(type, searchCurrentContext);
241 | }
242 | return res;
243 | };
244 | }
245 |
246 | private Func, Expression> FromConstants(Func, Expression> fallback)
247 | {
248 | return (type, searchCurrent) =>
249 | {
250 | if (constants.TryGetValue(type, out var cons))
251 | {
252 | return cons;
253 | }
254 | return fallback(type, searchCurrent);
255 | };
256 | }
257 |
258 | private Func, Expression> FromSources(Func, Expression> fallback)
259 | {
260 | return (type, searchCurrent) =>
261 | {
262 | if (sources.TryGetValue(type, out var source))
263 | {
264 | var parms = source.GetType().Parameters().Select(x => searchCurrent(x)).ToArray();
265 | var toCall = Expression.Constant(source);
266 | return Expression.Invoke(toCall, parms);
267 | }
268 | return fallback(type, searchCurrent);
269 | };
270 | }
271 |
272 | private Func, Expression> FromContainer()
273 | {
274 | return (type, searchCurrent) =>
275 | {
276 | if (fallbackSource == null)
277 | {
278 | throw new Exception("Parameter not found");
279 | }
280 |
281 | return fallbackSource(type);
282 | };
283 | }
284 |
285 | private static Type[] GetReturns(Type type)
286 | {
287 |
288 | var returnType = type.FuncReturnOrNull();
289 |
290 | if (returnType == null)
291 | {
292 | return new Type[0];
293 | }
294 | return new Type[] { returnType };
295 | }
296 |
297 | private static Func NoReturn()
298 | {
299 | return x => new Expression[0];
300 | }
301 |
302 | private static Func SingleReturn(Type type)
303 | {
304 | return x =>
305 | {
306 | return new Expression[]{
307 | x.Single()
308 | };
309 | };
310 | }
311 |
312 | private Func MultiReturn(Type type)
313 | {
314 | return x =>
315 | {
316 | var constructor = type.GetConstructor(type.GetGenericArguments());
317 | return new Expression[] {
318 | Expression.New(constructor, x)
319 | };
320 | };
321 | }
322 |
323 | private static Func FromInputs(object[] inputs)
324 | {
325 |
326 | var parameters = inputs.Select(x => Expression.Constant(x, x.GetType())).ToArray();
327 |
328 | return type => parameters.Single(x => x.Type == type);
329 | }
330 |
331 |
332 | #endregion
333 |
334 | private static LambdaExpression BuildExpression(
335 | Func, Expression> getParameter,
336 | bool parallel,
337 | Type[] returns,
338 | Func addReturn,
339 | ParameterExpression[] parameters,
340 | List todo)
341 | {
342 | var resultsDict = new Dictionary();
343 |
344 | var expressionInfos = todo.Select(x => new ExpressionInfo(x)).ToList();
345 |
346 | // discover dependency chain
347 | for (int i = 0; i < expressionInfos.Count; i++)
348 | {
349 | var expressionInfo = expressionInfos[i];
350 | for (var parmIndex = 0; parmIndex < expressionInfo.ParameterTypes.Length; parmIndex++)
351 | {
352 | var parmType = expressionInfo.ParameterTypes[parmIndex];
353 | expressionInfo.Inputs[parmIndex] = FindInput(expressionInfo, parmType, i);
354 | }
355 | if (expressionInfo.returnTypes != null && expressionInfo.returnTypes.Count() != 0) {
356 | for (int j = i - 1; j >= 0; j--)
357 | {
358 | var otherExpressionInfo = expressionInfos[j];
359 | if (otherExpressionInfo.SameOutput(expressionInfo)) {
360 | if (!expressionInfo.WillWaitFor(otherExpressionInfo))
361 | {
362 | expressionInfo.waitsOn.Add(otherExpressionInfo);
363 | }
364 | }
365 | }
366 | }
367 | }
368 |
369 | var results = new Expression[returns.Length];
370 |
371 | for (int i = 0; i < returns.Length; i++)
372 | {
373 | var type = returns[i];
374 | results[i] = FindInput(null, type, expressionInfos.Count);
375 | }
376 |
377 | var body = expressionInfos.Select(expressionInfo => {
378 | if (parallel)
379 | {
380 |
381 | expressionInfo.task = Expression.Parameter(typeof(Task));
382 | var tasks = expressionInfo.waitsOn.Select(x => x.task).Cast().ToArray();
383 | if (tasks.Any())
384 | {
385 | var whenAll = taskWhenAll.Value;
386 | var wait = Expression.Call(whenAll, Expression.NewArrayInit(typeof(Task), tasks));
387 |
388 | var lambda = MakeTaskLambda(expressionInfo);
389 | var run = Expression.Call(wait, taskContinueWith.Value, lambda);
390 | return Expression.Assign(expressionInfo.task, run);
391 | }
392 | else {
393 | var lambda = MakeLambda(expressionInfo);
394 | return Expression.Assign(expressionInfo.task, Expression.Call(taskRun.Value, lambda));
395 | }
396 | }
397 | return MakeSingle(expressionInfo);
398 | }).ToList();
399 |
400 | if (parallel) {
401 | var tasks = expressionInfos.Select(x => x.task).ToArray();
402 | var whenAll = Expression.Call(taskWhenAll.Value, Expression.NewArrayInit(typeof(Task), tasks));
403 | var wait = Expression.Call(whenAll, taskWait.Value);
404 | body.Add(wait);
405 | }
406 |
407 | body.AddRange(addReturn(results));
408 |
409 | var extraParametersBuilder = resultsDict.Select(x => x.Value);
410 | if (parallel)
411 | {
412 | extraParametersBuilder = extraParametersBuilder.Concat(expressionInfos.Select(x => x.task));
413 | }
414 | var extraParameters = extraParametersBuilder.ToArray();
415 |
416 | var allParameters = extraParameters.Concat(parameters);
417 |
418 | var inner = Expression.Lambda(Expression.Block(body), allParameters);
419 |
420 | if (!extraParameters.Any()) {
421 | return inner;
422 | }
423 |
424 | var agrs = extraParameters.Select(x => Expression.Constant(GetDefault(x.Type),x.Type)).Concat(parameters.Cast()).ToArray();
425 | var final = Expression.Lambda(Expression.Invoke(inner, agrs), parameters);
426 |
427 | return final;
428 |
429 | #region Help
430 |
431 | Expression MakeTaskLambda(ExpressionInfo target) {
432 | return Expression.Lambda>(MakeSingle(target), new ParameterExpression[] { Expression.Parameter(typeof(Task)) });
433 | }
434 |
435 |
436 | Expression MakeLambda(ExpressionInfo target)
437 | {
438 | var expression = MakeSingle(target);
439 |
440 | if (expression is Expression) {
441 | return expression;
442 | }
443 |
444 | return Expression.Lambda