Attributes

Attributes are not part of the grammatical structure backbones, occasionally some might affect the generation process in some context sensitive way, but mostly they are used to represent the concrete functionality. i.e. the semantics of the generated items. They may be simply understood as member variables and member functions in C++, but the underlying implementation may not directly correspond to the semantics (i.e. the pointers that are used to access the root of the item tree and construct the structure of the tree are not considered attributes, similarly for the generators, although they are implemented as member variables and functions). In GIGL syntax, anything declared in the wrapper blocks (except item generators) and the node blocks are considered attributes.

Wrapper VS Node

    • Wrapper attributes provides interface to the item, which can be explicitly defined in the node blocks. Node attributes defines the recursive property over the grammatical structures, which are declared in the node block and with implementation provided within the definition of each rule.

    • By default, node attributes are implicitly duplicated at the wrapper level (but not the other way), i.e. having their wrapper counter parts (which are wrapper attributes) by the following implementation (implicitly).

      • For variable, the values are copied from the root node of the item tree to the wrapper level as soon as the generation process finishes. An example is in the Monster demo, the "damage" attribute is implicitly declared at the wrapper level and it (implicitly) gets the value of the "damage" variable of the root node (should be a Monster type nonterminal node).

      • For functions, the wrapper one share the same signature with the node one, and the implementation is merely calling its node counterpart at the root node while passing down the parameters untouched and then return its return value. An example is in the Quiz demo, the "Evaluate" function is implicitly defined at the wrapper level and its implementation is:

      • return root->Evaluate();

    • Overloading the implicit wrapper function with a different signature is allowed as shown in the Quiz demo. The explicitly defined wrapper version of "Translate" function has a different signature from the node version, and this version only exists at the wrapper level but not the node level.

    • Currently, re-declaring node variables (with the same names) or overriding node functions (with the same names and signatures) will cause issues like redefinition errors, this is might be fixed later. Please avoid doing so (circumvent with having names, or not explicitly defining the wrapper attribute but accessing them through other functions/variables etc.) with the current version.

    • The wrapper block can also define generators, constructors, destructors etc., but they are not considered attributes, and their node counterparts is not declared in the node blocks but directly defined with each rules in the rule block, and all of them are implicitly declared in any situations. By default, the wrapper (item) generator is implemented to call the generator of the root node with no parameters (this may change later) if the root is not explicitly assigned, and the default wrapper (item) destructor delete the root node by its pointer which invoke the destructor of the root node if the root is not explicitly deleted. For discussions about generators and constructors, please refer to [Here].

    • It is true that the wrapper is like a special "root" nonterminal type, but it is a justified feature because:

      • It provides a encapsulated interface and conceptualizes it. This interface is also useful for using an "item" pointer inside the item tree to go back to visit the top level of the item, as mentioned in [Here].

      • Saves the trouble of defining a special "root" node. When this is not needed, the implicit duplication of node attributes already take care of this automatically. In many cases this is still needed, because some attributes only need to be at the item level, but does not need to act recursively over the item trees, just like regular C++ class members.

Variable VS Function

    • We call both variables and functions attributes (variable attributes and functional attributes respective), as semantically they are not that different, and they can often be used interchangeably for the same purpose.

    • Although often variables and functions can be used interchangeably, it is still justified to have both available, and some of the reasons (besides being convenient to do one over the other) are as follows.

      • Space-time trade off. Using variables to store contents often requires memory spaces but less accessing time, while using functions to compute often requires less memories but more computation time. The preference for one over another may depends on the actual case, such as when the data structure is very small but needs to be accessed many times, then computing it only in generation time and storing it in a variable may be preferred over computing it via a function each time. For example replacing the functional attribute "Evaluate" in the Quiz demo, with a variable attribute and computing it in node generators might be a better option.

      • Dynamic inputs. Aspects of the content that needs to be evaluated dynamically requires functions, and they cannot be achieved by merely using variables inside the item data structure. A simple example is when it needs some random numbers to evaluate, such as dealing a different damage in a random range each time etc. Another example is that, suppose in the Quiz demo, we want to use the generated "MathQuiz" items in various scenarios, sometimes on their own just as the example shows, but sometimes as sub-expressions of a larger expression, then the functionality of "Translate" attribute cannot be replaced by variables, because whether or not a parenthesis is needed depends on the precedence of the operator used in the parent level, which is must be dynamically input in each usage (though to achieve this we need to remove the declaration of "Translate" function in the wrapper block to make it copy the signature of the node level, so that it accepts the parameter "precede" at the item level).

      • Maintaining states. Maintaining states requires using variables, and it cannot be achieved by merely using functions. Suppose we want to record for each quiz whether it is already solved or not, then we probably need a variable attribute (a wrapper one should suffice), to record that.