L IS FOR LUA

LUA is a programming language based on Lua 5.2, which I made some time ago (in 2014) as a joke. Its purpose was to troll people who mispronounce Lua as an acronym, either in the Lua-L mailing list, or the #lua IRC channel.

LUA introduces a couple changes to the Lua syntax and semantics. Some more interesting, some less. This page explains the differences.

LUA's source is available on Github.

U IS FOR UPPERCASE

As you probably already figured out, all keywords and names in LUA are uppercase. This includes reserved words like IF RETURN FUNCTION and built-in stuff like STRING.MATCH PRINT MATH.SIN. In fact, in recent versions of LUA, using lowercase letters in identifiers is prohibited: you cannot write LOCAL foo, and the only way to access a lowercase field in a table s TABLE["foo"]

A IS FOR ABSURD

In Lua, indices start from 1, in LUA, indices start from... wait for it... -1! That's right, negative one! LOCAL T = {"foo", "bar"} FOR K, V IN PAIRS(T) DO PRINT(K, V) END -- -1 "foo" -- 0 "bar"

Implementing this requires hacking a lot of lua internals, including the hashtable implementations. Unfortunately I lack the understanding of Lua's table.sort function, so TABLE.SORT doesn't really work.

THE INVISIBLE  IDENTIFIER

An interesting feature of LUA is that  is a valid identifier. And by  I mean the empty string. Just like LOCAL FOO=3 you can write LOCAL=3. This allows for some really obfuscated code: ="hello" -- EMPTY="hello" =* -- EMPTY=EMPTY*EMPTY =- -- EMPTY=-EMPTY FOO(,0) -- FOO(EMPTY,0) FOR IN DO END -- FOR EMPTY IN EMPTY DO END

Note that in ambiguous cases, where the invisible identifier can be both present and not present, the latter is preferred. If you want the former, you can always force it with (): PRINT(()) -- PRINT((EMPTY)) =()- -- EMPTY=(EMPTY)-EMPTY ()() -- (EMPTY)()

This feature also required serious mangling of the Lua source, specifically the parser.

SHORT FUNCTIONS

LUA adds new synax for defining functions, influenced by perl. DO body END in expression context is equivalent to the following value: (FUNCTION(, ...) body END)

That is, a function whose first argument is bound to the empty identifier, and the rest is vararg'ed.

SCOPE OFFSETTING

LUA adds a new syntactic construct: @VARIABLE. Which tells the parser to ignore a layer of scoping when looking up a name. For example if you have both a global and a local named FOO, normally the local would shadow the global. In LUA, however, the global will be available through @FOO. Multiple @ signs strip multiple layers of scoping: X = "global" LOCAL X = "upvalue" FUNCTION F(X) PRINT(X) -- "argument" PRINT(@X) -- "upvalue" PRINT(@@X) -- "global" END F"argument"

Note that local shadowing doesn't introduce scope layers, so this will not work as you'd expect: LOCAL X = 1 DO LOCAL X = 2 PRINT(X, @X) END

The inner local shadows the outer, so @X will go straight to the global scope

FUNCTIONAL PROGRAMMING

A fun but complicated feature is that LUA implements some bits of functional programming. The base lib sets a metatable on functions, with a few operators having cool meanings. They are explained with the code below: -- function composition (backwards), multiple return values turn into multiple arguments: FUNCTION __MOD(F, G) RETURN FUNCTION(...) RETURN G(F(...)) END END -- currying: FUNCTION __INDEX(F, VALUE) RETURN FUNCTION(...) RETURN F(VALUE, ...) END END -- letting some arguments "fall" through the function: FUNCTION __ADD(F, OFFSET) RETURN FUNCTION(...) LOCAL RESULTS = TABLE.PACK(F(SELECT(OFFSET + 1, ...))) FOR I = 1, OFFSET DO TABLE.INSERT(RESULTS, 1, (SELECT(I, ...))) END RETURN TABLE.UNPACK(RESULTS, 1, RESULTS.N) END END -- this may seem complicated, but the usage is pretty simple: -- (F + 1)("foo", "bar", "baz", "quux") = "foo", F("bar", "baz", "quux") -- (F + 2)("foo", "bar", "baz", "quux") = "foo", "bar", F("baz", "quux") -- (F + 2)("foo") = "foo", NIL, F() -- argument reordering: FUNCTION __SUB(F, ORDER) RETURN FUNCTION(...) LOCAL ARGS = TABLE.PACK(...) LOCAL REORDERED = {} FOR I, V IN IPAIRS(ORDER) DO REORDERED[I] = ARGS[V] END RETURN F(TABLE.UNPACK(REORDERED, 1, #ORDER)) END END -- again the implementation is more complicated than the meaning: -- (F - {2,1,1,3})("first", "second", "third", "fourth") = F("second", "first", "first", "third") -- (F - {3})() = F(NIL) -- (F - {})("abc", "xyz") = F()

Another addition is a built-in function that mimics operator sections: its name is FOP. I forget what the name means now, but probably something like function-operator. It lets you capture the language's operators in function form: FOP"+" = FUNCTION(A, B) RETURN A + B END

As you can see, the first argument is a string, which determines the operator being used. Strings + - * / % ^ # < > have "obvious" meanings.

= designates the equality operator, _ is unary minus, ; is concatenation, | is logical 'OR', & is logical 'AND', and ! is 'NOT'.

A couple other fop's are better explained with code: FOP"." = FUNCTION(T, K) RETURN T[K] END FOP":" = FUNCTION(T, K) RETURN T[K][T] END -- imagine that T[K] is a function, then via __INDEX it will be partially applied to T, which is the lua class method calling convention FOP" " = FUNCTION(...) RETURN ... END FOP"@" = FUNCTION(F, ...) RETURN F(...) END FOP"$" = FUNCTION(...) RETURN ..., ... END -- by '..., ...' here I mean replicating the entire vararg, not simply taking its first element FOP(none of the above) = FUNCTION() END

These functional features let us write some cool point-free stuff! ESCAPE = (STRING.GSUB - {2, 3, 1})["[^ -~]"][STRING.BYTE % STRING.FORMAT["\\%d"]] PRINT(ESCAPE"foo\nbar") -- foo\010bar

The comments section is closed