Skip to content

Commit bc367c0

Browse files
authored
fix(rust): Add support for System.Text.Rune (#4997)
1 parent 389edef commit bc367c0

10 files changed

Lines changed: 783 additions & 1 deletion

File tree

‎src/Fable.Transforms/Replacements.Util.fs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,7 @@ type BuiltinType =
294294
| BclDateTimeOffset
295295
| BclDateOnly
296296
| BclTimeOnly
297+
| BclRune
297298
| BclTimer
298299
| BclHashSet of Type
299300
| BclDictionary of key: Type * value: Type
@@ -313,6 +314,7 @@ let (|BuiltinDefinition|_|) =
313314
| Types.datetimeOffset -> ValueSome BclDateTimeOffset
314315
| Types.dateOnly -> ValueSome BclDateOnly
315316
| Types.timeOnly -> ValueSome BclTimeOnly
317+
| Types.rune -> ValueSome BclRune
316318
| "System.Timers.Timer" -> ValueSome BclTimer
317319
| Types.fsharpSet -> ValueSome(FSharpSet(Any))
318320
| Types.fsharpMap -> ValueSome(FSharpMap(Any, Any))

‎src/Fable.Transforms/Rust/AST/Rust.AST.Helpers.fs‎

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,47 @@ module Literals =
337337
span = DUMMY_SP
338338
}
339339

