Skip to content

Commit

Permalink
Merge pull request #1543 from fable-compiler/chars-as-strings
Browse files Browse the repository at this point in the history
Compile chars as JS strings again
  • Loading branch information
alfonsogarciacaro authored Aug 28, 2018
2 parents 08569ed + 7df64a0 commit c8c639b
Show file tree
Hide file tree
Showing 8 changed files with 185 additions and 196 deletions.
8 changes: 3 additions & 5 deletions src/dotnet/Fable.Compiler/Transforms/Fable2Babel.fs
Original file line number Diff line number Diff line change
Expand Up @@ -210,8 +210,6 @@ module Util =
| Fable.ArrayAlloc(TransformExpr com ctx size) -> [|size|]
NewExpression(Identifier jsName, args) :> Expression
match typ with
| Fable.Char when com.Options.typedArrays ->
makeJsTypedArray "Uint16Array"
| Fable.Number kind when com.Options.typedArrays ->
getTypedArrayName com kind |> makeJsTypedArray
| _ ->
Expand Down Expand Up @@ -536,7 +534,7 @@ module Util =
| Fable.Null _ -> upcast NullLiteral ()
| Fable.UnitConstant -> upcast NullLiteral () // TODO: Use `void 0`?
| Fable.BoolConstant x -> upcast BooleanLiteral (x)
| Fable.CharConstant x -> upcast NumericLiteral (float x)
| Fable.CharConstant x -> upcast StringLiteral (string x)
| Fable.StringConstant x -> upcast StringLiteral (x)
| Fable.NumberConstant (x,_) ->
if x < 0.
Expand Down Expand Up @@ -849,8 +847,8 @@ module Util =
| Fable.Any -> upcast BooleanLiteral true
| Fable.Unit -> upcast BinaryExpression(BinaryEqual, com.TransformAsExpr(ctx, expr), NullLiteral(), ?loc=range)
| Fable.Boolean -> jsTypeof "boolean" expr
| Fable.String _ | Fable.EnumType(Fable.StringEnumType, _) -> jsTypeof "string" expr
| Fable.Number _ | Fable.Char | Fable.EnumType(Fable.NumberEnumType, _) -> jsTypeof "number" expr
| Fable.Char | Fable.String _ | Fable.EnumType(Fable.StringEnumType, _) -> jsTypeof "string" expr
| Fable.Number _ | Fable.EnumType(Fable.NumberEnumType, _) -> jsTypeof "number" expr
| Fable.Regex -> jsInstanceof (Identifier "RegExp") expr
// TODO: Fail for functions, arrays, tuples and list because we cannot check generics?
| Fable.FunctionType _ -> jsTypeof "function" expr
Expand Down
112 changes: 61 additions & 51 deletions src/dotnet/Fable.Compiler/Transforms/Replacements.fs
Original file line number Diff line number Diff line change
Expand Up @@ -306,8 +306,8 @@ let createAtom (value: Expr) =

let toChar (arg: Expr) =
match arg.Type with
| String -> Helper.InstanceCall(arg, "charCodeAt", Char, [makeIntConst 0])
| _ -> arg
| Char | String -> arg
| _ -> Helper.GlobalCall("String", Char, [arg], memb="fromCharCode")

let toString com r (args: Expr list) =
match args with
Expand All @@ -316,8 +316,7 @@ let toString com r (args: Expr list) =
|> addErrorAndReturnNull com r
| head::tail ->
match head.Type with
| String -> head
| Char -> Helper.GlobalCall("String", String, [head], memb="fromCharCode")
| Char | String -> head
| Unit | Boolean | Array _ | Tuple _ | FunctionType _ | EnumType _ ->
Helper.GlobalCall("String", String, [head])
| Builtin (BclInt64 | BclUInt64) -> Helper.CoreCall("Long", "toString", String, args)
Expand Down Expand Up @@ -420,7 +419,8 @@ let toInt com r (round: bool) targetType (args: Expr list) =
| Number Decimal -> "toDecimal"
| _ -> failwithf "Unexpected non-number type %A" typeTo
match sourceType with
| Char | EnumType(NumberEnumType,_) -> args.Head
| EnumType(NumberEnumType,_) -> args.Head
| Char -> Helper.InstanceCall(args.Head, "charCodeAt", targetType, [makeIntConst 0])
| String ->
match targetType with
| Builtin (BclInt64|BclUInt64 as kind) ->
Expand Down Expand Up @@ -484,7 +484,7 @@ let listToArray com r t (li: Expr) =
Helper.CoreCall("Array", "ofList", t, args, ?loc=r)

let stringToCharArray t e =
Helper.CoreCall("String", "toCharArray", t, [e])
Helper.InstanceCall(e, "split", t, [makeStrConst ""])

