1
// This is a _modified_ version of the original implementation of the FSharp CodeDomGenerator
3
// This CodeDomGenerator is used by monodevelop to generate code for ASP.NET, 'empty class', snippets and other things.
4
// The code generated before the modifications was really complicated because it was a full
5
// code generator. For monodevelop binding all we need is a codedom that generates nice looking
6
// code in the specific cases we care about. So we have started to adjust the codedom to do nice
7
// code for the samples generated by monodevelop.
9
// This code dom should not currently be used for F# code generation in other settings.
12
namespace Microsoft.FSharp.Compiler.CodeDom.Internal
13
#nowarn "57" // parametrized active patterns
14
#nowarn "62" // This construct is for ML compatibility.
19
open System.Reflection
20
open System.Collections
21
open System.Collections.Generic
23
open System.CodeDom.Compiler
25
open Microsoft.FSharp.Compiler.CodeDom.Internal.Visitor
28
module internal Generator =
30
type ResizeArray<'T> = System.Collections.Generic.List<'T> // alias
32
//---------------------------------------------------------------------------------------------
33
// Context and configuration
35
type AdditionalOptions =
36
/// No extra configuration
39
/// Reference inherited fields using "fld" instead of "this.fld"
40
/// (could be used in the future to allow implicit classes in ASP.NET?)
41
| UnknonwFieldsAsLocals = 1
43
/// Hacking for ASP.NET incorrect array initializers
44
/// They generate "string" where codedom test suite uses "string[]"
50
/// Some unique ID for every namespace (so we don't have name clashes)
53
/// Options, output, ...
54
Options:AdditionalOptions
55
Writer:IndentedTextWriter
57
// *** Method/type scope ***
59
/// Names of all type arguments in scope (need to rename T -> 'T etc.)
60
TypeArgumentNames:Set<string>
61
/// Types of all local variables in the method
62
LocalVariableTypes:Map<string,Type>;
63
/// Type of the method
64
CurrentMethodReturnType:CodeTypeReference option;
65
/// We use exception for returning value when generating complex
66
/// code that returns using imperative "return" statement
67
ReturnUsingException:bool;
69
// *** Information for the current class ***
71
CurrentType:CodeTypeDeclaration;
72
BaseTypes:CodeTypeReference option * CodeTypeReference list
73
AllFields:Map<string,CodeMemberField>;
74
AllProps:Map<string,CodeMemberProperty>;
75
AllMeths:Map<string,CodeMemberMethod>;
76
AllEvents:Map<string,CodeMemberEvent>;
77
FieldTypes:Map<string,CodeTypeReference>;
78
PropertyTypes:Map<string,CodeTypeReference>;
79
DeclaredEvents:CodeMemberEvent list;
81
// *** Namespace scope ***
83
// Renamed types (when flattening nested classes)
84
TypeRenames:Map<string,string>
85
// Current namespace (can't be used in the type reference expression)
86
CurrentNamespace:string;
87
// Set of interface names declared in the current namespace
88
DeclaredInterfaces:Set<string>
89
// A static Main method declared by one of the classes in this namespace
90
MainMethodForCurrentNamespace:(CodeEntryPointMethod * CodeTypeDeclaration) option
93
/// Create context using specified text writer and options
94
let createContext (wr:TextWriter) (opts:CodeGeneratorOptions) (addopts) =
95
{ UniqueID = (Guid.NewGuid()).ToString("N")
96
Writer = new IndentedTextWriter(wr); TypeRenames = Map.empty;
97
CurrentType = null; CurrentNamespace = "";
99
BaseTypes = (None, []);
100
AllFields = Map.empty;
101
AllEvents = Map.empty;
102
AllProps = Map.empty;
103
AllMeths = Map.empty;
104
FieldTypes = Map.empty
105
CurrentMethodReturnType = None;
106
LocalVariableTypes = Map.empty;
107
ReturnUsingException = false;
108
PropertyTypes = Map.empty
110
DeclaredInterfaces = Set.empty;
111
TypeArgumentNames = Set.empty;
112
MainMethodForCurrentNamespace = None }
114
/// Where are we generating member?
115
type MemberGenerateType =
116
| InsideInterface = 0
120
//---------------------------------------------------------------------------------------------
121
// Collections and combinators for generating
123
/// Function composition operator
124
let (+>) (ctx:Context -> Context) (foo:Context -> Context) x =
127
/// Print unique id using: "+> uniqid"
128
let uniqid (c:Context) =
129
c.Writer.Write(c.UniqueID);
132
/// Break-line and append specified string
133
let (++) (ctx:Context -> Context) (str:String) x =
135
c.Writer.WriteLine();
139
/// Append specified string without line-break
140
let (--) (ctx:Context -> Context) (str:String) x =
145
/// Call function, but give it context as an argument
149
/// Identity function
152
/// Print object converted to string
153
let str (o: 'T) (ctx:Context) =
154
ctx.Writer.Write(o :> obj);
157
/// Create closure to do the counting
158
/// (this is usend when we need indexing during collection processing)
159
let createCounter() =
161
(fun () -> i := (!i) + 1; !i)
163
/// Perform map and filter operations in one
164
let rec mapFilter f l =
167
| a::r -> match (f a) with | None -> (mapFilter f r) | Some el -> el::(mapFilter f r)
169
/// Process collection - keeps context through the whole processing calls 'f' for every
170
/// element in sequence and 'fs' between every two elements as a separator
171
let col fs (c:IEnumerable) f (ctx:Context) =
172
let mutable tryPick = true in
174
let e = c.GetEnumerator()
175
while (e.MoveNext()) do
176
if (tryPick) then tryPick <- false else st <- fs st
177
st <- f (unbox e.Current) st
180
/// Process collection - keeps context through the whole processing
181
/// calls 'f' for every element in sequence and 'fs' between every two elements
182
/// as a separator. This is a variant that works on typed collections.
183
let colT fs (c:seq<'T>) f (ctx:Context) =
184
let mutable tryPick = true in
186
let e = c.GetEnumerator();
187
while (e.MoveNext()) do
188
if (tryPick) then tryPick <- false else st <- fs st;
189
st <- f (e.Current) st;
192
/// Call specified function only on elements of specified type.
193
/// (performs dynamic type test using x.GetType())
194
let colFilterT<'T> fs (c:IEnumerable) (f: 'T -> Context -> Context) ctx =
196
= c |> Seq.cast |> Seq.filter (fun (o:obj) -> o.GetType() = typeof<'T>) |> Seq.cast
199
let colFilter<'T> fs (c:IEnumerable) (f: 'T -> Context -> Context) ctx =
200
let sq = c |> Seq.cast |> Seq.filter (fun (o:obj) -> o.GetType() = typeof<'T>)
203
// Separator functions
204
let sepDot = id -- "."
205
let sepWordAnd = id -- " and "
206
let sepSpace = id -- " "
207
let sepNln = id ++ ""
208
let sepArgs = id -- ", "
209
let sepArgsSemi = id -- "; "
211
let sepStar = id -- " * "
212
let sepNlnSemiSpace = id -- ";" ++ " "
214
//---------------------------------------------------------------------------------------------
215
// F# keywords and identifiers and also type resolving for standard .NET libraries
218
set ["abstract"; "and"; "as"; "assert"; "asr"; "base"; "begin"; "class"; "default"; "delegate"; "do"; "done";
219
"downcast"; "downto"; "elif"; "else"; "end"; "exception"; "extern"; "false"; "finally"; "for"; "fun";
220
"function"; "if"; "in"; "inherit"; "inline"; "interface"; "internal"; "land"; "lazy"; "let"; "lor"; "lsl"; "lsr"; "lxor";
221
"match"; "member"; "method"; "mod"; "module"; "mutable"; "namespace"; "new"; "null"; "of"; "open"; "or"; "override";
222
"private"; "public"; "rec"; "return"; "sig"; "static"; "struct"; "then"; "to"; "true"; "try"; "type"; "upcast"; "use"; "val"; "virtual"; "void"; "when";
223
"while"; "with"; "yield";
226
"checked"; "component"; "const"; "constraint"; "constructor"; "continue";
228
"fixed"; "fori"; "functor"; "global";"recursive";"measure";
229
"include"; (* "instance"; *)
231
"object"; "parallel"; "params"; "process"; "protected"; "pure"; (* "pattern"; *)
232
"sealed"; "trait"; "tailcall";
235
let isValidIdentifier str =
236
not (fsKeyWords.Contains(str))
238
let makeEscapedIdentifier str =
239
if (fsKeyWords.Contains(str)) then "i'"+str+"'" else str;
241
let makeValidIdentifier str =
242
if (fsKeyWords.Contains(str)) then "_"+str else str;
245
let counter = createCounter ()
246
(fun () -> "UnnamedMethod_" + counter().ToString())
248
// List of "known" libraries that we try to search when we need to resolve a type
250
["mscorlib"; "System"; "System.Web"; "System.Xml";
251
"System.Data"; "System.Deployment"; "System.Design"; "System.DirectoryServices";
252
"System.Drawing.Design"; "System.Drawing"; "System.EnterpriseServices";
253
"System.Management"; "System.Messaging"; "System.Runtime.Remoting";
254
"System.Security"; "System.ServiceProcess"; "System.Transactions";
255
"System.Configuration"; "System.Web.Mobile"; "System.Web.RegularExpressions";
256
"System.Web.Services"; "System.Windows.Forms"; "System.Core";
257
"PresentationCore"; "PresentationFramework"; "WindowsBase"; "WindowsFormsIntegration"]
258
|> List.map ( fun n -> lazy(try Some(System.Reflection.Assembly.LoadWithPartialName(n)) with _ -> None); );
260
let dict = new Dictionary<string, Type>();
262
/// Tries to find .NET type for specified type name
263
/// This is used when we need to know type in order to generate something correctly,
264
/// but it's just a fallback case
265
let (|FoundSystemType|_|) s =
266
if (dict.ContainsKey(s)) then Some dict.[s] else
267
let ty = coreAssemblies |> Seq.tryPick ( fun lazyAsm ->
268
match lazyAsm.Force() with
271
match (try asm.GetType(s) with _ -> null) with
274
match ty with | Some t -> dict.Add(s, t) | _ -> ()
277
//---------------------------------------------------------------------------------------------
278
// Interface recognition magic
280
// If the name of the type matches a name of interface declared in this file
281
// (stored in a set in the context) than we treat it as an interface, otherwise
282
// we rely on .NET naming pattern (starts with I followed by uppercase letter)
283
// We could search known DLLs, but that's useless since all DLLs we could find
284
// follow this naming pattern...
285
let isInterface (t:CodeTypeReference) (ctx:Context) =
286
let tn = t.BaseType.Substring(t.BaseType.LastIndexOf(".") + 1)
287
let decLoc = Set.contains tn ctx.DeclaredInterfaces
288
decLoc || (tn.StartsWith("I") && (((tn.ToUpper()).[1]) = (tn.[1])))
291
// Splits base types into base class and implemented interfaces
292
// using rules described in <c>isInterface</c>
293
// Returns optional base class and list of interfaces
294
let resolveHierarchy (c:CodeTypeDeclaration) ctx =
296
c.BaseTypes |> Seq.cast |> Seq.toList
297
|> List.partition ( fun (r:CodeTypeReference) -> isInterface r ctx )
299
if (bcl.Length = 0) then
300
// All supertypes all interfaces
302
elif (bcl.Length = 1) then
303
// Exactly one supertype is class, other were recognized as interfaces
304
(Some (List.head bcl), interf)
306
// Fallback case - we found more than one supertypes that look like a class
307
// so we just return the tryPick one and treat other as interfaces
308
(Some (List.head bcl), (List.tail bcl)@interf)
311
//---------------------------------------------------------------------------------------------
312
// Generating strings and working with context
314
let incIndent (ctx:Context) =
315
ctx.Writer.Indent <- ctx.Writer.Indent + 1
318
let decIndent (ctx:Context) =
319
ctx.Writer.Indent <- ctx.Writer.Indent - 1
322
/// Output string as a valid F# identifier
323
let (-!) (ctx:Context -> Context) (str:String) x =
325
c.Writer.Write(makeValidIdentifier str);
328
//---------------------------------------------------------------------------------------------
329
// Default values, types, generic parameters
331
let generateDefaultValue (t:CodeTypeReference) =
332
if (t.ArrayElementType <> null) then
333
id -- "Unchecked.defaultof<_>"
335
match t.BaseType with
336
| "System.Single" -> id -- "0.0f"
337
| "System.Double" -> id -- "0.0"
338
| "System.Char" -> id -- "'\000'"
339
| "System.Int16" -> id -- "0s"
340
| "System.Int32" -> id -- "0"
341
| "System.Int64" -> id -- "0L"
342
| "System.Byte" -> id -- "0uy"
343
| "System.SByte" -> id -- "0y"
344
| "System.UInt16" -> id -- "0us"
345
| "System.UInt32" -> id -- "0u"
346
| "System.UInt64" -> id -- "0UL"
347
| "System.String" -> id -- "\"\""
348
| "System.Boolean" -> id -- "false"
349
| _ -> id -- "Unchecked.defaultof<_>"
351
/// Get System.Type of know type (either standard type or resolved)
352
let tryGetSystemType (cr:CodeTypeReference option) =
355
| Some cr when (cr.ArrayRank = 0) ->
356
match cr.BaseType with
357
| "System.Single" -> Some (typeof<float32>)
358
| "System.Double" -> Some (typeof<float>)
359
| "System.Char" -> Some (typeof<char>)
360
| "System.Int16" -> Some (typeof<int16>)
361
| "System.Int32" -> Some (typeof<int>)
362
| "System.Int64" -> Some (typeof<int64>)
363
| "System.UInt16" -> Some (typeof<uint16>)
364
| "System.UInt32" -> Some (typeof<uint32>)
365
| "System.UInt64" -> Some (typeof<uint64>)
366
| "System.String" -> Some (typeof<string>)
367
| "System.Boolean" -> Some (typeof<bool>)
368
| FoundSystemType t -> Some t
372
/// Tries to resolve type of a variable and adds it to the Context dictionary
373
let tryAddVariableType (name:string) (cr:CodeTypeReference) (varTypes:Map<string,Type>) =
374
let ret t = Map.add name t varTypes
375
match tryGetSystemType (Some cr) with
379
// Returns string with type arguments
380
let rec getTypeArgs (tya:CodeTypeReferenceCollection) renames ns tyParams fsSyntax =
381
if (tya.Count > 0) then
382
let sb = new StringBuilder()
383
sb.Append("<") |> ignore
385
let str = (getTypeRef a renames ns tyParams fsSyntax):string
386
sb.Append(str).Append(", ") |> ignore
387
let s = sb.ToString()
388
s.Substring(0, s.Length - 2) + ">"
392
// Several standard renaming tricks
394
and isKnownSealedType (t:CodeTypeReference) =
396
match t.BaseType with
412
| "System.Boolean" -> true
414
/// Generates type reference (not for arrays)
415
and getBaseTypeRef (cr:CodeTypeReference) renames (ns:string) (tyParams:Set<string>) fsSyntax =
418
// Remove current namespace name, because it can't be used in this scope
420
if (cr.BaseType.StartsWith(ns+".")) then
421
cr.BaseType.Substring(ns.Length+1)
422
elif cr.Options &&& CodeTypeReferenceOptions.GlobalReference <> enum 0 then
423
"global."+cr.BaseType
427
// Several standard renaming tricks
428
match Map.tryFind bst renames with
429
// Renamed type (former nested type)
432
// It is a type paramter - rename T to 'T
433
| None when Set.contains cr.BaseType tyParams ->
436
// Try if it's standard F# type
437
// This also renames Void to unit, which may not be completly correct,
438
// but it works much better than if we don't do it
439
| None when fsSyntax ->
440
match cr.BaseType with
441
| "System.Void" -> "unit"
442
| "System.Object" -> "obj"
443
| "System.String" -> "string"
444
| "System.Single" -> "float32"
445
| "System.Double" -> "float"
446
| "System.Char" -> "char"
447
| "System.Int16" -> "int16"
448
| "System.Int32" -> "int"
449
| "System.Int64" -> "int64"
450
| "System.UInt16" -> "uint16"
451
| "System.UInt32" -> "uint32"
452
| "System.UInt64" -> "uint64"
453
| "System.Boolean" -> "bool"
456
// drop `xyz, replace "+" for nested classes with "."
457
let sb = new StringBuilder()
459
while i < s.Length do
462
| _ when c = '+' || c = '.' -> sb.Append('.') |> ignore;
464
while (i<s.Length && s.[i]>='0' && s.[i]<='9') do
466
| _ -> sb.Append(c) |> ignore
468
// generate type arguments
469
sb.Append(getTypeArgs cr.TypeArguments renames ns tyParams fsSyntax).ToString()
471
/// Generate type reference with empty context
472
and getBaseTypeRefString (s:string) =
473
getBaseTypeRef (CodeTypeReference(s)) Map.empty "" Set.empty true
476
/// Get full type reference using information from context
477
and getTypeRef (c:CodeTypeReference) (rens:Map<string,string>) (ns:string) (tyParams:Set<string>) (fsSyntax:bool) =
480
elif (c.ArrayRank = 0) then
481
getBaseTypeRef c rens ns tyParams fsSyntax
483
let baseType = (getTypeRef c.ArrayElementType rens ns tyParams fsSyntax)
484
baseType + "[" + (System.String.Concat (Array.create (c.ArrayRank - 1) ",")) + "]"
486
/// Get full type reference string using empty context
487
and getTypeRefSimple (c:CodeTypeReference) = getTypeRef c Map.empty "" Set.empty true
489
/// Get type reference, but don't rename .NET types to F# types
490
/// (this is only needed when calling static methods on the type)
491
let generateTypeRefNet (c:CodeTypeReference) =
492
id +> withCtxt ( fun ctx -> id -- getTypeRef c ctx.TypeRenames ctx.CurrentNamespace ctx.TypeArgumentNames false )
494
/// Generate type reference using context
495
/// (this is most commonly used method)
496
let generateTypeRef (c:CodeTypeReference) =
497
id +> withCtxt ( fun ctx -> id -- getTypeRef c ctx.TypeRenames ctx.CurrentNamespace ctx.TypeArgumentNames true )
499
/// Generate type arguments using context
500
let generateTypeArgs (c:CodeTypeReferenceCollection) =
501
id +> withCtxt ( fun ctx -> id -- getTypeArgs c ctx.TypeRenames ctx.CurrentNamespace ctx.TypeArgumentNames true )
504
/// Record specified type parameters in the context, call generating function
505
/// and then restore the original type parameters
506
/// (this works if someone uses nested type parameters with the same name)
507
let usingTyParams tyArgs f (x:Context) =
508
let o = x.TypeArgumentNames
509
let n = Array.foldBack Set.add (Array.ofSeq tyArgs) o
510
let x = f { x with TypeArgumentNames = n }
511
{ x with TypeArgumentNames = o }
514
/// Preprocess collection with type parameters
515
/// Returns array to be used with <c>usingTyParams</c> and
516
/// function to be called to generate < ... > code
517
let processTypeArgs (args:CodeTypeParameterCollection) =
518
let tyargs = seq { for (p:CodeTypeParameter) in args -> p.Name }
520
if (args.Count = 0) then id else
521
let s = tyargs |> Seq.fold (fun ctx s -> ctx + ", '" + s) ""
523
-- "<" -- s.Substring(2, s.Length-2)
524
+> if (args.Count = 0) then id -- ">" else
525
let argsWithConstr = args |> Seq.cast |> Seq.filter (fun (p:CodeTypeParameter) ->
526
p.Constraints.Count <> 0 || p.HasConstructorConstraint) |> Seq.cast |> Seq.toList
527
if (argsWithConstr.Length <> 0) then
529
col sepWordAnd argsWithConstr (fun (p:CodeTypeParameter) ->
530
col sepWordAnd p.Constraints (fun impl ->
531
id -- "'" -- p.Name -- " :> " +> generateTypeRef impl)
532
+> if (not p.HasConstructorConstraint) then id else
533
if (p.Constraints.Count <> 0) then id -- " and " else id
534
-- "'" -- p.Name -- " : (new:unit->'" -- p.Name -- ")")
539
//---------------------------------------------------------------------------------------------
540
// Binary operators and numeric functions
542
/// Generates code for binary operator using function for left and right operand
543
let binaryOp (op:CodeBinaryOperatorType) fleft fright =
546
| CodeBinaryOperatorType.Add -> fleft -- " + " +> fright;
547
| CodeBinaryOperatorType.BitwiseAnd -> fleft -- " &&& " +> fright;
548
| CodeBinaryOperatorType.BitwiseOr -> fleft -- " ||| " +> fright;
549
| CodeBinaryOperatorType.BooleanAnd -> fleft -- " && " +> fright;
550
| CodeBinaryOperatorType.BooleanOr -> fleft -- " || " +> fright;
551
| CodeBinaryOperatorType.Divide -> fleft -- " / " +> fright;
552
| CodeBinaryOperatorType.GreaterThan -> fleft -- " > " +> fright;
553
| CodeBinaryOperatorType.GreaterThanOrEqual -> fleft -- " >= " +> fright;
554
| CodeBinaryOperatorType.LessThan -> fleft -- " < " +> fright;
555
| CodeBinaryOperatorType.LessThanOrEqual -> fleft -- " <= " +> fright;
556
| CodeBinaryOperatorType.Modulus -> fleft -- " % " +> fright;
557
| CodeBinaryOperatorType.Multiply -> fleft -- " * " +> fright;
558
| CodeBinaryOperatorType.Subtract -> fleft -- " - " +> fright;
560
// REVIEW: this is not used in any tests and it is not sure what it means
561
| CodeBinaryOperatorType.Assign -> fleft -- " <- " +> fright;
563
// REVIEW: reference and value equality use C# semantics, so it is not sure what we should generate
564
| CodeBinaryOperatorType.ValueEquality -> fleft -- " = " +> fright;
565
| CodeBinaryOperatorType.IdentityEquality -> id -- "System.Object.ReferenceEquals(" +> fleft -- ", " +> fright -- ")";
566
| CodeBinaryOperatorType.IdentityInequality -> id -- "not (System.Object.ReferenceEquals(" +> fleft -- ", " +> fright -- "))";
567
| _ -> failwithf "unimplemented binary operator type '%A'" op;
570
/// Are both types numerical types where numeric conversion function can be applied?
571
let rec isNumericConversion (src:Type) (target:Type) =
572
convertFunc src <> "" && convertFunc target <> ""
575
/// Returns F# conversion function for the specified type (or empty string)
576
and convertFunc (ty:Type) =
577
if (ty = (typeof<int16>)) then "int16"
578
elif (ty = (typeof<int32>)) then "int32"
579
elif (ty = (typeof<int64>)) then "int64"
580
elif (ty = (typeof<int16>)) then "uint16"
581
elif (ty = (typeof<int32>)) then "uint32"
582
elif (ty = (typeof<int64>)) then "uint64"
583
elif (ty = (typeof<float>)) then "float"
584
elif (ty = (typeof<float32>)) then "float32"
585
elif (ty = (typeof<decimal>)) then "decimal"
586
elif (ty = (typeof<byte>)) then "byte"
587
elif (ty = (typeof<sbyte>)) then "sbyte"
591
/// Generate value of primitive expression
592
let generatePrimitiveExpr (reqty:Type option) (c:CodePrimitiveExpression) =
595
| :? Char as c -> (sprintf "%A" c, Some(typeof<Char>))
596
| :? String as s -> (sprintf "\"%s\"" (s.Replace("\\", "\\\\").Replace("\"", "\\\"").Replace("\n", "\\n").Replace("\t", "\\t").Replace("\r", "\\r").Replace("\b", "\\b")), Some(typeof<string>))
597
| :? Boolean as b -> ((if (b) then "true" else "false"), Some(typeof<bool>))
598
| :? Single as f -> (sprintf "%A" f, Some(typeof<float32>))
599
| :? Double as f -> (sprintf "%A" f, Some(typeof<float>))
600
| :? Byte as i -> (sprintf "%A" i, Some(typeof<Byte>))
601
| :? SByte as i -> (sprintf "%A" i, Some(typeof<SByte>))
602
| :? Int16 as i -> (sprintf "%A" i, Some(typeof<int16>))
603
| :? Int32 as i -> (sprintf "%A" i, Some(typeof<int>))
604
| :? Int64 as i -> (sprintf "%A" i, Some(typeof<int64>))
605
| :? UInt16 as i -> (sprintf "%A" i, Some(typeof<uint16>))
606
| :? UInt32 as i -> (sprintf "%A" i, Some(typeof<uint32>))
607
| :? UInt64 as i -> (sprintf "%A" i, Some(typeof<uint64>))
608
| null -> ("(Unchecked.defaultof<_>)", None)
609
| _ -> ("(* Unknown primitive value '"+c.Value.ToString()+"' of type '"+
610
c.Value.GetType().Name+"'. Please report this to the F# team. *)", None)
611
match typ, reqty with
612
| Some t, Some rt when t <> rt -> id -- convertFunc rt -- " (" -- value -- ")"
613
| _, _ -> id -- value
616
/// Generate array initializer. Checks generator options for ASP.NET workaround.
617
let rec generateArrayCreateExpr (c:CodeArrayCreateExpression) =
618
if (c.Initializers<>null && c.Initializers.Count>0) then
620
-- "([| " +> col sepArgsSemi c.Initializers generateExpression -- " |] : "
621
+> withCtxt (fun ctx ->
622
generateTypeRef c.CreateType
623
-- if (ctx.Options &&& AdditionalOptions.AspNetArrays <> enum 0) then "[]" else "")
627
-- "(Array.zeroCreate "
628
+> if (c.SizeExpression <> null) then
629
id -- "(" +> generateExpression c.SizeExpression -- ")"
633
+> withCtxt (fun ctx ->
634
generateTypeRef c.CreateType
635
-- if (ctx.Options &&& AdditionalOptions.AspNetArrays <> enum 0) then "[]" else "")
638
/// Tries to resolve if type is an array, so we can generate
639
/// appropriate code (it can be either indexer or array, but we need to generate
640
/// .Item call for indexers (no overloading is supported by .[]).
641
/// Returns: "None" - can't resolve, "Some" resovled (true/false - is it an array?)
642
and tryIsExpressionArray c (ctx:Context) =
643
match (c :> CodeExpression) with
644
| :? CodeFieldReferenceExpression as ce when
645
(ce.TargetObject :? CodeThisReferenceExpression) ->
646
match Map.tryFind ce.FieldName ctx.FieldTypes with
647
| Some t -> Some (t.ArrayRank > 0)
649
| :? CodePropertyReferenceExpression as ce when
650
(ce.TargetObject :? CodeThisReferenceExpression) ->
651
match Map.tryFind ce.PropertyName ctx.PropertyTypes with
652
| Some t -> Some (t.ArrayRank > 0)
657
/// Tries to resolve type of an expression using a few tricks:
658
/// * Fields of current type may have known type
659
/// * Properties of current type as well
660
/// * We can also try to resolve other properties (sometimes it helps)
661
/// * Resolve type for local variables or argument reference
662
and tryGetExpressionType c (ctx:Context) =
663
match (c :> CodeExpression) with
664
| :? CodeFieldReferenceExpression as ce when
665
(ce.TargetObject :? CodeThisReferenceExpression) ->
666
tryGetSystemType (Map.tryFind ce.FieldName ctx.FieldTypes)
667
| :? CodePropertyReferenceExpression as ce when
668
(ce.TargetObject :? CodeThisReferenceExpression) ->
669
tryGetSystemType (Map.tryFind ce.PropertyName ctx.PropertyTypes)
670
| :? CodePropertyReferenceExpression as ce ->
671
match (tryGetExpressionType ce.TargetObject ctx) with
675
Some (t.GetProperty(ce.PropertyName).PropertyType)
678
| :? CodeArgumentReferenceExpression as ce ->
679
Map.tryFind ce.ParameterName ctx.LocalVariableTypes
681
// XSD generates incorrect referenece (uses argument ref where it should be variable ref)
682
// and unfortunately it is followed by wrong numeric type, so we need to workaround this
683
| :? CodeVariableReferenceExpression as ce ->
684
Map.tryFind ce.VariableName ctx.LocalVariableTypes
687
//---------------------------------------------------------------------------------------------
688
// Generating code for expressions
690
/// Generates a "this" or "CurrentType" reference depending on whether a reference
691
/// is static or not. Used for "ambiguous" references without a type or object qualifier.
693
/// Unfortunately the Code tree isn't so kind as to tell us whether a reference is static
694
/// or not up front. Instead we predetermine a set of some static members and
695
/// assume all other references are instance references.
697
and generateExpressionDefaultThis isKnownStatic c =
701
// REVIEW: this is still incorrect if the reference is static and it is a reference from an inherited type
702
id -- (if isKnownStatic then ctx.CurrentType.Name else "this" )
703
| _ -> generateExpression c)
705
/// Matches array or indexer expression and corrects it if the generated CodeDOM is incorrect
706
and (|CodeArrayAccessOrIndexer|_|) (ctx:Context) (c:CodeExpression) =
707
let noneT b = match b with Some v -> v | _ -> true
708
let noneF b = match b with Some v -> v | _ -> false
710
| :? CodeArrayIndexerExpression as ce ->
711
Some(true && (noneT (tryIsExpressionArray ce.TargetObject ctx)), ce.TargetObject, ce.Indices)
712
| :? CodeIndexerExpression as ce ->
713
Some(false || (noneF (tryIsExpressionArray ce.TargetObject ctx)), ce.TargetObject, ce.Indices)
716
/// Generate expression - with unkonw type
717
and generateExpression c = generateExpressionTyped None c
719
// Generates code for CodeExpression
720
// If the caller knows the expected type of the expression it can be given as an argument,
721
// but currently it is used only when generating primitve expression to convert value to the right type
722
and generateExpressionTyped ty c ctx =
724
| :? CodeArgumentReferenceExpression as ce ->
725
id -! ce.ParameterName
726
| :? CodeArrayCreateExpression as ce ->
727
id +> generateArrayCreateExpr ce
729
// for indexers we generate get_Item to handle overloading
730
| CodeArrayAccessOrIndexer ctx (isArray, target, indices) ->
732
+> generateExpression target -- "."
733
+> id -- "[" +> col sepArgs indices generateExpression -- "]"
735
| :? CodeBaseReferenceExpression as ce ->
738
| :? CodeBinaryOperatorExpression as ce ->
739
binaryOp ce.Operator (generateExpressionTyped ty ce.Left) (generateExpressionTyped ty ce.Right)
741
// casting can also represent numeric conversion - we try to detect that case
742
| :? CodeCastExpression as ce ->
744
+> withCtxt (fun ctx ->
745
match tryGetExpressionType (ce.Expression) ctx, tryGetSystemType (Some ce.TargetType) with
746
| Some(t1), Some(t2) when isNumericConversion t1 t2 ->
748
-- "(" -- (convertFunc t2)
749
-- "(" +> generateExpression ce.Expression -- "))"
752
-- "((" +> generateExpression ce.Expression -- " :> obj) :?> " +> generateTypeRef ce.TargetType -- ")" )
754
// argument for "ref" or "out" C# parameter - both generated as byref in F#
755
| :? CodeDirectionExpression as ce ->
756
match ce.Direction with
758
| FieldDirection.Ref ->
759
id -- "&" +> generateExpression ce.Expression
761
id +> generateExpression ce.Expression
763
// for delegates, we use 'FuncFromTupled' to get the right function type
764
| :? CodeDelegateCreateExpression as ce ->
766
-- "new " +> generateTypeRef ce.DelegateType -- "(FuncConvert.FuncFromTupled "
767
+> generateExpression ce.TargetObject
768
-- "." -- ce.MethodName -- ")";
770
| :? CodeDelegateInvokeExpression as ce ->
772
+> match ce.TargetObject with
773
// "this.<DeclaredEventName>( ... )" - will be translated to a raise function returned
774
// by create_DelegateEvent
775
| :? CodeEventReferenceExpression as eref when
776
(eref.TargetObject :? CodeThisReferenceExpression)
777
&& ((ctx.DeclaredEvents |> List.tryFind (fun e -> e.Name = eref.EventName)) <> None) ->
778
// F# declared event..
780
-- "this._invoke_" -- eref.EventName -- " [| "
781
+> col sepArgsSemi ce.Parameters (fun (e:CodeExpression) ->
784
+> generateExpression e
786
// other than this.<Event>(). This may not be correct (but works on cases in test suite)
788
generateExpression ce.TargetObject
789
-- ".Invoke(" +> col sepArgs ce.Parameters generateExpression -- ")"
791
// this prevents using mutable variable in a way it would escape its scope
792
| :? CodeEventReferenceExpression as ce ->
793
id -- "let __e = " +> generateExpression ce.TargetObject -- " in __e." -- ce.EventName
795
| :? CodeFieldReferenceExpression as ce ->
798
// if 'UnknownFieldsAsLocals' is set than the code will generate
799
// "fld" instead of "this.fld" when accessing field that is not known
801
match ce.TargetObject with
802
| :? CodeThisReferenceExpression as t when
803
(ctx.Options &&& AdditionalOptions.UnknonwFieldsAsLocals <> enum 0) ->
804
Option.isNone (Map.tryFind ce.FieldName ctx.FieldTypes)
810
+> match ce.TargetObject with
811
| :? CodeTypeReferenceExpression as ct ->
812
id +> generateTypeRefNet ct.Type
815
match ce.TargetObject, ctx.AllFields.TryFind ce.FieldName with
817
(m.Attributes &&& MemberAttributes.ScopeMask = MemberAttributes.Static)
819
generateExpressionDefaultThis isKnownStatic ce.TargetObject
820
-- "." -- ce.FieldName )
822
| :? CodeMethodInvokeExpression as ce ->
824
+> generateExpression (ce.Method :> CodeExpression)
825
-- "(" +> col sepArgs ce.Parameters generateExpression -- ")"
827
| :? CodeMethodReferenceExpression as ce ->
829
+> match ce.TargetObject with
830
| :? CodeTypeReferenceExpression as ct ->
831
id +> generateTypeRefNet ct.Type
834
match ce.TargetObject, ctx.AllMeths.TryFind ce.MethodName with
836
(m.Attributes &&& MemberAttributes.ScopeMask = MemberAttributes.Static)
838
generateExpressionDefaultThis isKnownStatic ce.TargetObject
839
-- "." -- ce.MethodName
840
+> generateTypeArgs ce.TypeArguments
842
| :? CodeObjectCreateExpression as ce ->
844
-- "new " +> generateTypeRef ce.CreateType
845
-- "(" +> col sepArgs ce.Parameters generateExpression -- ")"
847
| :? CodePrimitiveExpression as ce ->
848
id +> generatePrimitiveExpr ty ce
850
| :? CodePropertyReferenceExpression as ce ->
852
+> match ce.TargetObject with
853
| :? CodeTypeReferenceExpression as ct ->
854
id +> generateTypeRefNet ct.Type
857
match ce.TargetObject, ctx.AllProps.TryFind ce.PropertyName with
859
(m.Attributes &&& MemberAttributes.ScopeMask = MemberAttributes.Static)
861
generateExpressionDefaultThis isKnownStatic ce.TargetObject
862
-- "." -- ce.PropertyName
864
| :? CodePropertySetValueReferenceExpression as ce ->
867
// we move all lines of "snippets" by 100 columns so it isn't violating #light rules
868
| :? CodeSnippetExpression as ce ->
870
ce.Value.Split([| '\r'; '\n' |], StringSplitOptions.RemoveEmptyEntries)
871
|> Array.map (fun s -> String(' ',100) + s )
872
colT sepNone strs (fun s -> id ++ s)
874
| :? CodeThisReferenceExpression as ce ->
877
| :? CodeTypeOfExpression as ce ->
878
id -- "(typeof<" +> generateTypeRef ce.Type -- ">)"
880
| :? CodeTypeReferenceExpression as ce ->
881
id +> generateTypeRef ce.Type
883
| :? CodeVariableReferenceExpression as ce ->
885
| Some t when (convertFunc t) <> "" -> id -- "(" -- (convertFunc t) -- " " -! ce.VariableName -- ")"
886
| _ -> id -! ce.VariableName
892
-- "(* Unknown expression type '" -- (c.GetType().Name)
893
-- "' please report this to the F# team. *)") ctx
895
//---------------------------------------------------------------------------------------------
896
// Generating code for statements
898
and generateVariableDeclStmt (c:CodeVariableDeclarationStatement) =
900
+> (fun ctx -> { ctx with LocalVariableTypes = tryAddVariableType c.Name c.Type ctx.LocalVariableTypes } )
901
++ "let mutable (" -! c.Name -- ":" +> generateTypeRef c.Type -- ") = "
902
+> if (c.InitExpression <> null) then
903
(generateExpressionTyped (tryGetSystemType (Some c.Type))) c.InitExpression
905
(generateDefaultValue c.Type);
907
// REVIEW: Line pragmas don't work with the #light syntax
908
let generateLinePragma (l:CodeLinePragma) =
909
if (l = null) then id else
911
++ "# " +> str l.LineNumber -- " \"" -- l.FileName -- "\""
913
let rec generateCatchClause (c:CodeCatchClause) =
915
++ "| :? " +> generateTypeRef c.CatchExceptionType
916
-- " as " -- c.LocalName -- " ->" +> incIndent
917
+> generateStatements c.Statements +> decIndent
919
and generateStatements (sts:CodeStatementCollection) =
921
if (sts.Count = 0 || (sts.[sts.Count - 1] :? CodeVariableDeclarationStatement))
922
then id ++ "()" else id
923
col sepNone sts generateStatement +> fix
925
// Generates block of statements which can return a value
926
and generateStatementBlock typ (statements:CodeStatementCollection) =
927
// determine if the block uses only "safe" return statements
928
// that can be translated to functional returns without using exceptions
931
|> codeDomCallbackWithScope (fun rcall safeScope res o ->
933
| :? CodeMethodReturnStatement as ret -> safeScope && res
934
| :? CodeTryCatchFinallyStatement as tfs -> rcall (safeScope && (tfs.CatchClauses.Count = 0)) res o
935
| :? CodeStatementCollection -> rcall safeScope res o
936
| _ -> rcall false res o ) true true
940
+> (fun ctx -> { ctx with CurrentMethodReturnType=typ;
941
LocalVariableTypes = Map.empty;
942
ReturnUsingException = not safeReturns })
943
// if returning using exception - wrap inside try .. catch
944
+> if (not safeReturns) then id ++ "try" +> incIndent else id
945
+> generateStatements statements
946
+> if (safeReturns) then id else
948
| Some t when t.BaseType <> "System.Void" ->
949
id ++ "failwith \"Code branch didn't return any value!\";"
951
++ "with" ++ " | ReturnException" +> uniqid -- " v -> (v :?> " +> generateTypeRef t -- ")"
953
id ++ "raise ReturnNoneException" +> uniqid
955
++ "with" ++ " | ReturnNoneException" +> uniqid -- " -> ()"
956
+> (fun ctx -> {ctx with CurrentMethodReturnType=None;
957
LocalVariableTypes = Map.empty;
958
ReturnUsingException = false })
961
and generateComment (c:CodeComment) =
963
-- if c.DocComment then "/// " else "// "
966
and generateExpressionThenUpCast e (t: CodeTypeReference) =
967
if isKnownSealedType t then
970
id -- "((" +> generateExpression e -- " :> obj) :?> " +> generateTypeRef t -- ")"
972
and generateStatement (c:CodeStatement) =
973
(generateLinePragma c.LinePragma) +>
975
| :? CodeAssignStatement as cs ->
977
| :? CodeIndexerExpression as ci ->
979
+> generateExpressionDefaultThis false ci.TargetObject -- ".set_Item("
980
+> col sepArgs ci.Indices generateExpression -- ", "
981
+> withCtxt (fun ctx -> generateExpressionTyped (tryGetExpressionType cs.Left ctx) cs.Right)
984
id ++ "" +> generateExpression cs.Left
986
+> withCtxt (fun ctx -> generateExpressionTyped (tryGetExpressionType cs.Left ctx) cs.Right)
988
| :? CodeAttachEventStatement as cs ->
989
id ++ "" +> generateExpression (cs.Event :> CodeExpression)
990
-- ".AddHandler(" +> generateExpression cs.Listener -- ")"
992
| :? CodeCommentStatement as cs ->
993
id ++ "" +> generateComment cs.Comment
995
| :? CodeConditionStatement as cs ->
997
++ "if " +> generateExpression cs.Condition -- " then"
998
+> incIndent +> col sepNone cs.TrueStatements generateStatement +> decIndent
999
+> if (cs.FalseStatements<>null && cs.FalseStatements.Count>0) then
1001
++ "else" +> incIndent
1002
+> col sepNone cs.FalseStatements generateStatement +> decIndent else id
1004
| :? CodeExpressionStatement as cs ->
1005
id ++ "" +> generateExpression cs.Expression -- " |> ignore";
1007
| :? CodeIterationStatement as cs ->
1009
+> generateStatement cs.InitStatement
1010
++ "while " +> generateExpression cs.TestExpression -- " do"
1012
+> col sepNone cs.Statements generateStatement
1013
+> generateStatement cs.IncrementStatement
1016
// Return - either throw "ReturnException" or just generate F# expression with the value
1017
| :? CodeMethodReturnStatement as cs ->
1019
+> withCtxt (fun ctx ->
1020
if (ctx.ReturnUsingException) then
1023
+> match ctx.CurrentMethodReturnType with
1024
| Some t when t.BaseType <> "System.Void" ->
1025
id -- "ReturnException" +> uniqid -- "(" +> generateExpressionThenUpCast cs.Expression t -- ")"
1027
id -- "ReturnNoneException" +> uniqid
1030
match ctx.CurrentMethodReturnType with
1031
| Some t when t.BaseType <> "System.Void" ->
1033
++ "" +> generateExpressionThenUpCast cs.Expression t
1036
| :? CodeSnippetStatement as cs ->
1037
let strs = cs.Value.Split([| '\r'; '\n' |], StringSplitOptions.RemoveEmptyEntries);
1038
colT sepNone strs (fun s -> id ++ s)
1040
| :? CodeVariableDeclarationStatement as cs ->
1041
id +> generateVariableDeclStmt cs
1043
| :? CodeThrowExceptionStatement as cs ->
1044
id ++ "raise (" +> generateExpression cs.ToThrow -- ")"
1046
// try .. catch .. finaly is generated as try (try .. catch) finally
1047
| :? CodeTryCatchFinallyStatement as cs ->
1048
let hasCatch = (cs.CatchClauses<>null && cs.CatchClauses.Count>0)
1049
let hasFinally = (cs.FinallyStatements<>null && cs.FinallyStatements.Count>0)
1051
++ "try" +> incIndent
1052
+> if (hasCatch && hasFinally) then id ++ "try" +> incIndent else id
1053
+> generateStatements cs.TryStatements
1054
+> if (cs.CatchClauses<>null && cs.CatchClauses.Count>0) then
1056
++ "with" +> incIndent
1057
+> col sepNone cs.CatchClauses generateCatchClause
1058
+> decIndent else id;
1059
+> if (cs.FinallyStatements<>null && cs.FinallyStatements.Count>0) then
1061
++ "finally" +> incIndent
1062
+> col sepNone cs.FinallyStatements generateStatement
1063
+> decIndent else id;
1066
-- "(* Unknown statement type '" -- (c.GetType().Name)
1067
-- "' please report this to the F# team. *)")
1069
//---------------------------------------------------------------------------------------------
1070
// Support for class members (Custom attributes, paramters, etc..)
1072
let generateAttributeArg (c:CodeAttributeArgument) =
1074
+> if (c.Name<> null && c.Name.Length>0) then
1075
id -- c.Name -- "=" else id
1076
+> generateExpression c.Value;
1078
let generateCustomAttrDecl (c:CodeAttributeDeclaration) =
1080
-- (getBaseTypeRefString c.Name)
1081
+> if (c.Arguments.Count = 0) then id else
1082
id -- "(" +> (col sepArgs c.Arguments generateAttributeArg) -- ")"
1084
let generateCustomAttrDeclsList (c:CodeAttributeDeclaration list) =
1085
if (c.Length = 0) then
1088
id ++ "[<" +> (colT sepNlnSemiSpace c generateCustomAttrDecl) -- ">]"
1090
let generateCustomAttrDeclsForType (c:Choice<CodeAttributeDeclaration,string> list) (a:Reflection.TypeAttributes) =
1092
+> if (c.Length = 0)
1093
&& (a &&& TypeAttributes.Abstract = enum 0)
1094
&& (a &&& TypeAttributes.Sealed = enum 0) then id
1097
+> (colT sepNlnSemiSpace [ for x in c do
1099
| Choice1Of2 a -> yield generateCustomAttrDecl a
1100
| Choice2Of2 s -> yield (id -- s)
1101
if a &&& TypeAttributes.Abstract <> enum 0 then yield (id -- "AbstractClass" +> sepNlnSemiSpace)
1102
if a &&& TypeAttributes.Sealed <> enum 0 then yield (id -- "Sealed" +> sepNlnSemiSpace) ]
1107
VisibilityMask Specifies type visibility information.
1108
NotPublic Specifies that the class is not public.
1109
Public Specifies that the class is public.
1110
NestedPublic Specifies that the class is nested with public visibility.
1111
NestedPrivate Specifies that the class is nested with private visibility.
1112
NestedFamily Specifies that the class is nested with family visibility, and is thus accessible only by methods within its own type and any subtypes.
1113
NestedAssembly Specifies that the class is nested with assembly visibility, and is thus accessible only by methods within its assembly.
1114
NestedFamANDAssem Specifies that the class is nested with assembly and family visibility, and is thus accessible only by methods lying in the intersection of its family and assembly.
1115
NestedFamORAssem Specifies that the class is nested with family or assembly visibility, and is thus accessible only by methods lying in the union of its family and assembly.
1116
LayoutMask Specifies class layout information.
1117
AutoLayout Specifies that class fields are automatically laid out by the common language runtime.
1118
SequentialLayout Specifies that class fields are laid out sequentially, in the order that the fields were emitted to the metadata.
1119
ExplicitLayout Specifies that class fields are laid out at the specified offsets.
1120
ClassSemanticsMask Specifies class semantics information; the current class is contextful (else agile).
1121
Class Specifies that the type is a class.
1122
Interface Specifies that the type is an interface.
1123
DONE: Abstract Specifies that the type is abstract.
1124
DONE: Sealed Specifies that the class is concrete and cannot be extended.
1125
SpecialName Specifies that the class is special in a way denoted by the name.
1126
Import Specifies that the class or interface is imported from another module.
1127
Serializable Specifies that the class can be serialized.
1128
StringFormatMask Used to retrieve string information for native interoperability.
1129
AnsiClass LPTSTR is interpreted as ANSI.
1130
UnicodeClass LPTSTR is interpreted as UNICODE.
1131
AutoClass LPTSTR is interpreted automatically.
1132
CustomFormatClass LPSTR is interpreted by some implementation-specific means, which includes the possibility of throwing a NotSupportedException.
1133
CustomFormatMask Used to retrieve non-standard encoding information for native interop. The meaning of the values of these 2 bits is unspecified.
1134
BeforeFieldInit Specifies that calling static methods of the type does not force the system to initialize the type.
1135
ReservedMask Attributes reserved for runtime use.
1136
RTSpecialName Runtime should check name encoding.
1140
let generateCustomAttrDecls (c:CodeAttributeDeclarationCollection) =
1141
generateCustomAttrDeclsList (c |> Seq.cast |> Seq.toList)
1143
// NOTE: may contain custom attributes - this isn't supported
1144
let generateParamDecl (c:CodeParameterDeclarationExpression) =
1145
let dir = if (c.Direction <> FieldDirection.In) then " byref" else ""
1147
-! c.Name -- ":" +> generateTypeRef c.Type -- dir;
1149
// NOTE: may contain custom attributes - this isn't supported
1150
let generateAbstractParamDecl (c:CodeParameterDeclarationExpression) =
1151
let dir = if (c.Direction <> FieldDirection.In) then " byref" else ""
1152
id +> generateTypeRef c.Type -- dir
1154
// Find all overloads of the method, so we can produce [<OverloadID>]
1155
let getMethodOverloads (membs:CodeTypeMemberCollection) =
1156
let getMethodOverload map (n:CodeMemberMethod) =
1157
let n = (n.Name, getTypeRefSimple n.PrivateImplementationType)
1158
match Map.tryFind n map with
1161
let incMethodOverload (n:CodeMemberMethod) map =
1162
let n = (n.Name, getTypeRefSimple n.PrivateImplementationType)
1163
match Map.tryFind n map with
1164
| Some v -> Map.add n (v+1) map
1165
| None -> Map.add n 1 map
1168
|> codeDomCallBackNoScope
1169
(fun rcall (res,mlst) o ->
1171
| :? CodeMemberMethod as meth when meth.GetType() = (typeof<CodeMemberMethod>) ->
1172
// we have found another method
1173
(incMethodOverload meth res,
1175
getMethodOverload res meth,
1176
getTypeRefSimple meth.PrivateImplementationType
1178
| :? CodeTypeMemberCollection ->
1179
// recursively walk through member collection
1183
getMethodOverload m, a
1185
//---------------------------------------------------------------------------------------------
1186
// Fields, properties, constructors, methods
1189
let generateField (c:CodeMemberField) =
1191
+> generateCustomAttrDecls c.CustomAttributes
1192
+> if ((c.Attributes &&& MemberAttributes.ScopeMask) = MemberAttributes.Static) then
1194
++ "[<DefaultValue(false)>]"
1195
++ "static val mutable private " -! c.Name -- ":" +> generateTypeRef c.Type
1196
//++ (match c.InitExpression with
1198
elif ((c.Attributes &&& MemberAttributes.ScopeMask) = MemberAttributes.Const) then
1200
++ "static member " -! c.Name -- " = " +> generateExpression c.InitExpression // should have initial value!
1202
id ++ "[<DefaultValue(false)>]"
1203
++ "val mutable " -! c.Name -- ":" +> generateTypeRef c.Type
1205
/// Abstract property in the interface
1206
let generateInterfaceMemberProperty (c:CodeMemberProperty) =
1208
++ "abstract " -- c.Name -- " : "
1209
+> (if c.Parameters.Count > 0 then col sepStar c.Parameters generateAbstractParamDecl -- " -> " else id)
1210
+> generateTypeRef c.Type -- " with " -- (if c.HasGet && not c.HasSet then "get" elif c.HasGet && c.HasSet then "get,set" else "set")
1212
// REVIEW: this is not correct, it should follow same abstract/default/override logic
1213
// as methods. Unfortunately it isn't possible to declare "abstract" property with "default" implementation
1214
let generateClassProperty (typ:MemberGenerateType) (p:CodeMemberProperty) =
1216
(if typ = MemberGenerateType.InsideStruct ||
1217
typ = MemberGenerateType.InsideClass || // refuse to generate abstract methods
1218
p.Attributes &&& MemberAttributes.ScopeMask = MemberAttributes.Override ||
1219
p.Attributes &&& MemberAttributes.ScopeMask = MemberAttributes.Static
1223
+> generateInterfaceMemberProperty p))
1224
+> generateCustomAttrDecls p.CustomAttributes
1225
++ if typ = MemberGenerateType.InsideStruct then "member this."
1226
elif typ = MemberGenerateType.InsideClass then "member this."
1227
elif (p.Attributes &&& MemberAttributes.ScopeMask = MemberAttributes.Override) then "override this."
1228
elif (p.Attributes &&& MemberAttributes.ScopeMask = MemberAttributes.Static) then "static member "
1229
else "default this."
1232
+> if (not p.HasGet) then id else
1235
+> col sepArgs p.Parameters generateParamDecl
1236
-- ") : " +> generateTypeRef p.Type -- " ="
1237
+> generateStatementBlock (Some p.Type) p.GetStatements
1239
+> if (not p.HasSet) then id else
1241
++ (if p.HasGet then "and" else "with") -- " set("
1242
+> col sepNone p.Parameters (fun p -> (generateParamDecl p) -- ", ")
1243
-- "value:" +> generateTypeRef p.Type
1245
+> generateStatementBlock None p.SetStatements
1248
// The argument 'c' can be null when generating default ctor
1249
// (which is not generated by the compiler as in C#)
1250
let generateConstructor (c:CodeConstructor) =
1251
// Find all (non-static) fields
1252
withCtxt (fun ctx ->
1254
ctx.CurrentType.Members
1255
|> codeDomFlatFilter (fun o ->
1257
| :? CodeMemberField as fld ->
1259
(fld.Attributes &&& MemberAttributes.ScopeMask <> MemberAttributes.Static) &&
1260
(fld.Attributes &&& MemberAttributes.ScopeMask <> MemberAttributes.Const) &&
1261
(match fld.InitExpression with null -> false | _ -> true)
1266
|> List.map ( fun f -> f :?> CodeMemberField )
1268
+> (if c <> null then generateCustomAttrDecls c.CustomAttributes else id)
1270
+> if (c <> null) then (col sepArgs c.Parameters generateParamDecl) else id
1275
// Calling base constructor?
1276
+> if (c = null || c.BaseConstructorArgs = null || c.BaseConstructorArgs.Count = 0) then id else
1277
let (b, i) = ctx.BaseTypes
1279
| None -> failwith "Calling constructor of nonexisting base?"
1282
++ "inherit " +> generateTypeRef t -- "("
1283
+> col sepArgs c.BaseConstructorArgs generateExpression
1288
+> if ((c <> null && c.Statements.Count > 0) || not fields.IsEmpty || not ctx.DeclaredEvents.IsEmpty) then
1293
// Initialize events
1294
+> colT sepNone ctx.DeclaredEvents ( fun e ->
1296
++ "let t_event_" -- e.Name -- " = new DelegateEvent<" +> generateTypeRef e.Type -- ">();"
1297
++ "this._event_" -- e.Name -- " <- t_event_" -- e.Name -- ".Publish;"
1298
++ "this._invoke_" -- e.Name -- " <- t_event_" -- e.Name -- ".Trigger;" )
1299
// Initialize fields
1300
+> colT sepNone fields (fun fld ->
1301
id ++ "this." -- fld.Name -- " <- " +> generateExpression fld.InitExpression-- ";" )
1302
// Run other initialization code
1303
+> (if c <> null && c.Statements.Count > 0 then
1306
+> generateStatementBlock (None) c.Statements
1316
/// Abstract method in the interface
1317
let generateInterfaceMemberMethod (c:CodeMemberMethod, overloadId:int) =
1318
let custAttrs = (c.CustomAttributes |> Seq.cast |> Seq.toList)
1320
let tyargs, genTyArgs = processTypeArgs c.TypeParameters
1321
usingTyParams tyargs
1323
+> col sepNone c.Comments generateStatement
1324
+> generateCustomAttrDeclsList custAttrs
1329
+> if (c.Parameters.Count > 0) then
1330
id +> col sepStar c.Parameters generateAbstractParamDecl
1334
+> generateTypeRef c.ReturnType)
1336
/// By default all CodeDOM generated methods are 'virtual' which means that
1337
/// we have to generate "abstract and default" (unless we're in struct or
1338
/// we're implementing an interface, or the method is overriden)
1339
/// (NOTE: the same logic isn't properly implemented for properties)
1340
let generateMethod (typ:MemberGenerateType) (c:CodeMemberMethod) genAttrFunc =
1343
if (typ = MemberGenerateType.InsideInterface) then
1345
elif (typ = MemberGenerateType.InsideStruct) then
1347
elif (c.Attributes &&& MemberAttributes.ScopeMask = MemberAttributes.Static) then
1348
id, "static member "
1349
elif (c :? CodeEntryPointMethod) then
1350
id, "static member "
1351
elif (c.Attributes &&& MemberAttributes.ScopeMask = MemberAttributes.Abstract) then
1352
// refuse to generate abstract methods
1353
if typ = MemberGenerateType.InsideClass then
1356
(id +> generateInterfaceMemberMethod (c, -1)), ""
1357
elif (c.Attributes &&& MemberAttributes.ScopeMask = MemberAttributes.Override) then
1358
id, "override this."
1360
(id +> generateInterfaceMemberMethod (c, -1)),
1363
//REVIEW: This is mutating the CodeMemberMethod which is a little questionable
1364
if c.Name = "" then c.Name <- freshName ()
1365
if (mnm = "") then prefx else
1366
let tyargs, genTyArgs = processTypeArgs c.TypeParameters
1367
usingTyParams tyargs
1370
++ mnm -- c.Name +> genTyArgs -- " "
1371
-- " (" +> col sepArgs c.Parameters generateParamDecl -- ")"
1375
// We need to create mutable copy of all arguments except for "byref" arguments which are mutable
1377
+> col sepNone c.Parameters (fun (c:CodeParameterDeclarationExpression) ->
1378
if (c.Direction <> FieldDirection.In) then id else
1379
id ++ "let mutable " -- c.Name -- " = " -- c.Name )
1382
+> generateStatementBlock (Some c.ReturnType) c.Statements)
1384
/// Generates method code
1385
/// Generates comments and than calls 'generatMethod'
1386
let generateClassMemberMethod (typ:MemberGenerateType) (c:CodeMemberMethod, overloadId:int) =
1387
let custAttrs = (c.CustomAttributes |> Seq.cast |> Seq.toList)
1389
+> col sepNone c.Comments generateStatement
1390
+> generateMethod typ c (generateCustomAttrDeclsList custAttrs)
1392
let generateEntryPointMethod (typ:MemberGenerateType) (c:CodeEntryPointMethod) =
1394
+> (fun ctx -> {ctx with MainMethodForCurrentNamespace = Some (c, ctx.CurrentType)})
1395
+> (generateClassMemberMethod typ ((c :> CodeMemberMethod), -1))
1397
let generateEvent (c:CodeMemberEvent) =
1399
+> generateCustomAttrDecls c.CustomAttributes
1401
++ "member this." -- c.Name -- " ="
1403
++ "this._event_" -- c.Name
1406
let generateEventField (c:CodeMemberEvent) =
1408
+> (fun ctx -> { ctx with DeclaredEvents = c::ctx.DeclaredEvents })
1409
++ "[<DefaultValue(false)>]"
1410
++ "val mutable _event_" -- c.Name -- " : IDelegateEvent<" +> generateTypeRef c.Type -- ">;"
1411
++ "[<DefaultValue(false)>]"
1412
++ "val mutable _invoke_" -- c.Name -- " : obj[] -> unit;";
1415
let generateCodeSnippetMember (c:CodeSnippetTypeMember) =
1417
// Remove additional spaces to make sure that the code aligns with the rest
1418
// CONSIDER: what to do with '\t' ?
1419
let countSpaces (s:string) =
1420
let rec countSpacesAux (s:string) i n =
1421
if i >= s.Length then n
1422
elif s.[i] = ' ' then countSpacesAux s (i + 1) (n + 1)
1424
countSpacesAux s 0 0
1426
let lines = c.Text.Split([| '\n'; '\r' |], StringSplitOptions.RemoveEmptyEntries)
1427
if lines.Length > 0 then
1428
let spaces = Array.foldBack (countSpaces >> min) lines Int32.MaxValue
1429
let lines = lines |> Array.map (fun s -> s.[spaces..])
1431
// ASP.NET doesn�t use any comments or custom attributes,
1432
// but I assume this would be the right order
1434
+> col sepNone c.Comments generateStatement
1435
+> generateLinePragma c.LinePragma
1436
+> generateCustomAttrDecls c.CustomAttributes
1437
+> colT sepNone lines ((++) id)
1442
//---------------------------------------------------------------------------------------------
1443
// Interfaces and classes and other types
1445
let generateInterfaceImplementation (ifcnfo:KeyValuePair<_, _>) =
1446
let name = ifcnfo.Key
1447
let membs = ifcnfo.Value
1449
++ "interface " -- name -- " with"
1451
+> colT sepNln membs (generateClassMemberMethod MemberGenerateType.InsideInterface)
1454
let generateClassMember typ (c:CodeTypeMember) =
1456
| :? CodeTypeDeclaration -> id
1457
| :? CodeMemberField
1458
| :? CodeMemberEvent
1459
| :? CodeConstructor
1460
| :? CodeMemberProperty ->
1462
+> col sepNone c.Comments generateStatement
1464
| :? CodeMemberField as cm -> generateField cm
1465
| :? CodeMemberEvent as cm -> generateEvent cm
1466
| :? CodeConstructor as cm -> generateConstructor cm
1467
| :? CodeMemberProperty as cm -> generateClassProperty typ cm
1468
| _ -> failwithf "unimplemented CodeTypeMember '%A'" c
1470
id ++ "(* Member of type '" +> str (c.GetType().Name) -- "' is not supported by the CodeDOM provider and was omitted *)"
1473
let generateClassOrStruct structOrCls (scope:string list) (c:CodeTypeDeclaration) ctx =
1476
if (structOrCls = "struct") then MemberGenerateType.InsideStruct
1477
else MemberGenerateType.InsideClass
1479
// Find all constructors
1481
c |> codeDomFlatFilter (fun o ->
1483
| :? CodeTypeDeclaration as dc -> (false, dc = c)
1484
| :? CodeConstructor as c -> (true, true)
1485
| _ -> (false, true) )
1486
let anyCtor = (ctors.Length > 0)
1487
let useImplicitCtor = (ctors.Length <= 1)
1489
// Find base classes
1490
let (baseClass, interfaces) = resolveHierarchy c ctx
1492
// Find fields and their types
1493
let (ft, pt, flds, props, meths, events) =
1494
c.Members |> codeDomCallBackNoScope (fun rcall ((ft, pt,flds, props, meths, events) as acc) o ->
1496
| :? CodeMemberField as fld -> (Map.add fld.Name fld.Type ft, pt, Map.add fld.Name fld flds, props, meths, events)
1497
| :? CodeMemberProperty as prop -> (ft, Map.add prop.Name prop.Type pt, flds, Map.add prop.Name prop props, meths, events)
1498
| :? CodeMemberMethod as meth -> (ft, pt, flds, props, Map.add meth.Name meth meths, events)
1499
| :? CodeMemberEvent as ev -> (ft, pt, flds, props, meths, Map.add ev.Name ev events)
1500
| :? CodeTypeMemberCollection -> rcall acc o
1501
| _ -> acc; ) (Map.empty, Map.empty, Map.empty, Map.empty, Map.empty, Map.empty)
1503
// Find all overloads of the method, so we can produce [<OverloadID>]
1504
let (getOverload, allmeths) = getMethodOverloads(c.Members)
1506
// Get tripple with method info, overload id and name of the interface where
1507
// it belongs (if it's "PrivateImplementationType")
1508
let allmeths = allmeths |> List.map ( fun (cm, ovIdx, intrfcName) ->
1509
match getOverload cm with | 1 -> (cm, -1, intrfcName) | _ -> (cm, ovIdx, intrfcName) )
1511
// Split between methods of the class
1512
// and methods that implemnet some interface
1513
let ifcTable = new Dictionary<string, ResizeArray<CodeMemberMethod*int>>()
1515
allmeths |> mapFilter (fun (m, idx, ifn) ->
1516
match m.PrivateImplementationType, m.ImplementationTypes.Count with
1517
| null, 0 -> Some((m,idx))
1519
let b,v = ifcTable.TryGetValue(ifn)
1522
let rs = new ResizeArray<CodeMemberMethod*int>()
1523
ifcTable.Add(ifn, rs)
1529
for implementedInterface in m.ImplementationTypes do
1530
let b,v = ifcTable.TryGetValue(getTypeRefSimple implementedInterface)
1533
let rs = new ResizeArray<CodeMemberMethod*int>()
1534
ifcTable.Add(getTypeRefSimple implementedInterface, rs)
1539
| _, _ -> failwith "CodeMethodMember must not have both ImplementationTypes and PrivateImplementationType set.")
1542
// NOTE: we ignore class visibility and also IsPartial property
1543
// Declare type arguments and generate class
1544
let tyargs, genTyArgs = processTypeArgs c.TypeParameters
1545
(usingTyParams tyargs
1547
+> (fun ctx -> { ctx with BaseTypes = (baseClass, interfaces); FieldTypes = ft; PropertyTypes = pt; AllFields=flds; AllProps=props; AllMeths=meths; AllEvents=events })
1548
+> (if c.IsPartial then id -- " (* partial *)" else id)
1549
+> col sepNone scope (fun s -> id -- s -- "_") -- c.Name
1551
// Generate implicit constructor args
1552
+> (if useImplicitCtor then
1555
| [ :? CodeConstructor as ctor ] -> [ for p in ctor.Parameters -> p ]
1557
id -- "(" +> col sepArgs parameters generateParamDecl -- ")"
1562
+> (match baseClass with
1564
if useImplicitCtor then
1566
| [ :? CodeConstructor as ctor ] when ctor.BaseConstructorArgs <> null || ctor.BaseConstructorArgs.Count <> 0 ->
1568
++ "inherit " +> generateTypeRef bc -- "("
1569
+> col sepArgs ctor.BaseConstructorArgs generateExpression
1572
id ++ "inherit " +> generateTypeRef bc -- "()"
1574
id ++ "inherit " +> generateTypeRef bc -- " "
1577
// Filter and generate members
1578
+> colFilterT<CodeMemberEvent> sepNln c.Members generateEventField
1579
+> colFilter<CodeMemberField> sepNln c.Members (generateClassMember typ)
1580
+> colFilter<CodeTypeConstructor> sepNln c.Members (generateClassMember typ)
1581
+> colFilter<CodeMemberEvent> sepNln c.Members (generateClassMember typ)
1583
// Generate default empty constructor for classes
1584
// without constructors (but not for structs!)
1585
+> if (not useImplicitCtor && anyCtor) then
1586
colFilter<CodeConstructor> sepNln c.Members (generateClassMember typ)
1587
elif (not useImplicitCtor && structOrCls = "class" && not c.IsPartial) then
1588
generateConstructor null
1593
+> colFilterT<CodeSnippetTypeMember> sepNln c.Members generateCodeSnippetMember
1594
// Properties, methods, interface implementations
1595
+> colFilter<CodeMemberProperty> sepNln c.Members (generateClassMember typ)
1596
+> colT sepNln allmeths (generateClassMemberMethod typ)
1597
+> colT sepNln ifcTable generateInterfaceImplementation
1598
+> colFilterT<CodeEntryPointMethod> sepNln c.Members (generateEntryPointMethod typ)
1601
let generateInterface (scope:string list) (c:CodeTypeDeclaration) =
1603
let (getOverload, allmeths) = getMethodOverloads c.Members
1604
let allmeths = allmeths |> List.map ( fun (cm, ovIdx, _) ->
1605
match getOverload cm with | 1 -> (cm, -1) | _ -> (cm, ovIdx) )
1607
let castToProp (a:CodeTypeMember) = (a :?> CodeMemberProperty)
1609
// NOTE: visibility is ignored
1610
let tyargs, genTyArgs = processTypeArgs c.TypeParameters
1611
usingTyParams tyargs
1613
+> col sepNone scope (fun s -> id -- s -- "_") -- c.Name
1617
+> col sepNln c.BaseTypes (fun (cr:CodeTypeReference) -> id ++ "inherit " +> generateTypeRef cr)
1618
+> colFilter<CodeMemberProperty> sepNln c.Members (castToProp >> generateInterfaceMemberProperty)
1619
+> colT sepNln allmeths generateInterfaceMemberMethod
1622
let generateDelegate (scope:string list) (c:CodeTypeDelegate) =
1623
let tyargs, genTyArgs = processTypeArgs c.TypeParameters
1624
usingTyParams tyargs
1626
+> col sepNone scope (fun s -> id -- s -- "_") -- c.Name
1628
-- " = delegate of "
1629
+> if (c.Parameters.Count = 0) then
1632
col sepStar c.Parameters (fun (p:CodeParameterDeclarationExpression) ->
1633
id +> generateTypeRef p.Type )
1635
+> match c.ReturnType with
1636
| null -> id -- "unit"
1637
| rt -> generateTypeRef rt)
1639
let generateEnumField (index:int) (c:CodeMemberField) =
1641
++ "| " -- c.Name -- " = "
1642
+> match c.InitExpression with
1644
| :? CodePrimitiveExpression as p -> generatePrimitiveExpr None p
1645
| _ -> failwith "Invalid enum !";
1647
let generateEnum (scope:string list) (c:CodeTypeDeclaration) =
1648
let counter = createCounter()
1650
+> col sepNone scope (fun s -> id -- s -- "_") -- c.Name
1653
+> col sepNone c.Members (fun c -> generateEnumField (counter()) c)
1656
let generateTypeDecl index (scope:string list, c:CodeTypeDeclaration) =
1658
[ for a in c.CustomAttributes do yield Choice1Of2 a
1660
| :? CodeTypeDelegate as cd -> ()
1661
| c when c.IsClass -> ()
1662
| c when c.IsInterface -> yield Choice2Of2 "Interface"
1663
| c when c.IsEnum -> ()
1664
| c when c.IsStruct -> yield Choice2Of2 "Struct"
1667
let genAttribs = generateCustomAttrDeclsForType attribs c.TypeAttributes
1670
// Attributes go before 'type' for the first type declaration and after 'and' for others
1671
+> (if index <> 0 then id else genAttribs)
1672
+> col sepNone c.Comments generateStatement
1673
++ if (index = 0) then "type " else "and "
1675
+> (if index = 0 then id else genAttribs)
1676
+> (fun ctx -> { ctx with CurrentType = c })
1678
| :? CodeTypeDelegate as cd -> generateDelegate scope cd
1679
| c when c.IsClass -> generateClassOrStruct "class" scope c
1680
| c when c.IsInterface -> generateInterface scope c
1681
| c when c.IsEnum -> generateEnum scope c
1682
| c when c.IsStruct -> generateClassOrStruct "struct" scope c
1684
// NOTE: I believe this is full match..
1685
id ++ "(* Type '" -- (c.Name) -- "' is not supported by the CodeDOM provider and was omitted. *)"
1687
+> (fun ctx -> { ctx with DeclaredEvents = []; CurrentType = null; BaseTypes = (None, []); FieldTypes = Map.empty; PropertyTypes = Map.empty; })
1689
/// Generates a main method.
1690
let generateMainMethod (c:CodeEntryPointMethod, t:CodeTypeDeclaration) (ns:CodeNamespace) =
1691
let retType = getTypeRefSimple c.ReturnType
1693
CodeAttributeDeclaration("EntryPoint", [||]) :: (c.CustomAttributes |> Seq.cast |> Seq.toList)
1695
if ((c.Parameters.Count = 0) || (c.Parameters.Count = 1 && (getTypeRefSimple c.Parameters.[0].Type) = "string[]" ))
1696
&& (retType = "int" || retType = "unit")
1699
++ "[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]"
1700
++ "module __EntryPoint ="
1702
+> (generateCustomAttrDeclsList custAttrs)
1703
++ "let Main (args:string[]) ="
1705
// REVIEW: Do we need to pass this through the "rename" table? Could use '(generateTypeRef t)', but we don't have a CodeTypeReference
1706
++ t.Name -- "." -- (c.Name)
1707
+> if c.Parameters.Count = 1
1710
// F# only supports main methods returning int. If we're asked to emit one that returns unit, just return 1.
1711
+> if retType = "unit" then id ++ "0" else id
1715
id ++ "(* Could not generate entry point for method '" -- (c.Name) -- "'. *)"
1717
//---------------------------------------------------------------------------------------------
1718
// Namespaces and compilation units
1720
/// Returns CodeNamespace, list of classes with scope (which includes class names
1721
/// of containing classes and sequence of class renames)
1722
let preprocessNamespace (c:CodeNamespace) =
1724
// Extract flat class structure
1727
|> codeDomCallbackWithScope (fun rcall scope acc o ->
1729
| :? CodeTypeDeclaration as dc ->
1730
//sprintf "preprocessNamespace: rcall for type c.Name = %s\n" dc.Name |> System.Windows.Forms.MessageBox.Show |> ignore
1731
rcall (dc.Name::scope) ((scope, dc)::acc) (box dc.Members)
1732
| _ -> rcall scope acc o) [] [];
1733
let flatClasses = flatClasses |> List.rev
1735
// Get all renamed classes - this changes file structure, but at least it works
1736
let addNameWithScope n (scope:string list) acc =
1737
let scn = String.Join("_",Array.ofList scope) + "_" + n
1739
scope |> List.fold ( fun (prefix,st) e ->
1740
let npref = e + prefix
1741
let nmap = Map.add (npref + n) scn st
1742
("." + npref, nmap) ) (".", Map.add n scn acc)
1745
//sprintf "c.Name = %s, #flatClasses = %d\n" c.Name flatClasses.Length |> System.Windows.Forms.MessageBox.Show |> ignore
1749
|> List.fold ( fun acc ((scope:string list), ty) ->
1750
if (scope.Length = 0) then acc
1751
else addNameWithScope ty.Name scope acc ) Map.empty
1753
//if (renames |> Seq.length) > 0 then
1754
// sprintf "#renames = %d\n" (renames |> Seq.length) |> System.Windows.Forms.MessageBox.Show |> ignore
1756
(c, flatClasses, renames |> Map.toSeq);
1758
let generateImport (c:CodeNamespaceImport) =
1759
id ++ "open " -- c.Namespace
1761
/// Generates namespace code - takes output from 'preprocessNamespace'
1762
let generateNamespaceInternal (c:CodeNamespace, extraImports:seq<CodeNamespaceImport>, flatClasses, containing) =
1763
let counter = createCounter()
1766
|> List.fold (fun st (scope, (c:CodeTypeDeclaration)) ->
1767
if (c.IsInterface) then
1768
let st = Set.add c.Name st
1769
Set.add (String.concat "." (scope@[c.Name])) st
1773
+> ( fun ctx -> { ctx with CurrentNamespace = c.Name; DeclaredInterfaces = ifcSet } )
1774
+> col sepNone c.Comments generateStatement
1775
+> ((if String.IsNullOrEmpty c.Name then id ++ "namespace global" else id ++ "namespace " -! c.Name) (* +> incIndent *) )
1777
++ "#nowarn \"49\" // uppercase argument names"
1778
++ "#nowarn \"67\" // this type test or downcast will always hold"
1779
++ "#nowarn \"66\" // this upcast is unnecessary - the types are identical"
1780
++ "#nowarn \"58\" // possible incorrect indentation.." // (when using CodeSnippets ie. in ASP.NET)
1781
++ "#nowarn \"57\" // do not use create_DelegateEvent"
1782
++ "#nowarn \"51\" // address-of operator can occur in the code"
1783
++ "#nowarn \"1183\" // unused 'this' reference"
1784
+> colT sepNone containing (fun s -> id ++ "open " -- s)
1786
+> col sepNone extraImports generateImport
1787
+> col sepNone c.Imports generateImport
1790
++ "exception ReturnException" +> uniqid -- " of obj"
1791
++ "exception ReturnNoneException" +> uniqid
1794
++ "module FuncConvertFinalOverload" +> uniqid -- " ="
1795
++ " // This extension member adds to the FuncConvert type and is the last resort member in the method overloading rules. "
1796
++ " type global.Microsoft.FSharp.Core.FuncConvert with"
1797
++ " /// A utility function to convert function values from tupled to curried form"
1798
++ " static member FuncFromTupled (f:'T -> 'Res) = f"
1801
+> colT sepNln flatClasses (fun c -> generateTypeDecl (counter()) c)
1802
+> withCtxt (fun ctx -> match ctx.MainMethodForCurrentNamespace with None -> id | Some mainMethod -> (generateMainMethod mainMethod c))
1803
+> if (c.Name<>null && c.Name.Length>0) then decIndent else id
1804
+> ( fun ctx -> { ctx with CurrentNamespace = ""; MainMethodForCurrentNamespace = None } )
1807
/// Generate code for namespace without compilation unit
1808
let generateNamespace (c:CodeNamespace) =
1809
let (cn, b, _) = preprocessNamespace c
1810
generateNamespaceInternal (cn, [], b, [])
1812
/// Generate code for type declaration (not included in namespace)
1813
let generateTypeDeclOnly (c:CodeTypeDeclaration) =
1814
let ns = new CodeNamespace()
1815
ns.Types.Add(c) |> ignore
1816
let ((_, flatClasses, _), containing) = (preprocessNamespace ns, [])
1817
let counter = createCounter()
1820
++ "exception ReturnException" +> uniqid -- " of obj"
1821
++ "exception ReturnNoneException" +> uniqid
1823
+> colT sepNln flatClasses (fun c -> generateTypeDecl (counter()) c)
1825
/// Generate code for compile unit (file)
1826
let generateCompileUnit (c:CodeCompileUnit) =
1828
// Generate code for the compilation unit
1830
| :? CodeSnippetCompileUnit as cs ->
1831
id +> generateLinePragma cs.LinePragma ++ cs.Value
1833
let preprocNs = c.Namespaces |> Seq.cast |> Seq.map preprocessNamespace
1834
let renames = preprocNs |> Seq.collect (fun (_, _, renames) -> renames)
1835
let getContainingNamespaces (c:CodeNamespace) nslist =
1836
nslist |> List.filter ( fun (n:string) -> c.Name.StartsWith(n) )
1837
let (namespacesWithPrev, _) =
1838
(([], []),preprocNs) ||> Seq.fold (fun (res, tmpNames) (c, cls, renames) ->
1839
(((c, cls, renames), getContainingNamespaces c tmpNames)::res, c.Name::tmpNames) )
1840
let namespacesWithPrev = namespacesWithPrev |> Seq.toList |> List.rev
1842
// Move a set of 'open' declarations with no namespace into the first real namespace
1843
let namespacesWithPrev =
1844
match namespacesWithPrev with
1845
| ((cn1, flatClasses1, renames1), containing1) ::
1846
((cn2, flatClasses2, renames2), containing2) ::
1848
when System.String.IsNullOrEmpty cn1.Name && flatClasses1.IsEmpty ->
1849
(cn2, [ for cni in cn1.Imports -> cni ], flatClasses2, containing2) ::
1850
(rest |> List.map (fun ((a,b,_),d) -> (a,[ ],b,d)))
1852
rest |> List.map (fun ((a,b,_),d) -> (a,[ ],b,d))
1854
(fun ctx -> { ctx with TypeRenames = Map.ofSeq renames; } )
1855
++ "//------------------------------------------------------------------------------"
1856
++ "// <autogenerated>"
1857
++ "// This code was generated by a tool."
1858
++ "// Runtime Version: " +> (str System.Environment.Version)
1860
++ "// Changes to this file may cause incorrect behavior and will be lost if "
1861
++ "// the code is regenerated."
1862
++ "// </autogenerated>"
1863
++ "//------------------------------------------------------------------------------"
1865
+> colT sepNln namespacesWithPrev generateNamespaceInternal
1867
//---------------------------------------------------------------------------------------------