Demo 2 - Quiz

This an example of generating arithmetic quizzes containing additions and multiplications of integers (called the Quiz example).

The Quiz Example. The GIGL source (left), the C++ interaction (top-right), and an execution I/O example (bottom-right).

View the GIGL source file in the Git repo

Instructions:

    • As a demo, the first step is to try compiling and running the given project. For instructions and tips about demos please refer to [Here].

    • When running the demo, it will first ask for an integer as a random seed. It then gives a sequence of five arithmetic quizzes for answering. The I/O process is encoded in C++ as the top-right of the figure indicates, and the example result is shown in the bottom right of the figure.

    • The same random seed might not give the exactly same result across different environments (difference in C++ library for RNG). The result shown in the figure is done on a Windows machine with Visual Studio 2015. In addition, the development of GIGL might also change some of the ordering of getting random numbers which might also affect the concrete results. Nevertheless, statistically, the results should be of the same distribution, as the semantics of the GIGL code does not change.

    • After the demo is working, you may want to try further reading the GIGL code and/or the notes below to get a better understanding of GIGL's syntax and semantics.

Comments/Notes:

    • Here the (parametrizable) item grammar encoded is

    • Expr := mulExpr(Expr, Expr)

    • | addExpr(Expr, Expr)

    • | intExpr(int),

    • where "int" is a terminal type (it is just the "int" in C++), and expanding an "Expr" nonterminal nodes would require recursively expanding more "Expr" nonterminal nodes if choosing the "mulExpr" or "addExpr" rules.

    • Here the wrapper block is used for explicitly define the initial action of item generator (they are implicit in the previous HelloWorld and Monster examples as they by default calls the node generator of the first nonterminal type to generate the root; but here we need some arguments to call the node generators) as well as some item level attributes (the "Translate" function at the wrapper level specifies that the initial value to pass down for the parameter "preced" is 0).

    • There are two node attributes here, "Translate" and "Evaluate", both functional attributes (Line 9). "Translate" prints the arithmetic expression (translates the abstract data structure to human readable form) and "Evaluate" computes the value of the arithmetic expression for checking correctness. The parameter "preced" for the "Translate" function is for printing nicely, i.e., only printing the necessary parentheses.

    • Nonterminal types can be declared with arguments, which are used to pass information down the item tree when generating. They are arguments to node generators. The "depth" on Line 12 is such an example. When a nonterminal type is declared with arguments, its generator (node generator) should be called explicitly (unlike in the previous HelloWorld and Monster examples where they are implicitly called), shown on Line 17 - 18 and Line 24 -25.

    • Generator configurations can be separately stored in variables declared with giglconfig specifier (Line 42 - 46), its effect is the same as substituting it inline to the quiz_config part at Line 48.

    • This example introduces a very important feature of GIGL, that is the lambda configuration feature. It is an important way for users to control of the stochastic expansion process over the grammar. For a more detailed discussion, see [Here].

      • Configure parameters are those used to parameterize the item grammar and the associated generation process (i. e. they are parameters for the item type) and they are set in the generator configurations.

      • The rule probabilities are by default configure parameters, and additional ones can be declared in parts that are wrapped in { and } in the item type definition, such as the "min_int" and "max_int" on line 33.

      • In general, each configure parameter is set with a lambda expression (in other words, a pointer to a function defined inline), which is evaluated when generating each node of the item tree, not when setting the configuration (of course it would not matter if it is just a constant etc.).

      • The arguments to the lambda expression is determined by whether or which nonterminal type the configure parameter is associated with. If the parameter is a rule probability, then the arguments to lambda expressions to set that parameter are the arguments to the nonterminal type that rule is expanding from. In this example, the argument to lambda expression for all rules is the "depth" declared on Line 12.

      • The rule probability settings on Line 44 - 46 are the most use of lambda expression feature in this example. They achieve the purpose of limiting the depth to exactly the value of the "diffic" parameter (difficulty setting).

        • The (depth < diffic ? 0.3 : 0.0) on Line 44 means the probability to select "mulExpr" is 0.3 if the depth of the node is less than the difficulty setting, and 0 otherwise.

      • The (depth < diffic ? 0.7 : 0.0) on Line 45 means the probability to select "addExpr" is 0.7 if the depth of the node is less than the difficulty setting, and 0 otherwise.

      • The depth >= diffic on Line 46 means the probability to select "intExpr" is 0 if the depth of the node is less than the difficulty setting, and 1 otherwise.

    • All variables in the generator configuration that are not arguments to lambda expressions, i.e. those in-scope ones, are by default bound by reference. For example, the "diffic" variable will be incremented when using the same configuration in different iterations of the loop (Line 47 - 48).