Advanced Programming II, Spring 2002

Programming Assignment 3 - An Example Solution


Table of Contents

Introduction

This page gives a solution to the third programming assignment, which involved writing an interpreter for a simple graphics programming language.

Design

The two main design questions are What is a turtle? and How is input handled?

Input isn't all that difficult to handle; the main issue seems to be whether to read input in all at once and checked before doing anything else (which is like batch mode) or to read input a line at a time and handle each line as it's read.

There doesn't seem to be an overwhelming argument for one side of the issue or the other. Reading and processing all the input initially allows for early detection of errors and avoids otherwise unnecessary work, but requires maintaining the input in a data structure (adimittedly, not a big deal, but it's still work). Reading and processing a line at a time is about the same amount of work without the extra data structures, at a cost of potentially processing an illegal input.

With no compelling arguments, go for the simpler solution - read and process input a line at a time.

The design question for the turtle is should it be a class or a set of stand-alone functions? The amount of work required by the turtle in each case is the about same. Because there's only one turtle needed to solve this problem, classes aren't really necessary. However, a class is a nice fit for this problem, so a turtle will be implemented as a class.

The remaining design issues are relatively mild, and can be dealt with as they come up during implementation.

Implementation

Main

main() inputs the start and transformation strings, using them to create a turtle. It then calls do_commands() to deal with the drawing commands. do_command() reads drawing commands from the input stream ins and applies them to the turtle t.

To make things easier to parse, do_command() deletes spaces from the command string. To check that the command string doesn't have extra spaces (as in le ft = 32. 03) it splits the command string at the spaces; there should be at most three pieces after the split (because spaces aren't required, there could be less than three pieces; also, the draw command can legally produce at most two pieces).

is_cmd() returns true if and only if the string str begins with the command cmd. If str does begin with cmd, is_cmd() also returns the associated value in val; the contents of val is undefined if str doesn't begin with cmd.

This code assumes all the space characters have been removed from str.

go_bang() prints the error message in emsg and dies. line, if given, is included in the error message.

The Turtle

The turtle has a simple public interface: it can be initialized with the start, pattern, and replacement strings, and responds to each of the four possible drawing commands. The private interface is a bit more complicated, including member variables for the start, pattern, and replacement strings, as well as the current parameter values and the location stack. The turtle also needs routines to execute a turtle string, move forward, return the top of the location stack, create a turtle string, and reset the turtle location to its initial state. Creating a new turtle is simple. The start, pattern, and replacement strings are stored without spaces to simplify handling. Set the turtle forward, left, and right parameters, specified in units and degrees, respectively. To draw, create a turtle string of the given generation and execute it. Generating a turtle string involves aging the start string the given amount. Aging a string is the left-to-right, non-overlapping substitution of the pattern string with the replacement string. Executing a turtle string is straightforward, given all the helper routines available. Move the turtle the forward distance along the given heading. Ah, trigonometry. Reposition the turtle at its home location in preparation for executing a turtle string. Clear the location stack too. Return a reference to the current turtle location, dying if there isn't one.

String Utilities

The string utilities are as they were for the second assignment with the addition of a new utility which returns a string equal the given string with all the given characters deleted; the default is to delete all space characters from the string.

The struct char_match is an example of a functor, structs whoes instances can be called like functions. Structs are turned into functors by defining the call operator operator ().

This code needs a functor because std::remove_copy_if() accepts a unary prediate, and the character test needs to pass in two arguments: the character and the list of characters to check against. The char_match functor helps to get around this problem by accepting the character list as a constructor argument. The call operator can then be defined as a unary predicate that accepts a single character and compares it against the character list stored as an instance variable of the struct.

Index


This page last modified on 2 April 2002.