IMP
Author: Grigore Roșu (grosu@illinois.edu)
Organization: University of Illinois at Urbana-Champaign
Abstract
This is the K semantic definition of the classic IMP language. IMP is considered a folklore language, without an official inventor, and has been used in many textbooks and papers, often with slight syntactic variations and often without being called IMP. It includes the most basic imperative language constructs, namely basic constructs for arithmetic and Boolean expressions, and variable assignment, conditional, while loop and sequential composition constructs for statements.
kmodule IMP-SYNTAX imports DOMAINS-SYNTAX
Syntax
This module defines the syntax of IMP.
Note that <=
is sequentially strict, and &&
is strict only in its first
argument, because we want to give it a short-circuit semantics.
ksyntax AExp ::= Int | Id | "-" Int [format(%1%2)] | AExp "/" AExp [left, strict, color(pink)] | "(" AExp ")" [bracket] > AExp "+" AExp [left, strict, color(pink)] syntax BExp ::= Bool | AExp "<=" AExp [seqstrict] | "!" BExp [strict, color(pink)] | "(" BExp ")" [bracket] > BExp "&&" BExp [left, strict(1), color(pink)] syntax Block ::= "{" "}" | "{" Stmt "}" [format(%1%i%n%2%d%n%3)] syntax Stmt ::= Block | Id "=" AExp ";" [strict(2), color(pink), format(%1 %2 %3%4)] | "if" "(" BExp ")" Block "else" Block [strict(1), colors(yellow, white, white, yellow), format(%1 %2%3%4 %5 %6 %7)] | "while" "(" BExp ")" Block [colors(yellow,white,white), format(%1 %2%3%4 %5)] > Stmt Stmt [left, format(%1%n%2)]
An IMP program declares a set of variables and then executes a
statement in the state obtained after initializing all those variables
to 0. K provides builtin support for generic syntactic lists:
List{Nonterminal,terminal}
stands for terminal
-separated lists of Nonterminal
elements.
ksyntax Pgm ::= "int" Ids ";" Stmt [format(%1 %2%3%n%4), colors(yellow,pink)] syntax Ids ::= List{Id,","} [format(%1%2 %3)] endmodule
We are done with the definition of IMP's syntax. Make sure that you write and parse several interesting programs before you move to the semantics.
kmodule IMP imports IMP-SYNTAX imports DOMAINS
Semantics
This module defines the semantics of IMP.
Before you start adding semantic rules to a K definition, you need to
define the basic semantic infrastructure consisting of definitions for
results
and the configuration
.
Values and results
IMP only has two types of values, or results of computations: integers and Booleans. We here use the K builtin variants for both of them.
ksyntax KResult ::= Int | Bool
Configuration
The configuration of IMP is trivial: it only contains two cells, one
for the computation and another for the state. For good encapsulation
and clarity, we place the two cells inside another cell, the top cell
which is labeled T
.
kconfiguration <T color="yellow"> <k color="green"> $PGM:Pgm </k> <state color="red"> .Map </state> </T>
The configuration variable PGM tells the K tool where to
place the program. More precisely, the command
krun program
parses the program and places the resulting
K abstract syntax tree in the k
cell before invoking the
semantic rules described in the sequel. The .
in the
state
cell, written .Map
in ASCII in the
imp.md
file, is K's way to say nothing. Technically, it
is a constant which is the unit, or identity, of all maps in K
(similar dot units exist for other K structures, such as lists, sets,
multi-sets, etc.).
Arithmetic expressions
The K semantics of each arithmetic construct is defined below.
Variable lookup
A program variable X
is looked up in the state by matching a binding
of the form X |-> I
in the state cell. If such a binding does not
exist, then the rewriting process will get stuck. Thus our semantics of
IMP disallows uses of uninitialized variables. Note that the variable
to be looked up is the first task in the k
cell (the cell is
closed to the left and torn to the right), while the binding can be
anywhere in the state
cell (the cell is torn at both sides).
krule <k> X:Id => I ...</k> <state>... X |-> I ...</state>
Arithmetic operators
There is nothing special about these, but recall that K's configuration
abstraction mechanism is at work here! That means that the rewrites in the
rules below all happen at the beginning of the k
cell.
krule I1 / I2 => I1 /Int I2 requires I2 =/=Int 0 rule I1 + I2 => I1 +Int I2 rule - I1 => 0 -Int I1
Boolean expressions
The rules below are straightforward. Note the short-circuited semantics
of &&
; this is the reason we annotated the syntax of
&&
with the K attribute strict(1)
instead of strict
.
krule I1 <= I2 => I1 <=Int I2 rule ! T => notBool T rule true && B => B rule false && _ => false
Blocks and Statements
There is one rule per statement construct except for the conditional, which needs two rules.
Blocks
The empty block {}
is simply dissolved. The .
below is the
unit of the computation list structure K
, that is, the empty task.
Similarly, the non-empty blocks are dissolved and replaced by their statement
contents, thus effectively giving them a bracket semantics; we can afford to
do this only because we have no block-local variable declarations yet in IMP.
krule {} => .K rule {S} => S
Assignment
The assigned variable is updated in the state. The variable is expected to be declared, otherwise the semantics will get stuck. At the same time, the assignment is dissolved.
krule <k> X = I:Int; => .K ...</k> <state>... X |-> (_ => I) ...</state>
Sequential composition
Sequential composition is simply structurally translated into K's builtin task sequentialization operation.
krule S1:Stmt S2:Stmt => S1 ~> S2
Conditional
The conditional statement has two semantic cases, corresponding to
when its condition evaluates to true
or to false
.
Recall that the conditional was annotated with the attribute
strict(1)
in the syntax module above, so only its first
argument is allowed to be evaluated.
krule if (true) S else _ => S rule if (false) _ else S => S
While loop
We give the semantics of the while
loop by unrolling.
krule while (B) S => if (B) {S while (B) S} else {}
Programs
The semantics of an IMP program is that its body statement is executed in a state initializing all its global variables to 0. Since K's syntactic lists are internally interpreted as cons-lists (i.e., lists constructed with a head element followed by a tail list), we need to distinguish two cases, one when the list has at least one element and another when the list is empty. In the first case we initialize the variable to 0 in the state, but only when the variable is not already declared (all variables are global and distinct in IMP).
krule <k> int (X,Xs => Xs);_ </k> <state> Rho:Map (.Map => X|->0) </state> requires notBool (X in keys(Rho)) rule int .Ids; S => S endmodule