340+
let private normalizeStringLiteral (value: Symbol) =
341+
let mutable builder: ValueOption<System.Text.StringBuilder> = ValueNone
342+
let mutable index = 0
343+
344+
while index < value.Length do
345+
let current = value[index]
346+
347+
if
348+
System.Char.IsHighSurrogate(current)
349+
&& index + 1 < value.Length
350+
&& System.Char.IsLowSurrogate(value[index + 1])
351+
then
352+
match builder with
353+
| ValueSome builder -> builder.Append(value, index, 2) |> ignore
354+
| ValueNone -> ()
355+
356+
index <- index + 2
357+
elif System.Char.IsSurrogate(current) then
358+
match builder with
359+
| ValueSome builder -> builder.Append('\uFFFD') |> ignore
360+
| ValueNone ->
361+
let newBuilder = System.Text.StringBuilder(value.Length)
362+
newBuilder.Append(value, 0, index) |> ignore
363+
newBuilder.Append('\uFFFD') |> ignore
364+
builder <- ValueSome newBuilder
365+
366+
index <- index + 1
367+
else
368+
match builder with
369+
| ValueSome builder -> builder.Append(current) |> ignore
370+
| ValueNone -> ()
371+
372+
index <- index + 1
373+
374+
match builder with
375+
| ValueSome builder -> builder.ToString()
376+
| ValueNone -> value
377+
340378
let mkStrLit (value: Symbol) : Lit =
379+
let value = normalizeStringLiteral value
380+
341381
{
342382
token = mkStrTokenLit (value.escape_debug ())
343383
kind = LitKind.Str(value, StrStyle.Cooked)

‎src/Fable.Transforms/Rust/Fable2Rust.fs‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -866,6 +866,8 @@ module TypeInfo =
866866
let transformTimeOnlyType com ctx : Rust.Ty =
867867
transformImportType com ctx [] "TimeOnly" "TimeOnly"
868868

869+
let transformRuneType _com _ctx : Rust.Ty = primitiveType "char"
870+
869871
let transformTimerType com ctx : Rust.Ty =
870872
transformImportType com ctx [] "DateTime" "Timer"
871873

@@ -1130,6 +1132,7 @@ module TypeInfo =
11301132
| Replacements.Util.BclDateTimeOffset -> transformDateTimeOffsetType com ctx
11311133
| Replacements.Util.BclDateOnly -> transformDateOnlyType com ctx
11321134
| Replacements.Util.BclTimeOnly -> transformTimeOnlyType com ctx
1135+
| Replacements.Util.BclRune -> transformRuneType com ctx
11331136
| Replacements.Util.BclTimer -> transformTimerType com ctx
11341137
| Replacements.Util.BclHashSet(genArg) -> transformHashSetType com ctx genArg
11351138
| Replacements.Util.BclDictionary(k, v) -> transformHashMapType com ctx [ k; v ]

‎src/Fable.Transforms/Rust/Replacements.fs‎

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ let coreModFor =
5353
| BclDateTimeOffset -> "DateTimeOffset"
5454
| BclDateOnly -> "DateOnly"
5555
| BclTimeOnly -> "TimeOnly"
56+
| BclRune -> "Rune"
5657
| BclTimer -> "Timer"
5758
| BclTimeSpan -> "TimeSpan"
5859
| FSharpSet _ -> "Set"
@@ -427,7 +428,8 @@ let applyOp (com: ICompiler) (ctx: Context) r t opName (args: Expr list) =
427428
match argTypes with
428429
// | Number(BigInt as kind,_)::_ ->
429430
// Helper.LibCall(com, "BigInt", opName, t, args, argTypes, ?loc=r)
430-
| Builtin(BclDateTime | BclDateTimeOffset | BclTimeOnly | BclTimeSpan) :: _ -> nativeOp opName argTypes args
431+
| Builtin(BclDateTime | BclDateTimeOffset | BclTimeOnly | BclTimeSpan | BclRune) :: _ ->
432+
nativeOp opName argTypes args
431433
| Builtin(FSharpSet _) :: _ ->
432434
let methName =
433435
match opName with
@@ -453,6 +455,7 @@ let isCompatibleWithNativeComparison =
453455
// | List _
454456
| Builtin(BclGuid) -> true
455457
| Builtin(BclTimeSpan) -> true
458+
| Builtin(BclRune) -> true
456459
| _ -> false
457460

458461
// Overview of hash rules:
@@ -471,6 +474,8 @@ let referenceHash (com: ICompiler) ctx r (arg: Expr) =
471474
let getHashCode (com: ICompiler) ctx r (arg: Expr) =
472475
match arg.Type with
473476
| HasReferenceEquality com _ -> referenceHash com ctx r arg
477+
| Builtin BclRune -> Helper.LibCall(com, "Rune", "getHashCode", Int32.Number, [ arg ], ?loc = r)
478+
| Char -> Helper.LibCall(com, "Char", "GetHashCode", Int32.Number, [ arg ], ?loc = r)
474479
| _ -> Helper.InstanceCall(arg, "getHashCode", Int32.Number, [], [], [], ?loc = r)
475480

476481
let objectHash (com: ICompiler) ctx r (arg: Expr) =
@@ -658,6 +663,7 @@ let rec getZero (com: ICompiler) (ctx: Context) (t: Type) =
658663
| Builtin BclDateTimeOffset -> Helper.LibCall(com, "DateTimeOffset", "zero", t, [])
659664
| Builtin BclDateOnly -> Helper.LibCall(com, "DateOnly", "zero", t, [])
660665
| Builtin BclTimeOnly -> Helper.LibCall(com, "TimeOnly", "zero", t, [])
666+
| Builtin BclRune -> Helper.LibCall(com, "Rune", "zero", t, [])
661667
| Builtin BclTimeSpan -> Helper.LibValue(com, "TimeSpan", "zero", t)
662668
| Builtin(FSharpSet genArg) -> makeSet com ctx None t [] genArg
663669
| Builtin BclGuid -> Helper.LibValue(com, "Guid", "empty", t)
@@ -1424,6 +1430,7 @@ let strings (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr opt
14241430
| _ -> None
14251431
| "get_Length", Some c, _ -> Helper.LibCall(com, "String", "length", t, c :: args, ?loc = r) |> Some
14261432
| "get_Chars", Some c, _ -> Helper.LibCall(com, "String", "getCharAt", t, c :: args, ?loc = r) |> Some
1433+
| "EnumerateRunes", Some c, _ -> Helper.LibCall(com, "String", "enumerateRunes", t, [ c ], ?loc = r) |> Some
14271434
| "CompareOrdinal", None, _ ->
14281435
match args with
14291436
| [ ExprType String; ExprType String ] ->
@@ -2860,6 +2867,69 @@ let timeOnly (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr op
28602867
| "Add" when args.Length = 2 -> makeInstanceCall r t i thisArg.Value "add2" args |> Some
28612868
| meth -> makeMemberCall com ctx r t i "TimeOnly" meth thisArg args |> Some
28622869

2870+
let rune (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr option) (args: Expr list) =
2871+
let libCall memberName callArgs =
2872+
Helper.LibCall(com, "Rune", memberName, t, callArgs, i.SignatureArgTypes, ?loc = r)
2873+
2874+
let instanceCall memberName callArgs =
2875+
libCall memberName (thisArg.Value :: callArgs)
2876+
2877+
let firstArg =
2878+
match args with
2879+
| arg :: _ -> [ arg ]
2880+
| [] -> []
2881+
2882+
let isCultureInfoArg (arg: Expr) =
2883+
match arg.Type with
2884+
| DeclaredType(ent, _) -> ent.FullName = "System.Globalization.CultureInfo"
2885+
| _ -> false
2886+
2887+
match i.CompiledName, thisArg, args with
2888+
| ".ctor", None, [ ExprType(Char) ] -> Some args.Head
2889+
| ".ctor", None, [ ExprType(Number(Int32, _)) ] -> libCall "newInt" args |> Some
2890+
| ".ctor", None, [ ExprType(Number(UInt32, _)) ] -> libCall "newUInt" args |> Some
2891+
| ".ctor", None, [ ExprType(Char); ExprType(Char) ] -> libCall "newPair" args |> Some
2892+
| ("ReplacementChar" | "get_ReplacementChar"), None, [] -> libCall "replacementChar" [] |> Some
2893+
| "op_Explicit", None, [ ExprType(Char) ] -> Some args.Head
2894+
| "op_Explicit", None, [ ExprType(Number(Int32, _)) ] -> libCall "newInt" args |> Some
2895+
| "op_Explicit", None, [ ExprType(Number(UInt32, _)) ] -> libCall "newUInt" args |> Some
2896+
| "TryCreate", None, [ ExprType(Char); _ ] -> libCall "tryCreateChar" args |> Some
2897+
| "TryCreate", None, [ ExprType(Char); ExprType(Char); _ ] -> libCall "tryCreatePair" args |> Some
2898+
| "TryCreate", None, [ ExprType(Number(Int32, _)); _ ] -> libCall "tryCreateInt" args |> Some
2899+
| "TryCreate", None, [ ExprType(Number(UInt32, _)); _ ] -> libCall "tryCreateUInt" args |> Some
2900+
| "get_Value", Some _, [] -> instanceCall "value" [] |> Some
2901+
| "get_Utf8SequenceLength", Some _, [] -> instanceCall "utf8SequenceLength" [] |> Some
2902+
| "get_Utf16SequenceLength", Some _, [] -> instanceCall "utf16SequenceLength" [] |> Some
2903+
| "get_IsAscii", Some _, [] -> instanceCall "isAscii" [] |> Some
2904+
| "get_IsBmp", Some _, [] -> instanceCall "isBmp" [] |> Some
2905+
| "get_Plane", Some _, [] -> instanceCall "plane" [] |> Some
2906+
| "GetNumericValue", None, _ -> libCall "getNumericValue" firstArg |> Some
2907+
| "GetUnicodeCategory", None, _ -> libCall "getUnicodeCategory" firstArg |> Some
2908+
| ("IsControl" | "IsDigit" | "IsLetter" | "IsLetterOrDigit" | "IsLower" | "IsNumber" | "IsPunctuation" | "IsSeparator" | "IsSymbol" | "IsUpper" | "IsWhiteSpace"),
2909+
None,
2910+
_ ->
2911+
let meth = Naming.lowerFirst i.CompiledName
2912+
libCall meth firstArg |> Some
2913+
| "IsValid", None, [ ExprType(Number(Int32, _)) ] -> libCall "isValidInt" args |> Some
2914+
| "IsValid", None, [ ExprType(Number(UInt32, _)) ] -> libCall "isValidUInt" args |> Some
2915+
| "ToLower", None, [ runeArg; cultureInfo ] when isCultureInfoArg cultureInfo ->
2916+
libCall "toLowerInvariant" [ runeArg ] |> Some
2917+
| "ToLowerInvariant", None, _ -> libCall "toLowerInvariant" firstArg |> Some
2918+
| "ToUpper", None, [ runeArg; cultureInfo ] when isCultureInfoArg cultureInfo ->
2919+
libCall "toUpperInvariant" [ runeArg ] |> Some
2920+
| "ToUpperInvariant", None, _ -> libCall "toUpperInvariant" firstArg |> Some
2921+
| "GetRuneAt", None, _ -> libCall "getRuneAt" args |> Some
2922+
| "TryGetRuneAt", None, _ -> libCall "tryGetRuneAt" args |> Some
2923+
| "Parse", None, _ -> libCall "parse" firstArg |> Some
2924+
| "TryParse", None, _ -> libCall "tryParse" args |> Some
2925+
| "ToString", Some _, _ -> instanceCall "toString" [] |> Some
2926+
| "CompareTo", Some _, _ -> instanceCall "compareTo" args |> Some
2927+
| "Compare", None, _ -> libCall "compareTo" args |> Some
2928+
| "Equals", Some _, _ -> instanceCall "equals" args |> Some
2929+
| "Equals", None, _ -> libCall "equals" args |> Some
2930+
| "GetHashCode", Some _, _ -> libCall "getHashCode" [ thisArg.Value ] |> Some
2931+
| _ -> None
2932+
28632933
let timeSpans (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr option) (args: Expr list) =
28642934
// let callee = match i.callee with Some c -> c | None -> i.args.Head
28652935
match i.CompiledName with
@@ -3691,6 +3761,8 @@ let tryField com t ownerTyp fieldName =
36913761
let meth = fieldName |> Naming.applyCaseRule Fable.Core.CaseRules.SnakeCase
36923762

36933763
Helper.LibValue(com, "TimeSpan", meth, t) |> Some
3764+
| Builtin BclRune, "ReplacementChar"
3765+
| Builtin BclRune, "get_ReplacementChar" -> Helper.LibValue(com, "Rune", "replacementChar", t) |> Some
36943766
| Builtin BclDateTime, _ ->
36953767
let meth = fieldName |> Naming.lowerFirst
36963768
makeStaticFieldCall com None t "DateTime" "DateTime" meth |> Some
@@ -3805,6 +3877,7 @@ let private replacedModules =
38053877
Types.datetimeOffset, dateTimeOffsets
38063878
Types.dateOnly, dateOnly
38073879
Types.timeOnly, timeOnly
3880+
Types.rune, rune
38083881
Types.timespan, timeSpans
38093882
"System.Timers.Timer", timers
38103883
"System.Environment", systemEnv
@@ -3974,6 +4047,7 @@ let tryType typ =
39744047
| BclDateTimeOffset -> Some(Types.datetimeOffset, dateTimeOffsets, [])
39754048
| BclDateOnly -> Some(Types.dateOnly, dateOnly, [])
39764049
| BclTimeOnly -> Some(Types.timeOnly, timeOnly, [])
4050+
| BclRune -> Some(Types.rune, rune, [])
39774051
| BclTimer -> Some("System.Timers.Timer", timers, [])
39784052
| BclTimeSpan -> Some(Types.timespan, timeSpans, [])
39794053
| BclHashSet genArg -> Some(Types.hashset, hashSets, [ genArg ])

‎src/Fable.Transforms/Transforms.Util.fs‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,9 @@ module Types =
228228
[<Literal>]
229229
let timeOnly = "System.TimeOnly"
230230

231+
[<Literal>]
232+
let rune = "System.Text.Rune"
233+
231234
[<Literal>]
232235
let timer = "System.Timers.Timer"
233236

0 commit comments

Comments
 (0)