Angel “Java” Lopez on Blog

December 22, 2008

AjTalk: my Smalltalk-like interpreter project updated

Filed under: .NET, AjTalk, C Sharp, Smalltalk — ajlopez @ 7:30 am

I committed new code to my Google code project AjTalk:

http://code.google.com/p/ajtalk/

The previous version was described in my post

AjTalk- a Smalltalk-like interpreter

It’s an interpreter written using C#. This post describes the work in progress in this solution:

There are interfaces and base classes to support objects and classes:

Every method is compiled to bytecodes. Block and Method are the classes that contains bytecodes programs:

Blocks are anonymous code block. A Method has name and belongs to a class.

The bytecodes are described in an enumeration:

using System; namespace AjTalk { public enum ByteCode : byte { Nop = 0, GetVariable = 1, SetVariable = 2, GetArgument = 3, SetArgument = 4, GetConstant = 5, GetLocal = 6, SetLocal = 7, GetClassVariable = 8, SetClassVariable = 9, GetGlobalVariable = 10, SetGlobalVariable = 11, GetBlock = 12, GetSelf = 20, GetClass = 21, GetSuperClass = 22, NewObject = 23, Pop = 24, ReturnSub = 25, ReturnPop = 26, Add = 40, Substract = 41, Multiply = 42, Divide = 43, Send = 50 } }

In this committed version, there is support to create and access .NET objects. You can write

@System.IO.FileInfo new: ‘aFile.txt’

to create a new .NET object (@ is used to mark a name with special chars like ‘.’).

In this version, I added support to load .st files. It’s not an standard .st file: I will use this files to define the initial class library. Now, there are used only in tests. An example:

nil subclass: #Figure instanceVariables: 'x y' !Figure methods! x ^x ! y ^y ! x: newX x := newX ! y: newY y := newY ! ! Figure subclass: #Rectangle instanceVariables: 'width height' !Rectangle methods! width ^width ! height ^height !

 I bootstrapped the system with a global nil variable. In the loader tests, the nil variable is bound to understand messages like subclass: and ifFalse:.

I changed the unit test to use VS test support. All in green:

The code coverage is good, 84.29%:

I added support to blocks in method, but there is no parameter support, yet:

nil ifFalse: [GlobalName := 'foo']

Next steps

The proposed roadmap is:

- Add more .NET object support

- Write the initial class in base library

- Implements Class, Metaclass, Behaviour “a la” Smalltalk-80

- Load initial library in console application

- Invoke AjTalk from .NET application

- Implements indexed variables

- Extends the bytecode set

As usual, I had fun written this code!

Angel “Java” Lopez
http://www.ajlopez.com/
http://twitter.com/ajlopez

July 14, 2008

AjTalk: a Smalltalk-like interpreter

Filed under: .NET, AjTalk, C Sharp, Smalltalk, Software Development — ajlopez @ 8:52 am

