-
Notifications
You must be signed in to change notification settings - Fork 77
Suggested New RiGrammar Feature #551
Description
Most generative grammar tools have the capability to save a generated value and then reuse it later. For example, you might want to generate the name of a story's hero and then use that name throughout the rest of the story. Tracery does this through "actions":
Often you want to save information. Tracery allows you to call actions, which are bracketed statements that appear before symbols in a tag #[someAction]someSymbol#. In their basic form, they create some new rules and push them onto a symbol, creating that symbol if it didn't exist, or hiding its previous value if it did.
You can experiment with Tracery's actions here.
RiTa doesn't currently have the capability to reuse generated values, but I have added it to my personal port of RiTaJS and propose adding it to the master branch.
The approach I have implemented is to indicate "One Time Rules" by prefixing the nonterminal on the LHS of a rule with a $. For example:
$pet => cat | dog | wombat
<start> => I just bought a $pet. There's no better pet than a $pet!
produces
I just bought a wombat. There's no better pet than a wombat!
I call these "One Time Rules" because they are only evaluated once; from then on the stored value is used whenever they are referenced. (I welcome any suggestions for a better name! :-)
One Time Rule values can also be used in callbacks, and will be seamlessly inserted as strings into the Javascript code, e.g.,
function mega(str) { return "Mega-"+str;}
$pet => cat | dog | wombat
<start> => I just bought a $pet. It's a `mega($pet)`
is turned into
mega('wombat')
for execution and produces
I just bought a wombat. It's a Mega-wombat!
One Time Rule values can also be used in rule weights, e.g.,
$dogweight => 100
$pet => cat | dog [$dogweight] | wombat
<start> => I just bought a $pet.
My current implementation works by (recursively) evaluating all the One Time Rules the first time expandFrom() is called and saving them in a dictionary (Javascript object). After that, whenever a nonterminal is expanded the engine checks to see if it has a value in the dictionary, and uses that if present.
The dictionary is accessible at Grammar.otrs. Prior to the initial call to expandFrom(), the user can manually place values into this dictionary. This allows the user to override the One Time Rules and provide a custom value. The user can actually provide a value for any nonterminal (including ones that do not start with $), enabling the user to manually turn any rule into a One Time Rule with a particular value.
I have also provided Grammar.otrsDisabled which can be set to true to turn off One Time Rules completely. I've also provided Grammar.otrsPrefix which is initially $ but which can be changed to another character if $ is not acceptable for some reason.
There are a few shortcomings with my implementation. There's no attempt made to order the execution of the One Time Rules, so using OTRs within OTRs may not work as expected. (In particular, they will not be executed in the order in which they were defined.) The detection of OTRs in rules and callbacks uses simple matching, so using an OTR with a name that is a substring of another OTR (e.g., $flower and $flowerColor) will lead to problems.
An alternative approach to providing this capability would be to turn any nonterminal into a One Time Rule by prepending a $ to the use of the nonterminal on the RHS of a rule, e.g.,
<pet> => cat | dog | wombat
<start> => I bought a $<pet>. I'm happy with my $<pet>! But maybe I should have bought a <pet>?
potentially producing
I bought a wombat. I'm happy with my wombat! But maybe I should have bought a dog?
Here the first use of $ creates a value that is reused by any subsequent use, but is still available for use as a normal rule. However, the current architecture of the rules engine makes this difficult to implement. Rewriting the engine to make expandFrom() recursive would make this possible (and simplify the engine code) but there might be concerns about performance.