A first question

Here is a complete Rhema file. Save it as first.rhm.

# The smallest useful question: the sum of two numbers.

synth value on E
synth latex on E

start E

E -> integer_between(10, 99) "+" integer_between(10, 99) {
  value = $1.value + $3.value
  latex = str($1.value) ++ " + " ++ str($3.value)
}

prompt "What is [`{E.latex}`]?"
answer E.value

Check it, then ask for three questions:

$ rhema check first.rhm
first.rhm: OK (1 rules; questions: main; sets: none)

$ rhema run first.rhm --count 3
 1. What is [`82 + 40`]?
    answer: 122
 2. What is [`83 + 52`]?
    answer: 135
 3. What is [`36 + 76`]?
    answer: 112

Productions

A Rhema file is a grammar. Its rules, called productions, say what a symbol may be made of. This file has one:

E -> integer_between(10, 99) "+" integer_between(10, 99) { ... }

Read it as: an E is a number from 10 to 99, a plus sign, and another such number. start E says that a question is an E. To make a question, the compiler starts from E, picks a production for it, and builds whatever the production asks for, which is how a question becomes a tree:

E
├── integer_between(10, 99)   82
├── "+"
└── integer_between(10, 99)   40

integer_between(10, 99) is a generator: a leaf that draws a value. A quoted string such as "+" is a leaf that stands for itself.

Attributes

A tree on its own is not a question. The braces after a production say what to compute at that node, from its children:

value = $1.value + $3.value
latex = str($1.value) ++ " + " ++ str($3.value)

$1 is the first symbol on the right of the arrow, $3 the third. Every generator leaf carries its drawn number as value; str turns a number into text and ++ joins text. So at the E above, value is 122 and latex is 82 + 40.

These are synthesized attributes: computed from the children, on the way back up the tree. Each has to be declared, with the symbol that carries it:

synth value on E
synth latex on E

The compiler checks that every production for E says how to compute both. Leave one out and it will not compile:

$ rhema check first.rhm
first.rhm:8:1: error: rule 'E' does not say how to compute 'latex'; add 'latex = ...' inside its braces

The prompt and the answer

The last two lines turn the finished tree into a question:

prompt "What is [`{E.latex}`]?"
answer E.value

{E.latex} in the prompt is replaced by the latex attribute of the start symbol. The prompt is written in WeBWorK's PGML, so [`...`] marks mathematics. answer names the attribute the student's answer is graded against.

This is the point of writing questions this way. The text the student reads and the answer the grader expects are two attributes of one tree, computed from the same drawn numbers, so they cannot drift apart. There is no answer key to keep in step with the questions.

What the compiler checks

Before any question is generated, rhema check makes sure that

  • every symbol used has productions or is a leaf,
  • every synthesized attribute is computed by every production that must carry it,
  • the prompt and answer name attributes that exist,
  • every builtin function is called with the right number of arguments.

These are checked once, for the file, not for each question drawn. A file that checks will not fail half way through a paper for one of these reasons.

Next: the questions above are all equally hard. Chapter 2 gives a grammar a budget, so that one file can ask easy and hard questions of the same kind.