Skip to content

Commit

Permalink
Merge pull request #434 from Thorium/niceName-opt
Browse files Browse the repository at this point in the history
Faster and less-memory-consuming niceName
  • Loading branch information
gdziadkiewicz authored Jul 9, 2024
2 parents 30d2a2b + 26e2938 commit 0aacf26
Showing 1 changed file with 39 additions and 16 deletions.
55 changes: 39 additions & 16 deletions src/FSharpx.Extras/Strings.fs
Original file line number Diff line number Diff line change
Expand Up @@ -91,31 +91,54 @@ module Strings =
let (|Upper|_|) = satisfies Char.IsUpper
let (|Lower|_|) = satisfies Char.IsLower

/// Faster patterns with ValueOption instead of Option
[<RequireQualifiedAccess>]
module ValuePatterns =
// Active patterns & operators for parsing strings
let inline take (s:string) i = if i >= s.Length then ValueNone else ValueSome s.[i]

let inline internal satisfies predicate (charOption:voption<char>) =
match charOption with
| ValueSome c when predicate c -> charOption
| _ -> ValueNone

[<return: Struct>]
let (|EOF|_|) = function
| ValueSome _ -> ValueNone
| _ -> ValueSome ()
[<return: Struct>]
let (|LetterDigit|_|) = satisfies Char.IsLetterOrDigit
[<return: Struct>]
let (|Upper|_|) = satisfies Char.IsUpper
[<return: Struct>]
let (|Lower|_|) = satisfies Char.IsLower

/// Turns a string into a nice PascalCase identifier
[<CompiledName("NiceName")>]
let niceName (s:string) =
if s = s.ToUpper() then s else
// Starting to parse a new segment
let rec restart i = seq {
match s @? i with
| EOF -> ()
| LetterDigit _ & Upper _ -> yield! upperStart i (i + 1)
| LetterDigit _ -> yield! consume i false (i + 1)
| _ -> yield! restart (i + 1) }
let rec restart i =
match ValuePatterns.take s i with
| ValuePatterns.EOF -> Seq.empty
| ValuePatterns.LetterDigit _ & ValuePatterns.Upper _ -> upperStart i (i + 1)
| ValuePatterns.LetterDigit _ -> consume i false (i + 1)
| _ -> restart (i + 1)

// Parsed first upper case letter, continue either all lower or all upper
and upperStart from i = seq {
match s @? i with
| Upper _ -> yield! consume from true (i + 1)
| Lower _ -> yield! consume from false (i + 1)
| _ -> yield! restart (i + 1) }
and upperStart from i =
match ValuePatterns.take s i with
| ValuePatterns.Upper _ -> consume from true (i + 1)
| ValuePatterns.Lower _ -> consume from false (i + 1)
| _ -> restart (i + 1)
// Consume are letters of the same kind (either all lower or all upper)
and consume from takeUpper i = seq {
match s @? i with
| Lower _ when not takeUpper -> yield! consume from takeUpper (i + 1)
| Upper _ when takeUpper -> yield! consume from takeUpper (i + 1)
and consume from takeUpper i =
match ValuePatterns.take s i with
| ValuePatterns.Lower _ when not takeUpper -> consume from takeUpper (i + 1)
| ValuePatterns.Upper _ when takeUpper -> consume from takeUpper (i + 1)
| _ ->
yield from, i
seq {
yield struct(from, i)
yield! restart i }

// Split string into segments and turn them to PascalCase
Expand Down

0 comments on commit 0aacf26

Please sign in to comment.