Skip to content

Commit

Permalink
Fix #660 add erased unions and option
Browse files Browse the repository at this point in the history
  • Loading branch information
Jand42 committed Feb 18, 2017
1 parent a02826e commit 3bd452a
Show file tree
Hide file tree
Showing 14 changed files with 421 additions and 185 deletions.
11 changes: 5 additions & 6 deletions src/compiler/WebSharper.Compiler.FSharp/CodeReader.fs
Expand Up @@ -776,12 +776,11 @@ let rec transformExpression (env: Environment) (expr: FSharpExpr) =
else
NewUnionCase(t, case.CompiledName, exprs |> List.map tr)
| P.UnionCaseGet (expr, typ, case, field) ->
let td = sr.ReadTypeDefinition typ.TypeDefinition
if erasedUnions.Contains td then
tr expr
else
let i = case.UnionCaseFields |> Seq.findIndex (fun f -> f = field)
ItemGet(tr expr, Value (String ("$" + string i)))
let t =
match sr.ReadType env.TParams typ with
| ConcreteType ct -> ct
| _ -> parsefailf "Expected a union type"
UnionCaseGet(tr expr, t, case.CompiledName, field.Name)
| P.UnionCaseTest (expr, typ, case) ->
let t =
match sr.ReadType env.TParams typ with
Expand Down
29 changes: 11 additions & 18 deletions src/compiler/WebSharper.Compiler/QuotationReader.fs
Expand Up @@ -213,25 +213,18 @@ let rec transformExpression (env: Environment) (expr: Expr) =
| Patterns.Coerce (expr, typ) ->
tr expr // TODO: type check when possible
| Patterns.NewUnionCase (case, exprs) ->
let annot = A.attrReader.GetMemberAnnot(A.TypeAnnotation.Empty, case.GetCustomAttributesData())
match annot.Kind with
| Some (A.MemberKind.Constant c) -> Value c
| _ ->
let i = case.Tag
CopyCtor(
Reflection.ReadTypeDefinition case.DeclaringType,
Object (
("$", Value (Int i)) ::
(exprs |> List.mapi (fun j e -> "$" + string j, tr e))
)
)
let t =
match Reflection.ReadType case.DeclaringType with
| ConcreteType ct -> ct
| _ -> parsefailf "Expected a union type"

