This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
Antelux's profile picture

[Language] Luva v1.12 - Compiles to Lua

Started by Antelux, 26 December 2015 - 05:10 PM
Antelux #1
Posted 26 December 2015 - 06:10 PM
So, I've always been fascinated by programming languages, virtual machines, and all the like.
As such, I've periodically searched the forum for any alternative programming languages.

Now, there were many interesting programming languages, but many of them weren't a real plausible alternative to Lua.
From the ones I've seen, they often had limited capabilities, or were an extension of Lua itself.

So, I took it upon myself to create an alternative to Lua, which, naturally, would compile to Lua.
I've looked at several other prominent programming languages, and drew inspiration from them, as well from Lua itself to create what I call Luva (pronounced "Luu-va").

You can find the compiler here (pastebin get Kus6yZwV). It probably isn't the most efficient compiler design, but it gets the job done.
The compiler itself is under the Creative Commons Attribution-NonCommercial 4.0 International Public License (a summary of the license can be found here).
Similarly, you can also find it's Github here if you plan to contribute or report bugs. Be sure to give the full error, as well as the code you attempted to use if you are reporting an error.

Here are the arguments you can give the compiler:
Luva <filename/foldername> <output>

If you run Luva without any arguments, you're put into a console that's similar to the Lua program.
Though, it's still buggy.

Giving it just a filename will have the compiler attempt to compile the file.
By default, it will save to 'compiled,' but this can be changed by using the <output> argument.

Lastly, you can supply the compiler with a folder.
What this will do is go through the folder and attempt to compile every file there.
The files will be saved to an identical folder, with the content placing saved.

The default folder name is also 'compiled' unless the <output> argument is given to specify a different location.