Weeks ago, I was working on my open source project AjTalk, an Smalltalk-like interpreter, written in .NET (using C#), and now, I want to present the status of that work. For years, I was interested in Smalltalk development, altought only in my free time, never as a professional developer. It’s not the first time I wrote such kind of interpreter (my first one was written in the eighties, and it was very simple: no garbage collector, only text commands), but this time I feel it could become a very complete implementation.

Current version is minimal, but it is taken form. The idea is to have dynamic objects, like in Smalltalk, and, at some point, add prototypes a la Self. The objects and the interpreter would access full .NET framework and other libraries, as I did in my AjBasic interpreter (included as part of AjGenesis code generation project).

The very initial version is now published at Google Code:

http://code.google.com/p/ajtalk/

The solution

I’m using Professional VS 2005. There are four projects in the solution.

AjTalk is the main project, a class library containing the core of the system.

AjTalk.Test01 and AjTalk.Test02 are console applications, only for manual tests.

AjTalk.Tests contains the unit tests, written using NUnit framework 2.2.8.

Most of the core system consists of interfaces, defining the base behaviors, and classes, implementing such interfaces.

The object

The base is to have an interface to represent any object:

using System; namespace AjTalk { public interface IObject { IClass Class { get; } object this[int n] { get; set;} object SendMessage(string msgname, object [] args); } }

I could make a message an object (of type Message), but for now, the message is only a name, and an array of arguments.

The index this[int n] access the intance variables. Each value in AjTalk can point to any .NET object, not only the ones that implements IObject. In this way, I can manage int, long, String, DataSet, from other IObject objects.

The class

I’m implementing a simple IClass interface, without distinguing between class, behaviours, and other classes, as in the classic Smalltalk. I’ll separate these classes in a future version. For now, I’m managing only a interface:

using System; using System.Collections; using System.Collections.Generic; namespace AjTalk { public interface IClass : IObject { IClass SuperClass { get; } string Name { get; } void DefineClassMethod(IMethod method); void DefineInstanceMethod(IMethod method); void DefineClassVariable(string varname); void DefineInstanceVariable(string varname); IObject NewObject(); IMethod GetClassMethod(string mthname); IMethod GetInstanceMethod(string mthname); int GetClassVariableOffset(string varname); int GetInstanceVariableOffset(string varname); } }

There is a dictionary of class and instance methods, and lists of class and instance variable names. It doesn’t have support for indexed variables, yet. IClass interface is now implemented in a BaseClass class.

The method

There is an interface implemented in Method class:

using System; namespace AjTalk { public interface IMethod { string Name { get; } IClass Class { get; } object Execute(IObject receiver, object [] args); } }

The concrete Method class, has an Execute method:

public object Execute(IObject receiver, object[] args) { return (new ExecutionBlock(receiver,receiver,this,args)).Execute(); }

Execution blocks have local variables and arguments. The Execute of an execution block takes the instructions (bytecodes) from “compiled” methods, and then it executes them. Here, I took a departure from Smalltalk ideas: the execution block is not an AjTalk object. In this way, I could run this interpreter without implementing a lot of base classes. I have to research the advantages and problems that this decision could have in the overall design and implementation.

The bytecodes

I must think about using a tree of objects (as in Interpreter pattern) instead of bytecodes. But in this version, bytecodes are used. These are the basic instruction that my “virtual machine” understands and executes step by step.

There is a bytecode list (an enumeration)

namespace AjTalk { public enum ByteCode : byte { Nop = 0, GetVariable = 1, SetVariable = 2, GetArgument = 3, SetArgument = 4, GetConstant = 5, GetLocal = 6, SetLocal = 7, GetClassVariable = 8, SetClassVariable = 9, GetSelf = 20, GetClass = 21, GetSuperClass = 22, NewObject = 23, Pop = 24, ReturnSub = 25, ReturnPop = 26, Add = 40, Substract = 41, Multiply = 42, Divide = 43, Send = 50 } }

The bytecodes contained in a method, are interpreted and executed by the execution block. An excerpt of that code:

while (ip < method.ByteCodes.Length) { ByteCode bc = (ByteCode) method.ByteCodes[ip]; Byte arg; switch (bc) { case ByteCode.ReturnSub: return null; case ByteCode.ReturnPop: return Top; case ByteCode.GetConstant: ip++; arg = method.ByteCodes[ip]; Push(method.GetConstant(arg)); break; case ByteCode.GetArgument: ip++; arg = method.ByteCodes[ip]; Push(arguments[arg]); break; ….

Test everywhere

The initial code was written in VB.NET. Last year I began to rewrote the original source code to C#. Then, this year I switched to “TDD mind”, so, late but sure, I added NUnit tests:

Bootstraping

I plan to use a text file, with an ad-hoc format, to inject the definitions of the initial classes and objects. Now, in the AjTest.Test02, there is an example of such format in Definitions.txt file:

class Point
variables x y

method
x ^x.

method
y ^y.

class Rectangle
variables point1 point2
class Square Rectangle

Next steps

There is a lot of work to do:

- Complete the hierarchy of base classes (Behaviour, Class, ….)

- More byte codes

- Support of local variables in methods

- Standard file text format

- Access to native .NET objects

- Use from .NET applications

- Define the classes and methods for a minimal implementation

- Serialization/deserialization of memory image

- Support for adding variables to a class with created instances (this is a tough problem)

- Support for become:

- And much more….

But I’m confident on the shape the project is taken. I’m applying “baby steps”, to improve the base code and functionality.

Angel “Java” Lopez
http://www.ajlopez.com/en

Blog at WordPress.com.