NewUnionCase(t, case.Name, exprs |> List.map tr)
| Patterns.UnionCaseTest (expr, case) ->
let annot = A.attrReader.GetMemberAnnot(A.TypeAnnotation.Empty, case.GetCustomAttributesData())
match annot.Kind with
| Some (A.MemberKind.Constant c) -> Binary (tr expr, BinaryOperator.``==``, Value c)
| _ ->
let i = case.Tag
Binary(ItemGet(tr expr, Value (String "$")), BinaryOperator.``==``, Value (Int i))
let t =
match Reflection.ReadType case.DeclaringType with
| ConcreteType ct -> ct
| _ -> parsefailf "Expected a union type"
UnionCaseTest(tr expr, t, case.Name)
| Patterns.NewRecord (typ, items) ->
let t =
match Reflection.ReadType typ with
Expand Down
281 changes: 157 additions & 124 deletions src/compiler/WebSharper.Compiler/Translator.fs

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions src/compiler/WebSharper.Core.JavaScript/Runtime.js
Expand Up @@ -84,10 +84,10 @@ IntelliFactory = {

SetOrDelete:
function (obj, field, value) {
if (value !== void (0)) {
obj[field] = value;
} else {
if (value === void (0)) {
delete obj[field];
} else {
obj[field] = value;
}
},

Expand Down
2 changes: 1 addition & 1 deletion src/stdlib/WebSharper.Main/Interop.fs
Expand Up @@ -143,7 +143,7 @@ type FuncWithArgsRestProxy<'TArgs, 'TRest, 'TResult> =
member this.Call (args: 'TArgs, [<PA>] rest: 'TRest[]) = Unchecked.defaultof<'TResult>

[<Proxy(typeof<Optional<_>>)>]
type Optional<'T> =
type OptionalProxy<'T> =
| Undefined
| Defined of 'T

Expand Down
123 changes: 123 additions & 0 deletions src/stdlib/WebSharper.Main/JavaScript.Pervasives.fs
Expand Up @@ -124,5 +124,128 @@ let GetJS<'T> (x: obj) (items: seq<string>) =
x <- x?(i)
As<'T> x

/// Erases generic parameters inside this expression during WebSharper translation.
/// You can get use this to translate `defaultof` inside a generic function.
[<Macro(typeof<M.DefaultToUndefined>)>]
let DefaultToUndefined<'T> (x: 'T) = x

module Optional =
/// Converts an F# option value to a JavaScript erased option
[<Inline>]
let ofOption x =
match x with
| None -> Undefined
| Some v -> Defined v

/// Converts a JavaScript erased option to an F# option value
[<Inline>]
let toOption x =
match x with
| Undefined -> None
| Defined v -> Some v

module Union =
// {{ generated by genInterop.fsx, do not modify
/// Converts an F# Choice value to a JavaScript erased union
[<Inline>]
let ofChoice2 x =
match x with
| Choice1Of2 v -> Union1Of2 v
| Choice2Of2 v -> Union2Of2 v
/// Converts a JavaScript erased union to an F# option value
[<Inline>]
let toChoice2 x =
match x with
| Union1Of2 v -> Choice1Of2 v
| Union2Of2 v -> Choice2Of2 v
/// Converts an F# Choice value to a JavaScript erased union
[<Inline>]
let ofChoice3 x =
match x with
| Choice1Of3 v -> Union1Of3 v
| Choice2Of3 v -> Union2Of3 v
| Choice3Of3 v -> Union3Of3 v
/// Converts a JavaScript erased union to an F# option value
[<Inline>]
let toChoice3 x =
match x with
| Union1Of3 v -> Choice1Of3 v
| Union2Of3 v -> Choice2Of3 v
| Union3Of3 v -> Choice3Of3 v
/// Converts an F# Choice value to a JavaScript erased union
[<Inline>]
let ofChoice4 x =
match x with
| Choice1Of4 v -> Union1Of4 v
| Choice2Of4 v -> Union2Of4 v
| Choice3Of4 v -> Union3Of4 v
| Choice4Of4 v -> Union4Of4 v
/// Converts a JavaScript erased union to an F# option value
[<Inline>]
let toChoice4 x =
match x with
| Union1Of4 v -> Choice1Of4 v
| Union2Of4 v -> Choice2Of4 v
| Union3Of4 v -> Choice3Of4 v
| Union4Of4 v -> Choice4Of4 v
/// Converts an F# Choice value to a JavaScript erased union
[<Inline>]
let ofChoice5 x =
match x with
| Choice1Of5 v -> Union1Of5 v
| Choice2Of5 v -> Union2Of5 v
| Choice3Of5 v -> Union3Of5 v
| Choice4Of5 v -> Union4Of5 v
| Choice5Of5 v -> Union5Of5 v
/// Converts a JavaScript erased union to an F# option value
[<Inline>]
let toChoice5 x =
match x with
| Union1Of5 v -> Choice1Of5 v
| Union2Of5 v -> Choice2Of5 v
| Union3Of5 v -> Choice3Of5 v
| Union4Of5 v -> Choice4Of5 v
| Union5Of5 v -> Choice5Of5 v
/// Converts an F# Choice value to a JavaScript erased union
[<Inline>]
let ofChoice6 x =
match x with
| Choice1Of6 v -> Union1Of6 v
| Choice2Of6 v -> Union2Of6 v
| Choice3Of6 v -> Union3Of6 v
| Choice4Of6 v -> Union4Of6 v
| Choice5Of6 v -> Union5Of6 v
| Choice6Of6 v -> Union6Of6 v
/// Converts a JavaScript erased union to an F# option value
[<Inline>]
let toChoice6 x =
match x with
| Union1Of6 v -> Choice1Of6 v
| Union2Of6 v -> Choice2Of6 v
| Union3Of6 v -> Choice3Of6 v
| Union4Of6 v -> Choice4Of6 v
| Union5Of6 v -> Choice5Of6 v
| Union6Of6 v -> Choice6Of6 v
/// Converts an F# Choice value to a JavaScript erased union
[<Inline>]
let ofChoice7 x =
match x with
| Choice1Of7 v -> Union1Of7 v
| Choice2Of7 v -> Union2Of7 v
| Choice3Of7 v -> Union3Of7 v
| Choice4Of7 v -> Union4Of7 v
| Choice5Of7 v -> Union5Of7 v
| Choice6Of7 v -> Union6Of7 v
| Choice7Of7 v -> Union7Of7 v
/// Converts a JavaScript erased union to an F# option value
[<Inline>]
let toChoice7 x =
match x with
| Union1Of7 v -> Choice1Of7 v
| Union2Of7 v -> Choice2Of7 v
| Union3Of7 v -> Choice3Of7 v
| Union4Of7 v -> Choice4Of7 v
| Union5Of7 v -> Choice5Of7 v
| Union6Of7 v -> Choice6Of7 v
| Union7Of7 v -> Choice7Of7 v
// }}
6 changes: 6 additions & 0 deletions src/stdlib/WebSharper.Main/Proxy/Choice.fs
Expand Up @@ -21,19 +21,22 @@
namespace WebSharper

[<Proxy(typeof<Choice<_,_>>)>]
[<DefaultAugmentation(false)>]
[<Name "WebSharper.Choice2">]
type private ChoiceProxy<'T1,'T2> =
| Choice1Of2 of 'T1
| Choice2Of2 of 'T2

[<Proxy(typeof<Choice<_,_,_>>)>]
[<DefaultAugmentation(false)>]
[<Name "WebSharper.Choice3">]
type private ChoiceProxy<'T1,'T2,'T3> =
| Choice1Of3 of 'T1
| Choice2Of3 of 'T2
| Choice3Of3 of 'T3

[<Proxy(typeof<Choice<_,_,_,_>>)>]
[<DefaultAugmentation(false)>]
[<Name "WebSharper.Choice4">]
type private ChoiceProxy<'T1,'T2,'T3,'T4> =
| Choice1Of4 of 'T1
Expand All @@ -42,6 +45,7 @@ type private ChoiceProxy<'T1,'T2,'T3,'T4> =
| Choice4Of4 of 'T4

[<Proxy(typeof<Choice<_,_,_,_,_>>)>]
[<DefaultAugmentation(false)>]
[<Name "WebSharper.Choice5">]
type private ChoiceProxy<'T1,'T2,'T3,'T4,'T5> =
| Choice1Of5 of 'T1
Expand All @@ -51,6 +55,7 @@ type private ChoiceProxy<'T1,'T2,'T3,'T4,'T5> =
| Choice5Of5 of 'T5

[<Proxy(typeof<Choice<_,_,_,_,_,_>>)>]
[<DefaultAugmentation(false)>]
[<Name "WebSharper.Choice6">]
type private ChoiceProxy<'T1,'T2,'T3,'T4,'T5,'T6> =
| Choice1Of6 of 'T1
Expand All @@ -61,6 +66,7 @@ type private ChoiceProxy<'T1,'T2,'T3,'T4,'T5,'T6> =
| Choice6Of6 of 'T6

[<Proxy(typeof<Choice<_,_,_,_,_,_,_>>)>]
[<DefaultAugmentation(false)>]
[<Name "WebSharper.Choice7">]
type private ChoiceProxy<'T1,'T2,'T3,'T4,'T5,'T6,'T7> =
| Choice1Of7 of 'T1
Expand Down
2 changes: 1 addition & 1 deletion src/stdlib/WebSharper.Main/Proxy/List.fs
Expand Up @@ -27,7 +27,7 @@ open WebSharper.JavaScript
[<DefaultAugmentation(false)>]
type private ListProxy<'T> =
| Empty
| Cons of 'T * List<'T>
| Cons of Head: 'T * Tail: List<'T>

[<Name "Construct">]
static member Cons(head: 'T, tail: list<'T>) = head :: tail
Expand Down
7 changes: 2 additions & 5 deletions src/stdlib/WebSharper.Main/Proxy/Option.fs
Expand Up @@ -27,11 +27,8 @@ open WebSharper.JavaScript
[<DefaultAugmentation(false)>]
[<RequireQualifiedAccess>]
type private OptionProxy<'T> =
| None
| Some of 'T

[<CompilationRepresentation (CompilationRepresentationFlags.Instance)>]
member this.Value with [<Inline "$this.$0"; Pure>] get () = X<'T>
| None
| Some of Value: 'T

[<Inline "$x != null">]
static member get_IsSome(x: option<'T>) = false
Expand Down
66 changes: 42 additions & 24 deletions src/stdlib/WebSharper.Main/genInterop.fsx
Expand Up @@ -35,17 +35,51 @@ let concatE s l =

let maxArgCount = 6

let code =
let replaceGenerated path code =
let allCode =
[|
let mutable incl = true
for l in System.IO.File.ReadAllLines(path) do
if incl then yield l
if l.Contains "// {{"
then
incl <- false
yield! code
elif l.Contains "// }}"
then
incl <- true
yield l
|]

System.IO.File.WriteAllLines(path, allCode)

let toAnonTypArgs ts = if List.isEmpty ts then "" else "<" + String.concat "," (ts |> Seq.map (fun _ -> "_")) + ">"

let jsPervasives =
let code = ResizeArray()
let inline cprintfn x = Printf.kprintf code.Add x

// for i = 0 to maxArgCount do
// cprintfn "type FuncWithRest<'TRest, %s'TResult> =" (tArgs i |> concatE ", ")
// cprintfn " inherit Function"
// cprintfn " new (func: %s'TRest[] -> 'TResult) = { }" (tArgs i |> concatE " * ")
// cprintfn " member this.Call (%s[<PA>] rest: 'TRest[]) = X<'TResult>" (args i |> concatE ", ")
for i = 2 to 7 do
cprintfn " /// Converts an F# Choice value to a JavaScript erased union"
cprintfn " [<Inline>]"
cprintfn " let ofChoice%d x =" i
cprintfn " match x with"
for j = 1 to i do
cprintfn " | Choice%dOf%d v -> Union%dOf%d v" j i j i
cprintfn " /// Converts a JavaScript erased union to an F# option value"
cprintfn " [<Inline>]"
cprintfn " let toChoice%d x =" i
cprintfn " match x with"
for j = 1 to i do
cprintfn " | Union%dOf%d v -> Choice%dOf%d v" j i j i

code.ToArray()

replaceGenerated (__SOURCE_DIRECTORY__ + @"\JavaScript.Pervasives.fs") jsPervasives

let toAnonTypArgs ts = if List.isEmpty ts then "" else "<" + String.concat "," (ts |> Seq.map (fun _ -> "_")) + ">"
let interop =
let code = ResizeArray()
let inline cprintfn x = Printf.kprintf code.Add x

for pars in [ false; true ] do
for this in [ false; true ] do
Expand All @@ -55,7 +89,6 @@ let code =
let thisPars = (if this then "This" else "") + (if pars then "Params" else "")
let name = thisPars + del
let inTr = thisPars + "Func"
// let outTr = inTr + "Out"
for i = 0 to maxArgCount do
let t = (if this then ["'TThis"] else[]) @ tArgs i @ (if pars then ["'TParams"] else []) @ (if ret then ["'TResult"] else [])
let toTypArgs ts = if List.isEmpty ts then "" else "<" + String.concat ", " ts + ">"
Expand All @@ -82,19 +115,4 @@ let code =

code.ToArray()

let allCode =
[|
let mutable incl = true
for l in System.IO.File.ReadAllLines(__SOURCE_DIRECTORY__ + @"\Interop.fs") do
if incl then yield l
if l.Contains "// {{"
then
incl <- false
yield! code
elif l.Contains "// }}"
then
incl <- true
yield l
|]

System.IO.File.WriteAllLines(__SOURCE_DIRECTORY__ + @"\Interop.fs", allCode)
replaceGenerated (__SOURCE_DIRECTORY__ + @"\Interop.fs") interop

0 comments on commit 3bd452a

Please sign in to comment.