Skip to content

aranega/lua-smalltalk

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

lua-smalltalk

Lua-Smalltalk is a small bootstrapped extensible Smalltalk object model implemented in Lua.

This project is a small proof-of-concept/experiment to try to patially map Smalltalk object model to the table/metatable model of Lua, as well as to give the possibility to experiment with small kernels. It implements a simple metaclass/class model over metatables with a small API to let you easily create object/classes in Lua. It includes a first extremely minimal Kernel where methods are either coded in Smalltalk or in Lua code.

The project also includes a Smalltalk parser written in Lua, and a Smalltalk to Lua compiler that uses the Lua API defined to manipulate the object model (a little bit like the C API for object manipulation in CPython). Consequently, there is no bytecode interpreter or AST interpreter, the Smalltalk code is directly compiled in Lua, which is compiled then in Lua-bytecode and executed by the Lua-VM.

All works with Lua and Lua-JIT, but it's recommended to use Lua-JIT, which seems to deal better with deep recursion (or there is a problem in the non-local return implementation).

Currently is supported:

  • classes/metaclasses
  • add methods coded in Lua in classes (class-side methods are supported also)
  • add methods coded in Smalltalk in classes (class-side methods are supported also)
  • Smalltalk lookup, and primitive doesNotUnderstand support
  • super (using a dedicated function)
  • cascade (using a dedicated function)
  • non-local return (using a dedicated function)
  • blocks
  • Smalltalk to Lua compiler

What's not yet supported:

  • instance variable access from Smatallk code
  • optimized Smalltalk code compilation, currently, every message send is compiled as Lua method call (no ifTrue:ifFalse: rewritten for example)
  • debugger
  • threads

One of the goal would also be to add a minimal graphical interface to extend/code directly the kernel in a Smalltalk environment with support for debugging.

Code Organization

The code is currently organized as such:

  • object_model.lua contains the basis for the object model and the basic Lua API for the object model, as well as a first very minimal kernel.
  • st2lua.lua contains the Smalltalk parser and the Smalltalk to Lua compiler.
  • test_parser1.lua is a simple small script I use to debug the parser. This script relies on inspect.lua (not included here)
  • exp.lua that is just some examples that I use to test the object model

Use the Object Model in Lua

The implementation is still rough on the edges, but it's going step by step towards something easier to use. To use the object model in Lua requires to get access to Object or any class from the kernel in the first place. Those can be accessed from World which contains the classes as well as some literals (nil, true, false, ...).

The simplest way to integrate the object model and have a direct access to the functions is to chain the project to the global env:

 local st = require("object_model")

local is_luajit = (type(jit) == "table")

local function chain(from)
	setmetatable(from, { __index = _G })
	return setmetatable({}, { __index = from })
end

if is_luajit then
	setfenv(1, chain(st))
else
	_ENV = chain(st)
end

Once it's loaded, here is the way to create a simple class and an instance:

local X = Object:subclass("X")
local x = X:new()
print(x)  -- displays "a X"

With a class created, it's possible to add methods in Lua, in Smalltalk, and to add some in the class-side:

X:addMethod("m1_", function(self, other)
    print("Hello from Lua", other)
end)

X:addStMethod([[
m2: other
    Transcript show: 'Hello from Smalltalk (actually Lua) '.
    Transcript show: other; cr
]], { debug = true})  -- the "debug = true" option displays the compiled Lua code

X.class:addMethod("create", function (self)
    return self:new()
end)

local x2 = X:create()
x2:m1_('in Lua')
x2:m2_('in Lua-Smalltalk')  -- keyword names are translated automatically by replacing ":" by "_" in the selector

Binary selectors and keyword selectors are translated automatically. To use them from Lua, you can replace : by _ for keyword selectors, or use the translate_selector(...) function that works for binary selectors and keyword selectors.

Here is how is defined the boolean class and some methods over it:

Boolean = Object:subclass("Boolean")
True = Boolean:subclass("True")
False = Boolean:subclass("False")
True:addStMethod([[
isTrue
    ^ self
]])
True:addStMethod([[
isFalse
    ^ false
]])
True:addStMethod([[
ifTrue: trueBlock ifFalse: falseBlock
    ^ trueBlock value
]])

The function super(...) is used to implement super. As example, here is how printString is defined for Method:

Method:addMethod("printString", function(self)
	return super(self, "printString")() .. " named " .. self.name
end)

To deal with non-local return, inside of a Block, the return_(...) function needs to be used:

X:addMethod("a", function(self)
	print("In a")
	local b = (Block:create(function()
		print("BLOCK EXEC")
		return_("BLOCKRETURN")
	end))
	self:b(b)
	print("In a, end (should not display)")
end)

X:addMethod("b", function(self, ablock)
	print("In b")
	self:c(ablock)
	print("In b, after value (should not display)")
end)

X:addMethod("c", function(self, ablock)
	print("In c")
	ablock:value()
	print("In c, after value (should not display)")
end)

local x = X:new()
print(x:a())
-- => Displays
-- In a
-- In b
-- In c
-- BLOCK EXEC
-- BLOCKRETURN

Here is how to extend the kernel classes with some methods:

World.Integer:addStMethod([[
factorial
	self = 0 ifTrue: [ ^ 1].
	self > 0 ifTrue: [ ^ self * (self - 1) factorial ].
]])

And here is how this code is compiled in Lua. We can see that the access to literals is done through World, which is mandatory to gain access to the API exposed by the Integer inside of the language kernel.

function factorial(self)
	self:_BIN_EQ(World["0"]):ifTrue_((Block:create(function()
		return_(World["1"])
	end)))
	self:_BIN_SUP(World["0"]):ifTrue_((Block:create(function()
		return_(self:_BIN_STAR((self:_BIN_MIN(World["1"])):factorial()))
	end)))
	return self
end

About

Small bootstrapped Smalltalk object model implemented in Lua

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages