Commit 4b64e0b7 authored by Stefan Marr's avatar Stefan Marr

Steady progress with transcription

- fixed imports of basic interpreter tests
- initializing first elements of object system
Signed-off-by: 's avatarStefan Marr <git@stefan-marr.de>
parent 114fa2ab
......@@ -41,6 +41,14 @@ class Universe(object):
return self._nilObject
def _create_bootstrap_method(self):
# Create a fake bootstrap method to simplify later frame traversal
bootstrap_method = self.new_method(self.symbol_for("bootstrap"), 1, 0)
bootstrap_method.set_bytecode(0, Bytecodes.halt)
bootstrap_method.set_number_of_locals(self.new_integer(0))
bootstrap_method.set_maximum_number_of_stack_elements(self.new_integer(2))
bootstrap_method.set_holder(self._systemClass)
def interpret(self, arguments):
# Check for command line switches
arguments = self.handleArguments(arguments)
......@@ -59,7 +67,7 @@ class Universe(object):
if arguments[i] == "-cp":
if i + 1 >= len(arguments):
self._print_usage_and_exit()
self._setup_classpath(arguments[i + 1])
self.setup_classpath(arguments[i + 1])
i += 1 # skip class path
got_classpath = True
elif arguments[i] == "-d":
......@@ -85,7 +93,7 @@ class Universe(object):
return remaining_args
def _setup_classpath(self, cp):
def setup_classpath(self, cp):
self._classpath = cp.split(os.pathsep)
# take argument of the form "../foo/Test.som" and return
......@@ -95,7 +103,7 @@ class Universe(object):
(file_name, ext) = os.path.splitext(file_name)
return (path, file_name, ext[1:])
def initialize(self, arguments):
def _initialize_object_system(self):
# Allocate the nil object
self._nilObject = Object(None)
......@@ -167,6 +175,41 @@ class Universe(object):
self.setGlobal(self.symbol_for("system"), self._systemObject)
self.setGlobal(self.symbol_for("System"), self._systemClass)
self.setGlobal(self.symbol_for("Block"), self._blockClass)
return system_object
def new_array_with_length(self, length):
# Allocate a new array and set its class to be the array class
result = Array(self._nilObject)
result.set_class(self._arrayClass)
# Set the number of indexable fields to the given value (length)
result.set_number_of_indexable_fields_and_clear(length, self._nilObject)
# Return the freshly allocated array
return result
def new_array_from_list(self, values):
# Allocate a new array with the same length as the list
result = self.new_array_with_length(len(values))
# Copy all elements from the list into the array
for i in range(len(values)):
result.set_indexable_field(i, values[i])
# Return the allocated and initialized array
return result
def new_array_with_strings(self, strings):
# Allocate a new array with the same length as the string array
result = self.new_array_with_length(len(strings))
# Copy all elements from the string array into the array
for i in range(len(strings)):
result.set_indexable_field(i, self.new_string(strings[i]))
# Return the allocated and initialized array
return result
def new_metaclass_class(self):
# Allocate the metaclass classes
......@@ -178,3 +221,56 @@ class Universe(object):
# Return the freshly allocated metaclass class
return result
def new_system_class(self):
# Allocate the new system class
system_class = Class(self)
# Setup the metaclass hierarchy
system_class.set_class(Class(self))
system_class.get_class().set_class(self._metaclassClass)
# Return the freshly allocated system class
return system_class
def _initialize_system_class(self, system_class, super_class, name):
# Initialize the superclass hierarchy
if super_class:
system_class.setSuperClass(super_class)
system_class.get_class().set_super_class(super_class.get_class())
else:
system_class.get_class().set_super_class(self._classClass)
# Initialize the array of instance fields
system_class.set_instance_fields(self.new_array_with_length(0))
system_class.get_class().set_instance_fields(self.new_array_with_length(0))
# Initialize the array of instance invokables
system_class.set_instance_invokables(self.new_array_with_length(0))
system_class.get_class().set_instance_invokables(self.new_array_with_length(0))
# Initialize the name of the system class
system_class.set_name(self.symbol_for(name))
system_class.get_class().setName(self.symbol_for(name + " class"));
# Insert the system class into the dictionary of globals
self.set_global(system_class.get_name(), system_class)
def get_global(self, name):
# Return the global with the given name if it's in the dictionary of globals
if self.has_global(name):
return self._globals[name]
# Global not found
return None
def set_global(self, name, value):
# Insert the given value into the dictionary of globals
self._globals[name] = value
def has_global(self, name):
# Returns if the universe has a value for the global of the given name
return name in self._globals
......@@ -38,9 +38,99 @@ class Class(Object):
def get_instance_fields(self):
# Get the instance fields by reading the field with the instance fields index
return self.get_field(self.INSTANCE_FIELD_INDEX)
return self.get_field(self.INSTANCE_FIELDS_INDEX)
def set_instance_fields(self, value):
# Set the instance fields by writing to the field with the instance fields index
self.set_field(self.INSTANCE_FIELD_INDEX, value)
self.set_field(self.INSTANCE_FIELDS_INDEX, value)
def get_instance_invokables(self):
# Get the instance invokables by reading the field with the instance
# invokables index
return self.get_field(self.INSTANCE_INVOKABLES_INDEX)
def set_instance_invokables(self, value):
# Set the instance invokables by writing to the field with the instance
# invokables index
self.set_field(self.INSTANCE_INVOKABLES_INDEX, value)
# Make sure this class is the holder of all invokables in the array
for invokable in self.get_instance_invokables():
invokable.set_holder(self)
def get_number_of_instance_invokables(self):
# Return the number of instance invokables in this class
return self.get_instance_invokables().get_number_of_indexable_fields()
def get_instance_invokable(self, index):
# Get the instance invokable with the given index
return self.get_instance_invokables().get_indexable_field(index)
def set_instance_invokable(self, index, value):
# Set this class as the holder of the given invokable
value.set_holder(self)
# Set the instance method with the given index to the given value
self.get_instance_invokables().set_indexable_field(index, value)
def _get_default_number_of_fields(self):
# Return the default number of fields in a class
return self.NUMBER_OF_CLASS_FIELDS
def lookup_invokable(self, signature):
# Lookup invokable and return if found
invokable = self._invokables_table[signature]
if invokable:
return invokable
# Lookup invokable with given signature in array of instance invokables
for invokable in self.get_instance_invokables():
# Return the invokable if the signature matches
if invokable.get_signature() == signature:
self._invokables_table[signature] = invokable
return invokable
# Traverse the super class chain by calling lookup on the super class
if self.has_super_class():
invokable = self.get_super_class().lookup_invokable(signature)
if invokable:
self._invokables_table[signature] = invokable
return invokable
# Invokable not found
return None
def lookup_field_index(self, fieldName):
# Lookup field with given name in array of instance fields
i = self.get_number_of_instance_fields() - 1
while i >= 0:
# Return the current index if the name matches
if fieldName == self.get_instance_field_name(i):
return i
i -= 1
# Field not found
return -1
def get_number_of_instance_fields(self):
# Get the total number of instance fields in this class
return (self.get_instance_fields().get_number_of_indexable_fields() +
self._get_number_of_super_instance_fields())
def _get_number_of_super_instance_fields(self):
# Get the total number of instance fields defined in super classes
if self.has_super_class():
return self.get_super_class().get_number_of_instance_fields()
else:
return 0
def has_primitives(self):
# Lookup invokable with given signature in array of instance invokables
for invokable in self.get_instance_invokables():
if invokable.is_primitive():
return True
return False
def __str__(self):
return "Class(" + self.get_name().get_string() + ")"
from som.vmobjects.object import Object
class Integer(Object):
def __init__(self, nilObject):
super(Integer, self).__init__(nilObject)
self._embedded_integer = 0
def get_embedded_integer(self):
return self
def set_embedded_integer(self, value):
self._embedded_integer = value
def __str__(self):
return str(self._embedded_integer)
\ No newline at end of file
......@@ -43,4 +43,52 @@ class Object(object):
def set_field(self, index, value):
# Set the field with the given index to the given value
self._fields[index] = value
\ No newline at end of file
self._fields[index] = value
def send(self, selectorString, arguments, universe, interpreter):
# Turn the selector string into a selector
selector = self._universe.symbol_for(selectorString)
# Push the receiver onto the stack
self._interpreter.get_frame().push(self)
# Push the arguments onto the stack
for arg in arguments:
interpreter.get_frame().push(arg)
# Lookup the invokable
invokable = self.get_class().lookup_invokable(selector)
# Invoke the invokable
invokable.invoke(self._interpreter.get_frame(), self_.interpreter);
def send_does_not_understand(self, selector, universe, interpreter):
# Compute the number of arguments
number_of_arguments = selector.get_number_of_signature_arguments()
frame = interpreter.get_frame()
# Allocate an array with enough room to hold all arguments
arguments_array = universe.new_array(number_of_arguments)
# Remove all arguments and put them in the freshly allocated array
i = number_of_arguments - 1
while i >= 0:
arguments_array.set_indexable_field(i, frame.pop())
i -= 1
args = (selector, arguments_array)
self.send("doesNotUnderstand:arguments:", args, universe, interpreter)
def send_unknown_global(self, global_name, universe, interpreter):
arguments = (global_name, )
self.send("unknownGlobal:", arguments, universe, interpreter)
def send_escaped_block(self, block, universe, interpreter):
arguments = (block, )
self.send("escapedBlock:", arguments, universe, interpreter)
def __str__(self):
return "a " + self.get_class().get_name().get_string()
import unittest
from parameterized import parameterized
from som.vm.universe import Universe
from som.vmobjects.integer import Integer
from som.vmobjects.clazz import Class
class BasicInterpreterTest(unittest.TestCase):
@parameterized.expand([
("MethodCall", "test", 42, Integer ),
("MethodCall", "test2", 42, Integer ),
("NonLocalReturn", "test", "NonLocalReturn", som.vmobjects.Class ),
("NonLocalReturn", "test", "NonLocalReturn", Class ),
("NonLocalReturn", "test1", 42, Integer ),
("NonLocalReturn", "test2", 43, Integer ),
("NonLocalReturn", "test3", 3, Integer ),
......@@ -18,16 +23,15 @@ class BasicInterpreterTest(unittest.TestCase):
("Blocks", "argAndLocal", 8, Integer ),
("Blocks", "argAndContext", 8, Integer ),
("Return", "returnSelf", "Return", som.vmobjects.Class ),
("Return", "returnSelfImplicitly", "Return", som.vmobjects.Class ),
("Return", "noReturnReturnsSelf", "Return", som.vmobjects.Class ),
("Return", "blockReturnsImplicitlyLastValue", 4, Integer )
])
("Return", "returnSelf", "Return", Class ),
("Return", "returnSelfImplicitly", "Return", Class ),
("Return", "noReturnReturnsSelf", "Return", Class ),
("Return", "blockReturnsImplicitlyLastValue", 4, Integer )])
def test_basic_interpreter_behavior(self, test_class, test_selector, expected_result, result_type):
u = Universe()
u.setup_classpath("Smalltalk:BasicInterpreterTests")
actual_result = u.interpret(test_class, test_selector)
actual_result = u.execute_method(test_class, test_selector)
self._assertEqualsSOMValue(expected_result, actual_result, result_type)
......@@ -36,7 +40,7 @@ class BasicInterpreterTest(unittest.TestCase):
self.assertEquals(expected_result, actual_result.embedded_integer)
return
if result_type is som.vmobjects.Class:
if result_type is Class:
self.assertEquals(expected_result, actual_result.name.string)
return
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment