Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Statements

8.05-eval-statement.watsup defines the Statement_eval relation:

relation Statement_eval:
  scope evalContext |- statement -| evalContext
  hint(input %0 %1 %2)

Read scope EC_0 |- s -| EC_1 as: “under context EC_0 at scope scope, statement s executes and produces context EC_1.”

This threading pattern mirrors Statement_ok from Section 3.4, but the context now carries values instead of types.

Empty Statement

rule Statement_eval/emptyStatement:
  scope EC |- emptyStatement -| EC

An empty statement is a no-op: it returns the incoming context unchanged.

Variable Declaration

rule VarDecl_eval:
  scope EC_0 |- type name ('=' expression) ';' -| EC_1
  -- Expr_eval:
      scope EC_0 |- expression : value
  -- if nameIR = $id(name)
  -- if EC_1 = $add_var_e(scope, EC_0, nameIR, value)

rule Statement_eval/variableDeclaration:
  scope EC_0 |- variableDeclaration -| EC_1
  -- VarDecl_eval: scope EC_0 |- variableDeclaration -| EC_1

VarDecl_eval evaluates the initializer expression to a value, converts the declared name to nameIR, and inserts the binding into the context with $add_var_e. The declared type annotation is not used at evaluation time; the static semantics already guarantees that the value has the right type.

Assignment Statement

rule Statement_eval/assignmentStatement:
  scope EC_0 |- lvalue '=' expression ';' -| EC_1
  -- Expr_eval:
      scope EC_0 |- expression : value
  -- Lvalue_write:
      scope EC_0 |- lvalue := value -| EC_1

Assignment evaluates the right-hand side to a value, then delegates the write to Lvalue_write. The updated context EC_1 carries the new binding. Note that both Expr_eval and Lvalue_write receive EC_0: the right-hand side is evaluated in the context before the write, which is the usual sequential evaluation order.

Call Statement

rule Statement_eval/callStatement:
  scope EC_0 |- lvalue `( argumentList `) ';' -| EC_1
  -- Callee_eval:
      scope EC_0 |- lvalue : callee
  -- if argument* = $flatten_argumentList(argumentList)
  -- Call_eval:
      scope EC_0 |- callee `( argument* `) -| EC_1

A call statement first resolves the callee name to a callee value via Callee_eval, flattens the argument list, then hands off to Call_eval. Call_eval handles binding arguments to parameters, executing the body, and propagating any out or inout writes back to the caller’s context. Both relations are covered in detail in Section 5.5: Call Convention.

Block Statement

rule Statement_eval/blockStatement:
  scope EC_0 |- blockStatement -| EC_3
  -- if EC_1 = $enter_e(EC_0)
  -- Block_eval:
      EC_1 |- blockStatement -| EC_2
  -- if EC_3 = $exit_e(EC_2)

A block pushes a fresh frame onto the local stack with $enter_e, evaluates the body under the extended context, then pops the frame with $exit_e. Any variables declared inside the block are discarded when the block exits. This is the runtime counterpart of Statement_ok/blockStatement from Section 3.4.

The helper relations that thread statements through a block are:

rule Block_eval:
  EC_0 |- `{ statementList `} -| EC_1
  -- if statement* = $flatten_statementList(statementList)
  -- Statements_eval:
      LOCAL EC_0 |- statement* -| EC_1

rule Statements_eval/nil:
  scope EC |- eps -| EC

rule Statements_eval/cons:
  scope EC_0 |- statement_h :: statement_t* -| EC_2
  -- Statement_eval:
      scope EC_0 |- statement_h -| EC_1
  -- Statements_eval:
      scope EC_1 |- statement_t* -| EC_2

Block_eval flattens the statement list and passes it to Statements_eval at LOCAL scope. Statements_eval threads the context left to right through each statement in sequence, just like Statements_ok in the type checker.

Conditional Statement

The two rules for conditionalStatement form a rulegroup. One rule fires when the condition evaluates to _B true and runs the then-branch block; the other fires when the condition evaluates to _B false and runs the else-branch block. Neither branch receives the other’s local declarations: each Block_eval call manages its own frame push and pop.

Compare with Statement_ok/conditionalStatement from Section 3.4, where both branches are checked under the same incoming context. At evaluation time, exactly one branch executes depending on the runtime value of the condition.

Exercise

Branch: exercise/5.4

Check out the exercise branch in the spec submodule:

git -C nano-p4/spec checkout exercise/5.4

Run the following test to observe the failure:

./nano-p4spectec eval nano-p4/spec -i nano-p4/include -p nano-p4/testdata/exercise/5.4.p4 -stf nano-p4/testdata/exercise/5.4.stf

The test should pass, but it fails. The rules for Statement_eval/conditionalStatement are missing. Write them yourself, and verify that 5.4.p4 passes.

When you are done, restore the original branch:

git -C nano-p4/spec checkout main