Advertisement
FUNtoon

FUNtoon Grammar Documentation

Jan 10th, 2020
149
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Markdown 13.51 KB | None | 0 0

FUNtoon Grammar Documentation



What is it?

The FUNtoon Inverse Generative Grammar system is a system designed to produce random text within a defined structure. Think of it as a Mad-Libs style fill-in-the-blank system on crack.



Grammar File Basics

A Grammar is made up of Rules. Rules act like the labels underneath the blanks in a Mad-Libs story (adjective, noun, verb ending in -ing, etc.) except they don't just have to be simple categories like that.
A Rule is made up of Entries, specifying all of the options that you can choose from when filling in a blank for that Rule. Think of the entries as the nouns and verbs themselves, like a dictionary.
An Entry can contain References to other rules. This is what makes the grammar so powerful.

Consider the following rule definition:

greeting = hello | hi there | hey

This defines a Rule named greeting which is made up of the entries hello, hi there, and hey.

(Note: whitespace on either end of each entry is ignored, so "hello | hi there | hey" and "hello|hi there|hey" are equivalent.)

If we were to ask to generate a greeting value, we would get one of those three options at random.

However, this greeting Rule can be used inside other Entries like so:

greeting = hello | hi there | hey
my_sentence = <greeting>! How are you? | <greeting>! What's up? | Oh, <greeting>! Nice to see you!

(Note: the order of Rule definitions in the grammar file does not matter for References, as they are only checked once all rules are loaded)

Now if we were to ask to generate a my_sentence value, one of the three Entries will be selected at random, as usual, and then the Reference to the <greeting> Rule in the selected Entry will automatically ask to generate a greeting value and replace itself with the selected greeting Entry.

This grammar can generate any of the following results for my_sentence:

hello! How are you?
hi there! How are you?
hi there! How are you?
hello! What's up?
hi there! What's up?
hi there! What's up?
Oh, hello! Nice to see you!
Oh, hi there! Nice to see you!
Oh, hi there! Nice to see you!

Notice that the capitalization does not change based on where it is in the sentence. Replacements are provided exactly as they are defined in the Entry.

A Grammar file can have any number of Rules, and each rule can have any number of Entries. Go crazy!



Anonymous Rules

Sometimes it's nice to have a small amount of randomization within one single Entry that doesn't really need to be used anywhere else. For this you can use <(Anonymous Rules)> to define in-line replacement sets within an Entry.

sentence = The <mood> <age> man <speed> walked down the <place>
mood = grumpy|sleepy|charming
age = old|young
speed = briskly|slowly
place = sidewalk|hallway|stairs

# -- IS EQUIVALENT TO --

sentence = The <(grumpy|sleepy|charming)> <(old|young)> man <(briskly|slowly)> walked down the <(sidewalk|hallway|stairs)>.

As you can see, the single sentence Rule only has one Entry, but the entry contains a number of Anonymous Rules embedded in it, and each of those contain multiple entries. This is a much simpler and cleaner approach for replacements that are not going to be frequently used anywhere else.



Adjusting Probabilities

In many cases you may want to have different probabilities for each Entry in a Rule. It may be tempting to accomplish this by simply adding multiple identical Entries to a Rule like this:

my_rule = a | b | b | b | c

This will certainly work, and will result in a 1/5 of the time, b 3/5 of the time, and c 1/5 of the time. However there is an easier way to accomplish the same thing by starting the Entry with the {weight} prefix:

my_rule = a | {3} b | c

Every Entry has a weight value of 1 by default, but you can use any positive number for an entry's weight. When generating a value for a Rule, all the weight values of each Entry are added up, and the probability of any given Entry being selected becomes that Entry's weight as a proportion of the sum of all weights in the Rule.

The value within a {weight} prefix doesn't have to be a simple number. It can be any valid expression. See the Expression Syntax section below for more.

If an entry has a weight value less-than or equal to zero, it cannot be selected, making it effectively deleted from the list. If all entries have a weight value less-than or equal to zero, the rule will generate an empty string (no text).

