RULES
- Strings are generated by rules.
- Every rule supports a "Clone" operation to make a copy at runtime
- A rule is initialized by calling "Initialize(max_allowed_length)". You must
then call "Reset_String()" to reset it.
- After initialization, strings can be enumerated by calling
"Check_For_String", and "Get_String". Check_For_String increments to the
next string (if necessary), returning true if there is a next string. The
rules for the prefix of the current string are passed as an argument to
Check_For_String in case the generation depends on the previous rules. For
example, generating an identifier may depend on the previously generated
identifiers (in order to avoid generating a duplicate inadvertantly). (The
prefix rule list is empty by default.)
- You can force Check_For_String to always return false by calling Invalidate.
It will do so until the next call to Reset_String.
- Every rule can self-describe the minimum and maximum lengths of strings that
it can generate. Some rules like terminals may already have this information
prior to being initialized. Other rules like nonterminals may not have this
information until they are initialized. Call "Can_Get_Minimum_Length" and
"Can_Get_Maximum_Length" respectively. After being initialized, both
functions will always return true.
- An example rule that depends on being initialized with the maximum possible
length is "A -> b | A b". Because of the recursive definition, there is no
natural maximum length for the generated string. As a result, the maximum
length will be equal to the maximum possible length. Note that "A -> A" (and
other rule lists involving rules of indeterminate size) will result in an
infinite loop. "A -> b | A b" works because the terminal b has a definite
size, and the length for "A" in the "A b" rule list will get increasingly
smaller.
- Here is an example:
A_RULE start;
start.Initialize(max_allowed_length);
// Can only check length after calling Initialize.
if (max_allowed_length > start.Get_Maximum_Length() ||
max_allowed_length < start.Get_Minimum_Length())
{
cerr << "The maximum length must be between " <<
start.Get_Minimum_Length() << " and " <<
start.Get_Maximum_Length() << endl;
return 1;
}
while(start.Check_For_String())
{
Print_String_List(start.Get_String());
}
- Why isn't Initialize automatically called in the constructor for the rule?
This is because the rule lists are first "declared" and then later expanded.
The expansion can fail if the given length is not correct for a rule in a
rule list. In this case Check_For_String will return false.
RULE TYPES
- Two types of rules are provided: nonterminal rules and terminal rules. Both
Nonterminal_Rule and Terminal_Rule derive from Rule.
- Terminal_Rule assumes that the length is 1, and that a terminal rule will
only generate one string. All that needs to be done is to provide a
definition of Get_String to specify what the string to generate will be.
- It is possible to generate a finite number of terminals by overriding
Check_For_String to check whether number_of_strings_generated is below some
threshold.
- Nonterminal rules are implemented using the Rule_List helper class. A
nonterminal rules has a member called nonterminal_lists which contains lists
of rules. For example, "A -> B C | C D" would have two rule lists in the
nonterminal lists, one for "B C" and one for "C D".