Wizard

COMP 524: Programming Language Concepts

Spring, 2008
Jeff Terrell
jsterrel AT cs.unc.edu
(919) 962-1791 (office: Sitterson 138)

COMP 524 Program 2 (Evaluator)

With thanks to Felix Hernandez-Campos.

General Instructions

First, review the assignment submission policy in the syllabus. Note that there is no collaboration allowed.

This assignment is due at 11:59pm on Tuesday, April 15th. Submit assignments to me via email. All of your code should be included in two files: 'turtle.py', and 'lsystem.py'. These two files should be the only thing that you turn in. When I am finished grading your assignment, I will email you your grade and any comments that I have. There are a total of 100 points.

Remember: start early! I guarantee my availability during office hours, but not at 11pm the night it is due.

When you turn in your files, there should be no statements at the top level that produce output or require input. Merely provide the required functions and anything the functions need--do not include any code related to your testing or debugging. When I import your functions into another script, I want the import to be silently successful. Thank you.

Warning: your programs must work on the example programs for you to receive credit! If the deadline comes around and they don't work on the example programs, you should take the 5% late penalty and keep working.

My solutions are now available: turtle.py and lsystem.py.

Overview

In this assignment, you will get some practice implementing an evaluator. Along the way, you'll create some pretty nifty line drawings.

Part 0: Graphics in Python

This part does not count towards your grade. It's just background on how to do simple graphics with the Tk library in Python.

Your main function should look something like this:

from Tkinter import *
import sys

def main(argv=None):
  global canvas
  # Initialize the graphical toolkit
  root = Tk()
  canvas = Canvas(root, width=300, height=300)
  canvas.pack(side=TOP)
  #say_hello()

  # parse the Turtle input
  if argv is None:
    argv = sys.argv
  if len(argv) != 2:
    sys.stderr.write('Error: specify the filename to read as input\n')
    sys.exit()

  # Evaluate!
  evaluate(parse(scan(argv[1])))

  # Display the canvas
  # This must come last, after all line-drawing commands
  root.mainloop()

global canvas

# So, main() will only get called if this program is run directly, but not when
# it's imported somewhere else.  Nifty, eh?
if __name__ == "__main__":
  main()

Then, before you call root.mainloop(), you can draw a line with canvas.create_line(X1, Y1, X2, Y2), where (X1,Y1) and (X2,Y2) are the endpoints of the line.

If you're running Mac OS X, to get Tkinter to work, you might need to download X11 and run your Python program from within an Xterm window in X11. If you run into problems getting Tkinter to work, ask me or the list for help.

Part 1: Turtle Evaluator (65 points)

In this part of the assignment, you will implement a simple interpreter for an extended version of the turtle graphics language (Turtle++). Your parser has to follow a recursive descent strategy, and the scanner must use Python's regular expressions library.

Turtle++ is a simple computer language used to control a drawing turtle. The turtle responds to simple orders like DRAW_FORWARD 10 by drawing a line with a length of 10 pixels. The state of the turtle is given by a tuple (x, y, alpha), where x is the horizontal location of the turtle in the canvas, y is its vertical location, and alpha is the orientation of the turtle in degrees. The initial state of the turtle in a canvas of 300 by 300 pixels is (150, 300, 90). The following two sample programs illustrate the semantics of this language:

This code:

VAR step 20
MOVE_FORWARD 150
REPEAT 12
  DRAW_FORWARD step
  ROTATE COUNTERCLOCKWISE 90
  DRAW_FORWARD step
  ROTATE COUNTERCLOCKWISE 90
  ADD step 20
END_REPEAT

...produces this output (source: Felix Hernandez-Campos):

And this code:

MOVE_FORWARD 250
ROTATE CLOCKWISE 198
REPEAT 5 DRAW_FORWARD 200 ROTATE COUNTERCLOCKWISE 144 END_REPEAT

...produces this output (source: ibid):

The LL(1) grammar of Turtle++ is given by the following production rules:

program              ::= statement_list EOF
statement_list       ::= statement statement_list | e
statement            ::= turtle_action | repeat_statement
                      |  variable_declaration | variable_operation
turtle_action        ::= DRAW_FORWARD argument 
                      |  MOVE_FORWARD argument
                      |  ROTATE direction argument 
                      |  SAVE_STATE 
                      |  RECOVER_STATE