If you want to start your Entry with the { character without defining a weight value, you can either use \{ to place a literal { character, {}{ to define an empty {weight} prefix followed by a { character, or <>{ to define a literal zero-length text value followed by a { character. See Escapes and Literals section below for more.



Modifying Existing Rules

You can split up Rule definitions into multiple lines and/or make adjustments to existing Rules by replacing the = operator with += or -=.

noun = dog | cat | mouse
noun += car | tree | dog

# -- IS EQUIVALENT TO -- 

noun = {2} dog | cat | mouse | car | tree

Notice how duplicate Entries have their weights added together.
Similarly, we can subtract from the weight of an existing Entry as well.

noun = {2} dog | cat | mouse | {3} bird
noun -= mouse | bird

# -- IS EQUIVALENT TO --

noun = {2} dog | cat | {0} mouse | {2} bird

When an entry has a weight less-than or equal to zero, it cannot be selected, making it effectively deleted from the list. See the Adjusting Probabilities section above for more.



Namespaces

Complex grammar files can often contain small groupings of Rules that are related. Namespaces can help keep these groups more organized and prevent common Rule names from overlapping.

rule_1 = some text

my_command {  # Defines the my_command namespace
    rule_1 = a | b | c  # This is a different rule than the previous `rule_1`
    rule_2 = <rule_1> xyz
    rule_1 += d  # Adds 'd' as an entry to `mycommand.rule_1`
}

other_command {
    rule_2 = <rule_1> abc  # This is a different `rule_2` than `my_command.rule_2`
                           # It also uses the first `rule_1` and not `my_command.rule_1`
    rule_3 = asdf
}

different_rule = <rule_1>  # This will use the "some text" entry
other_rule = <my_command.rule_1>  # This will use the "a | b | c" entries

rule_1 += more text  # Adds 'more text' as another entry to `rule_1`
my_command.rule_1 += d  # Adds 'd' as another entry to `mycommand.rule_1`

# IMPORTANT NOTE:
other_command.rule_3 += <rule_2>  # This uses `other_command.rule_2`
# equivalent to
other_command {
    rule_3 += <rule_2> # This uses `other_command.rule_2`
}

In the above example, because we have re-defined the rule_1 Rule inside the my_command Namespace, there is no way to access the original rule_1 Rule inside that Namespace. At the bottom of the example we show that even trying to use namespace.rule += <some_rule> will still refer to <some_rule> from within namespace.

If a Rule doesn't exist within a namespace (such as referencing <rule_1> from within the other_command Namespace in the example above) the reference name will be searched in each parent scope until it is found.



Using Custom Grammar Files

FUNtoon has a grammar file that is used by default in every channel. You can supply your own grammar file using the !grammar set <pastebin_link> command in your chat to add your own Rules using pastebin. This grammar file will be parsed and added on top of the default grammar file for your channel. It is effectively as if the file you submit is simply appended to the default grammar file, and you can treat it as such.

This means you can modify existing FUNtoon grammar values using += and -= to customize responses to existing commands in your chat, along with creating your own entirely new commands. See the Modifying Existing Rules section above for more.

Use the !grammar default command in your chat for a link to a copy of FUNtoon's base grammar file. Be aware that this file may change as new features are added or updated, but your grammar file will still be applied on top of it no matter what. Because of this, it is discouraged to directly copy a Rule from the default file. You should instead be modifying the existing Rules using += and -= to make the changes you want.

NOTE: FUNtoon saves a copy of the grammar file from your pastebin link. Editing the pastebin document will not update your grammar file. You must use the !grammar set <pastebin_link> command again for FUNtoon to see your updated grammar code.



Grammar In Custom Commands

You can include generated Rule outputs in your custom commands super easily! Simply include the text {grammar some_rule_name} in your command text. Example:

!addc moo The {grammar animal} says {grammar animal_sound}

This would use the animal Rule and the animal_sound Rule. Rules found within namespaces can be referenced using the same dot-notation used in grammar files.



Escapes, Literals, and Special Rules

There are a number of escape sequences available within the text for an Entry. Most notable is the use of \ followed by any character will escape that character, whether or not it has a special meaning. Some examples of this are included in the chart below along with other escape sequences. Escape Sequence Result
\\ literal \ character
\| or <|> literal | character
\< or << literal < character
\> or >> literal > character
\# or <#> literal # character
\_ (bascklash followed by a space) literal space, even at the beginning/end of an Entry
<> empty string (no text)
There are also a number of special Rule sequences that are included to make it easier to take your Rules to the next level. Rule Result
<-Sender-> When triggered by a command, the name of the user who send the command
<-Streamer-> The name of the broadcaster's channel.
<-Input-> The text placed after the !<command> in the chat message

By writing these rules in all lower-case (ex: <-sender->) or all upper-case (ex: <-SENDER->), the resulting output text will also be in all lower-case or all upper-case.

The \<A-An\> Special Rule

The <A-An> Rule is unique in that it will wait until all other replacements have been made before it is processed. It will look at the word immediately following it and attempt to determine whether to return A or An, depending on the first letters of the word. It will return An if the first letter is a e i o or u, unless the word starts with eu. Any other starting letters will result in A. Like the special rules described above, <A-An> can be written in all lower-case (<a-an>) or all upper-case (<A-AN>) to produce lower- or upper-case results.



Text Generation Process

When generating text from a given starting Rule, the bot will randomly select one of the Entries for that Rule and use the result for the generated output text. Any nested References inside the Entry are "resolved" to a value, each by using the same process of randomly selecting one Entry, and so on.

This will continue until all References have finally been replaced with Entries that do not contain further nested References (a "terminal state"). The bot will give up if it does not reach a terminal state after attempting to resolve a certain number of References (don't worry, it's quite a high number). If the bot gives up, it will retry a few times before eventually returning a generic error text.

If you find that the bot is frequently giving up when generating phrases from your grammar file, you can try using weight values to increase the chance that a valid terminal state is found. See the Adjusting Probabilities section above for more.



Expression Syntax

The expression syntax is a mixture of common operators from both pythonic and c-style languages.

In no particular order: Operator Description
... + ... Addition
... - ... Subtraction
... * ... Multiplication
... / ... Division
... % ... Modulo
... ^ ... or ... ** ... Exponentiation
-... Negation
+... Unary Plus
! ... or not ... Logical-NOT
... && ... or ... and ... Logical-AND
... || ... or ... or ... Logical-OR
... == ... Equals
... != ... Not-equals
... > ... Greater-than
... < ... Less-than
... >= ... Greater-than or equal to
... <= ... Less-than or equal to
... in ... Contains
... not in ... Not-contains
...cond... ? ...true... : ...false...` Ternary conditional
(...) Grouping
fn(...) function call

Values for True, False, and None follow the pythonic representation.

The expression syntax supports strings using either 'apostrophes' or "quotation marks", along with allowing <references> to Rules and even <(anonymous|rules)> directly in the expression. These references can be used as values, mostly for use with some of the built-in functions.

Built-In Variables and Functions

Function or Variable Description
WIP WIP
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement