From 71a0c01db994d9e0f1f720318ee0721887fbe3a3 Mon Sep 17 00:00:00 2001 From: Zorchenhimer Date: Sun, 21 Feb 2021 20:13:33 -0500 Subject: [PATCH] Implement RNG format blocks in sentences The RNG blocks follow a slightly different format than the word blocks. They are wrapped in square brackets instead of curly braces. The format is as follows: [cmd:arguments] `cmd` can be one of either "rng" or "hide". "rng" takes a comma separated list as its arguments and will choose one option from the list. Only raw words are accepted here (word blocks are not parsed). "hide" will randomly hide its arguments in the output string. The arguments for this command *are* parsed, so {word} and {{new_word}} blocks can be used. --- english.go | 85 ++++++++++++++++++++++++++++++++++++++++++------- word_lists.json | 3 +- 2 files changed, 75 insertions(+), 13 deletions(-) diff --git a/english.go b/english.go index 7f850c9..c087694 100644 --- a/english.go +++ b/english.go @@ -30,22 +30,31 @@ func NewEnglish(db database.DB) (HackerQuotes, error) { } /* - Sentence format + bare words {word_type:options} {{word_type:new word:properties}} + [rng:random,choice,list] // one word is chosen from the comma-separated list + + // Randomly hide/show the enclosed definition + [hide:{word_type:options}] + [hide:{{word_type:new word:options}}] + {pronoun} can't {verb:i,present} {noun_phrase}, it {verb:it,future} {noun_phrase}! {verb:you,present} {noun_phrase:indefinite}, then you can {verb:you,present} {noun_phrase:definite}! {noun_phrase} {verb}. With {noun_phrase:indifinite,noadj,compound}! {pronoun} {{verb:need:from_pronoun,present}} to {verb:i,present} {noun_phrase:definite}! + + {pronoun} [rng:can,may] {verb:i,present} {noun_phrase} [hide:{noun_phrase:singlular}], it {verb:it,future} {noun_phrase:definite}. */ func (g *english) Hack() (string, error) { //var fmtString string = `{verb:you,present} {noun_phrase:definite} then you can {verb:you,present} {noun_phrase:definite}!` //var str string = `{pronoun} {{verb:need:from_pronoun,present}} to {verb:i,present} {noun_phrase:definite}!` + //var str string = `{pronoun} [rng:can,may] {verb:i,present} {noun_phrase}[hide: {noun_phrase:singlular}], it {verb:it,future} {noun_phrase:definite}.` str, err := g.randomSentence() if err != nil { return "", err @@ -63,21 +72,20 @@ func (g *english) HackThis(fmtString string) (string, error) { for idx < len(fmtString) { if fmtString[idx] == '{' { - if fmtString[idx+1] == '{' { - nidx, err = g.consumeNewWord(fmtString, idx, output) - if err != nil { - return "", err - } - idx = nidx - continue - } - nidx, err = g.consumeWord(fmtString, idx, output) if err != nil { return "", err } idx = nidx continue + + } else if fmtString[idx] == '[' { + nidx, err = g.consumeRng(fmtString, idx, output) + if err != nil { + return "", err + } + idx = nidx + continue } nidx, err = g.consumeRaw(fmtString, idx, output) @@ -90,8 +98,57 @@ func (g *english) HackThis(fmtString string) (string, error) { return toCap(output.String()), nil } +func (g *english) consumeRng(fmtString string, idx int, output *strings.Builder) (int, error) { + idx++ + def := strings.Index(fmtString[idx:], ":") + if def == -1 { + return 0, fmt.Errorf("Missing command separator in RNG block") + } + def += idx + + end := strings.Index(fmtString[idx:], "]") + if end == -1 { + return 0, fmt.Errorf("Unclosed RNG block starting at offset %d", idx-1) + } + end += idx + + if def > end { + return 0, fmt.Errorf("Missing command separator in RNG block") + } + + switch fmtString[idx:def] { + case "rng": + choices := strings.Split(fmtString[def+1:end], ",") + ridx := rand.Intn(len(choices)) + output.WriteString(choices[ridx]) + + case "hide": + if rand.Int() % 2 == 0 { + return end+1, nil + } + + var newEnd int = def+1 + var err error + for newEnd < end { + if fmtString[newEnd] == '{' { + newEnd, err = g.consumeWord(fmtString, newEnd, output) + if err != nil { + return 0, err + } + } else { + newEnd, err = g.consumeRaw(fmtString, newEnd, output) + } + } + + default: + return 0, fmt.Errorf("RNG type %q is not implemented", fmtString[idx:def]) + } + + return end+1, nil +} + func (g *english) consumeRaw(fmtString string, idx int, output *strings.Builder) (int, error) { - end := strings.Index(fmtString[idx:], "{") + end := strings.IndexAny(fmtString[idx:], "{[") if end == -1 { output.WriteString(fmtString[idx:len(fmtString)]) return len(fmtString), nil @@ -243,6 +300,10 @@ func (g *english) consumeNewWord(fmtString string, idx int, output *strings.Buil } func (g *english) consumeWord(fmtString string, idx int, output *strings.Builder) (int, error) { + if fmtString[idx+1] == '{' { + return g.consumeNewWord(fmtString, idx, output) + } + idx++ var wordtype string var options string @@ -275,7 +336,7 @@ func (g *english) consumeWord(fmtString string, idx int, output *strings.Builder var plural bool = rand.Int() % 2 == 0 if sliceContains(opts, "plural") { plural = true - } else if sliceContains(opts, "singular") + } else if sliceContains(opts, "singular") { plural = false } diff --git a/word_lists.json b/word_lists.json index 066eeca..ad3b592 100644 --- a/word_lists.json +++ b/word_lists.json @@ -419,6 +419,7 @@ "{pronoun} can't {verb:i,present} {noun_phrase}, it {verb:it,future} {noun_phrase}!", "{verb:you,present} {noun_phrase:definite}, then you can {verb:you,present} {noun_phrase:definite}!", "{noun_phrase:it,past} {verb}. With {noun_phrase:indifinite,noadj,compound}!", - "{pronoun} {{verb:need:from_pronoun,present}} to {verb:i,present} {noun_phrase:definite}!" + "{pronoun} {{verb:need:from_pronoun,present}} to {verb:i,present} {noun_phrase:definite}!", + "{pronoun} [rng:can,may] {verb:i,present} {noun_phrase} [hide:{noun_phrase:singlular}], it {verb:it,future} {noun_phrase:definite}." ] }