let enumerator2iterator (e: Expr) =
Helper.CoreCall("Seq", "toIterator", e.Type, [e])
Expand All @@ -497,16 +497,11 @@ let toSeq r t (e: Expr) =
DelayedResolution(kind, t, r)
// Convert to array to get 16-bit code units, see #1279
| String -> stringToCharArray t e
| GenericParam _ ->
match t with
// Runtime check for generics upcasted to `char seq`
| DeclaredType(_, [Char]) -> Helper.CoreCall("String", "toCharIterable", t, [e])
| _ -> e
| _ -> e

let iterate r ident body xs =
let iterate r ident body (xs: Expr) =
let f = Function(Delegate [ident], body, None)
Helper.CoreCall("Seq", "iterate", Unit, [f; xs], ?loc=r)
Helper.CoreCall("Seq", "iterate", Unit, [f; toSeq r xs.Type xs], ?loc=r)

let applyOp (com: ICompiler) (ctx: Context) r t opName (args: Expr list) argTypes genArgs =
let (|CustomOp|_|) com ctx opName argTypes =
Expand Down Expand Up @@ -718,8 +713,8 @@ let makeHashSet (com: ICompiler) r t sourceSeq =

let getZero (com: ICompiler) (t: Type) =
match t with
| String -> makeStrConst ""
| Char | Builtin BclTimeSpan -> makeIntConst 0
| Char | String -> makeStrConst ""
| Builtin BclTimeSpan -> makeIntConst 0
| Builtin BclDateTime as t -> Helper.CoreCall("Date", "minValue", t, [])
| Builtin BclDateTimeOffset as t -> Helper.CoreCall("DateOffset", "minValue", t, [])
| Builtin (FSharpSet genArg) as t -> makeSet com None t "Empty" [] genArg
Expand Down Expand Up @@ -1125,29 +1120,25 @@ let operators (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr o
| _ -> None

let chars (com: ICompiler) (_: Context) r t (i: CallInfo) (_: Expr option) (args: Expr list) =
let icall r args argTypes memb =
let icall r t args argTypes memb =
match args, argTypes with
| thisArg::args, _::argTypes ->
let info = argInfo (toString com None [thisArg] |> Some) args (Some argTypes)
instanceCall r String info (makeStrConst memb |> Some) |> toChar |> Some
let info = argInfo (Some thisArg) args (Some argTypes)
instanceCall r t info (makeStrConst memb |> Some) |> Some
| _ -> None
match i.CompiledName with
| "ToUpper" -> icall r args i.SignatureArgTypes "toLocaleUpperCase"
| "ToUpperInvariant" -> icall r args i.SignatureArgTypes "toUpperCase"
| "ToLower" -> icall r args i.SignatureArgTypes "toLocaleLowerCase"
| "ToLowerInvariant" -> icall r args i.SignatureArgTypes "toLowerCase"
| "ToUpper" -> icall r t args i.SignatureArgTypes "toLocaleUpperCase"
| "ToUpperInvariant" -> icall r t args i.SignatureArgTypes "toUpperCase"
| "ToLower" -> icall r t args i.SignatureArgTypes "toLocaleLowerCase"
| "ToLowerInvariant" -> icall r t args i.SignatureArgTypes "toLowerCase"
| "ToString" -> toString com r args |> Some
| "GetUnicodeCategory" | "IsControl" | "IsDigit" | "IsLetter"
| "IsLetterOrDigit" | "IsUpper" | "IsLower" | "IsNumber"
| "IsPunctuation" | "IsSeparator" | "IsSymbol" | "IsWhiteSpace"
| "IsHighSurrogate" | "IsLowSurrogate" | "IsSurrogate" ->
let args =
match args with
| [str; index] -> [Helper.InstanceCall(str, "charCodeAt", Char, [index])]
| _ -> args
Helper.CoreCall("Char", Naming.lowerFirst i.CompiledName, t, args, i.SignatureArgTypes, ?loc=r) |> Some
| "IsSurrogatePair" | "Parse" ->
Helper.CoreCall("Char", Naming.lowerFirst i.CompiledName, t, args, i.SignatureArgTypes, ?loc=r) |> Some
| "IsHighSurrogate" | "IsLowSurrogate" | "IsSurrogate" | "IsSurrogatePair"
| "Parse" ->
let methName = Naming.lowerFirst i.CompiledName
Helper.CoreCall("Char", methName, t, args, i.SignatureArgTypes, ?loc=r) |> Some
| _ -> None

let implementedStringFunctions =
Expand All @@ -1159,20 +1150,34 @@ let implementedStringFunctions =
"Insert"
"IsNullOrEmpty"
"IsNullOrWhiteSpace"
"Join"
"PadLeft"
"PadRight"
"Remove"
"Replace"
|]

