Coverage report

case branchesDeclarationsif/else branchesLambdaslet declarations
(21) Syntax
13/24
8/10
3/8
17/20
4/4
(2) Test1
0/2
0/2
n/a
0/1
n/a
(6) Tracery
8/9
4/4
n/a
7/7
n/a
total
21/35
12/16
3/8
24/28
4/4

Module: Syntax

case branchesif/else branchesLambdaslet declarations
(10) isValid
8/10
3/6
8/8
2/2
(7) toString
0/6
0/2
0/1
n/a
(5) decodeDefinition
4/6
n/a
1/3
n/a
(2) decodeSyntax
1/2
n/a
2/2
n/a
(1) errorStringn/an/an/an/a
(1) expressionParsern/an/an/a
2/2
(1) sentenceParsern/an/a
3/3
n/a
(1) decodern/an/a
3/3
n/a
(1) fromStringn/an/an/an/a
(1) originStringn/an/an/an/a
(21) total
13/24
3/8
17/20
4/4

Declarations sorted by cyclomatic complexity

module Syntax exposing (Definition(..), Expression(..), Syntax, decoder,originString, fromString)

import Dict exposing (Dict)
import Json.Decode
import Json.Value exposing (JsonValue(..))
import Parser exposing ((|.), (|=), Parser)
import Result.Extra
import Set


type Expression
= Print String
| Insert String


type Definition
= Choose (List (List Expression))
| Let (List Expression)
| With Syntax


type alias Syntax =
Dict String Definition

originString : String
originString =
"origin"
isValid : List String -> Syntax -> Result String ()
isValid oldKeys dict =
let
keys =
dict |> Debug.log "dict" |> Dict.keys |> (++) oldKeys |> Debug.log "keys"

verify k sentences =
if
List.any
(\sentenceKeys ->
not (List.member k sentenceKeys)
)
sentences
then
sentences
|> List.concat
|> List.filterMap
(\sentenceKey ->
if List.member sentenceKey keys then
Nothing

else
Just sentenceKey
)
|> (\l ->
case l of
[] ->
Ok ()

[ a ] ->
"In the definition of "
++ k
++ " the variable "
++ a
++ " is not defined. Defined is "
++ (keys ++ oldKeys |> String.join ", ")
|> Err

head :: tail ->
"In the definition of "
++ k
++ " the variables "
++ (tail |> String.join ", ")
++ " and "
++ head
++ " are not defined."
|> Err
)

else
"The definition of "
++ k
++ " needs an option that does not contain itself."
|> Err
in
dict
|> Dict.toList
|> List.map
(\( k, v ) ->
if k /= originString && List.member k oldKeys then
Err (k ++ " has already been defined.")

else
case v of
Choose l ->
l
|> List.map
(\sentence ->
sentence
|> List.filterMap
(\exp ->
case exp of
Print _ ->
Nothing

Insert key ->
Just key
)
)
|> verify k

Let l ->
l
|> List.filterMap
(\exp ->
case exp of
Print _ ->
Nothing

Insert key ->
Just key
)
|> List.singleton
|> verify k

With subSyntax ->
isValid (keys) subSyntax
)
|> Result.Extra.combine
|> Result.map (\_ -> ())


{-|

import Dict

input : String
input =
"""{ "origin" : [ "Hello \\\\\\\\ World \\\\#", "#statement# and #statement#" ]
, "statement" :
{ "origin" : "my #myPet# is the #complement#"
, "myPet": "#pet#"
, "pet" : ["cat","dog"]
, "complement" : ["smartest #myPet# in the world","fastest #myPet# that i know of"]
}
}"""

output : Syntax
output =
Dict.fromList
[ ( "origin"
, Choose
[ [Print "Hello ", Print "\\", Print " World ",Print "#"]
, [Insert "statement", Print " and ", Insert "statement"]
]
)
, ( "statement"
, Dict.fromList
[ ( "origin"
, Let [ Print "my ", Insert "myPet", Print " is the ", Insert "complement"]
)
, ( "myPet",Let [Insert "pet"])
, ( "pet", Choose [[Print "cat"],[Print "dog"]])
, ( "complement"
, Choose
[ [ Print "smartest ", Insert "myPet", Print " in the world"]
, [ Print "fastest ", Insert "myPet", Print " that i know of"]
]
)
]
|> With
)
]

input |> fromString
--> Ok output

-}
fromString : String -> Result Json.Decode.Error Syntax
fromString =
Json.Decode.decodeString decoder


decoder : Json.Decode.Decoder Syntax
decoder =
Json.Value.decoder
|> Json.Decode.andThen
(\jsonValue ->
jsonValue
|> decodeSyntax
|> Result.andThen (\syntax ->
syntax
|> isValid []
|> Result.map (\() -> syntax)
)
|> Result.map Json.Decode.succeed
|> Result.Extra.extract Json.Decode.fail
)


decodeSyntax : JsonValue -> Result String Syntax
decodeSyntax jsonValue =
case jsonValue of
ObjectValue list ->
list
|> List.map
(\( k, v ) ->
decodeDefinition v
|> Result.map (\ok -> ( k, ok ))
)
|> Result.Extra.combine
|> Result.map Dict.fromList

_ ->
errorString
{ expected = "an object"
, got = jsonValue
}
|> Err


decodeDefinition : JsonValue -> Result String Definition
decodeDefinition jsonValue =
case jsonValue of
StringValue string ->
string
|> Parser.run sentenceParser
|> Result.mapError (\_ -> "")
|> Result.map Let

