In this assignment you will modify an interpreter for the PostFix language as described in the Turbak text, Chapters 1-3. Your interpreter will mirror the small-step operational sematics rewrite rules on page 47 of the text. This assignment will also give you an opportunity to use the pattern-matching facilities of Scheme+.
Files Getting Started with PostFixTo get started with PostFix, we first must load the Scheme+ file and the PostFix support file into the interpreter.
(load "scheme+.scm") (load "base.scm") (load "postfix.scm")
After that, we can begin testing some simple PostFix programs with the supplied semantics.
(postfix 0 3) |
lambda (list) (...) |
((postfix 0 3) '()) |
3 |
((postfix 2 pop) '(4 5)) |
5 |
((postfix 2 pop) '(4)) |
error "expected two arguments" |
((postfix 0 pop) '()) |
"stuck: pop" |
In general, to evaluate a PostFix program, apply it to a list of
numbers with as many elements as the first argument to
postfix
.
The interpreter closely follows the rewrite rules presented as part of the small-step operational semantics of PostFix. These rules are listed on page 47 of the Turbak text. Each rewrite rule transforms a configuration into another. In PostFix, a configuration is a tuple consisting of a list of commands and a stack. For example, the rule labeled [pop] takes a configuration with pop at the head of the command list and a value at the top of the stack, and returns a configuration with pop removed from the command list and the value at the top of the stack removed.
The interpreter makes use of a function small-step
which, given a PostFix configuration, returns a new PostFix
configuration representing the completion of one step of the PostFix
computation. That is, given a configuration, it applies one of
the rewrite rules and returns the rewritten configuration.
The PostFix support file contains the declarations of several datatypes which are used in the interpreter. These datatypes define what configurations, commands, and stack values look like to the small-step interpreter.
CommandsAs described in section 1.4.1 of the Turbak text, the commands in PostFix are:
The provided PostFix infrastructure converts PostFix syntax into a list of commands, where each command matches one of the following:
(@num n)
: An integer numeral(@seq s)
: An executable sequence(@arithop op)
: An arithmetic operation(@relop op)
: An arithmetic comparison(@exec)
,
(@nget)
, (@pop)
, (@sel)
, and
(@swap)
As stated in section 1.4.2 of the Turbak text: a value on the stack is either (1) an integer numeral or (2) an executable sequence. The provided infrastructure expects data matching one of the following:
($num n)
: An integer numeral($seq s)
: An executable sequenceNotice the difference between the constructors for stack data and commands: the stack data constructors begin with '$' while the commands begin with '@'.
Configurations
A configuration is simply a tuple containing a command list and a list
of stack values: ($conf command-list stack)
.
small-step
works
The infrastructure calls small-step
with a configuration,
and expects it to return a configuration. The code assumes that the
command list passed to small-step
is non-null; that is,
it has at least one command in it. The provided semantics uses Scheme+
pattern matching to get at that first command and the stack:
(define (small-step config) (match config (($conf (cons cmd rest-cmds) stack) ... )))
Notice that Scheme+ pattern matching was able to look not only inside the configuration, but also inside one of its elements, namely the command list. This facility will be useful for examining the state of the stack for some of the commands. In fact, the only way to inspect the type of values present on the stack is to use pattern matching.
Command ImplementationsNow looking inside the ... in the above code from the semantics:
(match cmd ((@num n) ($conf rest-cmds (cons ($num n) stack))) ((@pop) (match stack ((cons _ rest-stk) ($conf rest-cmds rest-stk)) (_ (stuck "pop")))) ...)
The first example matches numeric literals, and simply returns a new
configuration with the number removed from the command list and added
to the top of the stack. Each clause looks roughly like this,
matching a command name and returning a new configuration using
$conf
.
The second example above, @pop
, performs a
pattern match on the stack, ignores the top element, and returns the
rest of the stack. This example highlights the use of
stuck
: if the pattern match fails, then no rewrite rule
applies for this configuration in the operational semantics.
Therefore, we cannot continue computation; we say we are
stuck.
In the implementation of small-step
, if no rewrite rule
applies to the given configuration, the code calls
stuck
, passing it a string with the name of the command
that failed. Every syntactically correct PostFix program does not
cause a pattern match failure or otherwise cause an error; the
interpreter instead calls stuck
.
small-step
, except for nget.
Fill in the empty implementation of nget to complete the PostFix
interpreter.
Don't try PostText until you have nget working for PostFix!
Implementing nget will help you understand the inner workings of the
small-step interpreter.
One of the chief limitations of the PostFix language is that there is no way to name values. In this problem, we consider extending PostFix with a simple naming system. We will call the resulting language PostText. (This is adapted from a problem in the Turbak text.)
The grammar for PostText is the same as that for PostFix except that there are three new commands:
C ::= ... | I [Name] | def [Definition] | ref [Name-reference]
Here, I is an element of the syntactic domain Identifier, which includes all alphabetic names except for the PostText command names (pop, exec, def, etc.), which are treated as reserved words of the language.
The model of the PostText language extends the model of PostFix by including a current dictionary as well as a current stack. A dictionary is an object that maintains bindings between names and values. The commands inherited from PostFix have no effect on the dictionary. The informal behavior of the new commands is as follows:
I: I is a literal name that is similar to an immutable string literal in other languages. Executing this command simply pushes I on the stack. The Value domain must be extended to include identifiers in addition to numerals and executable sqeuences.
def: Let v1 be the top stack value and v2 be the next to top value. The def command pops both values off of the stack and updates the current dictionary to include a binding between v2 and v1. v2 should be a name, but v1 can be any value (including an executable sequence or name literal). It is an error if v2 is not a name.
ref: The ref command pops the top element vname off of the stack, where vname should be a name I. It looks up the value vval associated with I in the current dictionary and pushes vval on top of the stack. It is an error if there is no binding for I in the current dictionary or if vname is not a name.
Consider the following evaulations:
PostText program | Result |
---|---|
((posttext 0 average (add 2 div) def 3 7 average ref exec) '()) |
5 |
((posttext 0 a 3 def dbl (2 mul) def a ref dbl ref exec 4 dbl ref exec add) '()) |
14 |
((posttext 0 a b def a ref 7 def b ref) '()) |
7 |
((posttext 0 a 5 def a ref 7 def b ref) '()) |
"stuck: def" |
((posttext 0 c 4 def d ref 1 add) '()) |
"stuck: ref" |
In an SOS for PostText, the usual PostFix configuration space must be extended to include a dictionary object as a new state component:
CFPostText = Commands X Stack X Dictionary
Suppose that a dictionary is represented as a sequence of identifier/value pairs:
D ∈ Dictionary = (Identifier X Value)*
small-step
for PostText (12 Points) small-step
function for PostFix to handle PostText. Here is a brief list of hints
to get you started:
command
datatype must be extended to contain the new
commands provided by PostText. Once added, these commands can be used
in pattern matching expressions within the interpreter.parse-command
function must be taught how to parse
PostText syntax. Hint: to parse identifiers, the last line should pattern match on
(symbol->sexp sym)
to match all symbols other than
the ones listed above it (e.g. the commands).configuration
datatype
has to change to accomodate PostText. Keep in mind that this data
structure is both the argument and the return type of
small-step
, and is also used by evaluate
and returned by
make-initial-config
(which is the input function of the
operational semantics).
small-step
to handle all these changes to the
core data structures of the interpreter.
check
to a quoted
PostText program and its expected result. If the computation is
supposed to get stuck on the given test case, supply the name of the
command which gets stuck, appended to "stuck: ", as the expected value.
(check '((posttext 1 4 add) '(3)) 7) (check '((posttext 0 1 2 pop swap) '()) "stuck: swap")
small-step
stuck
if no rewrite rule appliessmall-step
to
implement the PostText semantics defined in problem 1check
to validate your
code