Neil is a cross-compiling language outputting to Lua, using the Lua interpreter to execute the code.

Why would I want such a language you wonder? Doesn't Lua itself cover it all up?

Well, Lua was originally only designed for configuration and and small addons only, and it serves greatly for that. Neil has first been taken into use when I started to remake Star Story. Yes, all the scripting is done in Neil. Lua doesn't need variable declarations, and not direct OOP support and more things. Not a problem (maybe even perfect) for small projects, but for a game with over 10 thousands line of code, this will lead to many issues. Typos in variable names can really haunt you and are next to impossible to debug, and although you can fake OOP in Lua, it's not handy. This is were Neil comes in. Neil requires variable declaration, has type checking, and full class support.

If you want to try out Neil yourself, you can just download QuickNeil from my Github page.

In order not to break with traditions, Hello World as it will work in QuickNeil

		
			Init
 Print("Hello world!")
End
		
	

Now what is important to note, despite Lua being case sensitive, Neil is case insensitive so "Print", "print" "PRINT" "PrInT" will all be handled the same.

Now what is important to note is that Neil is a procedurial language, everything must be inside functions. Init is a special function which will be executed as soon as the script is successfully loaded and compiled, and the function will after that be disposed.

Let's show you a bit more complicated version of Hello World to show you a bite more of what Neil can do.

		
			readonly string HW = "Hello world"
Void Hi()
   Print(HW)
End

Init
    Hi()
End
		
	

HW is a 'readonly' variable... Basically meaning that once defined it cannot be changed anymore... In classes readonly variables can still be changed by the constructors, but not by any other function.

So let's make this even more complicated, now that we are at it.

		
			Table L

class HW
    ReadOnly String S
    Constructor(String SS)
        S = SS
    End
    Void Show()
      Print(S)
    End
End

Init
  L += new HW("Hello World")
  L += new HW("Hallo Welt!")
  L += new HW("Hallo Wereld!")
  L += new HW("Salut Monde!")
  For H In Each(L)
      L.Show()
  End
End
    
// Hello world in four languages, demonstrated with a class
		
	

Yes folks, Neil will handle this fine and in Lua that would take a lot more code to write, or at least uglier code. Please note that I didn't put {} in table L. That is because Neil will in the base type put the default value upon declaration. An empty table is what goes for a table variable. The number 0 for the numberic types... False for booleans, and an empty string for string variables... All else will be "nil" by default, just in plain Lua.

Speaking of plain Lua, there are multiple ways to enable Neil to interface with Lua.

		
			-- Hello.lua
local hw = {}
function hw.Hello()
    print("hello world!")
end
return hw
		
	
		
			// Main.neil

#use "Hello"

Init
    Hello.Hello() // Outputs "hello world!" as instructed
End
		
	

Can this also be done backwards? Of course!

		
			// Hello.neil

module
   Void Hello()
      Print("HELLO WORLD!") 
   End 
End
		
	
		
			-- Main.lua
Neil = require "Neil" -- This line is NOT required when you use one of my engines designed to use Neil from the start.

local Hello = Neil.Use("Hello.neil")
Hello.Hello() -- Outputs "HELLO WORLD!" as instructed
		
	

Lua can use classes defined in Neil just fine. Now Neil has its own variable system (in order to ensure case insensitivity and type checking and a few more things), most of Lua's default libraries are imported into Neil automatically (if made available by the underlying engine), but if you really have stuff written in Lua Neil cannot access normally never fear...

		
			Init
    Lua.print("This works too!")
End
		
	

The variable "Lua" contains all global variables Lua knows. Both Lua's default globals as the globals made by scripts Lua has run, as globals created by underlying engines, everything.

Now "module" makes sure the functions are just returned as a kind of class in the example above, but if you have global identifiers in a Neil script (they do require the keyword "global" as otherwise Neil will assume it to be a local. This out of the policy you should avoid globals whenever possible. Goes for functions and variables. Classes are always global by themselves, doesn't mean the object made with these classes are). If you want to access global variables defined in Lua you'll have to do it like this.

		
			global readonly HW = "Hello World"
		
	
		
			Neil = require "Neil"
Neil.Use("Hello")
print(Neil.Globals.HW)
		
	

Now there are more advanced features to make exchanging between pure Lua and Neil possible, but I'll leave those for people really serious about using Neil.

Neil also accepts a few quick notations Lua doesn't cover:

		
			Init
 int i = 2
 i++
 print(i) // 3
 i--
 print(i) // 2
 i += 4
 print(i) // 6
 i -= 5
 print(i) // 1
end
		
	

Neil also has limited macro support

		
			#macro __HW__ Hello World
Init
 Print("__HW__") // Outputs Hello World
End
		
	

Static local support

		
			void Demo()
  static int i=0
  i++
  print(i)
End

Init
  Demo()
  Demo()
  Demo()
End

// Out puts 1,2 and 3 on three different lines.
		
	

Switch case

		
			Init
 int i = 4
 Switch i
    case 1
       print("One")
    case 2 
       print("Two")
    case 3
       print("Three")
    case 4
       print("Four")
       FallThrough
    case 5
       print("Five")
    default
       print("Whatever")
  end
end
		
	

I gotta note that "fallthrough" is NOT supported when you run Neil in Lua 5.1 (which is by the way the minimal version you need to run Neil at all). Now usage of "fallthrough" is overall dirty code anyway, but hey, I supported it.

QuickMeta

		
			QuickMeta QM
    index
       Printf("You expected a value from index '%s',Lua.tostring(key))
    end


    newindex
       printf("So you want to assign value '%s' to index '%s',Lua.tostring(value),Lua.tostring(key))
    end


    Len
      return math.random(1,20)

    end
end

Init
   QM.Hi = "Hello"
   QM.Yo = 4
   print(QM.Hi)
   print(QM[100])
   print(#QM)
End
 

		
	

QuickMeta may not solve all things you may wanna use metatables for, but for simple metatables it's perfect.

Oh, and yes, Neil does support printf and sprintf like C (although unlike C sprintf returns the string in stead of modifying one from the pointer you give).

You should note that despite being more tolerant on lower and upper case Neil is by far more restrictive than Lua. If you want the full freedom of Lua, Neil may not be for you, if you wanna avoid some serious issues Lua can cause in big projects, and quicker (and cleaner way) to handle OOP, it would work.

Now if you wanna ask if Neil works in LOVE2D... Technically it should, but you need to take in mind how Neil communicates with pure Lua, since the LOVE2D APIs were never intended to work in Neil only with pure Lua. Does it work to write a World of Warcraft Addon? Same answer... Does it work on <name your Lua based engine here>, same engine... as long as the engine uses Lua 5.1 or higher it SHOULD work, but since I've only used Neil so far in engines I developed myself, I honestly don't know.

The engine I wrote the new version of Star Story is in the Apollo engine. Oh, and if you saw some relation with the name... Yes, since "Lua" means "moon" in Portuguese, and the language even being named after the moon, I named my language after the first man on the moon and my engine to the mission that was all part of.

Hey, Jeroen! May I use your Apollo Engine for my own games?

The engine is open sourced and can be found on GitHub. Does that answer your question already? (if not, the answer is thus "yes"). Now I must note, this is a scheduled article, which I wrote in October 2020. I think I must have covered most stability issues and bugs by the time you read this, but I cannot make any promises the engine will be stable already. If you want me to release my engine though, hey, just tell me, and I may release it even here on Game Jolt with full documentation. Now Apollo was written in C++ and compiled with VisualStudio, and I know that can make it a disaster to compile it for Mac and Linux, still I hope I can still get it to work in at least Linux. But I hope you understand I am not gonna try to get it to work in Linux before Star Story does at least work perfectly (or as far as "perfect" goes in programming, as every program contains at least one bug, but you get the picture) in Windows before I'll try to get it to work on Linux. (Cut me some slack will ya, it's the first time I set something this big up in C/C++, so this was already a challenge on its own. My earlier engines were coded in Pascal, BlitzBasic, BlitzMax and C#. I already did small things in C++, but this was my first serious work in C++).



2 comments

Loading...

Next up

BallPlay future

Game redo on Cynthia Johnson

Killing only brings you so far!

Celebrities (almost) killed by one of their biggest fans

Disney Princesses go to Hogwarts

Why do people wanna be a #moderator? Is it really such a #cooljob?

Current situation on TFT REVAMPED

A few facts and fables about fire.

A #phrase of #wisdom from the French scientist and philosopher #BlaisePascal Two stories, the one tied to this game (new version), and a prequel novel based on this game have this quote. Perhaps you understand why....

Can you see why this pyramid deal could never be solved from the start (regardless what the already removed cards are)?