diff --git a/src/fable/Fable.Client.Node/Main.fs b/src/fable/Fable.Client.Node/Main.fs index a2ff0d91fe..c3e73039f8 100644 --- a/src/fable/Fable.Client.Node/Main.fs +++ b/src/fable/Fable.Client.Node/Main.fs @@ -304,7 +304,7 @@ type FileResolver() = kv.Value |> Seq.map (fun (srcFile, trgFile) -> // Use GetFullPath to prevent things like "parentDir/./childDir" // which can cause problems when calculating relative paths - srcFile, Path.GetFullPath <| Path.Combine(outDir, projDir, Path.ChangeExtension(trgFile, ".js")))) + srcFile, Path.GetFullPath <| Path.Combine3(outDir, projDir, Path.ChangeExtension(trgFile, ".js")))) |> Map let mergeProjectOpts (opts1: FSharpProjectOptions option, resolver: FileResolver) diff --git a/src/fable/Fable.Compiler/FSharp2Fable.Util.fs b/src/fable/Fable.Compiler/FSharp2Fable.Util.fs index 4a9576590a..116b5047ff 100644 --- a/src/fable/Fable.Compiler/FSharp2Fable.Util.fs +++ b/src/fable/Fable.Compiler/FSharp2Fable.Util.fs @@ -2,7 +2,9 @@ namespace Fable.FSharp2Fable open System open System.Collections.Generic +#if !FABLE_COMPILER open System.Reflection +#endif open System.Text.RegularExpressions open Microsoft.FSharp.Compiler open Microsoft.FSharp.Compiler.Ast @@ -11,7 +13,7 @@ open Fable open Fable.AST open Fable.AST.Fable.Util -#if DOTNETCORE +#if DOTNETCORE && !FABLE_COMPILER [] module ReflectionAdapters = type System.Reflection.Assembly with @@ -210,7 +212,7 @@ module Helpers = |> function | Some name -> name.ConstructorArguments.[0] |> snd |> string | None -> Naming.lowerFirst unionCase.DisplayName - |> makeConst + |> makeStrConst let getArgCount (meth: FSharpMemberOrFunctionOrValue) = let args = meth.CurriedParameterGroups @@ -519,7 +521,7 @@ module Patterns = Call(None,_op_Equality,[],[_typeInt], [ILAsm ("[I_ldlen; AI_conv DT_I4]",[],[_matchValue2]) Const (length,_typeInt2)]), - Const (_falseConst,_typeBool)) -> Some (matchValue, length) + Const (_falseConst,_typeBool)) -> Some (matchValue, length, _typeInt2) | _ -> None let (|NumberKind|_|) = function @@ -553,10 +555,10 @@ module Patterns = | _ when typ.TypeDefinition.IsEnum -> true | _ -> false let rec makeSwitch map matchValue e = - let addCase map (idx: int) (case: obj) = - match Map.tryFind idx map with - | Some cases -> Map.add idx (case::cases) map - | None -> Map.add idx [case] map + // let addCase map (idx: int) (case: obj) = + // match Map.tryFind idx map with + // | Some cases -> Map.add idx (case::cases) map + // | None -> Map.add idx [case] map match e with | IfThenElse(Call(None,op_Equality,[],_,[Value var; Const(case,_)]), DecisionTreeSuccess(idx, []), elseExpr) @@ -1102,6 +1104,7 @@ module Util = |||> Seq.fold2 (fun acc genPar (ResolveGeneric ctx t) -> (genPar.Name, t)::acc) |> List.rev +#if !FABLE_COMPILER let getEmitter = // Prevent ReflectionTypeLoadException // From http://stackoverflow.com/a/7889272 @@ -1125,6 +1128,7 @@ module Util = let typ = getTypes assembly |> Seq.find (fun x -> x.AssemblyQualifiedName = tdef.QualifiedName) System.Activator.CreateInstance(typ)) +#endif let emittedGenericArguments com (ctx: Context) r meth (typArgs, methTypArgs) macro (args: Fable.Expr list) = @@ -1162,6 +1166,7 @@ module Util = let macro, args = emittedGenericArguments com ctx r meth (typArgs, methTypArgs) macro args Fable.Apply(Fable.Emit(macro) |> Fable.Value, args, Fable.ApplyMeth, typ, r) |> Some +#if !FABLE_COMPILER | (:? FSharpType as emitFsType)::(:? string as emitMethName)::extraArg when emitFsType.HasTypeDefinition -> try @@ -1179,6 +1184,7 @@ module Util = sprintf "Error when invoking %s.%s" emitFsType.TypeDefinition.DisplayName emitMethName |> attachRange r |> fun msg -> Exception(msg + ": " + exMsg, ex) |> raise +#endif | _ -> "EmitAttribute must receive a string or Type argument" |> attachRange r |> failwith | _ -> None @@ -1267,7 +1273,7 @@ module Util = let loc = if meth.IsInstanceMember then Fable.InstanceLoc else Fable.StaticLoc match ent.TryGetMember(methName, getMemberKind meth, loc, argTypes) with | Some m -> m.OverloadName | None -> methName - let ext = makeGet r Fable.Any typRef (makeConst methName) + let ext = makeGet r Fable.Any typRef (makeStrConst methName) let bind = Fable.Emit("$0.bind($1)($2...)") |> Fable.Value Fable.Apply (bind, ext::callee::args, Fable.ApplyMeth, typ, r) |> Some | _ -> None @@ -1324,9 +1330,9 @@ module Util = (** *Check if this a getter or setter *) match getMemberKind meth with | Fable.Getter | Fable.Field -> - makeGetFrom com ctx r typ callee (makeConst methName) + makeGetFrom com ctx r typ callee (makeStrConst methName) | Fable.Setter -> - Fable.Set (callee, Some (makeConst methName), args.Head, r) + Fable.Set (callee, Some (makeStrConst methName), args.Head, r) (** *Check if this is an implicit constructor *) | Fable.Constructor -> Fable.Apply (callee, args, Fable.ApplyCons, typ, r) @@ -1334,7 +1340,7 @@ module Util = | Fable.Method as kind -> let applyMeth methName = // let calleeType = Fable.Function(Some argTypes, typ) - let m = makeGet r Fable.Any callee (makeConst methName) + let m = makeGet r Fable.Any callee (makeStrConst methName) Fable.Apply(m, args, Fable.ApplyMeth, typ, r) if belongsToInterfaceOrImportedEntity meth then @@ -1393,7 +1399,7 @@ module Util = // Cases when tryEnclosingEntity returns None are rare (see #237) // Let's assume the value belongs to the current enclosing module | None -> Fable.DeclaredType(ctx.enclosingEntity, []) |> makeNonGenTypeRef com - Fable.Apply (typeRef, [makeConst v.CompiledName], Fable.ApplyGet, typ, r) + Fable.Apply (typeRef, [makeStrConst v.CompiledName], Fable.ApplyGet, typ, r) let makeDelegateFrom (com: IFableCompiler) ctx delegateType fsExpr = let ctx = { ctx with isDelegate = true} diff --git a/src/fable/Fable.Compiler/FSharp2Fable.fs b/src/fable/Fable.Compiler/FSharp2Fable.fs index d5d0465b4d..4bbb9273d8 100644 --- a/src/fable/Fable.Compiler/FSharp2Fable.fs +++ b/src/fable/Fable.Compiler/FSharp2Fable.fs @@ -1,6 +1,8 @@ module Fable.FSharp2Fable.Compiler +#if !FABLE_COMPILER open System.IO +#endif open System.Collections.Generic open System.Text.RegularExpressions @@ -22,10 +24,10 @@ open Util let private (|SpecialValue|_|) com ctx = function | BasicPatterns.ILFieldGet (None, typ, fieldName) as fsExpr when typ.HasTypeDefinition -> match typ.TypeDefinition.TryFullName, fieldName with - | Some "System.String", "Empty" -> Some (makeConst "") - | Some "System.Guid", "Empty" -> Some (makeConst "00000000-0000-0000-0000-000000000000") + | Some "System.String", "Empty" -> Some (makeStrConst "") + | Some "System.Guid", "Empty" -> Some (makeStrConst "00000000-0000-0000-0000-000000000000") | Some "System.TimeSpan", "Zero" -> - Fable.Wrapped(makeConst 0, makeType com ctx.typeArgs fsExpr.Type) |> Some + Fable.Wrapped(makeIntConst 0, makeType com ctx.typeArgs fsExpr.Type) |> Some | Some "System.DateTime", "MaxValue" | Some "System.DateTime", "MinValue" -> CoreLibCall("Date", Some (Naming.lowerFirst fieldName), false, []) @@ -132,7 +134,7 @@ and private transformNonListNewUnionCase com ctx (fsExpr: FSharpExpr) fsType uni | KeyValueUnion -> let key, value = match argExprs with - | [] -> lowerCaseName unionCase, makeConst true + | [] -> lowerCaseName unionCase, makeBoolConst true | [expr] -> lowerCaseName unionCase, expr | [key; expr] when hasAtt Atts.erase unionCase.Attributes -> key, expr | _ -> FableError("KeyValue Union Cases must have one or zero fields: " + unionType.FullName, range) |> raise @@ -144,13 +146,13 @@ and private transformNonListNewUnionCase com ctx (fsExpr: FSharpExpr) fsType uni | PojoUnion -> List.zip (Seq.toList unionCase.UnionCaseFields) argExprs |> List.map (fun (fi, e) -> fi.Name, e) - |> List.append ["type", makeConst unionCase.Name] + |> List.append ["type", makeStrConst unionCase.Name] |> makeJsObject (Some range) | ListUnion -> failwithf "transformNonListNewUnionCase must not be used with List %O" range | OtherType -> let argExprs = [ - makeConst unionCase.Name // Include Tag name in args + makeStrConst unionCase.Name // Include Tag name in args Fable.Value(Fable.ArrayConst(Fable.ArrayValues argExprs, Fable.Any)) ] buildApplyInfo com ctx (Some range) unionType unionType (unionType.FullName) @@ -225,14 +227,14 @@ and private transformExprWithRole (role: Role) (com: IFableCompiler) ctx fsExpr | CreateEvent (callee, eventName, meth, typArgs, methTypArgs, methArgs) -> let callee, args = com.Transform ctx callee, List.map (com.Transform ctx) methArgs - let callee = Fable.Apply(callee, [makeConst eventName], Fable.ApplyGet, Fable.Any, None) + let callee = Fable.Apply(callee, [makeStrConst eventName], Fable.ApplyGet, Fable.Any, None) let r, typ = makeRangeFrom fsExpr, makeType com ctx.typeArgs fsExpr.Type makeCallFrom com ctx r typ meth (typArgs, methTypArgs) (Some callee) args - | CheckArrayLength (Transform com ctx arr, length) -> + | CheckArrayLength (Transform com ctx arr, length, FableType com ctx typ) -> let r = makeRangeFrom fsExpr - let lengthExpr = Fable.Apply(arr, [makeConst "length"], Fable.ApplyGet, Fable.Number Int32, r) - makeEqOp r [lengthExpr; makeConst length] BinaryEqualStrict + let lengthExpr = Fable.Apply(arr, [makeStrConst "length"], Fable.ApplyGet, Fable.Number Int32, r) + makeEqOp r [lengthExpr; makeTypeConst typ length] BinaryEqualStrict | PrintFormat (Transform com ctx expr) -> expr @@ -250,7 +252,7 @@ and private transformExprWithRole (role: Role) (com: IFableCompiler) ctx fsExpr ([record], updatedFields) ||> List.fold (fun acc (FieldName fieldName, e) -> let r, value = makeRangeFrom e, com.Transform ctx e - let e = Fable.Set(record, Some(makeConst fieldName), value, r) + let e = Fable.Set(record, Some(makeStrConst fieldName), value, r) e::acc) Fable.Sequential(assignments, r) @@ -280,20 +282,8 @@ and private transformExprWithRole (role: Role) (com: IFableCompiler) ctx fsExpr |> makeLoop (makeRangeFrom fsExpr) (** Values *) - // Arrays with small data (ushort, byte) won't fit the NewArray pattern - // as they would require too much memory - | BasicPatterns.Const(:? System.Array as arr, typ) -> - let arrExprs = [ - for i in 0 .. (arr.GetLength(0) - 1) -> - arr.GetValue(i) |> makeConst - ] - match arr.GetType().GetElementType().FullName with - | NumberKind kind -> Fable.Number kind - | _ -> Fable.Any - |> makeArray <| arrExprs - | BasicPatterns.Const(value, FableType com ctx typ) -> - let e = makeConst value + let e = makeTypeConst typ value if e.Type = typ then e // Enumerations are compiled as const but they have a different type else Fable.Wrapped (e, typ) @@ -456,11 +446,11 @@ and private transformExprWithRole (role: Role) (com: IFableCompiler) ctx fsExpr | None -> makeType com ctx.typeArgs calleeType |> makeNonGenTypeRef com let r, typ = makeRangeFrom fsExpr, makeType com ctx.typeArgs fsExpr.Type - makeGetFrom com ctx r typ callee (makeConst fieldName) + makeGetFrom com ctx r typ callee (makeStrConst fieldName) | BasicPatterns.TupleGet (_tupleType, tupleElemIndex, Transform com ctx tupleExpr) -> let r, typ = makeRangeFrom fsExpr, makeType com ctx.typeArgs fsExpr.Type - makeGetFrom com ctx r typ tupleExpr (makeConst tupleElemIndex) + makeGetFrom com ctx r typ tupleExpr (makeIntConst tupleElemIndex) | BasicPatterns.UnionCaseGet (Transform com ctx unionExpr, fsType, unionCase, FieldName fieldName) -> let typ, range = makeType com ctx.typeArgs fsExpr.Type, makeRangeFrom fsExpr @@ -468,17 +458,17 @@ and private transformExprWithRole (role: Role) (com: IFableCompiler) ctx fsExpr | ErasedUnion | OptionUnion -> Fable.Wrapped(unionExpr, typ) | ListUnion -> - makeGet range typ unionExpr (Naming.lowerFirst fieldName |> makeConst) + makeGet range typ unionExpr (Naming.lowerFirst fieldName |> makeStrConst) | PojoUnion -> - makeConst fieldName |> makeGet range typ unionExpr + makeStrConst fieldName |> makeGet range typ unionExpr | KeyValueUnion -> FableError("KeyValueUnion types cannot be used in pattern matching", ?range=range) |> raise | StringEnum -> FableError("StringEnum types cannot have fields", ?range=range) |> raise | OtherType -> let i = unionCase.UnionCaseFields |> Seq.findIndex (fun x -> x.Name = fieldName) - let fields = makeGet range typ unionExpr ("Fields" |> makeConst) - makeGet range typ fields (i |> makeConst) + let fields = makeGet range typ unionExpr ("Fields" |> makeStrConst) + makeGet range typ fields (i |> makeIntConst) | BasicPatterns.ILFieldSet (callee, typ, fieldName, value) -> failwithf "Unsupported ILField reference %O: %A" (makeRange fsExpr.Range) fsExpr @@ -488,11 +478,11 @@ and private transformExprWithRole (role: Role) (com: IFableCompiler) ctx fsExpr match callee with | Some (Transform com ctx callee) -> callee | None -> makeNonGenTypeRef com calleeType - Fable.Set (callee, Some (makeConst fieldName), value, makeRangeFrom fsExpr) + Fable.Set (callee, Some (makeStrConst fieldName), value, makeRangeFrom fsExpr) | BasicPatterns.UnionCaseTag (Transform com ctx unionExpr, _unionType) -> let r, typ = makeRangeFrom fsExpr, makeType com ctx.typeArgs fsExpr.Type - makeGetFrom com ctx r typ unionExpr (makeConst "tag") + makeGetFrom com ctx r typ unionExpr (makeStrConst "tag") | BasicPatterns.UnionCaseSet (Transform com ctx unionExpr, _type, _case, _caseField, _valueExpr) -> makeRange fsExpr.Range |> failwithf "Unexpected UnionCaseSet %O" @@ -639,8 +629,8 @@ and private transformExprWithRole (role: Role) (com: IFableCompiler) ctx fsExpr | BasicPatterns.UnionCaseTest(Transform com ctx unionExpr, fsType, unionCase) -> let checkCase name = - let left = makeGet None Fable.String unionExpr (makeConst name) - let right = makeConst unionCase.Name + let left = makeGet None Fable.String unionExpr (makeStrConst name) + let right = makeStrConst unionCase.Name makeBinOp (makeRangeFrom fsExpr) Fable.Boolean [left; right] BinaryEqualStrict match fsType with | ErasedUnion -> @@ -664,7 +654,7 @@ and private transformExprWithRole (role: Role) (com: IFableCompiler) ctx fsExpr makeBinOp (makeRangeFrom fsExpr) Fable.Boolean [unionExpr; Fable.Value Fable.Null] opKind | ListUnion -> let opKind = if unionCase.CompiledName = "Empty" then BinaryEqual else BinaryUnequal - let expr = makeGet None Fable.Any unionExpr (makeConst "tail") + let expr = makeGet None Fable.Any unionExpr (makeStrConst "tail") makeBinOp (makeRangeFrom fsExpr) Fable.Boolean [expr; Fable.Value Fable.Null] opKind | StringEnum -> makeBinOp (makeRangeFrom fsExpr) Fable.Boolean [unionExpr; lowerCaseName unionCase] BinaryEqualStrict @@ -677,7 +667,7 @@ and private transformExprWithRole (role: Role) (com: IFableCompiler) ctx fsExpr (** Pattern Matching *) | Switch(matchValue, cases, defaultCase, decisionTargets) -> - let transformCases assignVar = + let transformCases caseType assignVar = let transformBody idx = let body = transformExpr com ctx (snd decisionTargets.[idx]) match assignVar with @@ -685,7 +675,7 @@ and private transformExprWithRole (role: Role) (com: IFableCompiler) ctx fsExpr | None -> body let cases = cases |> Seq.map (fun kv -> - List.map makeConst kv.Value, transformBody kv.Key) + List.map (makeTypeConst caseType) kv.Value, transformBody kv.Key) |> Seq.toList let defaultCase = transformBody defaultCase cases, defaultCase @@ -693,14 +683,15 @@ and private transformExprWithRole (role: Role) (com: IFableCompiler) ctx fsExpr let t = makeType com ctx.typeArgs matchValue.FullType makeValueFrom com ctx None t UnknownRole matchValue let r, typ = makeRangeFrom fsExpr, makeType com ctx.typeArgs fsExpr.Type + let caseType = matchValue.Type match typ with | Fable.Unit -> - let cases, defaultCase = transformCases None + let cases, defaultCase = transformCases caseType None Fable.Switch(matchValue, cases, Some defaultCase, typ, r) | _ -> let assignVar = com.GetUniqueVar() |> makeIdent let cases, defaultCase = - Fable.IdentValue assignVar |> Fable.Value |> Some |> transformCases + Fable.IdentValue assignVar |> Fable.Value |> Some |> transformCases caseType makeSequential r [ Fable.VarDeclaration(assignVar, Fable.Value Fable.Null, true) Fable.Switch(matchValue, cases, Some defaultCase, typ, r) @@ -1188,6 +1179,9 @@ let private getProjectMaps (com: ICompiler) (parsedProj: FSharpCheckProjectResul // This dictionary must be mutable so `dict` cannot be used let dic = Dictionary() dic.Add(Naming.current, Map.empty) + +#if FABLE_COMPILER +#else parsedProj.ProjectContext.GetReferencedAssemblies() |> Seq.choose (fun assembly -> assembly.FileName |> Option.bind (fun asmPath -> @@ -1230,6 +1224,7 @@ let private getProjectMaps (com: ICompiler) (parsedProj: FSharpCheckProjectResul dic.Add(asmPath, fableMap) with _ -> () ) +#endif dic let transformFiles (com: ICompiler) (parsedProj: FSharpCheckProjectResults) (projInfo: FSProjectInfo) = @@ -1238,8 +1233,8 @@ let transformFiles (com: ICompiler) (parsedProj: FSharpCheckProjectResults) (pro ||> Map.findOrRun (fun () -> getProjectMaps com parsedProj projInfo) // Cache for entities and inline expressions let entitiesCache = Dictionary() - let inlineExprsCache: Dictionary * FSharpExpr> = - Map.findOrNew "inline" projInfo.Extra + let newCache = fun () -> Dictionary * FSharpExpr>() + let inlineExprsCache = Map.findOrRun newCache "inline" projInfo.Extra // Start transforming files let entryFile = parsedProj.AssemblyContents.ImplementationFiles diff --git a/src/fable/Fable.Compiler/Fable.Compiler.fsproj b/src/fable/Fable.Compiler/Fable.Compiler.fsproj index 1ad385adf1..7bdbf3a1f3 100644 --- a/src/fable/Fable.Compiler/Fable.Compiler.fsproj +++ b/src/fable/Fable.Compiler/Fable.Compiler.fsproj @@ -45,7 +45,7 @@ - + diff --git a/src/fable/Fable.Compiler/Fable2Babel.fs b/src/fable/Fable.Compiler/Fable2Babel.fs index 98fd25c4d3..370bc7312a 100644 --- a/src/fable/Fable.Compiler/Fable2Babel.fs +++ b/src/fable/Fable.Compiler/Fable2Babel.fs @@ -169,7 +169,7 @@ module Util = if fileInfo.targetFile.StartsWith("///") then fileInfo.targetFile.Substring(3) else Path.getRelativeFileOrDirPath false ctx.file.TargetFile false fileInfo.targetFile - |> fun x -> System.IO.Path.ChangeExtension(x, Naming.targetFileExtension) + |> fun x -> Path.ChangeExtension(x, Naming.targetFileExtension) getParts fileInfo.rootModule ent.FullName memb |> function | [] -> com.GetImportExpr ctx "*" importPath (Fable.Internal file) @@ -1000,7 +1000,7 @@ module Util = || (imports.Values |> Seq.exists (fun i -> i.localIdent = s))) let includeJs (sourcePath: string) = let getRelativePath name = - IO.Path.Combine(bcom.Options.outDir, "js_includes", name + ".js") + Path.Combine3(bcom.Options.outDir, "js_includes", name + ".js") |> Path.getRelativeFileOrDirPath false ctx.file.TargetFile false Seq.append prevJsIncludes jsIncludes |> Seq.tryFind (fun x -> x.sourcePath = sourcePath) @@ -1008,7 +1008,7 @@ module Util = | Some jsInclude -> getRelativePath jsInclude.name | None -> let name = - IO.Path.GetFileNameWithoutExtension(sourcePath) + Path.GetFileNameWithoutExtension(sourcePath) |> Naming.preventConflicts (fun name -> Seq.append prevJsIncludes jsIncludes |> Seq.exists (fun x -> x.name = name)) @@ -1016,8 +1016,8 @@ module Util = getRelativePath name let resolvePath (com: ICompiler) (ctx: Context) (importPath: string) = let resolveRelative (ctx: Context) (importPath: string) = - let fileDir = IO.Path.GetDirectoryName(ctx.file.SourceFile) - IO.Path.GetFullPath(IO.Path.Combine(fileDir, importPath)) + let fileDir = Path.GetDirectoryName(ctx.file.SourceFile) + Path.GetFullPath(Path.Combine(fileDir, importPath)) match com.Options.includeJs, importPath with | true, Naming.StartsWith "." _ -> resolveRelative ctx importPath |> includeJs @@ -1044,7 +1044,7 @@ module Util = | Fable.CoreLib -> let path = com.Options.coreLib + "/" + path + Naming.targetFileExtension if not(path.StartsWith ".") then path else - IO.Path.GetFullPath path + Path.GetFullPath path |> Path.getRelativePath ctx.file.TargetFile |> fun path -> path.TrimEnd('/') } @@ -1077,8 +1077,8 @@ module Compiler = ("projectMaps", extra) ||> Map.findOrRun (fun () -> failwith "Expected project maps") let prevJsIncludes = ResizeArray() - let dependenciesDic: Dictionary = - Map.findOrNew "dependencies" extra + let newCache = fun () -> Dictionary() + let dependenciesDic = Map.findOrRun newCache "dependencies" extra files |> Seq.map (fun (file: Fable.File) -> try // let t = PerfTimer("Fable > Babel") diff --git a/src/fable/Fable.Compiler/Replacements.fs b/src/fable/Fable.Compiler/Replacements.fs index cc8b234069..a4a65cde22 100644 --- a/src/fable/Fable.Compiler/Replacements.fs +++ b/src/fable/Fable.Compiler/Replacements.fs @@ -125,8 +125,8 @@ module Util = let defaultof (t: Fable.Type) = match t with - | Fable.Number _ -> makeConst 0 - | Fable.Boolean -> makeConst false + | Fable.Number _ -> makeIntConst 0 + | Fable.Boolean -> makeBoolConst false | _ -> Fable.Null |> Fable.Value let getProp r t callee (prop: string) = @@ -201,7 +201,7 @@ module Util = match args.Head.Type with | ExtNumber (Int64|UInt64) -> CoreLibCall("Long", Some "fromValue", false, args) - | _ -> CoreLibCall("Long", Some "fromNumber", false, args@[makeConst unsigned]) + | _ -> CoreLibCall("Long", Some "fromNumber", false, args@[makeBoolConst unsigned]) |> makeCall i.range i.returnType let emitBigInt (args: Fable.Expr list) = match args.Head.Type with @@ -239,13 +239,13 @@ module Util = | NoNumber -> failwith "Unexpected non-number type" match args.Head.Type with | Fable.Char -> - InstanceCall(args.Head, "charCodeAt", [makeConst 0]) + InstanceCall(args.Head, "charCodeAt", [makeIntConst 0]) |> makeCall i.range i.returnType | Fable.String -> match i.returnType with | Fable.ExtendedNumber (Int64|UInt64 as kind) -> let unsigned = kind = UInt64 - let args = [args.Head]@[makeConst unsigned]@args.Tail + let args = [args.Head]@[makeBoolConst unsigned]@args.Tail CoreLibCall ("Long", Some "fromString", false, args) |> makeCall i.range i.returnType | _ -> @@ -404,7 +404,7 @@ module Util = let wrapWith op comparison = match op with | None -> comparison - | Some op -> makeEqOp r [comparison; makeConst 0] op + | Some op -> makeEqOp r [comparison; makeIntConst 0] op let icall (args: Fable.Expr list) op = InstanceCall(args.Head, "CompareTo", args.Tail) |> makeCall r (Fable.Number Int32) |> wrapWith op @@ -448,7 +448,7 @@ module Util = CoreLibCall(modName, Some "create", false, args) |> makeCall i.range i.returnType -module private AstPass = +module AstPass = open Util let fableCoreLib com (i: Fable.ApplyInfo) = @@ -550,7 +550,7 @@ module private AstPass = | ".ctor" -> makeJsObject i.range [("contents", i.args.Head)] |> Some | "contents" | "value" -> - let prop = makeConst "contents" + let prop = makeStrConst "contents" match i.methodKind with | Fable.Getter _ -> makeGet i.range Fable.Any i.callee.Value prop |> Some @@ -662,8 +662,8 @@ module private AstPass = let pattern = System.String.Format("{0}=>{1}({2}({0}))", com.GetUniqueVar(), f1,f0) emit info pattern args |> Some // Reference - | "op_Dereference" -> makeGet r Fable.Any args.Head (makeConst "contents") |> Some - | "op_ColonEquals" -> Fable.Set(args.Head, Some(makeConst "contents"), args.Tail.Head, r) |> Some + | "op_Dereference" -> makeGet r Fable.Any args.Head (makeStrConst "contents") |> Some + | "op_ColonEquals" -> Fable.Set(args.Head, Some(makeStrConst "contents"), args.Tail.Head, r) |> Some | "ref" -> makeJsObject r [("contents", args.Head)] |> Some | "increment" | "decrement" -> if info.methodName = "increment" then "++" else "--" @@ -698,7 +698,7 @@ module private AstPass = // Tuples | "fst" | "snd" -> if info.methodName = "fst" then 0 else 1 - |> makeConst + |> makeIntConst |> makeGet r typ args.Head |> Some // Strings | "printFormatToString" // sprintf @@ -744,7 +744,7 @@ module private AstPass = fsFormat com i | "length" -> let c, _ = instanceArgs i.callee i.args - makeGet i.range i.returnType c (makeConst "length") |> Some + makeGet i.range i.returnType c (makeStrConst "length") |> Some | "equals" -> match i.callee, i.args with | Some x, [y] @@ -752,12 +752,12 @@ module private AstPass = makeEqOp i.range [x; y] BinaryEqualStrict |> Some | Some x, [y; kind] | None, [x; y; kind] -> - makeEqOp i.range [ccall com i "String" "compare" [x; y; kind]; makeConst 0] BinaryEqualStrict |> Some + makeEqOp i.range [ccall com i "String" "compare" [x; y; kind]; makeIntConst 0] BinaryEqualStrict |> Some | _ -> None | "contains" -> - makeEqOp i.range [icall com i "indexOf"; makeConst 0] BinaryGreaterOrEqual |> Some + makeEqOp i.range [icall com i "indexOf"; makeIntConst 0] BinaryGreaterOrEqual |> Some | "startsWith" -> - makeEqOp i.range [icall com i "indexOf"; makeConst 0] BinaryEqualStrict |> Some + makeEqOp i.range [icall com i "indexOf"; makeIntConst 0] BinaryEqualStrict |> Some | "substring" -> icall com i "substr" |> Some | "toUpper" -> icall com i "toLocaleUpperCase" |> Some | "toUpperInvariant" -> icall com i "toUpperCase" |> Some @@ -776,10 +776,10 @@ module private AstPass = | "trimStart" -> "start" | "trimEnd" -> "end" | _ -> "both" - CoreLibCall("String", Some "trim", false, i.callee.Value::(makeConst side)::i.args) + CoreLibCall("String", Some "trim", false, i.callee.Value::(makeStrConst side)::i.args) |> makeCall i.range i.returnType |> Some | "toCharArray" -> - InstanceCall(i.callee.Value, "split", [makeConst ""]) + InstanceCall(i.callee.Value, "split", [makeStrConst ""]) |> makeCall i.range i.returnType |> Some | "iterate" | "iterateIndexed" | "forAll" | "exists" -> CoreLibCall("Seq", Some i.methodName, false, deleg com i i.args) @@ -793,7 +793,7 @@ module private AstPass = | "concat" -> let args = if i.ownerFullName = "System.String" - then (makeConst "")::i.args else i.args + then (makeStrConst "")::i.args else i.args CoreLibCall("String", Some "join", false, args) |> makeCall i.range i.returnType |> Some | "split" -> @@ -845,7 +845,7 @@ module private AstPass = let meth, args, kind = if isFloat then "parseFloat", [str], Float64 - else "parseInt", [str; makeConst 10], Int32 + else "parseInt", [str; makeIntConst 10], Int32 GlobalCall("Number", Some meth, false, args) |> makeCall i.range (Fable.Number kind) match i.methodName with @@ -887,14 +887,23 @@ module private AstPass = | ".ctor", [Fable.Value (Fable.IdentValue _)] -> FableError("Passing bound values to the constructor is not supported.", ?range=i.range) |> raise | ".ctor", [Fable.Value (Fable.NumberConst (x, _))] -> - makeConst (new decimal(x)) |> Some +#if FABLE_COMPILER + makeNumConst (float x) |> Some +#else + makeDecConst (new decimal(x)) |> Some +#endif | ".ctor", [Fable.Value(Fable.ArrayConst(Fable.ArrayValues arVals, _))] -> match arVals with - | [ Fable.Value (Fable.NumberConst (i1, Int32)); - Fable.Value (Fable.NumberConst (i2, Int32)); - Fable.Value (Fable.NumberConst (i3, Int32)); - Fable.Value (Fable.NumberConst (i4, Int32)) ] -> - makeConst (new decimal([| int i1; int i2; int i3; int i4 |])) |> Some + | [ Fable.Value (Fable.NumberConst (low, Int32)); + Fable.Value (Fable.NumberConst (medium, Int32)); + Fable.Value (Fable.NumberConst (high, Int32)); + Fable.Value (Fable.NumberConst (scale, Int32)) ] -> +#if FABLE_COMPILER + makeNumConst ((float ((uint64 (uint32 medium)) <<< 32 ||| (uint64 (uint32 low)))) + / System.Math.Pow(10.0, float ((int scale) >>> 16 &&& 0xFF)) * (if scale < 0.0 then -1.0 else 1.0)) |> Some +#else + makeDecConst (new decimal([| int low; int medium; int high; int scale |])) |> Some +#endif | _ -> None | (".ctor" | "makeDecimal"), [ Fable.Value (Fable.NumberConst (low, Int32)); @@ -902,7 +911,12 @@ module private AstPass = Fable.Value (Fable.NumberConst (high, Int32)); Fable.Value (Fable.BoolConst isNegative); Fable.Value (Fable.NumberConst (scale, UInt8)) ] -> - makeConst (new decimal(int low, int medium, int high, isNegative, byte scale)) |> Some +#if FABLE_COMPILER + makeNumConst ((float ((uint64 (uint32 medium)) <<< 32 ||| (uint64 (uint32 low)))) + / System.Math.Pow(10.0, float scale) * (if isNegative then -1.0 else 1.0)) |> Some +#else + makeDecConst (new decimal(int low, int medium, int high, isNegative, byte scale)) |> Some +#endif | _,_ -> None let debug com (i: Fable.ApplyInfo) = @@ -922,8 +936,8 @@ module private AstPass = | _ -> None let regex com (i: Fable.ApplyInfo) = - let prop p callee = - makeGet i.range i.returnType callee (makeConst p) + let propInt p callee = makeGet i.range i.returnType callee (makeIntConst p) + let propStr p callee = makeGet i.range i.returnType callee (makeStrConst p) let isGroup = match i.callee with | Some (Type (EntFullName "System.Text.RegularExpressions.Group")) -> true @@ -939,16 +953,16 @@ module private AstPass = // Capture | "index" -> if not isGroup - then prop "index" i.callee.Value |> Some + then propStr "index" i.callee.Value |> Some else FableError("Accessing index of Regex groups is not supported", ?range=i.range) |> raise | "value" -> if isGroup then i.callee.Value |> wrap i.returnType |> Some - else prop 0 i.callee.Value |> Some + else propInt 0 i.callee.Value |> Some | "length" -> if isGroup - then prop "length" i.callee.Value |> Some - else prop 0 i.callee.Value |> prop "length" |> Some + then propStr "length" i.callee.Value |> Some + else propInt 0 i.callee.Value |> propStr "length" |> Some // Group | "success" -> makeEqOp i.range [i.callee.Value; Fable.Value Fable.Null] BinaryUnequal |> Some @@ -958,7 +972,7 @@ module private AstPass = | "item" -> makeGet i.range i.returnType i.callee.Value i.args.Head |> Some | "count" -> - prop "length" i.callee.Value |> Some + propStr "length" i.callee.Value |> Some | _ -> None let languagePrimitives com (i: Fable.ApplyInfo) = @@ -992,9 +1006,9 @@ module private AstPass = let upper = let t = Fable.Number Int32 match upper with - | Null _ -> makeGet None t ar (makeConst "length") + | Null _ -> makeGet None t ar (makeStrConst "length") | _ -> Fable.Apply(Fable.Value(Fable.BinaryOp BinaryPlus), - [upper; makeConst 1], Fable.ApplyMeth, t, None) + [upper; makeIntConst 1], Fable.ApplyMeth, t, None) InstanceCall (ar, "slice", [lower; upper]) |> makeCall i.range i.returnType |> Some | "setArraySlice", (None, args) -> @@ -1097,7 +1111,7 @@ module private AstPass = let last = List.last i.args match i.args.Length, last.Type with | 7, Fable.Enum "System.DateTimeKind" -> - (List.take 6 i.args)@[makeConst 0; last] + (List.take 6 i.args)@[makeIntConst 0; last] | _ -> i.args CoreLibCall("Date", Some "create", false, args) |> makeCall i.range i.returnType |> Some @@ -1111,8 +1125,8 @@ module private AstPass = | _ -> None let keyValuePairs com (i: Fable.ApplyInfo) = - let get (k: obj) = - makeGet i.range i.returnType i.callee.Value (makeConst k) |> Some + let get (k: int) = + makeGet i.range i.returnType i.callee.Value (makeIntConst k) |> Some match i.methodName with | ".ctor" -> Fable.Value(Fable.TupleConst i.args) |> Some | "key" -> get 0 @@ -1138,7 +1152,7 @@ module private AstPass = | "isReadOnly" -> Fable.BoolConst false |> Fable.Value |> Some | "count" -> - makeGet i.range i.returnType i.callee.Value (makeConst "size") |> Some + makeGet i.range i.returnType i.callee.Value (makeStrConst "size") |> Some | "containsValue" -> CoreLibCall ("Map", Some "containsValue", false, [i.args.Head; i.callee.Value]) |> makeCall i.range i.returnType |> Some @@ -1174,7 +1188,7 @@ module private AstPass = | Fable.Number Int32 -> makeSet [] |> Some | _ -> makeSet i.args |> Some | "count" -> - makeGet i.range i.returnType i.callee.Value (makeConst "size") |> Some + makeGet i.range i.returnType i.callee.Value (makeStrConst "size") |> Some | "isReadOnly" -> Fable.BoolConst false |> Fable.Value |> Some | "clear" -> icall com i "clear" |> Some @@ -1204,7 +1218,7 @@ module private AstPass = | None -> List.last i.args, List.take (i.args.Length-1) i.args let prop (prop: string) = let callee, _ = instanceArgs() - makeGet i.range i.returnType callee (makeConst prop) + makeGet i.range i.returnType callee (makeStrConst prop) let icall meth = let callee, args = instanceArgs() InstanceCall (callee, meth, args) @@ -1296,7 +1310,7 @@ module private AstPass = let collectionsSecondPass com (i: Fable.ApplyInfo) kind = let prop (meth: string) callee = - makeGet i.range i.returnType callee (makeConst meth) + makeGet i.range i.returnType callee (makeStrConst meth) let icall meth (callee, args) = InstanceCall (callee, meth, args) |> makeCall i.range i.returnType @@ -1312,7 +1326,7 @@ module private AstPass = match kind with | Seq -> ccall "Seq" meth args | Array -> - makeEqOp i.range [prop "length" args.Head; makeConst 0] BinaryEqualStrict + makeEqOp i.range [prop "length" args.Head; makeIntConst 0] BinaryEqualStrict | List -> let c, _ = instanceArgs c args makeEqOp i.range [prop "tail" c; Fable.Value Fable.Null] BinaryEqual @@ -1327,8 +1341,8 @@ module private AstPass = | List -> let c, _ = instanceArgs c args in prop meth c | Array -> let c, _ = instanceArgs c args - if meth = "head" then makeGet i.range i.returnType c (makeConst 0) - elif meth = "tail" then icall "slice" (c, [makeConst 1]) + if meth = "head" then makeGet i.range i.returnType c (makeIntConst 0) + elif meth = "tail" then icall "slice" (c, [makeIntConst 1]) else prop "length" c |> Some | "item" -> @@ -1377,7 +1391,7 @@ module private AstPass = | Array -> match i.returnType with | Fable.Array typ -> - Fable.ArrayConst (Fable.ArrayAlloc (makeConst 0), typ) |> Fable.Value + Fable.ArrayConst (Fable.ArrayAlloc (makeIntConst 0), typ) |> Fable.Value | _ -> "Expecting array type but got " + i.returnType.FullName |> attachRange i.range |> failwith | List -> CoreLibCall ("List", None, true, args) @@ -1421,7 +1435,7 @@ module private AstPass = | "addRange" -> ccall "Array" "addRangeInPlace" [args.Head; c.Value] |> Some | "clear" -> - icall "splice" (c.Value, [makeConst 0]) |> Some + icall "splice" (c.Value, [makeIntConst 0]) |> Some | "contains" -> match c, args with | Some c, args -> @@ -1436,11 +1450,11 @@ module private AstPass = | "indexOf" -> icall "indexOf" (c.Value, args) |> Some | "insert" -> - icall "splice" (c.Value, [args.Head; makeConst 0; args.Tail.Head]) |> Some + icall "splice" (c.Value, [args.Head; makeIntConst 0; args.Tail.Head]) |> Some | "remove" -> ccall "Array" "removeInPlace" [args.Head; c.Value] |> Some | "removeAt" -> - icall "splice" (c.Value, [args.Head; makeConst 1]) |> Some + icall "splice" (c.Value, [args.Head; makeIntConst 1]) |> Some | "reverse" when kind = Array -> match i.returnType with | Fable.Array _ -> @@ -1476,7 +1490,7 @@ module private AstPass = match meth, i.methodTypeArgs with | "sum", [Fable.DeclaredType(ent, _) as t] | "sumBy", [_;Fable.DeclaredType(ent, _) as t] -> - let zero = Fable.Apply(Fable.Value(Fable.TypeRef(ent,[])), [makeConst "Zero"], + let zero = Fable.Apply(Fable.Value(Fable.TypeRef(ent,[])), [makeStrConst "Zero"], Fable.ApplyGet, i.returnType, None) let fargs = [makeTypedIdent "x" t; makeTypedIdent "y" t] let addFn = wrapInLambda fargs (fun args -> applyOp com i args "op_Addition") @@ -1530,7 +1544,7 @@ module private AstPass = | "getSlice" -> listMeth "slice" (i.args@[i.callee.Value]) | "truncate" -> - listMeth "slice" ([makeConst 0]@i.args) + listMeth "slice" ([makeIntConst 0]@i.args) | Patterns.SetContains implementedListFunctions meth -> listMeth meth i.args | _ -> None @@ -1546,7 +1560,7 @@ module private AstPass = | ThreeArgs (ar, idx, value) -> Fable.Set (ar, Some idx, value, i.range) |> Some | _ -> None - | "take" -> icall "slice" (i.args.Tail.Head, [makeConst 0; i.args.Head]) + | "take" -> icall "slice" (i.args.Tail.Head, [makeIntConst 0; i.args.Head]) | "skip" -> icall "slice" (i.args.Tail.Head, [i.args.Head]) | "copy" -> icall "slice" (i.args.Head, []) | "getSubArray" | "fill" -> @@ -1637,7 +1651,7 @@ module private AstPass = | "namespace" -> str ent.Namespace |> Some | "fullName" -> str ent.FullName |> Some | "name" -> str ent.Name |> Some - | "isGenericType" -> ent.GenericParameters.Length > 0 |> makeConst |> Some + | "isGenericType" -> ent.GenericParameters.Length > 0 |> makeBoolConst |> Some | "getGenericTypeDefinition" -> makeTypeRefFrom com ent |> Some | _ -> None | _ -> @@ -1679,8 +1693,8 @@ module private AstPass = | "next" -> let min, max = match info.args with - | [] -> makeConst 0, makeConst System.Int32.MaxValue - | [max] -> makeConst 0, max + | [] -> makeIntConst 0, makeIntConst System.Int32.MaxValue + | [max] -> makeIntConst 0, max | [min; max] -> min, max | _ -> failwith "Unexpected arg count for Random.Next" ccall com info "Util" "randomNext" [min; max] |> Some @@ -1715,7 +1729,7 @@ module private AstPass = |> makeCall info.range info.returnType |> Some | "parse" -> info.args.Head |> Some | "tryParse" -> - Fable.TupleConst [makeConst true; info.args.Head] + Fable.TupleConst [makeBoolConst true; info.args.Head] |> Fable.Value |> Some | _ -> None @@ -1902,10 +1916,10 @@ let private coreLibPass com (info: Fable.ApplyInfo) = CoreLibCall(modName, None, true, deleg com info info.args) |> makeCall info.range info.returnType |> Some | _, Fable.Getter _, Some callee -> - let prop = Naming.upperFirst info.methodName |> makeConst + let prop = Naming.upperFirst info.methodName |> makeStrConst Fable.Apply(callee, [prop], Fable.ApplyGet, info.returnType, info.range) |> Some | _, Fable.Setter _, Some callee -> - let prop = Naming.upperFirst info.methodName |> makeConst + let prop = Naming.upperFirst info.methodName |> makeStrConst Fable.Set(callee, Some prop, info.args.Head, info.range) |> Some | _, _, Some callee -> InstanceCall (callee, Naming.upperFirst info.methodName, deleg com info info.args) diff --git a/src/fable/Fable.Compiler/Util.fs b/src/fable/Fable.Compiler/Utils.fs similarity index 93% rename from src/fable/Fable.Compiler/Util.fs rename to src/fable/Fable.Compiler/Utils.fs index f696a24d22..1649fd2252 100644 --- a/src/fable/Fable.Compiler/Util.fs +++ b/src/fable/Fable.Compiler/Utils.fs @@ -1,6 +1,6 @@ namespace Fable -#if DOTNETCORE +#if DOTNETCORE && !FABLE_COMPILER [] module ReflectionAdapters = open System.Reflection @@ -31,20 +31,20 @@ module Extensions = v module Map = - let findOrNew<'T when 'T : (new : unit -> 'T)> (k: string) (m: Map) = - match Map.tryFind k m with - | Some(:? 'T as x) -> x - | _ -> new 'T() let findOrRun<'T> (f: unit->'T) (k: string) (m: Map) = match Map.tryFind k m with - | Some(:? 'T as x) -> x + | Some x -> downcast x | _ -> f() + // let findOrNew<'T when 'T : (new : unit->'T)> (k: string) (m: Map) = + // findOrRun (fun () -> new 'T()) k m + module Option = let toBool (f: 'T->bool) (opt: 'T option) = match opt with Some x when f x -> true | _ -> false +#if !FABLE_COMPILER module Json = open System.Reflection open FSharp.Reflection @@ -90,6 +90,7 @@ module Json = writer.WritePropertyName(p.Name) serializer.Serialize(writer, p.GetValue(v))) writer.WriteEndObject() +#endif //!FABLE_COMPILER module Plugins = let tryPlugin<'T,'V when 'T:>IPlugin> r (f: 'T->'V option) = diff --git a/src/fable/Fable.Core/AST/AST.Babel.fs b/src/fable/Fable.Core/AST/AST.Babel.fs index f861f13f2c..d34c84974b 100644 --- a/src/fable/Fable.Core/AST/AST.Babel.fs +++ b/src/fable/Fable.Core/AST/AST.Babel.fs @@ -10,8 +10,8 @@ open Fable.AST /// otherwise it is an object consisting of a start position (the position of the first character of the parsed source region) /// and an end position (the position of the first character after the parsed source region): [] -type Node(typ, ?loc) = - member x.``type``: string = typ +type Node(``type``, ?loc) = + member x.``type``: string = ``type`` member x.loc: SourceLocation option = loc /// Since the left-hand side of an assignment may be any expression in general, an expression can also be a pattern. @@ -28,8 +28,8 @@ type Node(typ, ?loc) = [] type ModuleDeclaration(typ, ?loc) = inherit Node(typ, ?loc = loc) [] -type TypeAnnotationInfo(typ) = - member x.``type``: string = typ +type TypeAnnotationInfo(``type``) = + member x.``type``: string = ``type`` type TypeAnnotation(typeInfo) = member x.``type`` = "TypeAnnotation" @@ -277,11 +277,11 @@ type ForOfStatement(left, right, body, ?loc) = member x.right: Expression = right /// A function declaration. Note that id cannot be null. -type FunctionDeclaration(id, arguments, body, ?generator, ?async, +type FunctionDeclaration(id, ``params``, body, ?generator, ?async, ?returnType, ?typeParams, ?loc) = inherit Declaration("FunctionDeclaration", ?loc = loc) member x.id: Identifier = id - member x.``params``: Pattern list = arguments + member x.``params``: Pattern list = ``params`` member x.body: BlockStatement = body member x.generator = defaultArg generator false member x.async = defaultArg async false @@ -298,19 +298,19 @@ type ThisExpression(?loc) = inherit Expression("ThisExpression", ?loc = loc) /// A fat arrow function expression, e.g., let foo = (bar) => { /* body */ }. -type ArrowFunctionExpression(arguments, body, ?async, ?loc) = +type ArrowFunctionExpression(``params``, body, ?async, ?loc) = inherit Expression("ArrowFunctionExpression", ?loc = loc) member x.expression = match body with U2.Case1 _ -> false | U2.Case2 _ -> true - member x.``params``: Pattern list = arguments + member x.``params``: Pattern list = ``params`` member x.body: U2 = body member x.async: bool = defaultArg async false -type FunctionExpression(arguments, body, ?generator, ?async, +type FunctionExpression(``params``, body, ?generator, ?async, ?id, ?returnType, ?typeParams, ?loc) = inherit Expression("FunctionExpression", ?loc = loc) member x.id: Identifier option = id - member x.``params``: Pattern list = arguments + member x.``params``: Pattern list = ``params`` member x.body: BlockStatement = body member x.generator: bool = defaultArg generator false member x.async: bool = defaultArg async false @@ -365,13 +365,13 @@ type ObjectProperty(key, value, ?shorthand, ?computed, ?loc) = type ObjectMethodKind = ObjectGetter | ObjectSetter | ObjectMeth -type ObjectMethod(kind, key, arguments, body, ?computed, ?generator, +type ObjectMethod(kind, key, ``params``, body, ?computed, ?generator, ?async, ?returnType, ?typeParams, ?loc) = inherit ObjectMember("ObjectMethod", key, ?computed=computed, ?loc=loc) member x.kind = match kind with ObjectGetter -> "get" | ObjectSetter -> "set" | ObjectMeth -> "method" - member x.``params``: Pattern list = arguments + member x.``params``: Pattern list = ``params`` member x.body: BlockStatement = body member x.generator: bool = defaultArg generator false member x.async: bool = defaultArg async false @@ -527,7 +527,7 @@ type RestElement(argument, ?loc) = type ClassMethodKind = | ClassConstructor | ClassFunction | ClassGetter | ClassSetter -type ClassMethod(kind, key, args, body, computed, ``static``, +type ClassMethod(kind, key, ``params``, body, computed, ``static``, ?returnType, ?typeParams, ?loc) = inherit Node("ClassMethod", ?loc = loc) member x.kind = match kind with ClassConstructor -> "constructor" @@ -535,7 +535,7 @@ type ClassMethod(kind, key, args, body, computed, ``static``, | ClassSetter -> "set" | ClassFunction -> "method" member x.key: Expression = key - member x.``params``: Pattern list = args + member x.``params``: Pattern list = ``params`` member x.body: BlockStatement = body member x.computed: bool = computed member x.``static``: bool = ``static`` @@ -665,9 +665,9 @@ type FunctionTypeParam(name, typeInfo, ?optional) = member x.typeAnnotation: TypeAnnotationInfo = typeInfo member x.optional = defaultArg optional false -type FunctionTypeAnnotation(args, returnType, ?rest) = +type FunctionTypeAnnotation(``params``, returnType, ?rest) = inherit TypeAnnotationInfo("FunctionTypeAnnotation") - member x.``params``: FunctionTypeParam list = args + member x.``params``: FunctionTypeParam list = ``params`` member x.rest: FunctionTypeParam option = rest member x.returnType: TypeAnnotationInfo = returnType diff --git a/src/fable/Fable.Core/AST/AST.Fable.Util.fs b/src/fable/Fable.Core/AST/AST.Fable.Util.fs index 6136427d68..3405fd8d38 100644 --- a/src/fable/Fable.Core/AST/AST.Fable.Util.fs +++ b/src/fable/Fable.Core/AST/AST.Fable.Util.fs @@ -62,31 +62,54 @@ let makeFloat32 (x: float32) = let callee = Apply (makeIdentExpr "Math", [Value (StringConst "fround")], ApplyGet, Any, None) Apply (callee, args, ApplyMeth, Any, None) -let makeConst (value: obj) = - match value with +let makeBoolConst (x: bool) = BoolConst x |> Value +let makeStrConst (x: string) = StringConst x |> Value +let makeIntConst (x: int) = NumberConst (float x, Int32) |> Value +let makeNumConst (x: float) = NumberConst (float x, Float64) |> Value +let makeDecConst (x: decimal) = NumberConst (float x, Float64) |> Value + +let makeTypeConst (typ: Type) (value: obj) = + match typ, value with // Long Integer types - | :? int64 as x -> makeLongInt (uint64 x) false - | :? uint64 as x -> makeLongInt x true + | ExtendedNumber Int64, (:? int64 as x) -> makeLongInt (uint64 x) false + | ExtendedNumber UInt64, (:? uint64 as x) -> makeLongInt x true + // Enum 64-bit types (TODO: proper JS support, as Enum has no type) + | Enum _, (:? int64 as x) -> makeLongInt (uint64 x) false + | Enum _, (:? uint64 as x) -> makeLongInt x true // Short Float type - | :? float32 as x -> makeFloat32 x + | Number Float32, (:? float32 as x) -> makeFloat32 x | _ -> - match value with - | :? bool as x -> BoolConst x - | :? string as x -> StringConst x - | :? char as x -> StringConst (string x) + match typ, value with + | Boolean, (:? bool as x) -> BoolConst x + | String, (:? string as x) -> StringConst x + | Char, (:? char as x) -> StringConst (string x) // Integer types - | :? int as x -> NumberConst (float x, Int32) - | :? byte as x -> NumberConst (float x, UInt8) - | :? sbyte as x -> NumberConst (float x, Int8) - | :? int16 as x -> NumberConst (float x, Int16) - | :? uint16 as x -> NumberConst (float x, UInt16) - | :? uint32 as x -> NumberConst (float x, UInt32) + | Number UInt8, (:? byte as x) -> NumberConst (float x, UInt8) + | Number Int8, (:? sbyte as x) -> NumberConst (float x, Int8) + | Number Int16, (:? int16 as x) -> NumberConst (float x, Int16) + | Number UInt16, (:? uint16 as x) -> NumberConst (float x, UInt16) + | Number Int32, (:? int as x) -> NumberConst (float x, Int32) + | Number UInt32, (:? uint32 as x) -> NumberConst (float x, UInt32) // Float types - | :? float as x -> NumberConst (float x, Float64) - | :? decimal as x -> NumberConst (float x, Float64) + | Number Float64, (:? float as x) -> NumberConst (float x, Float64) + | Number Decimal, (:? decimal as x) -> NumberConst (float x, Float64) + // Enums (TODO: proper JS support, as Enum has no type) + | Enum _, (:? byte as x) -> NumberConst (float x, UInt8) + | Enum _, (:? sbyte as x) -> NumberConst (float x, Int8) + | Enum _, (:? int16 as x) -> NumberConst (float x, Int16) + | Enum _, (:? uint16 as x) -> NumberConst (float x, UInt16) + | Enum _, (:? int as x) -> NumberConst (float x, Int32) + | Enum _, (:? uint32 as x) -> NumberConst (float x, UInt32) // TODO: Regex - | :? unit | _ when isNull value -> Null - | _ -> failwithf "Unexpected literal %O" value + | Unit, (:? unit) | _ when isNull value -> Null + // Arrays with small data type (ushort, byte) come as Const + | Array (Number kind), (:? (byte[]) as arr) -> + let values = arr |> Array.map (fun x -> NumberConst (float x, kind) |> Value) |> Seq.toList + ArrayConst (ArrayValues values, Number kind) + | Array (Number kind), (:? (uint16[]) as arr) -> + let values = arr |> Array.map (fun x -> NumberConst (float x, kind) |> Value) |> Seq.toList + ArrayConst (ArrayValues values, Number kind) + | _ -> failwithf "Unexpected type %A, literal %O" typ value |> Value let makeFnType args (body: Expr) = @@ -174,7 +197,7 @@ let rec makeTypeRef (com: ICompiler) (genInfo: GenericInfo) typ = | Unit -> makeNonDeclaredTypeRef NonDeclUnit | Array (Number kind) when not com.Options.noTypedArrays -> let def = Ident(getTypedArrayName com kind, MetaType) |> IdentValue |> Value - Apply(makeCoreRef "Util" (Some "Array"), [def; makeConst true], ApplyMeth, MetaType, None) + Apply(makeCoreRef "Util" (Some "Array"), [def; makeBoolConst true], ApplyMeth, MetaType, None) | Array genArg -> makeTypeRef com genInfo genArg |> NonDeclArray @@ -210,7 +233,7 @@ and makeCall (range: SourceLocation option) typ kind = | None -> owner | Some meth -> // let fnTyp = Function(List.map Expr.getType args |> Some, returnType) - Apply (owner, [makeConst meth], ApplyGet, Any, None) + Apply (owner, [makeStrConst meth], ApplyGet, Any, None) let apply kind args callee = Apply(callee, args, kind, typ, range) let getKind isCons = @@ -218,7 +241,7 @@ and makeCall (range: SourceLocation option) typ kind = match kind with | InstanceCall (callee, meth, args) -> // let fnTyp = Function(List.map Expr.getType args |> Some, typ) - Apply (callee, [makeConst meth], ApplyGet, Any, None) + Apply (callee, [makeStrConst meth], ApplyGet, Any, None) |> apply ApplyMeth args | ImportCall (importPath, modName, meth, isCons, args) -> Value (ImportRef (modName, importPath, CustomImport)) @@ -245,7 +268,7 @@ let makeEmit r t args macro = let rec makeTypeTest com range (typ: Type) expr = let jsTypeof (primitiveType: string) expr = let typof = makeUnOp None String [expr] UnaryTypeof - makeBinOp range Boolean [typof; makeConst primitiveType] BinaryEqualStrict + makeBinOp range Boolean [typof; makeStrConst primitiveType] BinaryEqualStrict let jsInstanceOf (typeRef: Expr) expr = makeBinOp None Boolean [expr; typeRef] BinaryInstanceOf match typ with @@ -263,12 +286,12 @@ let rec makeTypeTest com range (typ: Type) expr = | Array _ | Tuple _ -> CoreLibCall ("Util", Some "isArray", false, [expr]) |> makeCall range Boolean - | Any -> makeConst true + | Any -> makeBoolConst true | Option typ -> makeTypeTest com range typ expr | DeclaredType(typEnt, _) -> match typEnt.Kind with | Interface -> - CoreLibCall ("Util", Some "hasInterface", false, [expr; makeConst typEnt.FullName]) + CoreLibCall ("Util", Some "hasInterface", false, [expr; makeStrConst typEnt.FullName]) |> makeCall range Boolean | _ -> makeBinOp range Boolean [expr; makeNonGenTypeRef com typ] BinaryInstanceOf @@ -300,7 +323,7 @@ let makeRecordCons com (ent: Entity) (props: (string*Type) list) = let body = List.zip args props |> List.map (fun (arg, (propName, _)) -> - Set(Value This, Some(makeConst propName), makeIdentExpr arg.Name, None)) + Set(Value This, Some(makeStrConst propName), makeIdentExpr arg.Name, None)) |> fun setters -> match ent.Kind with | Fable.Exception _ -> diff --git a/src/fable/Fable.Core/Util.fs b/src/fable/Fable.Core/Util.fs index 45a97ef247..7f109972b9 100644 --- a/src/fable/Fable.Core/Util.fs +++ b/src/fable/Fable.Core/Util.fs @@ -11,8 +11,6 @@ module Patterns = if Set.contains item set then Some item else None module Naming = - open System - open System.IO open System.Text.RegularExpressions open Patterns @@ -69,7 +67,7 @@ module Naming = let replaceIdentForbiddenChars (ident: string) = identForbiddenCharsRegex.Replace(ident, fun (m: Match) -> - "$" + (int m.Value.[0]).ToString("X") + "$") + sprintf "$%X$" (int m.Value.[0]) ) let removeGetSetPrefix = let reg2 = Regex(@"^[gs]et_") @@ -121,14 +119,62 @@ module Naming = module Path = open System - open System.IO open Patterns + let Combine (path1: string, path2: string) = +#if FABLE_COMPILER + (path1.TrimEnd [|'\\';'/'|]) + "/" + (path2.TrimStart [|'\\';'/'|]) +#else + IO.Path.Combine(path1, path2) +#endif + + let Combine3 (path1: string, path2: string, path3: string) = +#if FABLE_COMPILER + (path1.TrimEnd [|'\\';'/'|]) + "/" + (path2.Trim [|'\\';'/'|]) + "/" + (path3.TrimStart [|'\\';'/'|]) +#else + IO.Path.Combine(path1, path2, path3) +#endif + + let ChangeExtension (path: string, ext: string) = +#if FABLE_COMPILER + path+ext //TODO: proper implementation +#else + IO.Path.ChangeExtension(path, ext) +#endif + + let GetExtension (path: string) = +#if FABLE_COMPILER + path //TODO: proper implementation +#else + IO.Path.GetExtension(path) +#endif + + let GetFileNameWithoutExtension (path: string) = +#if FABLE_COMPILER + path //TODO: proper implementation +#else + IO.Path.GetFileNameWithoutExtension(path) +#endif + + let GetDirectoryName (path: string) = +#if FABLE_COMPILER + path //TODO: proper implementation +#else + IO.Path.GetDirectoryName(path) +#endif + + let GetFullPath (path: string) = +#if FABLE_COMPILER + path //TODO: proper implementation +#else + IO.Path.GetFullPath(path) +#endif + let normalizePath (path: string) = path.Replace("\\", "/") let normalizeFullPath (path: string) = - Path.GetFullPath(path).Replace("\\", "/") + normalizePath (GetFullPath path) /// Creates a relative path from one file or folder to another. let getRelativeFileOrDirPath fromIsDir fromPath toIsDir toPath = @@ -146,17 +192,17 @@ module Path = elif c = path1.Length && c = path2.Length then String.Empty else - let builder = new System.Text.StringBuilder() + let mutable builder = "" while c < path1.Length do - if path1.[c] = '/' then builder.Append("../") |> ignore + if path1.[c] = '/' then builder <- builder + "../" c <- c + 1 if builder.Length = 0 && path2.Length - 1 = d then "./" - else builder.ToString() + path2.Substring(d + 1) + else builder + path2.Substring(d + 1) // Add a dummy file to make it work correctly with dirs let addDummyFile isDir path = if isDir - then IO.Path.Combine(path, Naming.dummyFile) + then Combine (path, Naming.dummyFile) else path let fromPath = addDummyFile fromIsDir fromPath let toPath = addDummyFile toIsDir toPath @@ -167,7 +213,7 @@ module Path = let getRelativePath fromPath toPath = // This is not 100% reliable, but IO.Directory.Exists doesn't // work either if the directory doesn't exist (e.g. `outDir`) - let isDir = Path.GetExtension >> String.IsNullOrWhiteSpace + let isDir = GetExtension >> String.IsNullOrWhiteSpace // let isDir = IO.Directory.Exists getRelativeFileOrDirPath (isDir fromPath) fromPath (isDir toPath) toPath @@ -194,7 +240,7 @@ module Path = filePaths |> Seq.map (fun filePath -> filePath - |> Path.GetDirectoryName + |> GetDirectoryName |> normalizePath |> fun path -> path.Split('/') |> Array.filter (String.IsNullOrWhiteSpace >> not)) diff --git a/src/netcore/Fable.Client.Browser/.gitignore b/src/netcore/Fable.Client.Browser/.gitignore new file mode 100644 index 0000000000..9cc72b968c --- /dev/null +++ b/src/netcore/Fable.Client.Browser/.gitignore @@ -0,0 +1,5 @@ +# Output +out/ + +# Node +node_modules/ diff --git a/src/netcore/Fable.Client.Browser/Fable.Client.fs b/src/netcore/Fable.Client.Browser/Fable.Client.fs new file mode 100644 index 0000000000..63a0b8dbcb --- /dev/null +++ b/src/netcore/Fable.Client.Browser/Fable.Client.fs @@ -0,0 +1,183 @@ +module Fable.Client + +open System +open Microsoft.FSharp.Compiler.SourceCodeServices +open Fable +open Fable.AST + +type FSProjInfo = FSharp2Fable.Compiler.FSProjectInfo + +type CompilerMessage = + | Error of message: string * stack: string option + | Log of message: string + static member toDic = function + | Error (msg, Some stack) -> + dict [ ("type", "ERROR"); ("message", msg); ("stack", stack) ] + | Error (msg, None) -> + dict [ ("type", "ERROR"); ("message", msg) ] + | Log msg -> + dict [ ("type", "LOG"); ("message", msg) ] + +let readOptions argv = + let splitKeyValue (x: string) = + let xs = x.Split('=') + if xs.Length > 1 + then xs.[0], xs.[1] + else xs.[0], (string true) + let def opts key defArg f = + defaultArg (Map.tryFind key opts |> Option.map f) defArg + let rec readOpts opts = function + | [] -> opts + | (opt: string)::rest -> + let k = opt.Substring(2) + match Map.tryFind k opts with + | None -> Map.add k (U2.Case1 rest.Head) opts + | Some (U2.Case1 v) -> Map.add k (U2.Case2 [rest.Head;v]) opts + | Some (U2.Case2 v) -> Map.add k (U2.Case2 (rest.Head::v)) opts + |> readOpts <| rest.Tail + let boolParse (s:string) = (s.ToLower() = "true") + let un f = function U2.Case1 v -> f v | U2.Case2 _ -> failwith "Unexpected multiple argument" + let li f = function U2.Case1 v -> [f v] | U2.Case2 v -> List.map f v + let opts = readOpts Map.empty<_,_> (List.ofArray argv) + let opts = { + projFile = def opts "projFile" [] (li Path.GetFullPath) |> List.rev + outDir = def opts "outDir" "." (un Path.GetFullPath) + coreLib = "fable-core" + moduleSystem = def opts "module" "es2015" (un id) + symbols = def opts "symbols" [] (li id) |> List.append ["FABLE_COMPILER"] |> List.distinct + plugins = def opts "plugins" [] (li id) + rollup = def opts "rollup" false (un boolParse) + watch = def opts "watch" false (un boolParse) + dll = def opts "dll" false (un boolParse) + includeJs = def opts "includeJs" false (un boolParse) + noTypedArrays = def opts "noTypedArrays" false (un boolParse) + clamp = def opts "clamp" false (un boolParse) + declaration = def opts "declaration" false (un boolParse) + refs = Map(def opts "refs" [] (li splitKeyValue)) + extra = Map(def opts "extra" [] (li splitKeyValue)) + } + match Map.tryFind "Fable.Core" opts.refs with + | Some coreLib -> { opts with coreLib = coreLib } + | None when not opts.rollup && Naming.umdModules.Contains opts.moduleSystem -> + { opts with coreLib = "fable-core/umd" } + | None -> opts + +/// Returns an (errors, warnings) tuple +let parseErrors errors = + let parseError (er: FSharpErrorInfo) = + let isError, severity = + match er.Severity with + | FSharpErrorSeverity.Warning -> false, "warning" + | FSharpErrorSeverity.Error -> true, "error" + isError, sprintf "%s(L%i,%i) : %s FSHARP: %s" + er.FileName er.StartLineAlternate er.StartColumn + severity er.Message + errors + |> Array.map parseError + |> Array.partition fst + |> fun (ers, wns) -> Array.map snd ers, Array.map snd wns + +let parseFSharpProject (com: ICompiler) (checker: InteractiveChecker) (fileName,source) = + let _,_,checkProjectResults = + (fileName,source) |> checker.ParseAndCheckScript + let errors, warnings = + parseErrors checkProjectResults.Errors + if errors.Length = 0 + then warnings |> Array.map Warning, checkProjectResults + else errors + |> Seq.append ["F# project contains errors:"] + |> String.concat "\n" + |> FableError |> raise + +let makeCompiler opts plugins = + let id = ref 0 + let logs = ResizeArray() + let projDir = "." //Fable.Path.getCommonBaseDir opts.projFile + { new ICompiler with + member __.Options = opts + member __.ProjDir = projDir + member __.Plugins = plugins + member __.AddLog msg = logs.Add msg + member __.GetLogs() = + let copy = logs.ToArray() + logs.Clear() + upcast copy + member __.GetUniqueVar() = + id := !id + 1 + "$var" + string !id } + +let printFile = + fun (file: AST.Babel.Program) -> + printfn "%A" file + +let printMessages (msgs: seq) = + msgs + |> Seq.map CompilerMessage.toDic + |> Seq.iter (printfn "%A") + +let printException (ex: Exception) = + let msg, stackTrace = + match ex with + // Don't print stack trace for known Fable errors + | :? FableError as err -> err.FormattedMessage, None + | ex -> ex.Message, Some(ex.StackTrace) + printMessages [Error(msg, stackTrace)] + +let makeProjInfo (com: ICompiler) fileName = + let projOptions: FSharpProjectOptions = + { ProjectFileName = "test.fsx" + ProjectFileNames = [| |] + OtherOptions = [| |] + ReferencedProjects = [| |] + IsIncompleteTypeCheckEnvironment = false + UseScriptResolutionRules = false + LoadTime = DateTime.Now } + let filePairs = + [fileName] + |> Seq.map (fun fileName -> + fileName, Fable.Path.Combine(com.Options.outDir, Fable.Path.ChangeExtension(fileName, ".js"))) + |> Map + let projInfo = FSProjInfo(projOptions, filePairs) + projInfo + +let compileAst (com: ICompiler) checker (fileName, source) = + let warnings, parsedProj = parseFSharpProject com checker (fileName, source) + let projInfo = makeProjInfo com fileName + let extraInfo, files = + FSharp2Fable.Compiler.transformFiles com parsedProj projInfo + |> Fable2Babel.Compiler.transformFiles com + files + +let compile (com: ICompiler) checker (fileName, source) = + try + // Parse project (F# Compiler Services) and print diagnostic info + // -------------------------------------------------------------- + let warnings, parsedProj = + parseFSharpProject com checker (fileName, source) + + warnings |> Seq.map (string >> Log) |> printMessages + + [Log "F# compilation complete. Starting Fable..."] |> printMessages + + // make projInfo + let projInfo = makeProjInfo com fileName + + // Compile project files, print them and get extra info + // ---------------------------------------------------- + let extraInfo, files = + FSharp2Fable.Compiler.transformFiles com parsedProj projInfo + |> Fable2Babel.Compiler.transformFiles com + + files + |> Seq.iter printFile + + // Print logs + // ---------- + com.GetLogs() |> Seq.map (string >> Log) |> printMessages + + printfn "[SIGSUCCESS]" + true + with ex -> + printException ex + printfn "[SIGFAIL]" + false diff --git a/src/netcore/Fable.Client.Browser/Fable.Compiler.fsx b/src/netcore/Fable.Client.Browser/Fable.Compiler.fsx new file mode 100644 index 0000000000..17e1e2a005 --- /dev/null +++ b/src/netcore/Fable.Client.Browser/Fable.Compiler.fsx @@ -0,0 +1,28 @@ +#if FABLE_COMPILER && !DOTNETCORE && !DOTNET40 +//#r "System.Threading.dll" +#r "System.Text.RegularExpressions.dll" +#endif + +#load + "../../../../FSharp.Compiler.Service_fable/src/fsharp/Fable.FCS/Fable.FCS.fsx" + + "../../fable/Fable.Core/Compiler.fs" + "../../fable/Fable.Core/Util.fs" + "../../fable/Fable.Core/AST/AST.Common.fs" + "../../fable/Fable.Core/AST/AST.Fable.fs" + "../../fable/Fable.Core/AST/AST.Fable.Util.fs" + "../../fable/Fable.Core/AST/AST.Babel.fs" + "../../fable/Fable.Core/Plugins.fs" + "../../fable/Fable.Core/Fable.Core.fs" + // "../../fable/Fable.Core/Import/Fable.Import.JS.fs" + // "../../fable/Fable.Core/Import/Fable.Import.Browser.fs" + // "../../fable/Fable.Core/Import/Fable.Import.Node.fs" + // "../../fable/Fable.Core/Fable.Core.Extensions.fs" + + "../../fable/Fable.Compiler/Utils.fs" + "../../fable/Fable.Compiler/Replacements.fs" + "../../fable/Fable.Compiler/FSharp2Fable.Util.fs" + "../../fable/Fable.Compiler/FSharp2Fable.fs" + "../../fable/Fable.Compiler/Fable2Babel.fs" + + "Fable.Client.fs" diff --git a/src/netcore/Fable.Client.Browser/app.fs b/src/netcore/Fable.Client.Browser/app.fs new file mode 100644 index 0000000000..97c6031121 --- /dev/null +++ b/src/netcore/Fable.Client.Browser/app.fs @@ -0,0 +1,37 @@ +module App + +open Microsoft.FSharp.Compiler.SourceCodeServices +open Fable.Client + +#if !DOTNETCORE && !DOTNET40 + +[] +let readFileSync: System.Func = failwith "JS only" + +#else + +[] +#endif +let main argv = + try + let source = """ +type BB() = member x.MMM() = 2 * 3 +printfn "answer: %A" (42 * BB().MMM()) +""" + let metadataPath = "/temp/metadata/" + let references = ["mscorlib";"System";"System.Core";"System.Data";"System.IO";"System.Xml";"System.Numerics"] +#if DOTNETCORE || DOTNET40 + let readAllBytes = fun (fileName:string) -> System.IO.File.ReadAllBytes (metadataPath + fileName) +#else + let readAllBytes = fun (fileName:string) -> readFileSync.Invoke (metadataPath + fileName) +#endif + + let checker = InteractiveChecker(references, readAllBytes) + let opts = readOptions argv + let com = makeCompiler opts [] + let fileName = "stdin.fsx" + let success = compile com checker (fileName, source) + () + with ex -> + printException ex + 0 diff --git a/src/netcore/Fable.Client.Browser/demo/fableconfig.json b/src/netcore/Fable.Client.Browser/demo/fableconfig.json new file mode 100644 index 0000000000..f9d56beabe --- /dev/null +++ b/src/netcore/Fable.Client.Browser/demo/fableconfig.json @@ -0,0 +1,18 @@ +{ + "projFile": "project.fsx", + "ecma": "es2015", + "sourceMaps": true, + "rollup": { + "dest": "./out/bundle.js" + }, + "symbols": [ + "FX_NO_CORHOST_SIGNER", + "FX_NO_LINKEDRESOURCES", + "FX_NO_PDB_READER", + "FX_NO_PDB_WRITER", + "NO_COMPILER_BACKEND", + "NO_INLINE_IL_PARSER", + "TRACE", + "DEBUG" + ] +} \ No newline at end of file diff --git a/src/netcore/Fable.Client.Browser/demo/index.html b/src/netcore/Fable.Client.Browser/demo/index.html new file mode 100644 index 0000000000..b7bbfba195 --- /dev/null +++ b/src/netcore/Fable.Client.Browser/demo/index.html @@ -0,0 +1,62 @@ + + + + + + +Fable JS + + + + +
+
+
+
+
+ Presets: + + + +
+
+
+
+
+
+
+
+
Fable JS v0.0.1, Babel v
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/netcore/Fable.Client.Browser/demo/project.fsx b/src/netcore/Fable.Client.Browser/demo/project.fsx new file mode 100644 index 0000000000..9992c2ba68 --- /dev/null +++ b/src/netcore/Fable.Client.Browser/demo/project.fsx @@ -0,0 +1,12 @@ +#load "../Fable.Compiler.fsx" + +open Microsoft.FSharp.Compiler.SourceCodeServices +open Fable.Client + +let compileSource readAllBytes references source = + let checker = InteractiveChecker(List.ofArray references, readAllBytes) + let opts = readOptions [||] + let com = makeCompiler opts [] + let fileName = "stdin.fsx" + let files = compileAst com checker (fileName, source) + files |> Array.ofSeq diff --git a/src/netcore/Fable.Client.Browser/demo/repl.js b/src/netcore/Fable.Client.Browser/demo/repl.js new file mode 100644 index 0000000000..224c3e0897 --- /dev/null +++ b/src/netcore/Fable.Client.Browser/demo/repl.js @@ -0,0 +1,540 @@ +(function(babel, $, _, ace, window) { + 'use strict'; + + var presets = [ + 'es2015', + 'es2015-loose', + 'es2016', + 'es2017', + 'latest', + 'react', + 'stage-0', + 'stage-1', + 'stage-2', + 'stage-3' + ]; + + /* Throw meaningful errors for getters of commonjs. */ + var enableCommonJSError = true; + ["module", "exports", "require"].forEach(function(commonVar){ + Object.defineProperty(window, commonVar, { + configurable: true, + get: function () { + if (enableCommonJSError) { + throw new Error(commonVar + " is not supported in the browser, you need a commonjs environment such as node.js/io.js, browserify/webpack etc"); + } + } + }); + }); + + /* + * Utils for working with the browser's URI (e.g. the query params) + */ + function UriUtils () {} + + UriUtils.encode = function (value) { + return window.encodeURIComponent(value); + }; + + UriUtils.decode = function (value) { + try { + return window.decodeURIComponent('' + value); + } catch (err) { + return value; + } + }; + + UriUtils.parseQuery = function () { + var query = window.location.hash.replace(/^\#\?/, ''); + + if (!query) { + return null; + } + + return query.split('&').map(function(param) { + var splitPoint = param.indexOf('='); + + return { + key : param.substring(0, splitPoint), + value : param.substring(splitPoint + 1) + }; + }).reduce(function(params, param){ + if (param.key && param.value) { + params[param.key] = UriUtils.decode(param.value); + } + return params; + }, {}); + }; + + UriUtils.updateQuery = function (object) { + var query = Object.keys(object).map(function(key){ + return key + '=' + UriUtils.encode(object[key]); + }).join('&'); + + window.location.hash = '?' + query; + }; + + /* + * Long term storage for persistence of state/etc + */ + function StorageService () { + this.store = window.localStorage; + } + + StorageService.prototype.get = function (key) { + try { + return JSON.parse(this.store.getItem(key)); + } catch(e) {} + }; + + StorageService.prototype.set = function (key, value) { + try { + this.store.setItem(key, JSON.stringify(value)); + } catch(e) {} + }; + + /* + * Decorating the ACE editor + */ + function Editor(selector) { + this.$el = $(selector); + this.editor = ace.edit(this.$el[0]); + this.session = this.editor.getSession(); + this.document = this.session.getDocument(); + + this.editor.setTheme('ace/theme/tomorrow'); + this.editor.setShowPrintMargin(false); + this.editor.commands.removeCommands(['gotoline', 'find']); + this.$el.css({ + fontFamily: '"Operator Mono", "Fira Code", "Ubuntu Mono", "Droid Sans Mono", "Liberation Mono", "Source Code Pro", Menlo, Monaco, Consolas, "Courier New", monospace', + lineHeight: 'inherit' + }); + + this.session.setMode('ace/mode/javascript'); + this.session.setUseSoftTabs(true); + this.session.setTabSize(2); + this.session.setUseWorker(false); + + this.editor.setOption('scrollPastEnd', 0.33); + } + + /* + * Options exposed for the REPL that will influence Babel's transpiling + */ + function $checkbox($element){ + return { + get: function () { + return $element.is(":checked"); + } , + set: function (value) { + var setting = value !== 'false' && value !== false; + $element.prop('checked', setting); + }, + enumerable: true, + configurable: false + }; + } + + /** + * By default, Bootstrap closes dropdown menus whenever an item in them is + * clicked. This function overrides that behaviour for the presets dropdown, + * and ensures the checkbox is selected correctly. + */ + function handlePresetClick($input, evt) { + evt.preventDefault(); + evt.stopPropagation(); + + // Needs to run in a timeout to properly handle clicks directly on the + // checkbox. + setTimeout(function() { + $input.checked = !$input.checked; + onPresetChange(); + }, 0); + } + + /** + * Options for selecting presets to use. + */ + function getPresetOptions() { + // Create the checkboxes for all available presets + var $presetContainer = document.getElementById('babel-repl-preset-dropdown'); + var $presets = []; + presets.forEach(function(presetName) { + var $input = document.createElement('input'); + $input.type = 'checkbox'; + $input.name = 'preset'; + $input.value = presetName; + $input.id = 'option-' + presetName; + + // This should really be a