9.0.0.1

5 — Core: CESK🔗

Due Thursday, 09 October 2025, 11:59:59pm

Purpose to build a semantic model for a language with lexically scoped variables

Delivery Deliver your solutions in a directory called 5 within your assigned GitHub repository. The directory should contain the following items:

  • the executables xcesk as specified below;

  • the sub-directory 5/Tests/ with the required integration test; and

  • an optional sub-directory called 5/Other/ for all auxiliary files.

The directory 5/ may also contain a Makefile (see Make) in case you need us to build your executable. An executable should not be checked into a repository.

Programming Task Using your favorite programming language, you are to design a data representation for the states of the CESK machine for Core; see figure 6. Next, implement the necessary functionality to get the machine running:
  • load, which consumes an AST of a Core program (see 4 — Core: Validity) and constructs an initial CESK state;

  • transition, which consumes a non-final CESK state and constructs the next state as specified in figure 7; and

  • unload, which consumes a final CESK state and returns an answer: a number or an informative error message.

The CESK machine itself is a function from Core ASTs to answers: it loads the program, loops the transition function, and unloads the final state.

  • C contains a control string, which is either a † (pronounced “I am searching for an expression”) token or a Core expression.

  • E contains an “environment”, which associates variables with Locations.

  • S contains a “store”, which associates Locations with numbers.

  • K contains the rest of the instructions that the machine must execute once the expression in C is evaluated. It is represented as a stack of closures. Each closure corresponds to a nested block, and the bottom-most one represents the “root” of the program.

An initial state contains † in C, empty associations in E and S, and a stack frame with the given program AST in K.

A final state contains a number in C and empty stack in K.

A closure combines a program AST with an environment. The AST represents the remaining instructions of either the top-most program (bottom-most stack frame) or a nested block (all others); if the AST represents a nested block, the expression field is filled with a dummy value. The environment represents the meaning of variables in the surrounding context.

The set of Locations is an infinitely large set of arbitrary values. Here “infinite” means that, give some finite collection of locations, an algorithm can create a new one.

Figure 6: The CESK semantics of the Core language: states

Exercise Design the CESK transition for the comparison expression (Variable == Variable). Its purpose is to compare the current values of the two variables.

As the warning in figure 4 says, it is imperative not to use plain = to compare inexact (double) numbers. Due to their approximate nature. The video lecture linked in with this intermezzo (see margin note) is particularly instructive if you ever need to write algorithms on doubles. Instead numerical computer scientists have recommended for decades to check whether two such numbers are close:

| x y | < ϵ

for some small number ϵ. Let’s use 0.0000000001 for ϵ for our CESK machine.

The rule ``search encounters nested block'' has been updated.

In the following tables, < def*, stmt*, r > is notation for a stack whose top-most frame contains the block consisting of def*, a potentially empty sequence of definitions; stmt*, a potentially empty sequence of statements; and r, a return expressions (which may just be $). The notation f::others denotes a sequence with at least one element f, followed by a potentially empty sequences of others of the same kind.

Blocks and Definitions

 

Control  

 

Environment  

 

Store  

 

Kontinuation

search ends with def rhs

before:

 

 

e

 

s

 

< (def x rhs)::def*, stmt*, r >

after:

 

rhs

 

e

 

s

 

< (def x rhs)::def*, stmt*, r >

value for right-hand side of declaration

before:

 

n

 

e

 

s

 

< (def x rhs)::def*, stmt*, r >

after:

 

 

e[x = xl]

 

s[xl = n]

 

< def*, stmt*, r >

 where

 

xl

 

=

 

a new (relative to s) location

search encounters nested block

before:

 

 

e

 

s

 

< [ ], (block d* s*)::stmt*, r >

after:

 

 

e

 

s

 

push[ cl, k ]

 where

 

k

 

=

 

< [ ], stmt*, r >

 and

 

cl

 

=

 

closure: (d* s* $) in e

search exhausts nested block

before:

 

 

e

 

s

 

< [ ], [ ], $ >

after:

 

 

e1

 

s

 

pop[ k ]

 where

 

e1

 

=

 

the environment in the top-most closure

 and

 

k

 

=

 

< [ ], [ ], $ >

search ends with the return expression

before:

 

 

e

 

s

 

< [ ], [ ], r >

after:

 

r

 

e

 

s

 

< [ ], [ ], r >

search ends with evaluated return expression

before:

 

v

 

e

 

s

 

< [ ], [ ], r >

after:

 

v

 

e1

 

s

 

pop[ k ]

 where

 

e1

 

=

 

