Jump to content




LuaLua - OOP Programming Language for CC. NEW - Anonymous Function Syntax


  • You cannot reply to this topic
60 replies to this topic

#41 DigitalMisha

  • Members
  • 9 posts

Posted 04 January 2015 - 06:01 PM

Thank you for your answer!

#42 cdel

  • Banned
  • 496 posts
  • LocationMelbourne, Australia

Posted 06 February 2015 - 09:24 AM

I am really looking into creating full-scale programs with this, kudos to you. +1

#43 ElvishJerricco

  • Members
  • 803 posts

Posted 06 February 2015 - 11:13 AM

View Postcdel, on 06 February 2015 - 09:24 AM, said:

I am really looking into creating full-scale programs with this, kudos to you. +1

=) Thanks. I've made a few. Having require() and classes is a huge help in larger scale projects. My typical format is to have class files that look like this:

-- MyClass.lua
return @class : require("MySuperClass.lua")
	function (init)
		|super init|
		return self
	end
end

Basically just returning anonymously written classes.

Edited by ElvishJerricco, 06 February 2015 - 11:15 AM.


#44 ElvishJerricco

  • Members
  • 803 posts

Posted 03 April 2015 - 12:08 AM

Updated: Migrated to new ownership at Team C^3

#45 DigitalMisha

  • Members
  • 9 posts

Posted 31 May 2015 - 04:46 PM

What about writing that compiler/interpreter/translator for the real LuaVM. You can compile it to Lua bytecode or to Lua code

#46 biggest yikes

  • Members
  • 573 posts

Posted 20 June 2015 - 04:49 PM

Neat.
If only there was a way to check if the LuaLua compiler is enabled from a normal program.

EDIT:
local r, error = loadstring('@"test" = true')
print(error == nil)

Edited by Atenefyr, 20 June 2015 - 04:58 PM.


#47 immibis

    Lua God

  • Members
  • 1,033 posts
  • LocationWellington, New Zealand

Posted 22 June 2015 - 04:50 AM

Why the name LuaLua? Everyone knows you have to call it Lua=Lua+1.

#48 Lupus590

  • Members
  • 2,028 posts
  • LocationUK

Posted 22 June 2015 - 09:41 AM

View Postimmibis, on 22 June 2015 - 04:50 AM, said:

Why the name LuaLua? Everyone knows you have to call it Lua=Lua+1.

That's a bit long, whay not do what C/C++ did, Lua++

Edited by Lupus590, 22 June 2015 - 09:41 AM.


#49 MKlegoman357

  • Members
  • 1,170 posts
  • LocationKaunas, Lithuania

Posted 22 June 2015 - 09:59 AM

View PostLupus590, on 22 June 2015 - 09:41 AM, said:

View Postimmibis, on 22 June 2015 - 04:50 AM, said:

Why the name LuaLua? Everyone knows you have to call it Lua=Lua+1.

That's a bit long, whay not do what C/C++ did, Lua++

Because Lua has no '++' operator. Thus, you'd do 'Lua=Lua+1' :D

#50 ElvishJerricco

  • Members
  • 803 posts

Posted 26 June 2015 - 03:08 AM

View Postimmibis, on 22 June 2015 - 04:50 AM, said:

Why the name LuaLua? Everyone knows you have to call it Lua=Lua+1.

Haha shouldn't it be Lua..Lua?

#51 Creeper9207

  • Members
  • 211 posts

Posted 27 June 2015 - 04:59 PM

I'm a swift developer... *stands in corner*

#52 ElvishJerricco

  • Members
  • 803 posts

Posted 30 June 2015 - 11:17 AM

View PostCreeper9207, on 27 June 2015 - 04:59 PM, said:

I'm a swift developer... *stands in corner*

I too am definitely preferring Swift. Such a great language (even if it's missing some fundamental features, like the ability to define a proper Monad or Functor protocol). But making a Swift-like language for CC would be a challenge.

Edited by ElvishJerricco, 30 June 2015 - 11:19 AM.


#53 tommyroyall

  • Members
  • 136 posts

Posted 01 July 2015 - 12:43 PM

This is amazing! I haven't seen what the CC community has been making in a while but this is very impressive. What influenced the usage of the @ symbol pertaining to OOP however?

Also, has anybody implemented Moonscript yet?

#54 ElvishJerricco

  • Members
  • 803 posts

Posted 02 July 2015 - 05:35 AM

View Posttommyroyall, on 01 July 2015 - 12:43 PM, said:

What influenced the usage of the @ symbol pertaining to OOP however?

Objective-C entirely. It was just a familiar symbol to use in such places.

#55 SquidDev

    Frickin' laser beams | Resident Necromancer

  • Members
  • 1,427 posts
  • LocationDoes anyone put something serious here?

Posted 02 July 2015 - 08:44 PM

View Posttommyroyall, on 01 July 2015 - 12:43 PM, said:

Also, has anybody implemented Moonscript yet?

I do want to implement Moonscript after Tua, but you can always use Vanilla Lua to compile it and it should run fine on CC's Lua.

#56 TechMasterGeneral

  • Members
  • 149 posts
  • LocationUnited States

Posted 03 July 2015 - 03:15 PM

View PostElvishJerricco, on 09 September 2013 - 05:01 PM, said:

LuaLua is available on Team C^3's github here.

LuaLua
LuaLua is an extension of Lua. The compiler is based on the yueliang Lua compiler, which is written in Lua. LuaLua adds:
  • A new function syntax
  • A class system with runtime library for support
  • A properties system for easily declaring setter/getter methods
  • A module system for separating code in different files
LuaLua was built for ComputerCraft. That's the only Lua environment I've dealt with, and I don't expect to deal with many others. But the load.lua file is the only one that should need modification to run properly in any Lua 5.1 environment. LuaJIT will not work because of the differing bytecode.

Usage

To install LuaLua, use Grin-Get.
grin-get install Team-CC-Corp/LuaLua
Then reboot your computer. Now any loaded code will use the LuaLua compiler.

Functions

LuaLua brings a new (optional) syntax to writing and calling functions.

local function (myFunction) -- Creates local function myFunction
	print("Hello, World!")
end

|@ myFunction| -- Calls myFunction

This simplistic demonstration shows that your function name or call is encapsulated inside the brackets rather than followed by them. This is because the method name and parameters are mixed together. You declare a function with one of these types of names by putting parentheses around the name instead of after. Parameters go along with the name. For example.

local function (doSomething:some withThing:thing)
	print(some, thing)
end

|@ doSomething:"dog" withThing:"frisbee"|

This makes it a bit more clear why you would want this. Function names are more descriptive of their parameters this way. Every single parameter is named just by knowing the name of the function. It should be noted that you can still do vararg functions and calls with this. But only in the last named parameter.

local function (thisIsA:vararg func: a,b,c, ... )
	print(...)
end

|@ thisIsA:"vararg" func: 1,2,3,4,5,6,7,8,9|

So every parameter is named, and vararg is still possible. But how are the functions stored internally? That is, if this function were named globally, what would the global key be? For every parameter in the function, there is a colon in the name. The above example would have the name

thisIsA:func:

and that's the string you would have to use to reference it from the global table. In fact, LuaLua actually adds a way to name global variables with symbols like colons in their names by using another new syntax.

@"someGlobalName?can#have^anything)in*it." = 4

The compiler sees the @ followed by a string and parses it as a name. You can even do it with locals. And obviously LuaLua functions.

local function (someFunction:param secondParam:param2)
	return param + param2
end
local x = @"someFunction:secondParam:"(firstParam, secondParam)

LuaLua also adds a new syntax for anonymous functions. It's much less wordy so it looks more lightweight.

somethingThatTakesAFunction(\(p1, p2, ...)
	print(...)
end)

Classes

Classes in LuaLua are very Objective-C inspired. That's where the new function syntax came from! So let's jump right in.

local @class MyClass : LuaObject
	function (init)
		|super init|
		print("MyClass initializing")
		return self
	end
	function (myMethod:param)
		print(param)
	end
end

local obj = ||MyClass new| init|
|obj myMethod:"method!"|

Three things should be apparent from this.

1. Functions stored in a table can also be indexed and called using the new syntax. In a global or local call, the @ represents using the global scope as an object. Here, you actually use an object as the object.
2. Classes are objects! They're just special in that they have code to create an instance of themselves, invoked by calling the "new" method on them.
3. The self variable is equivalent to the object returned by new and init.

But some of the details might be unapparent. For one, every class MUST have a superclass. LuaObject is the only class without one. The superclass is denoted by the expression after the colon after the class name. Classes can be local or global, and even be indexes of tables (@class tData.MyClass : tData.SuperClass). Classes really are just objects you can shuffle around just like anything else. You can even declare them anonymously like functions.

local t = {}
for i = 1, 100 do
	t[i] = @class : LuaObject
		function (test)

		end
	end
end

Since classes are also objects, they can have their own instance methods (that's what the "new" method is, for example).

local @class MyClass : LuaObject @static
	-- static class stuff
	local numberOfInstances = 0
	function (new)
		numberOfInstances = numberOfInstances + 1
		return |super new|
	end

	function (printNumInstances)
		print(numberOfInstances)
	end

end -- end static class

	-- instance object stuff
	function (instanceMethod)
		print("instance!")
	end
end -- end instance class

|MyClass printNumInstances| -- prints 0
local obj = |MyClass new|
|MyClass printNumInstances| -- prints 1
|obj instanceMethod| -- prints instance!

The least obvious thing about this implementation of OOP in Lua is exactly how it works. How is it that when @(test) is declared, it puts it in the method space of the object instead of the global space the class was declared in? In LuaLua, classes are implemented via closures, or functions. Here's an example which doesn't not use the syntactic sugar of @class to declare a class.

local MyClass = |LuaObject subclassWithClassInstantiator:function(self, super)
	-- static class stuff
	local numberOfInstances = 0
	function (new)
		numberOfInstances = numberOfInstances + 1
		return |super new|
	end

	function (printNumInstances)
		print(numberOfInstances)
	end
end andObjectInstantiator:function(self, super)
	-- instance object stuff
	function (instanceMethod)
		print("instance!")
	end
end|

Notice that inside those closures, the static and instance class code are the exact same. When creating a class, you call the subclass method of the superclass. The first argument is a closure function for the static class. The second is for the instances. Before explaining exactly what the static class is (or rather, the metaclass), first it's important to know how these closures are instantiated.

A class has the object instantiator held inside itself. Whenever you call new, an object is created, and essentially the instantiator function has its global environment set to the object, then the function is called. Thus, any globals declared by the function are placed in the object. Of course it's much more complicated than that in reality in order to allow for some of the features of the LuaLua runtime and to allow the super parameter to those functions to work, but you get the gist.

What is this metaclass thing though? Well every object must have a class. And every class is an object. So what's the class for a class? The metaclass! The structure of classes and metaclasses in LuaLua is directly copied from Objective-C. An object of a unique type is instantiated from unique instantation details from its class. Since classes have unique details such as static methods, they have to be instantiated from some other class that is much less unique. The metaclass. All metaclasses are instances of LuaObject's metaclass, which is an instance of itself. Oh my... Worse, that base metaclass is a subclass of LuaObject, which is an instance of that metaclass. It's very complicated so you can research the Objective-C metaclass system if you want to know more. Or just read the runtime.lua file. But the point is, every single class is an instance of some class, and LuaObject is the only class without a superclass. No other exeptions to either rule.

Properties

Just like Objective-C, an owner of a LuaLua object cannot access that object's data directly. All instance data is stored in local variables for the object. Everything available from the object is methods. You must use accessor methods. Fortunately, the @property directive of LuaLua makes this painless. This directs the compiler to create a local variable, a setter, and a getter. As of the latest version, properties can only be created inside classes.

@class MyClass : LuaObject
	@property myProp
	
	function init()
		|super init|
		|@ setMyProp:3|
		print(|@ getMyProp|) -- prints 3
		return self
	end
end

The name of the local variable used by the setter and getter is always an underscore followed by the property name. However, this doesn't matter because you can't even access the local variable. It's closed by the VM immediately after creating the methods, as if their declarations where in a do-end block. But if you want access to the local variable, no problem! It's easy to make that so.

@class MyClass : LuaObject
	@property myProp = myLocalName
	
	function init()
		|super init|
		|@ setMyProp:3|
		myLocalName = 4
		print(|@ getMyProp|) -- prints 4
		return self
	end
end

It simply sets the name to whatever name is after the = sign, and doesn't close it after declaration.

The LuaLua runtime also adds some nice features for accessing properties in objects. The @property syntax is just fancy syntax for a certain method call.

@property a
-- equivalent to
do
	local _a
	|@ setProperty:"a"
		withGetter:function() return _a end
			 named:"getA"
		 andSetter:function(v) _a = v end
			 named:"setA"|
end

This method is an instance method of LuaObject which lets you create a property. What this means is that properties associate a property name with a getter, getter name, setter, and setter name. Whenever indexing an object with a key that is a known property name, the getter is found by the associated getter name, it's called, and the result is returned.

local @class A:LuaObject
	@property(setter=setMe,getter=getMe) prop = _prop
	function (setMe:v)
		print(v)
		_prop = v
	end
	function getMe()
		print(_prop)
		return _prop
	end
end

local obj = ||A new| init|
obj.prop = 3 -- prints 3
print(obj.prop) -- prints 3 twice

When prop is set, the object searches its property map for the property prop, finds the associated setter, which is overwritten in this case with a custom setter, and calls it.

This also shows the attributes system for properties. Currently there are four property attributes you can set.
  • setter=<name>
    The name to associate the property's setter with.
  • getter=<name>
    The name to associate the property's getter with.
  • readonly
    Makes the property read only. Attempting to write it results in error.
  • writeonly
    Makes the property write only. Attempting to read it results in error.
Finally, indexing the properties as globals from within the class will work the same as indexing them from the object or from self.

local @class A:LuaObject
	@property a
	function init()
		|super init|
		
		a = 4
		-- equivalent to
		self.a = 4
		
		return self
	end
end

Modules
LuaLua also adds a nice module system. It works by overwriting os.run and inserting a "require" function in the environment. So any programs run through os.run have access to the module system.

-- MyClass.lua
local @class MyClass : LuaObject
	function (test)
		print("test")
	end
end
return MyClass
-- program.lua
local MyClass = require("MyClass.lua")
local obj = ||MyClass new| init|
|obj test|

How it works should be fairly obvious. A file run through os.run gets a require function which finds a file by the name passed to it and runs it. Whatever the file returns is returned by the require() call. Require looks for the file in the same directory as the file calling require(). So if you have this directory structure:

folder
| program.lua
| subfolder
| | MyClass.lua

program.lua would use require("subfolder/MyClass.lua"), not require("folder/subfolder/MyClass.lua"). And of course if the path passed to require begins with a / or \ it starts at the root directory instead. And obviously whenever a module calls require(), the path is relative to the module, not the program initially run.

Requiring a module twice does not run the file twice. The value returned the first time is saved in a table and then returned every time. But that's only on a per-program basis. That is, if your program exits, any programs run afterwards (or even while yours is still running) won't get the saved result, but a new one from running the file again. This is so that modules can have per-program data instead of global data.


Have fun!

I hope LuaLua is useful to some of you. I spent more time than I'd care to admit making the compiler work. Use it only for good! Or evil. Or whatever.
ew its all objective-cish... :P

#57 SquidDev

    Frickin' laser beams | Resident Necromancer

  • Members
  • 1,427 posts
  • LocationDoes anyone put something serious here?

Posted 03 July 2015 - 05:11 PM

View PostTechMasterGeneral, on 03 July 2015 - 03:15 PM, said:

ew its all objective-cish... :P

That kinda is the point! Though I'm pretty sure even ElvishJerricco agrees the syntax is a bit odd. Little tip though: you can modify the quote, and so don't need to have the entire OP in your quote.

#58 TechMasterGeneral

  • Members
  • 149 posts
  • LocationUnited States

Posted 03 July 2015 - 07:51 PM

View PostSquidDev, on 03 July 2015 - 05:11 PM, said:

View PostTechMasterGeneral, on 03 July 2015 - 03:15 PM, said:

ew its all objective-cish... :P

That kinda is the point! Though I'm pretty sure even ElvishJerricco agrees the syntax is a bit odd. Little tip though: you can modify the quote, and so don't need to have the entire OP in your quote.

Yeah ik i was just lazy.. I'm not an objective c person.. its hard for me to understand

#59 クデル

  • Members
  • 349 posts

Posted 04 July 2015 - 07:10 AM

I really wish I had to thoroughly learn this, god damn!

#60 Creeper9207

  • Members
  • 211 posts

Posted 06 July 2015 - 09:59 PM

View PostElvishJerricco, on 30 June 2015 - 11:17 AM, said:

View PostCreeper9207, on 27 June 2015 - 04:59 PM, said:

I'm a swift developer... *stands in corner*

I too am definitely preferring Swift. Such a great language (even if it's missing some fundamental features, like the ability to define a proper Monad or Functor protocol). But making a Swift-like language for CC would be a challenge.
I would like to make something similar to skript in Lua for beginners, skript is a bukkit plug in that has code like this:
On right click with stick:
-tab-stride lightening on targeted entity
My attempt to make alternative code was endiscript and it flopped instantly
Edit: you have no idea how hard that was to do on a tablet with autocorrect

Edited by Creeper9207, 06 July 2015 - 10:01 PM.






1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users