forked from dotnet/fsharp
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsr.fs
More file actions
174 lines (145 loc) · 8.09 KB
/
Copy pathsr.fs
File metadata and controls
174 lines (145 loc) · 8.09 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
// Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.FSharp.Compiler
open Microsoft.FSharp.Core
open Microsoft.FSharp.Core.Operators
open Microsoft.FSharp.Collections
open Microsoft.FSharp.Reflection
open System.Globalization
open System.IO
open System.Text
open System.Reflection
module internal SR =
let private resources = lazy (new System.Resources.ResourceManager("fsstrings", System.Reflection.Assembly.GetExecutingAssembly()))
let GetString(name:string) =
let s = resources.Force().GetString(name, System.Globalization.CultureInfo.CurrentUICulture)
#if DEBUG
if null = s then
System.Diagnostics.Debug.Assert(false, sprintf "**RESOURCE ERROR**: Resource token %s does not exist!" name)
#endif
s
let GetObject(name:string) =
let o = resources.Force().GetObject(name, System.Globalization.CultureInfo.CurrentUICulture)
#if DEBUG
if null = o then
System.Diagnostics.Debug.Assert(false, sprintf "**RESOURCE ERROR**: Resource token %s does not exist!" name)
#endif
o
module internal DiagnosticMessage =
open Microsoft.FSharp.Core.LanguagePrimitives.IntrinsicOperators
open Microsoft.FSharp.Reflection
open System.Reflection
open Internal.Utilities.StructuredFormat
let staticInvokeFlags = BindingFlags.Public ||| BindingFlags.InvokeMethod ||| BindingFlags.Static
let mkFunctionValue (tys: System.Type[]) (impl:obj->obj) =
FSharpValue.MakeFunction(FSharpType.MakeFunctionType(tys.[0],tys.[1]), impl)
let funTyC = typeof<(obj -> obj)>.GetGenericTypeDefinition()
let mkFunTy a b = funTyC.MakeGenericType([| a;b |])
let isNamedType(ty:System.Type) = not (ty.IsArray || ty.IsByRef || ty.IsPointer)
let isFunctionType (ty1:System.Type) =
isNamedType(ty1) && ty1.IsGenericType && (ty1.GetGenericTypeDefinition()).Equals(funTyC)
let rec destFunTy (ty:System.Type) =
if isFunctionType ty then
ty, ty.GetGenericArguments()
else
match ty.BaseType with
| null -> failwith "destFunTy: not a function type"
| b -> destFunTy b
let buildFunctionForOneArgPat (ty: System.Type) impl =
let _,tys = destFunTy ty
let rty = tys.[1]
// PERF: this technique is a bit slow (e.g. in simple cases, like 'sprintf "%x"')
mkFunctionValue tys (fun inp -> impl rty inp)
let capture1 (fmt:string) i args ty (go : obj list -> System.Type -> int -> obj) : obj =
match fmt.[i] with
| '%' -> go args ty (i+1)
| 'd'
| 'f'
| 's' -> buildFunctionForOneArgPat ty (fun rty n -> go (n::args) rty (i+1))
| _ -> failwith "bad format specifier"
// newlines and tabs get converted to strings when read from a resource file
// this will preserve their original intention
let postProcessString (s : string) =
s.Replace("\\n","\n").Replace("\\t","\t")
let createMessageString (messageString : string) (fmt : Printf.StringFormat<'T>) : 'T =
let fmt = fmt.Value // here, we use the actual error string, as opposed to the one stored as fmt
let len = fmt.Length
/// Function to capture the arguments and then run.
let rec capture args ty i =
if i >= len || (fmt.[i] = '%' && i+1 >= len) then
let b = new System.Text.StringBuilder()
b.AppendFormat(messageString, (Array.ofList (List.rev args))) |> ignore
box(b.ToString())
// REVIEW: For these purposes, this should be a nop, but I'm leaving it
// in incase we ever decide to support labels for the error format string
// E.g., "<name>%s<foo>%d"
elif System.Char.IsSurrogatePair(fmt,i) then
capture args ty (i+2)
else
match fmt.[i] with
| '%' ->
let i = i+1
capture1 fmt i args ty capture
| _ ->
capture args ty (i+1)
(unbox (capture [] (typeof<'T>) 0) : 'T)
type ResourceString<'T>(fmtString : string, fmt : Printf.StringFormat<'T>) =
member a.Format =
createMessageString fmtString fmt
let DeclareResourceString ((messageID : string),(fmt : Printf.StringFormat<'T>)) =
let mutable messageString = SR.GetString(messageID)
#if DEBUG
// validate that the message string exists
let fmtString = fmt.Value
if null = messageString then
System.Diagnostics.Debug.Assert(false, sprintf "**DECLARED MESSAGE ERROR** String resource %s does not exist" messageID)
messageString <- ""
// validate the formatting specifiers
let countFormatHoles (s : string) =
// remove escaped format holes
let s = s.Replace("{{","").Replace("}}","")
let len = s.Length - 2
let mutable pos = 0
let mutable nHoles = 0
let mutable order = Set.empty<int>
while pos < len do
if s.[pos] = '{' then
let mutable pos' = pos+1
while System.Char.IsNumber(s.[pos']) do
pos' <- pos' + 1
if pos' > pos+1 && s.[pos'] = '}' then
nHoles <- nHoles + 1
let ordern = (int) (s.[(pos+1) .. (pos'-1)])
order <- order.Add(ordern)
pos <- pos'
pos <- pos + 1
// the sort should be unnecessary, but better safe than sorry
nHoles,Set.toList order |> List.sort |> List.rev
let countFormatPlaceholders (s : string) =
// strip any escaped % characters - yes, this will fail if given %%%...
let s = s.Replace("%%","")
if s = "" then
0
else
let len = s.Length - 1
let mutable pos = 0
let mutable nFmt = 0
while pos < len do
if s.[pos] = '%' &&
(s.[pos+1] = 'd' || s.[pos+1] = 's' || s.[pos+1] = 'f') then
nFmt <- nFmt + 1
pos <- pos + 2 ;
else
pos <- pos + 1 ;
nFmt
let nHoles,holes = countFormatHoles messageString
let nPlaceholders = countFormatPlaceholders fmtString
// first, verify that the number of holes in the message string does not exceed the
// largest hole reference
if holes <> [] && holes.[0] > nHoles - 1 then
System.Diagnostics.Debug.Assert(false, sprintf "**DECLARED MESSAGE ERROR** Message string %s contains %d holes, but references hole %d" messageID nHoles holes.[0])
// next, verify that the number of format placeholders is the same as the number of holes
if nHoles <> nPlaceholders then
System.Diagnostics.Debug.Assert(false, sprintf "**DECLARED MESSAGE ERROR** Message string %s contains %d holes, but its format specifier contains %d placeholders" messageID nHoles nPlaceholders)
#endif
messageString <- postProcessString messageString
new ResourceString<'T>(messageString, fmt)