direction            ::= CLOCKWISE | COUNTERCLOCKWISE
repeat_statement     ::= REPEAT argument statement_list END_REPEAT
variable_declaration ::= VAR IDENTIFIER argument
variable_operation   ::= MUL IDENTIFIER argument
                      |  ADD IDENTIFIER argument
                      |  SUB IDENTIFIER argument
                      |  DIV IDENTIFIER argument
argument             ::= IDENTIFIER | INTEGER

Note that this is a free format language, so tokens can be separated by one or more whitespaces, tabs and end of line characters. The two literals IDENTIFIER and INTEGER are generated by the following regular expressions:

IDENTIFIER           ::= letter { letter | digit | _ } *
INTEGER              ::= [ + | - ] ? digit { digit } *

Your interpreter must be able to execute any syntactically correct program. Do not worry about elaborate error-checking. I will not give you any illegal Turtle programs.

The semantics of the language is straightforward. Rotation angles are given in degrees. SAVE_STATE saves the current state of the turtle, defined by the tuple (x, y, alpha), and the current set of variables. RECOVER_STATE will restore the state and the variables saved in the most recent call to SAVE_STATE. Note that that these two statements can be nested, and they work in a first-in, last-out fashion. The arithmetic operators receive two parameters, a variable name and an argument. The result of the operation is stored in the variable. For instance,

VAR x 10
ADD x 5

makes the values of x equal to 15.

The interpreter will be invoked with python turtle.py program_name, where program_name is the name of a file containing the Turtle++ source code to be executed. After this, you program should create a visualization of the given turtle program.

Warning: you cannot use Python's turtle graphics module in this assignment.

Program structure

You must implement the scanner, parser, and evaluator individually. Your scan function should accept a filename and return a list of tokens. Your parse function should accept a token list and return a parse tree. (Use recursive-descent parsing.) Your evaluate function should accept a parse tree and return void, with the side effect of drawing the lines as indicated by the given parse tree.

Part 2: Lindenmayer Systems Translator (35 points)

In this part of the assignment, you will implement a translator that, given a definition file specifying a Lindenmayer system (L-System), will generate a Turtle++ program. This program will correspond to a visualization of the L-system at a given depth.

An L-system is a mathematical formalism, invented by biologist Aristid Lindenmayer in 1968, that is used to describe growth patterns, such as the way in which a plant grows. They provide a remarkably compact way of describing fractal structures. L-system descripions consist of several elements:

For instance, the L-System:

Axiom: B
Rules: 
  B -> F[-B]+B
  F -> FF
Angle: 20
Initial Length: 20

at depth 0 results in the string B (the axiom). At depth one, this axiom is replaced using the first rule, resulting in F[-B]+B. At depth two, the first rule is applied to substitute B by F[-B]+B, and the second rules is applied to substitute F by FF. The results is therefore FF[-F[-B]+B]+F[-B]+B. Note that the production rules are applied simultaneously.

The resulting string directly maps to actions in the Turtle language:

The Initial Length field in the L-system definition provides the initial value of Current_Length. The following figures show the expected visualization of the L-System specified above (source: ibid):

Depth 1:

Depth 2:

Depth 3:

Depth 4:

Here are two more examples, thanks to Felix Hernandez-Campos:

Weed:

Axiom: F
Rules: 
  F -> |[-F]|[+F]F
Angle: 25
Initial Length: 150

...produces at depth 4:

Koch Curve:

Axiom: F++F++F
Rules: 
  F -> F-F++F-F
Angle: 60
Initial Length: 9

...produces at depth 3:

Note that a 30-degree rotation was manually added in the last example to center the curve.

Your L-system translator program should be invoked with python lsystem.py input_definition depth output_program, where input_definition is the name of a file containing an L-system definition, depth is the expected depth of the visualization, and output_program is the name of the file where your L-system translator should put the generated Turtle++ program (that produces an image of the input L-system at depth depth).

Note: you are free to parse the L-system specification however you like. You can create a grammar, scanner, and parser if you want, but you do not need to.

program2.php: Last Modified: 04/29/08@18:47:49 | Size: 11635 bytes | View Source Valid XHTML 1.1 Valid CSS