Generate & Construct

While GIGL is mainly dedicated to stochastic generation (i.e. the concept of generate), we consider it a convenient to add some feature for direct specification of deterministic expansion (i.e. the concept of construct). Note that this construct or its derived concept constructor, are different from the concept of constructor in C++, as an object is created with some constructor, implicitly or explicitly, whether it is generated or constructed in GIGL. The table below shows the main technical comparisons/contrasts between construct and generate. The examples in this page uses the item type from the Quiz demo unless specified.

    • An example call of node generator can be

    • generate Expr(depth + 1)

    • which generates a sub-expression stochastically (while passing incremented "depth" value down), or it can be

    • generate addExpr(depth + 1)

    • which generates a sub-expression that is guaranteed to be an additive expression (at the top level).

    • An example call of node constructor can be

    • construct intExpr(6)

    • which constructs a sub-expression that is an integer constant 6, or it can be

    • construct mulExpr(intExpr(3), addExpr(intExpr(1), intExpr(2)))

    • which constructs a sub-expression that is "3 * (1 + 2)". As indicated in the table above, constructors cannot be invoked by nonterminal type name (i.e. construct Expr(...) is invalid), and the constructor calls encode the determinism by having the children of the rule passed in as arguments (instead of passing down the nonterminal argument "depth"). Note that the if a rule child is an array type, there are some special syntax setting to pass it into the constructor, as C++ does not allow passing in initialization list for array type (or pointer type) arguments, which is covered in more details at [Here].

    • An example call of item generator can be

    • generate MathQuiz with <* ... *>

    • where the configuration must be provided (omitted when showing above).

    • An example call of item constructor can be

    • construct MathQuiz(mulExpr(intExpr(3), addExpr(intExpr(1), intExpr(2))) noconfig

    • which construct the whole quiz to be "3 * (1 + 2)" and no configuration provided, or it can be

    • construct MathQuiz(mulExpr(intExpr(3), addExpr(intExpr(1), intExpr(2))) with <* ... *>

    • where the configuration is provided. In this particular example, the configuration would be useful as the whole structure is already provided and there is no other use of the configuration. However, in some other cases, configurations might be needed for some other non-structural parts, or it can be used in some mixed-construct-generate procedure (discussed below).

    • Construction and generation can be used in mix flexibly. A relatively obvious case is to call node constructor in some node/item generator to construct some lower part of the item tree structure deterministically but other parts of the tree are still generated by generator. However the mix can be the other way, i.e. having generator nested in constructors, and GIGL provide syntax supporting that. For example,

    • construct addExpr(mulExpr(intExpr(2), intExpr(5)), generate Expr(depth + 2))

    • creates a sub-expression that is "2 * 5 + ..." where the omitted part is a sub-expression to be stochastically generated. Another example, at the item level instead, can be

    • construct MathQuiz(mulExpr(intExpr(2), intExpr(5)), generate Expr(depth + 2))) with <* ... *>

    • creates the same type of expression but as the whole quiz. Here the configuration part is needed because there are part of the item tree that are stochastically generated which at least need the rule probabilities in the configuration.

    • In the parameter scope of an argument calls, the construct keywords can be omitted and is often omitted (which are the case in the example above) for compact code. The example

    • construct mulExpr(intExpr(3), addExpr(intExpr(1), intExpr(2)))

    • above is actually an alias of

    • construct mulExpr(construct intExpr(3), construct addExpr(construct intExpr(1), construct intExpr(2)))

    • In other words, in a sequence of nested constructor calls, only the outmost one requires the construct keyword be indicated. The generate keyword cannot be omitted though.

    • The definition of the syntax for the various kinds of generator calls and constructor calls can be seen at [Here] (item level) and [Here] (node level). It may include some features not covered here, such as the maybe-semantics.

    • The implementation of item level generators and constructors are defined in the wrapper block, leaded by keywords generator and constructor respectively. There is another keyword gencontor that can lead a block of statements that are shared in the generator and constructor. Multiple blocks of statements applicable for the generator will be concatenated to form the actual generator, similar for the constructor (also similar for destructor). For example the wrapper block contains

    • gencontor { A; }

    • generator { B; }

    • gencontor { C; D; }

    • constructor { E; }

    • generator { F; }

    • where A, B, C, D, E, F represent some statements. The generator will have statements A;B;C;D;F; and the constructor will have statements A;C;D;E;, in addition to what is implicitly added by the GIGL framework. The syntax for this part can be found at [Here]. Usually we do not need this many segmented parts, and some simple usage of wrapper generators can be seen at the Quiz, Tree, Dungeon demos.

  • The implementation of node level generators and constructors are defined in the rule block and in the scope of each rule (the generation and construction is generally different for each rule). All conversions in the wrapper level applies analogously here. In addition, for the node level, as we allow implicit defining the assignment of variable attributes outside the generator and constructor (also see [Here]), those will be treated the same as statements in a "gencontor" block, i.e. inserted into both the generator and constructor. The syntax for this part can be found at [Here]. An example of this is the assignment of the "damage" attribute in the Monster demo, which is added to both the generator and constructor (only the generator is used in the example though). The following are some notes about the implicit insertion to generator and constructor implementation.

    • If those (variable attribute assignment as well as statements in the "gencontor" blocks) depend on nonterminal arguments, they might not work properly in the constructor as those are not passed to constructors.

    • The assignment like implementation for functional attributes are different from the variable counterparts, they are not part of generators or constructors, but just a definition for function implementation, e.g. the implementation of "Print" in the HelloWorld demo.