diff --git a/src/Fable.Cli/CHANGELOG.md b/src/Fable.Cli/CHANGELOG.md index 4c6132263c..b699a1d543 100644 --- a/src/Fable.Cli/CHANGELOG.md +++ b/src/Fable.Cli/CHANGELOG.md @@ -6,24 +6,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Changed + #### Python * Use `Any` type for all non-repeated generic arguments (by @dbrattli) * Don't generate unnecessary type type-vars if generic type is replaced by `Any` (by @dbrattli) * Generate new style `_T | None` instead of `Optional[_T]` (by @dbrattli) +#### Rust + +* Support multiple namespaces sharing a prefix in the same file (by @ncave) +* Support imports with the same namespace across multiple files (by @ncave) + ### Fixed #### JavaScript * Fix #3571: `[]` not compatible with f# member `this.Item` (by @ncave) -### Changed - -#### Rust - -* Support multiple namespaces sharing a prefix in the same file (by @ncave) - ## 4.4.1 - 2023-10-25 ### Changed diff --git a/src/Fable.Transforms/Rust/AST/Rust.AST.Helpers.fs b/src/Fable.Transforms/Rust/AST/Rust.AST.Helpers.fs index 21b27d14a4..f848b6ce45 100644 --- a/src/Fable.Transforms/Rust/AST/Rust.AST.Helpers.fs +++ b/src/Fable.Transforms/Rust/AST/Rust.AST.Helpers.fs @@ -589,6 +589,14 @@ module Attrs = let kind = mkAttrKind name args mkAttribute kind AttrStyle.Outer + let mkLineCommentAttr (comment: Symbol): Attribute = + let kind = AttrKind.DocComment(token.CommentKind.Line, comment) + mkAttribute kind AttrStyle.Outer + + let mkBlockCommentAttr (comment: Symbol): Attribute = + let kind = AttrKind.DocComment(token.CommentKind.Block, comment) + mkAttribute kind AttrStyle.Outer + let mkEqAttr (name: Symbol) (value: Symbol): Attribute = let args = MacArgs.Eq(DUMMY_SP, mkStrToken value) let kind = mkAttrKind name args diff --git a/src/Fable.Transforms/Rust/Fable2Rust.fs b/src/Fable.Transforms/Rust/Fable2Rust.fs index 5cc7d4e81b..2a0551c215 100644 --- a/src/Fable.Transforms/Rust/Fable2Rust.fs +++ b/src/Fable.Transforms/Rust/Fable2Rust.fs @@ -13,7 +13,6 @@ type HashSet<'T> = System.Collections.Generic.HashSet<'T> type Import = { Selector: string LocalIdent: string - ModuleName: string ModulePath: string Path: string mutable Depths: int list @@ -62,7 +61,9 @@ type IRustCompiler = abstract WarnOnlyOnce: string * ?range: SourceLocation -> unit abstract GetAllImports: Context -> Import list abstract ClearAllImports: Context -> unit - abstract GetAllModules: unit -> (string * string) list + abstract GetAllModules: unit -> string list + abstract GetAllNamespaces: unit -> (string * string) list + abstract AddNamespace: string * string -> unit abstract GetImportName: Context * selector: string * path: string * SourceLocation option -> string abstract TransformExpr: Context * Fable.Expr -> Rust.Expr abstract GetEntity: entRef: Fable.EntityRef -> Fable.Entity @@ -80,6 +81,48 @@ module Helpers = | Some old -> acc |> Map.add key (aggregateFn old value) | None -> acc |> Map.add key value) + +module Namespace = + + type Trie<'K, 'V when 'K: comparison and 'V: comparison> = { + Values: Set<'V> + Children: Map<'K, Trie<'K, 'V>> + } + + module Trie = + let empty = { + Values = Set.empty + Children = Map.empty + } + + let isLeaf trie = + not (Set.isEmpty trie.Values) + + let isEmpty trie = + Map.isEmpty trie.Children && not (isLeaf trie) + + let rec add path value trie = + match path with + | [] -> + { trie with Values = Set.add value trie.Values } + | x::xs -> + let child = + trie.Children + |> Map.tryFind x + |> Option.defaultValue empty + |> add xs value + let children = + trie.Children + |> Map.add x child + { trie with Children = children } + + let ofSeq (xs: (string * string) seq) = + (empty, xs) + ||> Seq.fold (fun st (m, n) -> + let path = n.Split('.') |> List.ofArray + add path m st + ) + module UsageTracking = // type ConsumptionType = @@ -3025,19 +3068,44 @@ module Util = let relPath = Path.getRelativePath com.CurrentFile filePath com.GetImportName(ctx, "*", relPath, None) |> ignore ) - let makeModItems (modulePath, moduleName) = + // re-export modules at crate level + let makeModItems modulePath = let relPath = Path.getRelativePath com.CurrentFile modulePath + let modName = getImportModuleName com modulePath let attrs = [mkEqAttr "path" relPath] - let modItem = mkUnloadedModItem attrs moduleName - let useItem = mkGlobUseItem [] [moduleName] - [modItem; useItem |> mkPublicItem] // re-export modules at top level + let modItem = mkUnloadedModItem attrs modName + let useItem = mkGlobUseItem [] [modName] + [modItem; useItem |> mkPublicItem] let modItems = com.GetAllModules() - |> List.sortBy fst + |> List.sort |> List.collect makeModItems modItems else [] + let getNamespaceItems (com: IRustCompiler) ctx = + if isLastFileInProject com then + // convert namespace trie to modules and glob use items + let rec toItems mods trie: Rust.Item list = [ + if Namespace.Trie.isLeaf trie then + let modNames = List.rev mods + for filePath in trie.Values do + let modName = getImportModuleName com filePath + let useItem = mkGlobUseItem [] ("crate"::modName::modNames) + yield useItem |> mkPublicItem + for KeyValue(key, trie) in trie.Children do + let items = toItems (key::mods) trie + let modItem = mkModItem [] key items + yield modItem |> mkPublicItem + ] + // re-export globally merged namespaces at crate level + let nsItems = + com.GetAllNamespaces() + |> Namespace.Trie.ofSeq + |> toItems [] + nsItems + else [] + let getEntryPointItems (com: IRustCompiler) ctx decls = let entryPoint = decls |> List.tryPick (tryFindEntryPoint com) match entryPoint with @@ -4139,7 +4207,9 @@ module Util = [] // don't output empty modules else let ent = com.GetEntity(decl.Entity) - // if ent.IsNamespace then // maybe do something different + if ent.IsNamespace then + // add the namespace to a global list to be re-exported + com.AddNamespace(com.CurrentFile, ent.FullName) let useDecls = let useItem = mkGlobUseItem [] ["super"] let importItems = com.GetAllImports(ctx) |> transformImports com ctx @@ -4177,9 +4247,18 @@ module Util = transformClassDecl com ctx decl let transformDeclarations (com: IRustCompiler) ctx decls = - decls - |> mergeNamespaceDecls com ctx - |> List.collect (transformDecl com ctx) + let items = + decls + |> mergeNamespaceDecls com ctx + |> List.collect (transformDecl com ctx) + // wrap the last file in a module to consistently handle namespaces + if isLastFileInProject com then + let modPath = fixFileExtension com com.CurrentFile + let modName = getImportModuleName com modPath + let modItem = mkModItem [] modName items + let useItem = mkGlobUseItem [] [modName] + [modItem; useItem |> mkPublicItem] + else items // F# hash function is unstable and gives different results in different runs // Taken from fable-library/Util.ts. Possible variant in https://stackoverflow.com/a/1660613 @@ -4208,7 +4287,7 @@ module Util = modulePath let getImportModuleName (com: IRustCompiler) (modulePath: string) = - let relPath = Path.getRelativePath com.CurrentFile modulePath + let relPath = Path.getRelativePath com.ProjectFile modulePath System.String.Format("module_{0:x}", stableStringHash relPath) let transformImports (com: IRustCompiler) ctx (imports: Import list): Rust.Item list = @@ -4225,18 +4304,22 @@ module Util = else if isFableLibraryPath com import.Path then ["fable_library_rust"] - else ["crate"; import.ModuleName] + else ["crate"] match import.Selector with | "" | "*" | "default" -> - mkGlobUseItem [] modPath + // let useItem = mkGlobUseItem [] modPath + // [useItem] + [] | _ -> let parts = splitNameParts import.Selector let alias = if List.last parts <> import.LocalIdent then Some(import.LocalIdent) else None - mkSimpleUseItem [] (modPath @ parts) alias + let useItem = mkSimpleUseItem [] (modPath @ parts) alias + [useItem] ) + |> List.concat ) let getIdentForImport (ctx: Context) (path: string) (selector: string) = @@ -4245,14 +4328,20 @@ module Util = | _ -> splitNameParts selector |> List.last |> getUniqueNameInRootScope ctx + let fixFileExtension (com: IRustCompiler) (path: string) = + if path.EndsWith(".fs") then + let fileExt = com.Options.FileExtension + Path.ChangeExtension(path, fileExt) + else path module Compiler = open System.Collections.Generic open System.Collections.Concurrent open Util - // global list of import modules (across files) - let importModules = ConcurrentDictionary() + // global list of import modules and namespaces (across files) + let importModules = ConcurrentDictionary() + let importNamespaces = ConcurrentDictionary() // per file type RustCompiler (com: Fable.Compiler) = @@ -4270,11 +4359,7 @@ module Compiler = |> addError com [] r let isMacro = selector.EndsWith("!") let selector = selector |> Fable.Naming.replaceSuffix "!" "" - let path = - if path.EndsWith(".fs") then - let fileExt = (self :> Compiler).Options.FileExtension - Path.ChangeExtension(path, fileExt) - else path + let path = fixFileExtension self path let cacheKey = let selector = selector.Replace(".", "::").Replace("`", "_") if (isFableLibraryPath self path) @@ -4290,18 +4375,16 @@ module Compiler = | false, _ -> let localIdent = getIdentForImport ctx path selector let modulePath = getImportModulePath self path - let moduleName = getImportModuleName self modulePath let import = { Selector = selector LocalIdent = localIdent - ModuleName = moduleName ModulePath = modulePath Path = path Depths = [ctx.ModuleDepth] } // add import module to a global list (across files) if path.Length > 0 && not (isFableLibraryPath self path) then - importModules.TryAdd(modulePath, moduleName) |> ignore + importModules.TryAdd(modulePath, true) |> ignore imports.Add(cacheKey, import) import @@ -4326,7 +4409,14 @@ module Compiler = ctx.UsedNames.RootScope.Remove(import.Value.LocalIdent) |> ignore member _.GetAllModules() = - importModules |> Seq.map (fun p -> p.Key, p.Value) |> Seq.toList + importModules.Keys |> Seq.toList + + member _.GetAllNamespaces() = + importNamespaces.Keys |> Seq.toList + + member self.AddNamespace(path, entFullName) = + let path = fixFileExtension self path + importNamespaces.TryAdd((path, entFullName), true) |> ignore member com.TransformExpr(ctx, e) = transformExpr com ctx e @@ -4413,8 +4503,9 @@ module Compiler = let entryPointItems = file.Declarations |> getEntryPointItems com ctx let importItems = com.GetAllImports(ctx) |> transformImports com ctx let declItems = file.Declarations |> transformDeclarations com ctx - let moduleItems = getModuleItems com ctx // global module imports - let crateItems = importItems @ declItems @ moduleItems @ entryPointItems + let modItems = getModuleItems com ctx // global module imports + let nsItems = getNamespaceItems com ctx // global namespace imports + let crateItems = importItems @ declItems @ modItems @ nsItems @ entryPointItems let innerAttrs = file.Declarations |> getInnerAttributes com ctx let crateAttrs = topAttrs @ innerAttrs let crate = mkCrate crateAttrs crateItems diff --git a/src/fable-library-rust/src/Fable.Library.fsproj b/src/fable-library-rust/src/Fable.Library.fsproj index a66d1eac3b..95cd041625 100644 --- a/src/fable-library-rust/src/Fable.Library.fsproj +++ b/src/fable-library-rust/src/Fable.Library.fsproj @@ -8,6 +8,8 @@ + + diff --git a/src/fable-library-rust/src/System.Collections.Generic.fs b/src/fable-library-rust/src/System.Collections.Generic.fs new file mode 100644 index 0000000000..a927120474 --- /dev/null +++ b/src/fable-library-rust/src/System.Collections.Generic.fs @@ -0,0 +1,216 @@ +namespace System.Collections.Generic + +open Global_ + +// type Comparer<'T when 'T: comparison>() = +// static member Default = Comparer<'T>() +// interface IComparer<'T> with +// member _.Compare(x, y) = LanguagePrimitives.GenericComparison x y + +// type EqualityComparer<'T when 'T: equality>() = +// static member Default = EqualityComparer<'T>() +// interface IEqualityComparer<'T> with +// member _.Equals(x, y) = LanguagePrimitives.GenericEquality x y +// member _.GetHashCode(x) = LanguagePrimitives.GenericHash x + +type Stack<'T when 'T: equality> private (initialContents: 'T[], initialCount) = + let mutable contents = initialContents + let mutable count = initialCount + + let toSeq() = + let count = count + let contents = contents + seq { for i = count - 1 downto 0 do contents[i] } + + new (initialCapacity: int) = + let arr = Array.zeroCreate<'T>(initialCapacity) + Stack<'T>(arr, 0) + + new () = Stack<'T>(4) + + new (xs: IEnumerable<'T>) = + let arr = Array.ofSeq xs + Stack<'T>(arr, arr.Length) + + member _.Ensure(newSize) = + let oldSize = contents.Length + if newSize > oldSize then + let old = contents + contents <- Array.zeroCreate (max newSize (oldSize * 2)) + Array.blit old 0 contents 0 count + + member _.Count = count + + member _.Pop() = + count <- count - 1 + contents[count] + + member _.Peek() = + contents[count - 1] + + member _.Contains(x: 'T) = + let mutable found = false + let mutable i = 0 + while i < count && not found do + if x = contents[i] then + found <- true + else + i <- i + 1 + found + + member this.TryPeek(result: 'T byref) = + if count > 0 + then + result <- this.Peek() + true + else + false + + member this.TryPop(result: 'T byref) = + if count > 0 + then + result <- this.Pop() + true + else + false + + member this.Push(x) = + this.Ensure(count + 1) + contents[count] <- x + count <- count + 1 + + member _.Clear() = + count <- 0 + Array.fill contents 0 contents.Length Unchecked.defaultof<_> + + member this.TrimExcess() = + if float count / float contents.Length > 0.9 + then + this.Ensure(count) + + member _.ToArray() = + let res = ResizeArray<_>(count) + for i = 0 to count - 1 do + res.Add(contents[count - 1 - i]) + res |> asArray + + interface IEnumerable<'T> with + member _.GetEnumerator(): IEnumerator<'T> = + toSeq().GetEnumerator() + + interface System.Collections.IEnumerable with + member _.GetEnumerator(): System.Collections.IEnumerator = + toSeq().GetEnumerator() :> System.Collections.IEnumerator + +type Queue<'T when 'T: equality> private (initialContents, initialCount) = + let mutable contents: 'T array = initialContents + let mutable count = initialCount + let mutable head = 0 + let mutable tail = initialCount + + let size() = contents.Length + + let toIndex i = (head + i) % size() + + let ensure(requiredSize: int) = + let newBuffer: 'T array = Array.zeroCreate requiredSize + + if head < tail then + Array.blit contents head newBuffer 0 count + else + Array.blit contents head newBuffer 0 (size() - head) + Array.blit contents 0 newBuffer (size() - head) tail + + head <- 0 + tail <- count + contents <- newBuffer + + let toSeq() = + let head = head + let count = count + let contents = contents + let inline toIndex i = (head + i) % contents.Length + seq { for i = 0 to count - 1 do contents[toIndex i] } + + new (initialCapacity: int) = + if initialCapacity < 0 then failwith "capacity is less than 0" + Queue<'T>(Array.zeroCreate<'T>(initialCapacity), 0) + + new () = Queue<'T>(4) + + new (xs: IEnumerable<'T>) = + let arr = Array.ofSeq xs + Queue<'T>(arr, arr.Length) + + member _.Count = count + + member _.Enqueue (value: 'T) = + if count = size() then + ensure(count + 1) + contents[tail] <- value + tail <- (tail + 1) % size() + count <- count + 1 + + member _.Dequeue (): 'T = + if count = 0 then invalidOp "Queue is empty" + let value = contents[head] + head <- (head + 1) % size() + count <- count - 1 + value + + member _.Peek (): 'T = + if count = 0 then invalidOp "Queue is empty" + contents[head] + + member this.TryDequeue (result: 'T byref): bool = + if count = 0 then + false + else + result <- this.Dequeue() + true + + member this.TryPeek (result: 'T byref): bool = + if count = 0 then + false + else + result <- this.Peek() + true + + member _.Contains(x: 'T) = + let mutable found = false + let mutable i = 0 + while i < count && not found do + if x = contents[toIndex i] then + found <- true + else + i <- i + 1 + found + + member _.Clear() = + count <- 0 + head <- 0 + tail <- 0 + Array.fill contents 0 (size()) Unchecked.defaultof<_> + + member _.TrimExcess() = + if float count / float contents.Length > 0.9 then + ensure(count) + + member _.ToArray(): 'T[] = + let res = ResizeArray<_>(count) + for i = 0 to count - 1 do + res.Add(contents[toIndex i]) + res |> asArray + + member _.CopyTo(target: 'T array, start: int) = + let mutable i = start + for i = 0 to count - 1 do + target[start + i] <- contents[toIndex i] + + interface IEnumerable<'T> with + member _.GetEnumerator(): IEnumerator<'T> = + toSeq().GetEnumerator() + + interface System.Collections.IEnumerable with + member _.GetEnumerator(): System.Collections.IEnumerator = + toSeq().GetEnumerator() :> System.Collections.IEnumerator diff --git a/src/fable-library-rust/src/System.Text.fs b/src/fable-library-rust/src/System.Text.fs new file mode 100644 index 0000000000..39bda19fe0 --- /dev/null +++ b/src/fable-library-rust/src/System.Text.fs @@ -0,0 +1,35 @@ +namespace System.Text + +open Global_ + +type StringBuilder(value: string, capacity: int) = + let buf = ResizeArray(capacity) + do if not (System.String.IsNullOrEmpty value) then buf.Add(value) + new (capacity: int) = StringBuilder("", capacity) + new (value: string) = StringBuilder(value, 16) + new () = StringBuilder("", 16) + member x.Append(s: string) = buf.Add(s); x + member x.Append(o: bool) = x.Append(string o) + member x.Append(c: char) = x.Append(string c) + member x.Append(o: int8) = x.Append(string o) + member x.Append(o: byte) = x.Append(string o) + member x.Append(o: int16) = x.Append(string o) + member x.Append(o: uint16) = x.Append(string o) + member x.Append(o: int32) = x.Append(string o) + member x.Append(o: uint32) = x.Append(string o) + member x.Append(o: int64) = x.Append(string o) + member x.Append(o: uint64) = x.Append(string o) + member x.Append(o: float32) = x.Append(string o) + member x.Append(o: float) = x.Append(string o) + member x.Append(s: string, index: int, count: int) = x.Append(s.Substring(index, count)) + member x.Append(cs: char[]) = x.Append(System.String(cs)) + member x.Append(sb: StringBuilder) = x.Append(sb.ToString()) + // member x.Append(o: obj) = x.Append(string o) + // member x.AppendFormat(fmt: string, o: obj) = x.Append(System.String.Format(fmt, o)) + // member x.AppendFormat(provider: System.IFormatProvider, fmt: string, o: obj) = x.Append(System.String.Format(provider, fmt, o)) + member x.AppendLine() = x.Append(System.Environment.NewLine) + member x.AppendLine(s: string) = x.Append(s).AppendLine() + member x.Clear() = buf.Clear(); x + member x.Length = buf |> asArray |> Array.sumBy (fun s -> s.Length) + override _.ToString() = System.String.Concat(buf |> asArray) + member x.ToString(index: int, count: int) = x.ToString().Substring(index, count) diff --git a/src/fable-library-rust/src/System.fs b/src/fable-library-rust/src/System.fs index 190cc0a253..25c9fd3fe8 100644 --- a/src/fable-library-rust/src/System.fs +++ b/src/fable-library-rust/src/System.fs @@ -1,7 +1,6 @@ -module System +namespace System open Global_ -open System.Collections.Generic type Array() = class end type Enum() = class end @@ -45,254 +44,3 @@ type ArgumentOutOfRangeException(paramName: string, message: string) = then message else message + " (Parameter '" + paramName + "')" member _.ParamName = paramName - -module Collections = - - module Generic = - - // type Comparer<'T when 'T: comparison>() = - // static member Default = Comparer<'T>() - // interface IComparer<'T> with - // member _.Compare(x, y) = LanguagePrimitives.GenericComparison x y - - // type EqualityComparer<'T when 'T: equality>() = - // static member Default = EqualityComparer<'T>() - // interface IEqualityComparer<'T> with - // member _.Equals(x, y) = LanguagePrimitives.GenericEquality x y - // member _.GetHashCode(x) = LanguagePrimitives.GenericHash x - - type Stack<'T when 'T: equality> private (initialContents: 'T[], initialCount) = - let mutable contents = initialContents - let mutable count = initialCount - - let toSeq() = - let count = count - let contents = contents - seq { for i = count - 1 downto 0 do contents[i] } - - new (initialCapacity: int) = - let arr = Array.zeroCreate<'T>(initialCapacity) - Stack<'T>(arr, 0) - - new () = Stack<'T>(4) - - new (xs: IEnumerable<'T>) = - let arr = Array.ofSeq xs - Stack<'T>(arr, arr.Length) - - member _.Ensure(newSize) = - let oldSize = contents.Length - if newSize > oldSize then - let old = contents - contents <- Array.zeroCreate (max newSize (oldSize * 2)) - Array.blit old 0 contents 0 count - - member _.Count = count - - member _.Pop() = - count <- count - 1 - contents[count] - - member _.Peek() = - contents[count - 1] - - member _.Contains(x: 'T) = - let mutable found = false - let mutable i = 0 - while i < count && not found do - if x = contents[i] then - found <- true - else - i <- i + 1 - found - - member this.TryPeek(result: 'T byref) = - if count > 0 - then - result <- this.Peek() - true - else - false - - member this.TryPop(result: 'T byref) = - if count > 0 - then - result <- this.Pop() - true - else - false - - member this.Push(x) = - this.Ensure(count + 1) - contents[count] <- x - count <- count + 1 - - member _.Clear() = - count <- 0 - Array.fill contents 0 contents.Length Unchecked.defaultof<_> - - member this.TrimExcess() = - if float count / float contents.Length > 0.9 - then - this.Ensure(count) - - member _.ToArray() = - let res = ResizeArray<_>(count) - for i = 0 to count - 1 do - res.Add(contents[count - 1 - i]) - res |> asArray - - interface IEnumerable<'T> with - member _.GetEnumerator(): IEnumerator<'T> = - toSeq().GetEnumerator() - - interface System.Collections.IEnumerable with - member _.GetEnumerator(): System.Collections.IEnumerator = - toSeq().GetEnumerator() :> System.Collections.IEnumerator - - type Queue<'T when 'T: equality> private (initialContents, initialCount) = - let mutable contents: 'T array = initialContents - let mutable count = initialCount - let mutable head = 0 - let mutable tail = initialCount - - let size() = contents.Length - - let toIndex i = (head + i) % size() - - let ensure(requiredSize: int) = - let newBuffer: 'T array = Array.zeroCreate requiredSize - - if head < tail then - Array.blit contents head newBuffer 0 count - else - Array.blit contents head newBuffer 0 (size() - head) - Array.blit contents 0 newBuffer (size() - head) tail - - head <- 0 - tail <- count - contents <- newBuffer - - let toSeq() = - let head = head - let count = count - let contents = contents - let inline toIndex i = (head + i) % contents.Length - seq { for i = 0 to count - 1 do contents[toIndex i] } - - new (initialCapacity: int) = - if initialCapacity < 0 then failwith "capacity is less than 0" - Queue<'T>(Array.zeroCreate<'T>(initialCapacity), 0) - - new () = Queue<'T>(4) - - new (xs: IEnumerable<'T>) = - let arr = Array.ofSeq xs - Queue<'T>(arr, arr.Length) - - member _.Count = count - - member _.Enqueue (value: 'T) = - if count = size() then - ensure(count + 1) - contents[tail] <- value - tail <- (tail + 1) % size() - count <- count + 1 - - member _.Dequeue (): 'T = - if count = 0 then invalidOp "Queue is empty" - let value = contents[head] - head <- (head + 1) % size() - count <- count - 1 - value - - member _.Peek (): 'T = - if count = 0 then invalidOp "Queue is empty" - contents[head] - - member this.TryDequeue (result: 'T byref): bool = - if count = 0 then - false - else - result <- this.Dequeue() - true - - member this.TryPeek (result: 'T byref): bool = - if count = 0 then - false - else - result <- this.Peek() - true - - member _.Contains(x: 'T) = - let mutable found = false - let mutable i = 0 - while i < count && not found do - if x = contents[toIndex i] then - found <- true - else - i <- i + 1 - found - - member _.Clear() = - count <- 0 - head <- 0 - tail <- 0 - Array.fill contents 0 (size()) Unchecked.defaultof<_> - - member _.TrimExcess() = - if float count / float contents.Length > 0.9 then - ensure(count) - - member _.ToArray(): 'T[] = - let res = ResizeArray<_>(count) - for i = 0 to count - 1 do - res.Add(contents[toIndex i]) - res |> asArray - - member _.CopyTo(target: 'T array, start: int) = - let mutable i = start - for i = 0 to count - 1 do - target[start + i] <- contents[toIndex i] - - interface IEnumerable<'T> with - member _.GetEnumerator(): IEnumerator<'T> = - toSeq().GetEnumerator() - - interface System.Collections.IEnumerable with - member _.GetEnumerator(): System.Collections.IEnumerator = - toSeq().GetEnumerator() :> System.Collections.IEnumerator - -module Text = - - type StringBuilder(value: string, capacity: int) = - let buf = ResizeArray(capacity) - do if not (System.String.IsNullOrEmpty value) then buf.Add(value) - new (capacity: int) = StringBuilder("", capacity) - new (value: string) = StringBuilder(value, 16) - new () = StringBuilder("", 16) - member x.Append(s: string) = buf.Add(s); x - member x.Append(o: bool) = x.Append(string o) - member x.Append(c: char) = x.Append(string c) - member x.Append(o: int8) = x.Append(string o) - member x.Append(o: byte) = x.Append(string o) - member x.Append(o: int16) = x.Append(string o) - member x.Append(o: uint16) = x.Append(string o) - member x.Append(o: int32) = x.Append(string o) - member x.Append(o: uint32) = x.Append(string o) - member x.Append(o: int64) = x.Append(string o) - member x.Append(o: uint64) = x.Append(string o) - member x.Append(o: float32) = x.Append(string o) - member x.Append(o: float) = x.Append(string o) - member x.Append(s: string, index: int, count: int) = x.Append(s.Substring(index, count)) - member x.Append(cs: char[]) = x.Append(System.String(cs)) - member x.Append(sb: StringBuilder) = x.Append(sb.ToString()) - // member x.Append(o: obj) = x.Append(string o) - // member x.AppendFormat(fmt: string, o: obj) = x.Append(System.String.Format(fmt, o)) - // member x.AppendFormat(provider: System.IFormatProvider, fmt: string, o: obj) = x.Append(System.String.Format(provider, fmt, o)) - member x.AppendLine() = x.Append(System.Environment.NewLine) - member x.AppendLine(s: string) = x.Append(s).AppendLine() - member x.Clear() = buf.Clear(); x - member x.Length = buf |> asArray |> Array.sumBy (fun s -> s.Length) - override _.ToString() = System.String.Concat(buf |> asArray) - member x.ToString(index: int, count: int) = x.ToString().Substring(index, count)