ELM / Haskell: uses regular expressions (Regex) for string search and display Html

what I'm trying to do is to analyze the string using regular expressions and get the Html element as the output, so the signature function should be as follows:

parse : String -> Html Msg 

before we dive into the code, let's take an example of how to behave as the code to have a clear idea, considering the following line below:

input: Hi (!! BOLD) I am bold text (BOLD!!) bla bla la

Expected result: div [] [text "hello", b [] [text "I'm bold text"], the text "bla la la"]]

To achieve this goal, I used regular expression library provided in the ELM package

 replace : HowMany -> Regex -> (Match -> String) -> String -> String 

on top of the above functions, I created a function 2 indicated here after:

 myReplace str expression = replace (All) ( regex expression ) matchToString str --myreplace "hello (!BOLD!) bold (!BOLD!) " "(!BOLD!)" == "hello b[][] hello" matchToString str myReplace str expression = replace (All) ( regex expression ) matchToString str --myreplace "hello (!BOLD!) bold (!BOLD!) " "(!BOLD!)" == "hello b[][] hello" 

with the subsidiary function, this function takes a line and sets the start and end of the regular expression

 matchToString : Match -> String matchToString match = case match.number `rem` 2 of 0 ->"]" -- mtaches the close bracket _ -> "B [][" --mtaches the open bracket close bracket matchToString : Match -> String matchToString match = case match.number `rem` 2 of 0 ->"]" -- mtaches the close bracket _ -> "B [][" --mtaches the open bracket 

but I want to get: div [][text "hello", b[][text "bold"]]

How can I improve my code and write a complete parser? or how can I achieve the same goal in haskell?

link: source regular expression elm

+3
source share
1 answer

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!

+4
source

All Articles