Regular expressions are beginning to lose their strength and become too difficult in such cases. Instead, I recommend a look at Parser combinators , which are much more effective when they are easier to maintain and reason.
In this example, I'll use a package Bogdanp / elm-combine .
Here we will create a parser that takes a string, and implies that he will Unstyled until it reaches (!BOLD!) , And will be bold until it finds another record (!BOLD!) . The output will be a list of tuples of characters and whether they are Unstyled or Bold . Disclaimer: for this is probably shorter schemers, but I am relatively new to the art.
import Html exposing (..) import Html.Attributes exposing (..) import Combine exposing (..) import Combine.Char exposing (..) import Combine.Infix exposing (..) import String import List.Extra exposing (groupWhile) type Style = Unstyled | Bold styleParser : Bool -> Parser (List (Char, Style)) styleParser bolded = let style = if bolded then Bold else Unstyled in (end `andThen` always (succeed [])) <|> (string "(!BOLD!)" `andThen` \_ -> styleParser (not bolded)) <|> (anyChar `andThen` \c -> styleParser bolded `andThen` \cs -> (succeed ((c, style) :: cs))) (Char, Style)) import Html exposing (..) import Html.Attributes exposing (..) import Combine exposing (..) import Combine.Char exposing (..) import Combine.Infix exposing (..) import String import List.Extra exposing (groupWhile) type Style = Unstyled | Bold styleParser : Bool -> Parser (List (Char, Style)) styleParser bolded = let style = if bolded then Bold else Unstyled in (end `andThen` always (succeed [])) <|> (string "(!BOLD!)" `andThen` \_ -> styleParser (not bolded)) <|> (anyChar `andThen` \c -> styleParser bolded `andThen` \cs -> (succeed ((c, style) :: cs))) ])) import Html exposing (..) import Html.Attributes exposing (..) import Combine exposing (..) import Combine.Char exposing (..) import Combine.Infix exposing (..) import String import List.Extra exposing (groupWhile) type Style = Unstyled | Bold styleParser : Bool -> Parser (List (Char, Style)) styleParser bolded = let style = if bolded then Bold else Unstyled in (end `andThen` always (succeed [])) <|> (string "(!BOLD!)" `andThen` \_ -> styleParser (not bolded)) <|> (anyChar `andThen` \c -> styleParser bolded `andThen` \cs -> (succeed ((c, style) :: cs))) )!!" `AndThen` \ _ -> styleParser (not bolded)) import Html exposing (..) import Html.Attributes exposing (..) import Combine exposing (..) import Combine.Char exposing (..) import Combine.Infix exposing (..) import String import List.Extra exposing (groupWhile) type Style = Unstyled | Bold styleParser : Bool -> Parser (List (Char, Style)) styleParser bolded = let style = if bolded then Bold else Unstyled in (end `andThen` always (succeed [])) <|> (string "(!BOLD!)" `andThen` \_ -> styleParser (not bolded)) <|> (anyChar `andThen` \c -> styleParser bolded `andThen` \cs -> (succeed ((c, style) :: cs)))
The result of this analyzer for example "a(!BOLD!)b(!BOLD!)c" (! BOLD!) C" will contain a list of [('a', Unstyled), ('b', Bold), ('c', Unstyled)] , therefore, we need to do some mapping and coagulation, to turn it into a list of values Html msg :
htmlParser : Parser (List (Html msg)) htmlParser = styleParser False `andThen` (succeed << foldStyledHtml) foldStyledHtml : List (Char, Style) -> List (Html msg) foldStyledHtml chars = let foldSingleStyledHtml = List.foldr (\(c, s) (cs, _) -> (c :: cs, s)) ([], Unstyled) >> \(chars, style) -> let str = String.fromList chars in case style of Unstyled -> text str Bold -> b [] [ text str ] in groupWhile (\ab -> snd a == snd b) chars |> List.map foldSingleStyledHtml -> List (Html msg) htmlParser : Parser (List (Html msg)) htmlParser = styleParser False `andThen` (succeed << foldStyledHtml) foldStyledHtml : List (Char, Style) -> List (Html msg) foldStyledHtml chars = let foldSingleStyledHtml = List.foldr (\(c, s) (cs, _) -> (c :: cs, s)) ([], Unstyled) >> \(chars, style) -> let str = String.fromList chars in case style of Unstyled -> text str Bold -> b [] [ text str ] in groupWhile (\ab -> snd a == snd b) chars |> List.map foldSingleStyledHtml (cs, _) -> (c :: cs, s)) ([], Unstyled) htmlParser : Parser (List (Html msg)) htmlParser = styleParser False `andThen` (succeed << foldStyledHtml) foldStyledHtml : List (Char, Style) -> List (Html msg) foldStyledHtml chars = let foldSingleStyledHtml = List.foldr (\(c, s) (cs, _) -> (c :: cs, s)) ([], Unstyled) >> \(chars, style) -> let str = String.fromList chars in case style of Unstyled -> text str Bold -> b [] [ text str ] in groupWhile (\ab -> snd a == snd b) chars |> List.map foldSingleStyledHtml str] htmlParser : Parser (List (Html msg)) htmlParser = styleParser False `andThen` (succeed << foldStyledHtml) foldStyledHtml : List (Char, Style) -> List (Html msg) foldStyledHtml chars = let foldSingleStyledHtml = List.foldr (\(c, s) (cs, _) -> (c :: cs, s)) ([], Unstyled) >> \(chars, style) -> let str = String.fromList chars in case style of Unstyled -> text str Bold -> b [] [ text str ] in groupWhile (\ab -> snd a == snd b) chars |> List.map foldSingleStyledHtml == snd b) chars htmlParser : Parser (List (Html msg)) htmlParser = styleParser False `andThen` (succeed << foldStyledHtml) foldStyledHtml : List (Char, Style) -> List (Html msg) foldStyledHtml chars = let foldSingleStyledHtml = List.foldr (\(c, s) (cs, _) -> (c :: cs, s)) ([], Unstyled) >> \(chars, style) -> let str = String.fromList chars in case style of Unstyled -> text str Bold -> b [] [ text str ] in groupWhile (\ab -> snd a == snd b) chars |> List.map foldSingleStyledHtml
You can then output the following example text using the following:
main = case parse htmlParser testInput of (Ok htmls, _) -> div [] htmls (Err err, _) -> div [ style [("color", "red")] ] [ text <| toString <| err] div [] htmls main = case parse htmlParser testInput of (Ok htmls, _) -> div [] htmls (Err err, _) -> div [ style [("color", "red")] ] [ text <| toString <| err]
I have published full source of this in essence . You will also need a package elm-community/list-extra . Hope this helps!