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.
This commit is contained in:
Zorchenhimer 2021-02-21 20:13:33 -05:00
parent 92e3a5aeb0
commit 71a0c01db9
Signed by: Zorchenhimer
GPG Key ID: 70A1AB767AAB9C20
2 changed files with 75 additions and 13 deletions

View File

@ -30,22 +30,31 @@ func NewEnglish(db database.DB) (HackerQuotes, error) {
} }
/* /*
Sentence format Sentence format
bare words
{word_type:options} {word_type:options}
{{word_type:new word:properties}} {{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}! {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}! {verb:you,present} {noun_phrase:indefinite}, then you can {verb:you,present} {noun_phrase:definite}!
{noun_phrase} {verb}. With {noun_phrase:indifinite,noadj,compound}! {noun_phrase} {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}.
*/ */
func (g *english) Hack() (string, error) { 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 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} {{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() str, err := g.randomSentence()
if err != nil { if err != nil {
return "", err return "", err
@ -63,16 +72,15 @@ func (g *english) HackThis(fmtString string) (string, error) {
for idx < len(fmtString) { for idx < len(fmtString) {
if fmtString[idx] == '{' { if fmtString[idx] == '{' {
if fmtString[idx+1] == '{' { nidx, err = g.consumeWord(fmtString, idx, output)
nidx, err = g.consumeNewWord(fmtString, idx, output)
if err != nil { if err != nil {
return "", err return "", err
} }
idx = nidx idx = nidx
continue continue
}
nidx, err = g.consumeWord(fmtString, idx, output) } else if fmtString[idx] == '[' {
nidx, err = g.consumeRng(fmtString, idx, output)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -90,8 +98,57 @@ func (g *english) HackThis(fmtString string) (string, error) {
return toCap(output.String()), nil 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) { 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 { if end == -1 {
output.WriteString(fmtString[idx:len(fmtString)]) output.WriteString(fmtString[idx:len(fmtString)])
return len(fmtString), nil 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) { func (g *english) consumeWord(fmtString string, idx int, output *strings.Builder) (int, error) {
if fmtString[idx+1] == '{' {
return g.consumeNewWord(fmtString, idx, output)
}
idx++ idx++
var wordtype string var wordtype string
var options 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 var plural bool = rand.Int() % 2 == 0
if sliceContains(opts, "plural") { if sliceContains(opts, "plural") {
plural = true plural = true
} else if sliceContains(opts, "singular") } else if sliceContains(opts, "singular") {
plural = false plural = false
} }

View File

@ -419,6 +419,7 @@
"{pronoun} can't {verb:i,present} {noun_phrase}, it {verb:it,future} {noun_phrase}!", "{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}!", "{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}!", "{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}."
] ]
} }