let strings (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr option) (args: Expr list) =
let join nonDelimiterArgType args =
let hasSpread =
match i.Spread, nonDelimiterArgType with
| SeqSpread, _ -> true
| _, EntFullName Types.ienumerableGeneric -> true
| _ -> false
Helper.CoreCall("String", "join", t, args, hasSpread=hasSpread, ?loc=r)
match i.CompiledName, thisArg, args with
| ".ctor", _, _ ->
match List.head i.SignatureArgTypes with
| Char -> Helper.CoreCall("String", "fromChar", t, args, i.SignatureArgTypes, ?loc=r) |> Some
| Array _ -> Helper.CoreCall("String", "fromCharArray", t, args, i.SignatureArgTypes, ?loc=r) |> Some
| _ -> fsFormat com ctx r t i thisArg args
| ".ctor", _, fstArg::_ ->
match fstArg.Type with
| Char ->
match args with
| [_; _] -> emitJs r t args "Array($1 + 1).join($0)" |> Some // String(char, int)
| _ -> addErrorAndReturnNull com r "Unexpected arguments in System.String constructor." |> Some
| Array _ ->
match args with
| [_] -> emitJs r t args "$0.join('')" |> Some // String(char[])
| [_; _; _] -> emitJs r t args "$0.join('').substr($1, $2)" |> Some // String(char[], int, int)
| _ -> addErrorAndReturnNull com r "Unexpected arguments in System.String constructor." |> Some
| _ ->
fsFormat com ctx r t i thisArg args
| "get_Length", Some c, _ -> get r t c "length" |> Some
| "get_Chars", Some c, _ ->
Helper.CoreCall("String", "getCharAtIndex", t, args, i.SignatureArgTypes, c, ?loc=r) |> Some
Expand All @@ -1199,13 +1204,11 @@ let strings (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr opt
Helper.InstanceCall(c, methName, t, args, i.SignatureArgTypes, ?loc=r) |> Some
| ("IndexOf" | "LastIndexOf"), Some c, _ ->
match args with
| [ExprType Char]
| [ExprType String]
| [ExprType Char; ExprType(Number Int32)]
| [ExprType String; ExprType(Number Int32)] ->
Helper.InstanceCall(c, Naming.lowerFirst i.CompiledName, t, args, i.SignatureArgTypes, ?loc=r) |> Some
| [ExprType Char]
| [ExprType Char; ExprType(Number Int32)] ->
let args = match args with head::tail -> (toString com None [head])::tail | [] -> []
Helper.InstanceCall(c, Naming.lowerFirst i.CompiledName, t, args, i.SignatureArgTypes, ?loc=r) |> Some
| _ -> "The only extra argument accepted for String.IndexOf/LastIndexOf is startIndex."
|> addErrorAndReturnNull com r |> Some
| ("Trim" | "TrimStart" | "TrimEnd"), Some c, _ ->
Expand All @@ -1227,7 +1230,7 @@ let strings (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr opt
| [Value(CharConstant _) as separator]
| [Value(StringConstant _) as separator]
| [Value(NewArray(ArrayValues [separator],_))] ->
Helper.InstanceCall(c, "split", t, [toString com None [separator]]) |> Some
Helper.InstanceCall(c, "split", t, [separator]) |> Some
| [arg1; ExprType(EnumType _) as arg2] ->
let arg1 =
match arg1.Type with
Expand All @@ -1237,9 +1240,12 @@ let strings (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr opt
Helper.CoreCall("String", "split", t, c::args, ?loc=r) |> Some
| args ->
Helper.CoreCall("String", "split", t, args, i.SignatureArgTypes, ?thisArg=thisArg, ?loc=r) |> Some
| "Join", None, [_delimiter; _parts; ExprType(Number _); ExprType(Number _)] ->
Helper.CoreCall("String", "joinWithIndices", t, args, ?loc=r) |> Some
| "Join", None, _ ->
join (List.item 1 i.SignatureArgTypes) args |> Some
| "Concat", None, _ ->
// TODO: String.Concat can also accept non-string arguments
Helper.CoreCall("String", "join", t, (makeStrConst "")::args, ?loc=r) |> Some
join (List.head i.SignatureArgTypes) ((makeStrConst "")::args) |> Some
| "CompareOrdinal", None, _ ->
Helper.CoreCall("String", "compareOrdinal", t, args, ?loc=r) |> Some
| Patterns.SetContains implementedStringFunctions, thisArg, args ->
Expand All @@ -1252,11 +1258,15 @@ let stringModule (com: ICompiler) (_: Context) r t (i: CallInfo) (_: Expr option
match i.CompiledName, args with
| "Length", [arg] -> get r t arg "length" |> Some
| ("Iterate" | "IterateIndexed" | "ForAll" | "Exists"), _ ->
// Cast the string to array<char>, see #1279
// Cast the string to char[], see #1279
let args = args |> List.replaceLast (fun e -> stringToCharArray e.Type e)
Helper.CoreCall("Array", Naming.lowerFirst i.CompiledName, t, args, i.SignatureArgTypes, ?loc=r) |> Some
Helper.CoreCall("Seq", Naming.lowerFirst i.CompiledName, t, args, i.SignatureArgTypes, ?loc=r) |> Some
| ("Map" | "MapIndexed" | "Collect"), _ ->
// Cast the string to char[], see #1279 let args = args |> List.replaceLast (fun e -> stringToCharArray e.Type e)
let name = Naming.lowerFirst i.CompiledName
emitJs r t [Helper.CoreCall("Seq", name, Any, args)] "Array.from($0).join('')" |> Some
| "Concat", _ ->
Helper.CoreCall("String", "join", t, args, ?loc=r) |> Some
Helper.CoreCall("String", "join", t, args, hasSpread=true, ?loc=r) |> Some
// Rest of StringModule methods
| meth, args ->
Helper.CoreCall("String", Naming.lowerFirst meth, t, args, i.SignatureArgTypes, ?loc=r) |> Some
Expand Down Expand Up @@ -1734,8 +1744,7 @@ let intrinsicFunctions (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisAr
Helper.CoreCall("Util", "downcast", t, [arg]) |> Some
| _ -> Some arg
| "MakeDecimal", _, _ -> decimals com ctx r t i thisArg args
| "GetString", _, [str; idx] ->
Helper.CoreCall("String", "getCharAtIndex", t, [idx], i.SignatureArgTypes, str, ?loc=r) |> Some
| "GetString", _, [ar; idx]
| "GetArray", _, [ar; idx] -> getExpr r t ar idx |> Some
| "SetArray", _, [ar; idx; value] -> Set(ar, ExprSet idx, value, r) |> Some
| ("GetArraySlice" | "GetStringSlice"), None, [ar; lower; upper] ->
Expand Down Expand Up @@ -1926,11 +1935,11 @@ let bitConvert (_: ICompiler) (_: Context) r (_: Type) (i: CallInfo) (_: Expr op
if i.CompiledName = "GetBytes" then
match args.Head.Type with
| Boolean -> "getBytesBoolean"
| String -> "getBytesChar"
| Char | String -> "getBytesChar"
| Number Int16 -> "getBytesInt16"
| Number Int32 -> "getBytesInt32"
| Builtin BclInt64 -> "getBytesInt64"
| Char | Number UInt16 -> "getBytesUInt16"
| Number UInt16 -> "getBytesUInt16"
| Builtin BclUInt64 -> "getBytesUInt64"
| Number UInt32 -> "getBytesUInt32"
| Number Float32 -> "getBytesSingle"
Expand Down Expand Up @@ -2158,7 +2167,8 @@ let encoding (_: ICompiler) (_: Context) r t (i: CallInfo) (thisArg: Expr option
| ("get_Unicode" | "get_UTF8"), _, _ ->
Helper.CoreCall("Encoding", i.CompiledName, t, args, i.SignatureArgTypes, ?loc=r) |> Some
| ("GetBytes" | "GetString"), Some callee, (1 | 3) ->
Helper.InstanceCall(callee, Naming.lowerFirst i.CompiledName, t, args, i.SignatureArgTypes, ?loc=r) |> Some
let meth = Naming.lowerFirst i.CompiledName
Helper.InstanceCall(callee, meth, t, args, i.SignatureArgTypes, ?loc=r) |> Some
| _ -> None

let enumerables (_: ICompiler) (_: Context) r t (i: CallInfo) (thisArg: Expr option) (_: Expr list) =
Expand Down
6 changes: 4 additions & 2 deletions src/js/fable-core/BitConverter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export function getBytesBoolean(value: boolean) {
new DataView(bytes.buffer).setUint8(0, value ? 1 : 0);
return bytes;
}
export function getBytesString(value: string) {
export function getBytesChar(value: string) {
const bytes = new Uint8Array(2);
new DataView(bytes.buffer).setUint16(0, value.charCodeAt(0), littleEndian);
return bytes;
Expand Down Expand Up @@ -74,8 +74,10 @@ export function toBoolean(bytes: Uint8Array, offset: number): boolean {
return new DataView(bytes.buffer).getUint8(offset) === 1 ? true : false;
}
export function toChar(bytes: Uint8Array, offset: number) {
return new DataView(bytes.buffer).getUint16(offset, littleEndian);
const code = new DataView(bytes.buffer).getUint16(offset, littleEndian);
return String.fromCharCode(code);
}

export function toInt16(bytes: Uint8Array, offset: number) {
return new DataView(bytes.buffer).getInt16(offset, littleEndian);
}
Expand Down
Loading

0 comments on commit c8c639b

Please sign in to comment.