ArrayValue l ->
l
|> List.map
(\sentence ->
case sentence of
StringValue string ->
string
|> Parser.run sentenceParser
|> Result.mapError (\_ -> "")

_ ->
errorString
{ expected = "a string"
, got = sentence
}
|> Err
)
|> Result.Extra.combine
|> Result.map Choose

ObjectValue _ ->
decodeSyntax jsonValue
|> Result.map With

_ ->
errorString
{ expected = "a string, list or object"
, got = jsonValue
}
|> Err


sentenceParser : Parser (List Expression)
sentenceParser =
Parser.loop []
(\list ->
Parser.oneOf
[ Parser.succeed (\stmt -> Parser.Loop (stmt :: list))
|= expressionParser
, Parser.succeed ()
|> Parser.map (\_ -> Parser.Done (List.reverse list))
]
)


expressionParser : Parser Expression
expressionParser =
let
validChar char =
char /= '#' && char /= '\\'

variable =
Parser.variable
{ start = validChar
, inner = validChar
, reserved = Set.empty
}
in
Parser.oneOf
[ variable
|> Parser.map Print
, Parser.succeed (Print "\\")
|. Parser.token "\\\\"
, Parser.succeed (Print "#")
|. Parser.token "\\#"
, Parser.succeed Insert
|. Parser.token "#"
|= variable
|. Parser.token "#"
]


errorString : { expected : String, got : JsonValue } -> String
errorString args =
"expected " ++ args.expected ++ " but got " ++ toString args.got ++ "."


toString : JsonValue -> String
toString jsonValue =
case jsonValue of
ObjectValue list ->
"{ " ++ (list |> List.map (\( k, v ) -> k ++ " : " ++ toString v) |> String.join ", ") ++ " }"

ArrayValue list ->
"[ " ++ (list |> List.map toString |> String.join ", ") ++ " ]"

BoolValue bool ->
if bool then
"True"

else
"False"

NullValue ->
"Null"

NumericValue float ->
float |> String.fromFloat

StringValue string ->
"\"" ++ string ++ "\""

Module: Test1

case branchesif/else branchesLambdaslet declarations
(2) main
0/2
n/a
0/1
n/a
(1) seedn/an/an/an/a
(2) total
0/2
n/a
0/1
n/a

Declarations sorted by cyclomatic complexity

module Test1 exposing (main)

import Tracery
import Random
import Json.Decode
import Html

seed : Random.Seed
seed =
Random.initialSeed 42

main =
"""
{ "origin":"I have a #favoriteAnimal# named #favoriteAnimalName# and a #animal# named #name#. I love #favoriteAnimalName# the most. It's the best #favoriteAnimal# in the world."
, "favoriteAnimal" :
{ "origin":"#color# #animal#"
, "color": ["white","black","brown"]
}
, "favoriteAnimalName" : "#name#"
, "animal":["cat","dog","parrot"]
, "name": ["Johnny","Charlie","Twinkle","Charles"]
}
"""
|> Tracery.fromJson
|> (\result ->
case result of
Err err -> Json.Decode.errorToString err
Ok generator ->
Random.step generator seed
|> Tuple.first
)
|> Html.text

Module: Tracery

case branchesif/else branchesLambdaslet declarations
(5) generateStory
6/7
n/a
3/3
n/a
(2) generateSentence
2/2
n/a
4/4
n/a
(1) fromSyntaxn/an/an/an/a
(1) fromJsonn/an/an/an/a
(6) total
8/9
n/a
7/7
n/a

Declarations sorted by cyclomatic complexity

module Tracery exposing (fromJson, fromSyntax)

import Dict exposing (Dict)
import Json.Decode
import Json.Value exposing (JsonValue(..))
import Parser exposing ((|.), (|=))
import Random exposing (Generator)
import Syntax exposing (Definition(..), Expression(..), Syntax)


fromJson : String -> Result Json.Decode.Error (Generator String)
fromJson string =
string |> Syntax.fromString |> Result.map fromSyntax


fromSyntax : Syntax -> Generator String
fromSyntax syntax =
generateStory Syntax.originString Dict.empty syntax |> Random.map Tuple.first


generateStory : String -> Dict String String -> Syntax -> Generator ( String, Dict String String )
generateStory k0 constants syntax =
Dict.get k0 syntax
|> Maybe.map
(\definition ->
case definition of
Choose statements ->
case statements of
[] ->
Random.constant ( "", constants )

head :: tail ->
Random.uniform head tail
|> Random.andThen (generateSentence constants syntax)

Let sentence ->
case constants |> Dict.get k0 of
Just string ->
Random.constant ( string, constants )

Nothing ->
sentence
|> generateSentence constants syntax
|> Random.map (\(s,c) -> (s,c |> Dict.insert k0 s))

With subSyntax ->
(subSyntax |> Dict.union (syntax |> Dict.remove Syntax.originString))
|> generateStory Syntax.originString constants
|> Random.map (\(s,c) -> (s,c |> Dict.insert k0 s))
)
|> Maybe.withDefault (Random.constant ( "", constants ))


generateSentence : Dict String String -> Syntax -> List Expression -> Generator ( String, Dict String String )
generateSentence constants syntax sentence =
sentence
|> List.foldl
(\exp generator ->
case exp of
Print string ->
generator |> Random.map (Tuple.mapFirst (\it -> it ++ string))

Insert key ->
generator
|> Random.andThen
(\( s1, g1 ) ->
generateStory key g1 syntax
|> Random.map (Tuple.mapFirst (\s2 -> s1 ++ s2))
)
)
(Random.constant ( "", constants ))