the environment in the top-most closure

 and

 

k

 

=

 

< [ ], [ ], r >

Expressions

 

Control  

 

Environment  

 

Store  

 

Kontinuation

evaluate a variable

before:

 

y

 

e

 

s

 

k

after:

 

n

 

e

 

s

 

k

 where

 

yl

 

=

 

e[y]

 and

 

n

 

=

 

s[yl]

evaluate an addition

before:

 

(y + z)

 

e

 

s

 

k

after:

 

+

 

e

 

s

 

k

 where

 

yl

 

=

 

e[y]

 and

 

zl

 

=

 

e[z]

 and

 

yn

 

=

 

s[yl]

 and

 

zn

 

=

 

s[zl]

evaluate a division (success)

before:

 

(y / z)

 

e

 

s

 

k

 subject to:

 

z is not 0.0

after:

 

double ieee division of yn by zn

 

e

 

s

 

k

 where

 

yl

 

=

 

e[y]

 and

 

zl

 

=

 

e[z]

 and

 

yn

 

=

 

s[yl]

 and

 

zn

 

=

 

s[zl]

evaluate a division (failure)

before:

 

(y / z)

 

e

 

s

 

k

 subject to:

 

z is 0.0

after:

 

error

 

e

 

s

 

[ ]

Assignment Statements

 

Control  

 

Environment  

 

Store  

 

Kontinuation

search ends with right-hand side of assignment

before:

 

 

e

 

s

 

< [ ], (x = rhs)::stmt*, r >

after:

 

rhs

 

e

 

s

 

< [ ], (x = rhs)::stmt*, r >

value for right-hand side of assignment

before:

 

n

 

e

 

s

 

< [ ], (x = rhs)::stmt*, r >

after:

 

 

e

 

s[xl = n]

 

< [ ], stmt*, r >

 where

 

xl

 

=

 

e[x]

While Loops

 

Control  

 

Environment  

 

Store  

 

Kontinuation

search for expression in while

before:

 

 

e

 

s

 

< [ ], (while0 tst body)::stmt*, r >

after:

 

tst

 

e

 

s

 

< [ ], (while0 tst body)::stmt*, r >

decide whether to run while loop (positive)

before:

 

n

 

e

 

s

 

< [ ], (while0 tst body)::stmt*, r >

 subject to:

 

n is 0.0

after:

 

 

e

 

s

 

< [ ], body::o, r >

 where

 

o

 

=

 

(while0 tst body)::stmt*

decide whether to run while loop (negative)

before:

 

n

 

e

 

s

 

< [ ], (while0 tst body)::stmt*, r >

 subject to:

 

n is not 0.0

after:

 

 

e

 

s

 

< [ ], stmt*, r >

Conditionals

 

Control  

 

Environment  

 

Store  

 

Kontinuation

search for expression in if

before:

 

 

e

 

s

 

< [ ], (if0 tst thn els)::stmt*, r >

after:

 

tst

 

e

 

s

 

< [ ], (if0 tst thn els)::stmt*, r >

pick then branch from if0

before:

 

n

 

e

 

s

 

< [ ], (if0 tst thn els)::stmt*, r >

 subject to:

 

n is 0.0

after:

 

 

e

 

s

 

< [ ], thn::stmt*, r >

pick else branch from if0

before:

 

n

 

e

 

s

 

< [ ], (if0 tst thn els)::stmt*, r >

 subject to:

 

n is not 0.0

after:

 

 

e

 

s

 

< [ ], els::stmt*, r >

Figure 7: The CESK semantics of the Core language: transition function

The xcesk program reads one ExampleCC from STDIN, constructs the AST, checks its validity, runs the CESK machine on it, and writes one of the following strings to STDOUT: The outcomes are listed in the order in which they may occur for a given program.

  • "parser error" if the given ExampleCC is not an element of the Core grammar;

  • "undeclared variable error" if in the well-formed Core program one of its variable occurrences comes without declaration;

  • a number n if the CESK machine produces a number n for the well-formed and valid Core program;

  • "run-time error" if the CESK machine produces an error outcome for the well-formed and valid Core program.

Testing Task Create 10 integration tests for xcesk.

A test always consists of inputs, expected output, and an automated procedure for comparing the actual output with the expected one. — For this course, an integration test consists of a pair of files: n-in.ss, the input file, and n-out.ss, the expected output file, where n is an integer between 0 and the requested number of tests (exclusive). The comparison procedure is (string or numeric) equality on the content of the output file and the actual output. — Constraint No test file may exceed the size limit of 5Kb.

Readings Nested Blocks (CESK)