Here's what sets Luva and Lua apart:
  • Overloaded functions.
  • Different combinations of repeat/while/until for a more customized loop.
  • 20+ extra operators, such as >>, +=, –, etc.
  • Syntax of tables ({1, 2, 3} becomes [1, 2, 3], and {A = "test", [2] = 30, B3 = 10} becomes [A: "test", 2: 30, B3: 10]
  • then/end keywords replaced with { } (If <condition> { <statement> })
  • Ability to make objects have their own types.
  • Type specification in function declaration.
  • Assigning default values when one isn't given.
  • Switch statements.
  • Preallocation of arrays.
  • Native support for binary numbers.
  • You don't need to end long comments, making it quicker to block off large sections of code.
  • Three-way-comparison operator (<=>). A < B = false, A == B = nil, A > B = true. (Why is it here? Just because.)
  • Identical operator (===), and it's counterpart (~==) to compare tables ([1,2,3] == [1,2,3] is false, [1,2,3] === [1,2,3] is true. Beware that the operator uses lazy index checking).
  • Unless statements.
  • Guard statements.
  • Try, With, and Catch statements.
  • OOP /w inheritance and sub classes.
  • And some other stuff I've probably forgot to list.
And here's the feature(s) that are planned for the future, which I'm still working in:
  • Require function to import APIs.
  • Make code indented.
Here's what the current update, Version 1.12 changes:
Spoiler
  • Classes are now fully implemented.
  • Classes have been much more heavily optimized.
  • Added new 'metamethod' keyword for classes.
  • Optimized the header, only declares variables when needed.
  • Added a console line for quick testing (still sorta buggy).
  • Optimized type checking a bit.
  • Added a new 'unless' keyword, which is similar to 'guard' statements.
  • Added ability to compile an entire folder of files.
  • Fixed being able to use ':' (So, now you can do object:method() for example.)
  • Added ability to use default values (=>) for classes.
  • Added ability to use 'extends' keyword for sub-classes.
  • Added a sort of 'property' type thing for classes (probably not what you're thinking of).
Currently, only one property, '@super.' Used to pass arguments to the super during its creation.
I'll probably have more properties which would make classes be compiled differently, for example.

You can find the tutorials on the language at its Github Wiki. Though, at this moment, it's still incomplete, so do bare with me.

I'd like feedback, more criticism than not, if possible; I'd like to know what you guys think, what you like, and what you dislike (maybe even why? :P/>)
Edited on 14 February 2016 - 02:06 PM
InDieTasten #2
Posted 26 December 2015 - 06:24 PM
+1 looking forward to see more features
Blue #3
Posted 26 December 2015 - 06:35 PM
Looks amazing!
SquidDev #4
Posted 26 December 2015 - 06:40 PM
Every time someone releases a new language I think ' I need to get back to work on mine' and then get distracted by something else fun.
  • The overloading looks nice: though it would be nice to expand it to full pattern matching - Metalua has a decent (though suboptimal) implementation of it.
  • With operators like += does it evaluate the left side multiple times (so foo().x += 1 is shouldn't be expanded to foo().x = foo().x + 1 but local temp = foo() temp.x = temp.x + 1)
  • I like the binary operators!
  • Not sure about new table and boolean operator syntax - Lua's and and or are more complex than &amp;&amp; and || (though I guess Javascript uses them so no harm). The table syntax doesn't allow something like: { [foo] = bar } which is sometimes useful.
Antelux #5
Posted 26 December 2015 - 06:59 PM
Every time someone releases a new language I think ' I need to get back to work on mine' and then get distracted by something else fun.
Heh, I know what you mean.

  • The overloading looks nice: though it would be nice to expand it to full pattern matching - Metalua has a decent (though suboptimal) implementation of it.
Yeah, I would like such a feature as well. I just haven't thought of a good way to do it yet.

  • With operators like += does it evaluate the left side multiple times (so foo().x += 1 is shouldn't be expanded to foo().x = foo().x + 1 but local temp = foo() temp.x = temp.x + 1)
Unfortunately, as of now, it does. I have yet to make the compiler create efficient code like that. However, I would like to add in the ability to do that in the future.

  • I like the binary operators!
Thanks, thought they'd be useful :)/>.

  • Not sure about new table and boolean operator syntax - Lua's and and or are more complex than &amp;&amp; and || (though I guess Javascript uses them so no harm). The table syntax doesn't allow something like: { [foo] = bar } which is sometimes useful.
Tables use '[]' instead of '{}' because of how they would often interfere with statements.
Consider the following below:


if x == 1 { print("yay!") }

The compiler would often try to make the line '{ print("yay!") }' into a table, which of course would error.

With your example of { [foo] = bar }, I agree I will need to find a work around for doing such in my language.
Any thoughts on doing this to overcome the problem?


local foo, bar = 10, 20
local a = ["foo": bar, foo: bar]

As for the boolean operators, they behave exactly like lua's and, or, and nots.
I'm using &amp;&amp; and the other symbols because I wanted to try out how it would feel to program in my language using them.
In my opinion, they are a bit weird, so I've thought about just switching back to using and/or/not.
Edited on 26 December 2015 - 06:01 PM
SquidDev #6
Posted 26 December 2015 - 07:27 PM
With your example of { [foo] = bar }, I agree I will need to find a work around for doing such in my language.
Any thoughts on doing this to overcome the problem?


local foo, bar = 10, 20
local a = ["foo": bar, foo: bar]

I'd forgotten you'd used braces for statements. However raw strings for properties in tables is useful (like { x = 23, y = 23, radius = 10 }). One solution would be to parse the key as an expression and if it is an identifier, then convert it to a string:


x = [
  foo: "string key",
  "bar": "string key",
  (foo): "key from variable",
]

Though I'm not 100% convinced by that either.
Edited on 26 December 2015 - 06:27 PM
Antelux #7
Posted 26 December 2015 - 07:41 PM
Perhaps a colon can be used instead?


local table = [
     foo: "string key",
     :bar: "variable key",
]

Or perhaps a period instead of a colon.
Antelux #8
Posted 26 December 2015 - 09:26 PM
Just added switch statements. For now, they're just if-elseif-else statements in a different syntax. I have an idea of how to make them faster in the future, however.
Creator #9
Posted 26 December 2015 - 10:12 PM
Is Luva on GitHub? Can we help?
Antelux #10
Posted 26 December 2015 - 10:16 PM
At the moment, it isn't. I may put it on Github once it's more fully developed, however.
For now, the help I would appreciate is any criticism you may have while I develop it :)/>.
Edited on 26 December 2015 - 09:17 PM
Creator #11
Posted 26 December 2015 - 10:42 PM
The things I don't like:
  • { } instead of then end
  • tables being declared with [ ] and not with { } because it does not allow for smth = { [foo] = bar }
The things I like:
  • += and company.
Lyqyd #12
Posted 26 December 2015 - 10:53 PM
So you can't use both "2" and 2 as keys?
Creator #13
Posted 26 December 2015 - 10:55 PM
So you can't use both "2" and 2 as keys?

A good point too. I add it to my I don't like it list.
Antelux #14
Posted 26 December 2015 - 11:14 PM
The things I don't like:
  • { } instead of then end
  • tables being declared with [ ] and not with { } because it does not allow for smth = { [foo] = bar }

I chose to use { } in order to help speed up the programming of statements (It feels less tedious to me to use { } rather than then-end).
As far as as tables using [ ] instead of { } go, that's a result of having statements use { }.

Also, it's possible to solve the problem of smth = { [foo] = bar }. I just haven't decided on how it should be done in terms of syntax.

Here's what I'm considering currently:

local smth = [2: "Numerical Index", "2": "String Index", foo: "Variable Index"]
local smth = [[2]: "Numerical Index", 2: "String Index", :foo: "Variable Index"]
local smth = [(2): "Numerical Index", 2: "String Index", (foo): "Variable Index"]
Edited on 26 December 2015 - 11:19 PM
Antelux #15
Posted 27 December 2015 - 04:13 PM
Alright, just made some final tweaks to tables. Here's the syntax for them:


local x, y = 10, 20
local z = x + y
print(z)

local w = [10, 20, 30, 40, 50, 9:90]
print(w[1] + w[2]); w[6] = 60

local v = [A: 20, B: 40, C: 60, "D": 80, "2": 2, 10: 5, :z: w[3], :10 + (30 / 2): "yay!"]
print(v.A, v.B, v.C, v.D, v["2"], v[10], v[z], v[25]) // Prints '204060802530yay!'

// Basically, anything that isn't a string or a number, such as an expression, will need to be in :<expression>:
// You can also do other stuff like this:
local a = [:[1, 2, 3]: 10, [4, 5, 6, [7, 8, 9, [10, 11, 12]]]]
// Just tables in tables as well as indexing by a table.
Antelux #16
Posted 27 December 2015 - 07:15 PM
Made preallocating arrays different in syntax and more useful. Also added ability to more easily define binary numbers. Check the bottom of the main post for more info.
Antelux #17
Posted 29 December 2015 - 03:24 PM
Made more changes:

– Better expression parsing
– Numerical for loops, just like how it's done in lua.
– Semi colons can be used, but are optional.
– Added 'guard' statements

I plan to start working on classes once all the other features I want to get out of the way are done.
They're mostly little things, such as optimizations. But, as it is now, you can pretty much port any Lua code
to Luva fully, and it should work just fine.

EDIT: Just so I can get an idea of things, anyone plan to use Luva?
Edited on 29 December 2015 - 02:25 PM
Creator #18
Posted 29 December 2015 - 08:57 PM
If it has some proper class implementation, maybe. I could help you out with it.
SquidDev #19
Posted 29 December 2015 - 10:26 PM
– Added 'guard' statements

Those look fancy! Would you consider something like try with resources/using blocks:

with x = fs.open("foobar", "r") do
  x.write("foo bar baz")
end
compiles to

local x = fs.open("foobar", "r")
local s, m = pcall(function() x.write("foo bar baz") end)
x.close()
if not s then error(m, 0) end

In fact: the whole try/catch thing would be pretty awesome. The hard bit is capturing returns from these blocks, but Metalua has an implementation of it.

EDIT: Just so I can get an idea of things, anyone plan to use Luva?

We in the forums do suffer from "Not invented here", but I might give it a go.
Edited on 29 December 2015 - 09:26 PM
Antelux #20
Posted 29 December 2015 - 11:22 PM
If it has some proper class implementation, maybe. I could help you out with it.

Anything in mind you'd like to see?

Would you consider something like try with resources/using blocks:

with x = fs.open("foobar", "r") do
  x.write("foo bar baz")
end
compiles to

local x = fs.open("foobar", "r")
local s, m = pcall(function() x.write("foo bar baz") end)
x.close()
if not s then error(m, 0) end

In fact: the whole try/catch thing would be pretty awesome. The hard bit is capturing returns from these blocks, but Metalua has an implementation of it.

Oh, I like the look of that. I think I'll try to give it a go. Guess I'll also have a more in-depth look at Metalua as well.

EDIT: Just so I can get an idea of things, anyone plan to use Luva?

We in the forums do suffer from "Not invented here", but I might give it a go.

Didn't know there was a term for it. Learn something new every day.
Creator #21
Posted 30 December 2015 - 12:21 AM
Classes:


class Example {
private:
foo
foo2
public:
bar
bar2
constructor:
Example (a,b,c) {
foo = a
foo2 = b
bar(c)
}
}

would compile to:


function Example(a,b,c)
  --Private
  local foo
  local foo2
  --Public
  local self = {}
  self.bar = 0 --It exists
  self.bar2 = function(wow)
    --actions
  end
  --Constructor
  foo = a
  foo2 = b
  self.bar(c)
  return self
end
Antelux #22
Posted 30 December 2015 - 01:24 AM
Oh, only public and private variables? I thought you had something more specific in mind. I plan to do those, as well as a few other kinds. As well as a constructor too, of course.
Creator #23
Posted 30 December 2015 - 01:33 AM
Only a basic example.
LeDark Lua #24
Posted 30 December 2015 - 01:37 PM
I would say, don't change the the regular Lua much for faster learning purposes :)/>
Edited on 30 December 2015 - 12:38 PM
Antelux #25
Posted 30 December 2015 - 07:20 PM
Anything in particular that you want to stay from Lua?
LeDark Lua #26
Posted 30 December 2015 - 07:56 PM
Nope, Lua is wonderful the way it is but your introduction to new function styles and stuff is amazing!

EDIT:
One thing:

function test
   (x) {
	  
   }
   (x, y) {
	  
   }
   (x, y, z) {
	  
   }
end
"end" at the end of the function

EDIT2:
Ok now I understood your question…. silly me, I'm half asleep sorry :D/>
Edited on 30 December 2015 - 07:00 PM
Antelux #27
Posted 30 December 2015 - 08:06 PM
I'm not sure if 'end' really 'belongs' for that overloaded function. If I were to implement it, it would be the only one to use end as of now.
Antelux #28
Posted 30 December 2015 - 08:53 PM
As of now, I'm still working on classes. Here's what the current code looks like:


local class Test {
	 // We can define private and public variables like so:
	 private a, b, c = 1, 2, 3
	 public d, e, f = 4, 5, 6

	 // There are also public static and public shared variables.
	 // Static variables are variables that cannot be changed (I know in other languages static means something different, but I feel as though it makes more sense for it to be this way).
	 public static g, h, i = 7, 8, 9
	 // Shared variables are variables that if one object changes it, all other objects have the same changed value. Therefore, the variable is 'shared' between objects.
	 public shared j, k, l = 10, 11, 12

	 // We can also define variables like this. It's a preference thing, really.
	 private
		  one = 1
		  two = 2
		  three = 3

	 public
		  four = 4
		  five = 5
		  six = 6

	 // Common function declaration
	 private function ABC() {
		  // When we refer to private variables, we don't use 'self.' Instead, we write them as they are.
		  print(a, b, c)
	 }

	 public function DEF() {
		  // When we refer to public variables, we do use 'self.' as to refer to the individual object.
		  print(self.d, self.e, self.f)
	 }
}

This is some example code for classes once they are fully complete:

local class Animal {
	 public name = "Unknown"
	 public species = "Unknown"
	 public sound = "Silence"
	 private age = 0

	 private shared totalAnimals = 0
	 public static kingdom = "Animalia"

	 public static function makeSound() {
		  print(self.sound)
	 }

	 public static function getOlder(amount: number) {
		  age += amount
	 }

	 public static function getAge() {
		  return age
	 }

	 public static function getTotal() {
		  return totalAnimals
	 }

	 private function __init() { // Ran every time a new object is created.
		  totalAnimals++ // All objects from this class now have this updated variable.
	 }
}

local Dog = new Animal(species: "Dog", sound: "Bark")
Dog.name = "Stray Dog"
Dog.makeSound() // Prints "Bark"

// We can put the type of the object in () during a class declaration.
local class Cat (feline) extends Animal {
	 public static override species = "Cat"
	 public static override sound = "Meow"

	 private function __init() {
		  super.makeSound() // super refers to the class it extends
	 }
}

local cat1 = new Cat() // Prints "Meow"
if type(cat1) == "feline" { // True, as we assigned the object's type as 'feline'
	 print("I think it's a cat.")
} else {
	 print("Dunno what it is.")
}
print("Number of animals: " ..cat1.getTotal()) // prints "Number of animals: 2"

Also, as a side note, I added a new operator. It's for strings, really.

local str = "test"
str.="ing"
print(str) // Prints "testing"
Edited on 30 December 2015 - 08:10 PM
Creator #29
Posted 30 December 2015 - 09:08 PM
I would change

private
		  one = 1
		  two = 2
		  three = 3

to


private:
		  one = 1
		  two = 2
		  three = 3

because it can be interpreted as


private one = 1
		  two = 2 --undefined
		  three = 3 --undefined

And I would use the self table to refer to public objects, not from public methods.

Static would use metatables, making it VERY slow.

Also, shared variables can be in a shared table, declared outside the function generating the object.
Antelux #30
Posted 30 December 2015 - 09:20 PM
I would change

private
		  one = 1
		  two = 2
		  three = 3

to


private:
		  one = 1
		  two = 2
		  three = 3

because it can be interpreted as


private one = 1
		  two = 2 --undefined
		  three = 3 --undefined

I've made sure it wouldn't interpret it as such.

And I would use the self table to refer to public objects, not from public methods.

The self table is used in order to tell what function you're calling exactly.
Consider the following example:

local function test() {
// Does stuff
}

local class A {
	 public function test() {
	 // Does stuff
	 }

	 public function abc() {
		  test() // Can't tell without 'self' if calling outside function or not.
	 }
}

Static would use metatables, making it VERY slow.

These classes are designed with power over performance in mind.
However, there have been optimizations made to make sure that if you don't use static or shared, it won't use metatables.

Also, shared variables can be in a shared table, declared outside the function generating the object.

I know. That was already being done. Thanks for the reminder, though.
Edited on 30 December 2015 - 08:23 PM
Creator #31
Posted 30 December 2015 - 09:30 PM
The self table is used in order to tell what function you're calling exactly.
Consider the following example:

local function test() {
// Does stuff
}

local class A {
	 public function test() {
	 // Does stuff
	 }

	 public function abc() {
		  test() // Can't tell without 'self' if calling outside function or not.
	 }
}


Local scope gets priority over global scope, so the test function inside the class definition would be called. Also, there is no reason to use the self table when using public functions, since public and private functions both get the same environment.

That makes sense?
Antelux #32
Posted 30 December 2015 - 09:32 PM
Local scope gets priority over global scope, so the test function inside the class definition would be called. Also, there is no reason to use the self table when using public functions, since public and private functions both get the same environment.

That makes sense?

Assuming I didn't use self, how would I call test() from the global scope, then?
Creator #33
Posted 30 December 2015 - 09:40 PM
Assuming I didn't use self, how would I call test() from the global scope, then?

Since test is defined as public, to call the test inside the class, you use self.test, and because there is no private self, test would simply be equal to the outside one.
Antelux #34
Posted 30 December 2015 - 09:53 PM
Since test is defined as public, to call the test inside the class, you use self.test, and because there is no private self, test would simply be equal to the outside one.

To me, it just makes more sense to use self.test when referring to the object itself, rather than when referring to an outside source.
Though, with the current set up, if you really wanted to call a function without self, you can just make a private function.


local class Test {
     private function doSomething() {
          // Does stuff
     }

     public function ABC() {
          doSomething()
     }
}

The way classes are created doesn't really allow for what you're speaking about: referring to an outside source using self.
Creator #35
Posted 30 December 2015 - 10:11 PM
I wasn't suggesting referring to an outside source using self. I thought you wanted to refer to the object itself when using public function using self.

TL;DR: Use self to refer to public attributes/methods and use "only variable name" when referring to local stuff.
Antelux #36
Posted 30 December 2015 - 10:13 PM
Oh, my bad then. Well, that's how it's done at the moment.

local class Test {
	 public function ABC() { }
	 public function test() {
		  self.ABC()
	 }
}
Again, it's because of the way classes are structured at the moment.
Edited on 30 December 2015 - 09:13 PM
Creator #37
Posted 30 December 2015 - 10:21 PM
Exactly! That it how it should be done.
Antelux #38
Posted 30 December 2015 - 10:38 PM
And now I'm laughing because I most likely misinterpreted you earlier. Looks like we don't have any disagreements, then. :)/>
Antelux #39
Posted 01 January 2016 - 03:15 AM
After taking a little break from classes, I've implemented try, with, and catch statements. Check the bottom of the main post for more info and their usage.
Quartz101 #40
Posted 01 January 2016 - 04:57 AM
I haz an idea:

use fs.open("/hello", "w") {
 write("Hello!")
 close()
}
Edited on 01 January 2016 - 03:57 AM
Lemmmy #41
Posted 01 January 2016 - 06:51 AM
I haz an idea:

use fs.open("/hello", "w") {
write("Hello!")
close()
}

Using `use` could also make the resource self-closing (close when the scope is closed).
FUNCTION MAN! #42
Posted 01 January 2016 - 02:26 PM
I'd say something more like python's with-as:


with fs.open("/test", "r+") as file {
  file.write("hello")
}

Which would compile into something like:


do
 local _rsx0_file = fs.open("/test", "r+")
 _rsx0_file.write("hello")

 if _rsx0_file.close then
  _rsx0_file.close()
 end
end
Edited on 01 January 2016 - 01:26 PM
SquidDev #43
Posted 01 January 2016 - 03:04 PM
I haz an idea:

use fs.open("/hello", "w") {
write("Hello!")
close()
}
The trouble with this is that scope management is a pain: you don't know if the variable you are trying to access is in the table or in the current scope. Whilst you could solve this with metatables, this introduces more cognitive load for the programmer: what does this produce:?

use aMethod() {
write(foobar)
}
foobar (and write) could either be a local variable or a property on the table.

I'd say something more like python's with-as:


with fs.open("/test", "r+") as file {
  file.write("hello")
}
This is already implemented as:

with file = fs.open("test", "w") {
		 file.write("testing!")
		 file.close()
}
Edited on 01 January 2016 - 02:04 PM
Konlab #44
Posted 06 January 2016 - 12:49 PM
Just a quick question:
I have a table with __call metamethod. I want to call it with one argument (table) and I hate nested brackets, I can call it with tbl{}. Question: is tbl[] it translated to tbl{}?
Edited on 06 January 2016 - 11:52 AM
Antelux #45
Posted 06 January 2016 - 09:11 PM
At the moment, you can't call functions without using (). However, doing tbl([]) would translate to tbl({})

EDIT: An early release of the compiler has been made. Check the main post for details.
Edited on 06 January 2016 - 08:31 PM
Creator #46
Posted 06 January 2016 - 09:55 PM
Would it be possible to have a GitHub repo where we have issues and everybody can help &amp; contribute?
Antelux #47
Posted 06 January 2016 - 09:56 PM
Yeah. I was going to put it there as well. I put the pastebin link for now so others can get to using it quicker. I just have to go soon, so I don't have time to put it on Github yet.
Edited on 06 January 2016 - 08:59 PM
Antelux #48
Posted 07 January 2016 - 01:19 AM
Added a Github for the program, which can be found here for those who wish to contribute or report bugs. :)/>
In the future, the wiki will host the tutorials for using the language.
Edited on 07 January 2016 - 12:19 AM
Antelux #49
Posted 18 January 2016 - 04:29 PM
Sorry that the next update it taking so long. The language isn't dead, It's just that finals are coming up, I need to study, etc. Though, that isn't to say I haven't been making progress. I've mostly been working on the class implementation, so here's another example of what you can do with it.


local class Vector3 (vec3) {
	 public x, y, z = 0, 0, 0
	 private shared: // Faster than regular private as its only declared once.
	       sqrt = math.sqrt
	       floor = math.floor

	 public:
		  function dot(v: vec3) { return self.x * v.x + self.y * v.y + self.z * v.z }
		  function cross(v: vec3) { return new Vector3(x: self.y*v.z-self.z*v.y, y: self.z*v.x-self.x*v.z, z: self.x*v.y-self.y*v.x ) }
		  function length() { return sqrt(self.x*self.x + self.y*self.y + self.z*self.z) }
		  function normalize() { return self * (1 / self.length()) }
		  function round() { return new Vector3(x: floor(self.x + 0.5), y: floor(self.y + 0.5), z: floor(self.z + 0.5)) }

	 metamethod: // Metamethods automatically have '__' added before each one, so metamethod add would be accessed as object.__add
		  function add(v: vec3) { return new Vector3(x: self.x += v.x, y: self.y += v.y, z: self.z += v.z) }
		  function sub(v: vec3) { return new Vector3(x: self.x -= v.x, y: self.y -= v.y, z: self.z -= v.z) }
		  function div(m: number) { return new Vector3(x: self.x *= m, y: self.y *= m, z: self.z *= m) }
		  function mul(m: number) { return new Vector3(x: self.x /= m, y: self.y /= m, z: self.z /= m) }
		  function unm(m: number) { return new Vector3(x: -self.x, y: -self.y, z: -self.z) }
		  function tostring() { return self.x..","..self.y..","..self.z }
}

You might notice I've added a "metamethod" keyword.
This example may not be the final product, however. I've sort of stuck on how I should implement things. It's not that I don't know how to code it, it's just that I'm not sure if I should do features over performance, or vice-versa.
Anyway, I should probably get back to studying. I'll keep updates about the progress of the language.
Edited on 18 January 2016 - 03:31 PM
LeDark Lua #50
Posted 19 January 2016 - 06:32 PM
Tables use '[]' instead of '{}' because of how they would often interfere with statements.
Consider the following below:


if x == 1 { print("yay!") }

The compiler would often try to make the line '{ print("yay!") }' into a table, which of course would error.

I dont know if anyone posted this but you can make like:

if ifFound then
   foundIF = true
end

if found "}" or "{" then
   do stuff...
   foundIF = false
end
Antelux #51
Posted 19 January 2016 - 09:20 PM
I dont know if anyone posted this but you can make like:

if ifFound then
   foundIF = true
end

if found "}" or "{" then
   do stuff...
   foundIF = false
end

That wouldn't work unfortunately, as it isn't recursive.
I also feel as thought using {} for statements and not for tables flows a bit better. Though it may just be me.
Antelux #52
Posted 21 January 2016 - 01:32 AM
So, I've found a sort of compromise between speed and power for classes.
After running a few benchmarks, it turns out that classes are just about as fast, if not as fast, as tables, when it comes to reading, writing, and creation time.
Though, I used a skeleton class for the example, which contained no private variables. I doubt it would slow it down much.
However, when writing to a public shared variable, it's about 3x as slow, so you should probably create a private shared variable and use a function to write to it instead if you need to rely on it's write speed.

Also, objects will have to use object:method() now in order to supply the method with the 'self' table.

One last thing that should be noted is that with this new implementation I have, all static and shared vars, regardless of it being private or public, cannot access regular private variables. Only public and private vars can. I thought this was worth the speed gain, but if anyone thinks otherwise, do lemme know.
Edited on 21 January 2016 - 12:34 AM
Antelux #53
Posted 01 February 2016 - 08:10 PM
I'm very close to releasing the next version, just have to sort out a few bugs here and there.
The metamethod keyword has been changed. It must be used after public, but can now be combined with shared or static.

I've also changed up classes a bit to allow for arguments to be given in a similar fashion to how a function gets them.
Therefore, I've also changed how you declare the custom type of the class's objects.

Here's an example below showing off these changes.

local class Vector3(x: number, y: number, z: number) @vec3 {
     public x, y, z = x or 0, y or 0, z or 0
     private shared floor, sqrt = math.floor, math.sqrt

     // Static and shared functions and variables allow for faster object creation time.
     // Also, you can have a ':' following the declared variables and functions. Though,
     // It should be noted that this is as optional as ';' is for statements.
     public static:
          function dot(v: vec3) { return self.x * v.x + self.y * v.y + self.z * v.z }
          function cross(v: vec3) { return Vector3(self.y*v.z-self.z*v.y, self.z*v.x-self.x*v.z, self.x*v.y-self.y*v.x ) }
          function length() { return sqrt(self.x*self.x + self.y*self.y + self.z*self.z) }
          function normalize() { return self * (1 / self.length()) }
          function round() { return Vector3(floor(self.x + 0.5), floor(self.y + 0.5), floor(self.z + 0.5)) }

     // Of course all these metamethods are accessed with a '__' prefix. For example, function add is accessed
     // as object.__add. However, as said before, we can use 'static' allowing for greater control over
     // metamethods. Naturally, the faster creation time benefit that comes with static follows here as well.
     public static metamethod:
          function add(v1: vec3, v2: vec3) { return Vector3(v1.x + v2.x, v1.y + v2.y, v1.z + v2.z) }
          function sub(v1: vec3, v2: vec3) { return Vector3(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z) }
          function mul(v: vec3, m: number) { return Vector3(v.x * m, v.y * m, v.z * m) }
          function div(v: vec3, m: number) { return Vector3(v.x / m, v.y / m, v.z / m) }
          function unm(v: vec3, m: number) { return Vector3(-v.x, -v.y, -v.z) }
          function tostring(v) { return v.x..","..v.y..","..v.z }
}

Many class optimizations have also been put in, depending on what kind of declarations you use.
When comparing classes in Luva to their Lua output, they're about 2-3x shorter, which means you can program classes faster in Luva.

Anyway, if anyone has any questions, comments or criticism about the above example, do let me know.
Edited on 02 February 2016 - 08:52 PM
Antelux #54
Posted 05 February 2016 - 09:24 PM
I've been thinking about the ability to add default values for classes and functions.
Consider the following code:

local class Object(color: number, size: number, shape: string) {
     public:
          color = color
          size = size
          shape = shape
}

Using the current implementation, I cannot have both optional values and type checking.
If any one of those arguments aren't given, then it will error saying it's not a number.

To solve this, like I said before, I'd like to add the ability to use default values.
Though, the thing is, I'm not sure what I should have for the syntax.

I'm currently considering this:

local class Object(color: number => 1, size: number => 1, shape: string => "square") {
     public:
          color = color
          size = size
          shape = shape
}

Here, color and size default to 1 if not given an argument, and shape defaults to "square" if not given.
Of course, if they are given, the arguments must conform to the type specification.

I'd just like some thoughts on the syntax, as I'm not too sure about it.
Antelux #55
Posted 13 February 2016 - 08:24 PM
After quite some time, I've updated Luva, with its biggest feature being Classes.
For now, you can refer to its Github wiki for tutorials, or you can look at the below example to see what you can do with classes.


local class Entity(name: string => "Unknown", king: string => "Unknown", spec: string => "Unknown", sound: string => "...", test => "boolean") @entity {
     public name, kingdom, species, sound = name, king, spec, sound
     private shared totalEntities = 0

     public static:
          function sayName() {
               print("My name is " ..self.name)
          }

          function makeSound() {
               print(self.sound)
          }

          function getTotal() {
               print("There are " ..totalEntities.. " entities.")
          }

     private function init() {
          totalEntities++
     }
}

local class Animal(name: string, spec: string, sound: string) @animal extends Entity {
     @super(name, "Animalia", spec, sound)

     private shared totalAnimals = 0
     public sound = sound

     public static function getTotal() {
          print("There are " ..totalAnimals.. " animals.")
     }

     private function init() {
          totalAnimals++
     }
}

local class Human(name: string, age: number) @human extends Animal {
     @super(name, "Homo sapien", "Hi there!")

     private shared totalHumans = 0
     private age = age

     public function getAge() {
          print("I'm " ..age.. " years old.")
     }

     public static function getTotal() {
          print("There are " ..totalHumans.. " humans.")
     }

     private function init() {
          totalHumans++
     }
}

local e1 = Entity()
local a1 = Animal("Leo", "Lion", "RAWR!")
a1:makeSound()

local h1 = Human("Bob", 24)
h1:getAge()

e1:getTotal()
a1:getTotal()
h1:getTotal()

/* The output of the program:
RAWR!
I'm 24 years old.
There are 3 entities.
There are 2 animals.
There are 1 humans.
*/
Edited on 13 February 2016 - 07:27 PM
LeDark Lua #56
Posted 13 February 2016 - 08:42 PM
Cool update, gonna try it out tomorrow cuz I'm on the phone.

EDIT: Tried it, loved it. I'm starting to use this language as a Main one.
Edited on 14 February 2016 - 02:10 PM
Antelux #57
Posted 14 February 2016 - 03:17 PM
Glad to hear it. Was hoping at least one person would use the language. :P/>
Anyway, do let me know if you run into any bugs. I don't think there are any, but then again, I usually don't know about any anyway.
They're always hiding until they're found. :P/>
Antelux #58
Posted 04 March 2016 - 11:16 PM
So, here's a progress report:

I've been working on re-writing the entire program.
Right now, the size is a lot smaller, and I've added quite a few things.
Here's what you can expect in the next version:
  • Generally cleaned up, rewritten code.
  • for-in loops are now possible.
  • functions can now use default values.
  • Using () with function creation is now optional.
  • catch now works with non-string values.
  • Re-added switch statements.
  • Compiled code is now indented.
  • Plenty of minor bug-fixes.
And here's what I'd like to add to this version, but I'm still working on:
  • Making two kinds of switch statements. The first one, 'switch', would act as a table. The second one, 'switch!', would simply be a short-hand version of writing if-then statements.
  • Add the 'extension' keyword for extending classes.
  • Making overloaded functions more flexible (Such as multiple functions that have the same amount of arguments, but different types. Also working on this at the moment.)
  • A type of operator chaining. For example, 1 < 2 < 3 == 3, 4 > 2 < 6 ~= 5, etc. I'm just not sure about what syntax I should use for it.
  • More code optimizations in general.
It'll be quite the update, I suppose. If anyone else has any ideas they'd like to chip in for said upcoming update, do say them.
Edited on 20 March 2016 - 01:26 PM
Konlab #59
Posted 20 March 2016 - 09:17 AM
Question: Is this pure Lua? (Is it possible to run it outside CC?)
Idea: Custom compiler parameters that can add custom keywords/etc. Or an API that can do this.
I really like this project. I will definitely use it when I return to lua side of computercraft.
Antelux #60
Posted 20 March 2016 - 02:18 PM
Well, the compiled output is pure Lua, so it is possible to use it in places outside of ComputerCraft, such as for LÖVE.
Also, can you give an example of the custom keyword thing you're thinking about?

EDIT: Also, I've made arithmetic and binary assignment operators a bit more useful:

local x, y, = 10, 20
x, y += 30
// The above is translated into x, y = x + 30, y + 30
// Can be used with any amount of vars, and with things such as *=, >>=, &amp;=, .=, etc
Edited on 20 March 2016 - 01:22 PM
Konlab #61
Posted 21 March 2016 - 09:41 AM
Well, the compiled output is pure Lua, so it is possible to use it in places outside of ComputerCraft, such as for LÖVE.
Also, can you give an example of the custom keyword thing you're thinking about?

EDIT: Also, I've made arithmetic and binary assignment operators a bit more useful:

local x, y, = 10, 20
x, y += 30
// The above is translated into x, y = x + 30, y + 30
// Can be used with any amount of vars, and with things such as *=, >>=, &amp;=, .=, etc
But I need to compile in CC, right?
About custom keywords I meant like adding interfaces, delegates, events etc. Or generics or other things I want through an API
Edited on 21 March 2016 - 08:41 AM
Creator #62
Posted 21 March 2016 - 10:02 AM
I think that you only need to compile in Lua.
Antelux #63
Posted 21 March 2016 - 09:20 PM
The only thing it really uses from CC is the fs and term APIs. But, that's only if you use the Luva.lua file (From github).
Using the other two files, Lexer.lua and Compiler.lua, should be fine in a standard Lua environment as far as I know.

Also:
About custom keywords I meant like adding interfaces, delegates, events etc. Or generics or other things I want through an API

I don't really think interfaces are needed, as, if you were to have this C# example (found here):

using System;

public interface IGenerate
{
	int Generate();
}

// Dependencies
public class KnownNumber : IGenerate
{
	public int Generate()
	{
		return 5;
	}  
}

public class SecretNumber : IGenerate
{
	public int Generate()
	{
		return new Random().Next(0, 10);
	}
}

class Game
{
	public Game(IGenerate generator)
	{
		Console.WriteLine(generator.Generate())
	}
}

new Game(new SecretNumber());
new Game(new KnownNumber());

You could just make it become:

local SecretNumber = [Generate: function() { return math.random(0, 10) }]
local KnownNumber = [Generate: function() { return 5 }]

local class Game(generator: table) {
	 private function init {
		  guard type(generator.Generate) == "function" else { error("Table must have a Generate function!") }
		  print(generator.Generate())
	 }
}

Game(SecretNumber)
Game(KnownNumber)

Events (or Delegates):

local class Event {
	 private:
		  subscribers = []
		  subs = 0

	 public static:
		  function add(f: function) {
			   subs++; subscribers[subs] = f; return subs
		  }

		  function rem(n: number) {
			   if n == subs { subs-- }; subscribers[n] = nil
		  }

		  function clr {
			   for i = 1, subs { subscribers[i] = nil }; subs = 0
		  }

	 public static metamethod:
		  function add = self.add
		  function sub = self.sub
		  function call(...) {
			   for i = 1, subs { subscribers[i](...) }
		  }
}

I also feel as if Generics aren't really needed either, since type specification is optional in Luva.
Feel free to call me out if my examples or something I said here is wrong or something, since I don't really use interfaces, delegates, etc.
Edited on 21 March 2016 - 08:33 PM