|
| 1 | +# Example 3: Variables in Expressions, Explicit Substitution |
| 2 | + |
| 3 | +In this exercise, we take the expression calculator of the [previous exercise](../02-calc-bool/README.md) and extend the syntax of integer expressions with variables. To evaluate such expressions, we move from the functional to the stateful fragment of K, using configurations to model a variable store (hard-coded for this example), and evaluate expressions using explicit substitution. In the syntax, variables are modelled using the `K` built-in `Id` sort, and the integer and Boolean expressions contain all of the previously seen operators: |
| 4 | + |
| 5 | +```k |
| 6 | +module SUBST-SYNTAX |
| 7 | + imports INT-SYNTAX // Int is the K built-in integer sort |
| 8 | + imports BOOL-SYNTAX // Bool is the K built-in Boolean sort |
| 9 | + imports ID // Id is the K built-in sort for identifiers (variables) |
| 10 | +
|
| 11 | + // Expressions are either integer or Boolean expressions |
| 12 | + syntax Exp ::= IExp | BExp |
| 13 | +
|
| 14 | + // An integer expression is either an integer value or a variable identifier |
| 15 | + syntax IExp ::= Int | Id |
| 16 | + // or any of the arithmetic operators |
| 17 | + syntax IExp ::= IExp "^" IExp |
| 18 | + > IExp "*" IExp |
| 19 | + | IExp "/" IExp |
| 20 | + > IExp "+" IExp |
| 21 | + | IExp "-" IExp |
| 22 | + // or a bracketed integer expression |
| 23 | + syntax IExp ::= "(" IExp ")" [bracket] |
| 24 | + |
| 25 | + // A Boolean expression is either a Boolean value |
| 26 | + syntax BExp ::= Bool |
| 27 | + // or any of the comparison operators |
| 28 | + syntax BExp ::= IExp "<=" IExp |
| 29 | + | IExp "<" IExp |
| 30 | + | IExp ">=" IExp |
| 31 | + | IExp ">" IExp |
| 32 | + | IExp "==" IExp |
| 33 | + | IExp "!=" IExp |
| 34 | + // or any of the propositional connectives |
| 35 | + syntax BExp ::= BExp "&&" BExp |
| 36 | + | BExp "||" BExp |
| 37 | + // or a bracketed Boolean expression |
| 38 | + syntax BExp ::= "(" BExp ")" [bracket] |
| 39 | +endmodule |
| 40 | +``` |
| 41 | + |
| 42 | +The semantics, on the other hand, becomes more involved. First, we have to model the variable store, which we do using `K` configurations. In particular, our configuration is of the form: |
| 43 | + |
| 44 | +```k |
| 45 | + configuration |
| 46 | + // K cell, containing the expression to be evaluated |
| 47 | + <k> $PGM:Exp </k> |
| 48 | + // Variable store, modelled as a K map |
| 49 | + <store> |
| 50 | + #token("a", "Id") |-> 16 |
| 51 | + #token("b", "Id") |-> 9 |
| 52 | + #token("c", "Id") |-> 4 |
| 53 | + #token("d", "Id") |-> 2 |
| 54 | + </store> |
| 55 | +``` |
| 56 | + |
| 57 | +and contains two components. The first is a `K` cell, which contains the expression to be evaluated and which is initially populated with the contents of the passed input file (denoted by `$PGM`). The second is the variable store, which is modelled as a `K` map, and which contains some hard-coded variables (`a`, `b`, `c`, and `d`) with their values. |
| 58 | + |
| 59 | +Next, we declare that expressions should be evaluated, with each type of expressions having its dedicated substitution (`substI` and `substB`): |
| 60 | + |
| 61 | +```k |
| 62 | + // Integer expression evaluation via explicit substitution |
| 63 | + rule |
| 64 | + <k> IE:IExp => substI(IE, STORE) ... </k> |
| 65 | + <store> STORE </store> |
| 66 | + requires notBool isInt(IE) |
| 67 | +
|
| 68 | + // Boolean expression evaluation via explicit substitution |
| 69 | + rule |
| 70 | + <k> BE:BExp => substB(BE, STORE) ... </k> |
| 71 | + <store> STORE </store> |
| 72 | + requires notBool isBool(BE) |
| 73 | +``` |
| 74 | + |
| 75 | +The first rule states that, given a configuration containing an integer expression `IE` and store `STORE`, if `IE` is not an integer value, then it should be rewritten to the result of the substitution given on the right hand side, and the store should remain the same. The second rule is analogous. The side conditions, given by the `requires` clauses, are there to ensure that the evaluation terminates.[^1] |
| 76 | + |
| 77 | +The actual integer substitution function is defined as follows (with some non-instructive cases elided): |
| 78 | + |
| 79 | +```k |
| 80 | + // Integer expression substitution |
| 81 | + syntax Int ::= substI ( IExp , Map ) [function] |
| 82 | + |
| 83 | + // Base case: integer values evaluate to themselves |
| 84 | + rule substI(I:Int, _SUBST) => I |
| 85 | +
|
| 86 | + // Base case: identifiers evaluate to their value in the store |
| 87 | + rule substI(I:Id, SUBST) => {SUBST [ I ]}:>Int |
| 88 | +
|
| 89 | + // Inductive cases |
| 90 | + rule substI(I1 + I2, SUBST) => substI(I1, SUBST) +Int substI(I2, SUBST) |
| 91 | + rule substI(I1 - I2, SUBST) => substI(I1, SUBST) -Int substI(I2, SUBST) |
| 92 | + ... |
| 93 | +``` |
| 94 | + |
| 95 | +The substitution is defined as a function mapping an integer expression (`IExp`) and a variable store (`Map`) to an integer. It has two base cases, which state that integers evaluate to themselves, and that variables evaluate to their value in the store (denoted by `[SUBST [I]]`). The cases for all of the arithmetic operators are straightforward, that is, the substitution is simply applied to the operands inductively. The Boolean substitution function is defined analogously, using the integer substitution function where required (for example, in the comparison operators). |
| 96 | + |
| 97 | +[^1] One might think, given the base cases of the substitutions in which integers and Booleans evaluate to themselves, that these side conditions are not necessary. However, if they were removed, `K` would simply keep attempting to rewrite the final obtained value using the substitution, and this would keep succeeding, the value would keep being rewritten to itself, and the evaluation wound not terminate. |
| 98 | + |
| 99 | +## Compiling and running the exercise |
| 100 | + |
| 101 | +Follow the instructions from the top-level [`README`](../README.md) file to compile this exercise and run the provided tests. Create some further examples and/or change the hard-coded variables and their values to check if you get back the expected results. |
0 commit comments