Lab 2: Utility classes for an interpreter January 15, 2015 The goal of this lab is for you to develop a few classes that will be usefull when implementing the tree interpreter in Lab 3. For each of the class, you are given a prototype, a test suite and a command to run to check if your implementation is successfull. 1 The espy interpreter 1.1 Get the code This is the first lab developing the espy interpreter. To conveniently run the test suites on the university computer you should run the following commands: 1 2 module add /home/TDDA69/modulefiles/tdda69 module initadd /home/TDDA69/modulefiles/tdda69 The base code used in the labs is in the directory /home/TDDA69/Labs. To get the code and start working on it, in your home directory: 1 cp -r /home/TDDA69/Labs/espy $HOME This will copy the skeleton for the espy interpreter, you can now find it in the directory $HOME/espy. In the rest of the document and in other lab, we will refer to this directory as dir to espy. 1.2 Code base structure If you look at the code base for the espy interpreter, you will see the following files and directory: • es.py this is the main executable of your interpreter, it is run with the following command: 1 ./es.py [file.js] • ECMAScriptParser this the code of the parser that was generated by antrl4 • antrl4 this is the runtime library used by the parser • Utils this is a directory with usefull classes and functions. You should have a look at it to see what is available • Interpreter this is the directory where you will do most of your work, it should contains skeleton classes that you will need to implement during the labs. 1 For this lab, your focus should be on the followig files in the Interpreter class: • Environment.py skeleton class for an environment for the interpreter • Function.py skeleton class to represent ECMAScript function • Property skeleton class to represent the property of an ECMAScript object In /home/TDDA69/Labs/Lab2/Tests, you will find the source code for the unit tests corresponding to each of the class that should be implementing during this lab. 2 Environment The purpose of this class is to handle the variable. This class contains functions for defining a variable, setting its value and accessing the value. 2.1 1 2 3 4 5 6 7 8 9 10 Interface class Environment: """ Environment class used to define variables. """ def __init__(self, parent = None): """ Initialise an environment. The parent is an other environment where value for variables can be looked up recursively. """ pass 11 12 13 14 15 16 17 def defineVariable(self, name, init = None): """ Create a new variable with the name "name" and the initial value "init". """ pass 18 19 20 21 22 23 24 25 26 def setVariable(self, name, value): """ Set the value of a variable. If the variable is not defined in this environment, it should look in the parent environment. If it is not found in the root environment, it should raise the exception Utils.UnknownVariable. """ pass 27 28 29 30 31 32 33 34 def value(self, name): """ Get the value of a variable. If the variable is not defined in this environment, it should look in the parent environment. If it is not found in the root environment, it should raise the exception Utils.UnknownVariable. """ 2 pass 35 2.2 Run the test tdda69_lab2_tests dir_to_espy environment 1 3 Function This class is intended to help executing functions in the interpreter. 3.1 1 Interface from Interpreter.Environment import Environment 2 3 4 5 6 class Function: ’’’ This class represent a JavaScript function. It is a callable python object. 7 8 9 10 11 It can be used like this: f = Function(["arg1", "arg2"], Environment(), lambda env: print(env.value("arg1") + env.value("arg2"))) f(1,2) 12 13 And it should print 3. 14 15 16 17 18 19 An other example of use would be: f = Function(["arg1", "arg2"], Environment(), lambda env: raise ControlExceptions.ReturnException( print(env.value("arg1") + env.value("arg2")))) print(f(1,2)) 20 21 And it should also print 3. 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 ’’’ def __init__(self, args, environment, body): ’’’ This creates a new function with a set of args (which is an array of string with the name of the variables used as arguments), the global environment used when defining the function and a lambda function defining the body to be called (should take one single argument, which is the environment) ’’’ pass def call(self, that, this, *args): ’’’ Call the function. This function is usefull since in ECMAScript, a function is an object and it can be called with the function "call". For instance: 38 3 function MyFunction(arg) { console.log(arg) } MyFunction.call(2) 39 40 41 42 43 44 In which case, that is a pointer to MyFunction and this to None. But where it becomes tricky is with: 45 46 47 var obj = { member: function(arg) { this.value = arg } } obj.call(2) 48 49 50 In which case that contains obj.member and this contains obj. 51 52 In practice, the that argument can be ignored. 53 54 In other word: * that is the pointer to the object of the function * this is the pointer to the object (equivalent of self in python) * args is the list of arguments passed to the function ’’’ pass def __call__(self, this, *args): ’’’ Call the function. With the this argument. ’’’ pass 55 56 57 58 59 60 61 62 63 64 65 66 3.2 Run the test tdda69_lab2_tests dir_to_espy function 1 4 Property In ECMAScript it is possible to define properties to an object that are actually calling a function. For instance: var obj = { val: 2, get value: function() { return this.val + 1; }, set value: function(v) { this.val = v - 1 } } obj.value = 4 console.log(obj.val) console.log(obj.value) 1 2 3 4 5 6 Will show 4.1 Interface To support this feature in your interpreter, we suggest to use the following class: 4 1 import Utils 2 3 4 5 6 7 8 class ReadOnlyException(Exception): ’’’ Exception thrown when accessing a read only property ’’’ def __init__(self): pass 9 10 11 12 13 14 15 class WriteOnlyException(Exception): ’’’ Exception thrown when accessing a write only property ’’’ def __init__(self): pass 16 17 18 19 20 21 22 23 24 25 26 class Property: ’’’ Define an ECMAScript style property. This should contains three members: * getter a Function that is called when accessing the value * setter a Function that is called when setting the value * this the object to which this property belongs ’’’ def __init__(self): pass 27 28 29 30 31 32 def get(self): ’’’ Get the value or raise WriteOnlyException ’’’ pass 33 34 35 36 37 38 def set(self, value): ’’’ Set the value or raise ReadOnlyException ’’’ pass 39 40 41 42 43 44 45 46 47 48 49 def merge(self, other): ’’’ Merge two properties. ’’’ pass def clone(self): ’’’ Clone a property (useful when creating new objects). ’’’ pass 5 4.2 Run the test tdda69_lab2_tests dir_to_espy property 1 5 Submit your code Before submiting your code to the lab assistant, check that all the tests are working properly: 1 tdda69_lab2_tests dir_to_espy 6