Coming from Lua
Both languages are small, embedded, dynamically typed and reference counted, and both are made to be driven from C. The differences are around tables: an array and a table are separate types, indices start at 0, a missing key is an error rather than nil, and no name is global unless you declare it global.
The syntax is the C family: braces, !=, &&, // for a comment.
Values and bindings
| Lua | Quirrel |
|---|---|
local x = 1 | local x = 1 - rebindable; let x = 1 - binds once |
a name with no local is global | there are no implicit globals; every name is declared |
nil | null |
x == nil | x == null |
~= | != |
.. | $"{a}{b}", or concat; + joins too, and the analyzer flags it as w264 |
#s, #t | s.len(), t.len() |
and, or, not | &&, ||, !, and they keep the operand value |
| one number type | integer and float; 7 / 2 is 3, 7 / 2.0 is 3.5 |
// floor division | / on two integers, which truncates; // starts a comment |
-7 % 3 is 2 | -1; the sign follows the left side, as in C |
tostring(x), tonumber(s) | x.tostring(), s.tointeger(), s.tofloat() |
string.format(...) | format(...) from string |
[[two lines]] | @"two lines", a verbatim string with no escapes |
-- comment | // comment or /* ... */ |
Tables and arrays
| Lua | Quirrel |
|---|---|
{ 1, 2, 3 }, keys 1 to 3 | [1, 2, 3], an array, and its first index is 0 |
{ a = 1 } | { a = 1 }, a table, with = inside |
t.k = v on a new key | t.k <- v; plain = writes a slot that is already there |
t.k on a missing key gives nil | it throws; t?.k gives null, and ?? fills in |
t[k] = nil removes the key | it stores null and keeps the slot; t.$rawdelete(k) removes |
#a | a.len() |
table.insert(a, v) | a.append(v), and it takes several values |
table.insert(a, 1, v) | a.insert(0, v) |
table.remove(a, i) | a.remove(i) |
table.remove(a) | a.pop() |
table.concat(a, ",") | ",".join(a), which is a string method |
table.sort(a, cmp) | a.sort(cmp), with @(x, y) x <=> y |
pairs(t) | foreach (k, v in t) |
ipairs(a) | foreach (v in a), or foreach (i, v in a) for the index too |
next(t) | not there; use foreach |
s:sub(2, 3), 1-based and inclusive | s.slice(1, 3), 0-based and end exclusive |
s:find(sub) | s.indexof(sub), and null when the text is not there |
s:gsub(a, b) for plain text | s.replace(a, b), and it replaces every match |
| a Lua pattern | regexp("[0-9]+") from string |
setmetatable(t, mt) | not there; a class carries the metamethods |
mt.__index | a base class, or the _get metamethod |
mt.__add, mt.__tostring | _add, _tostring, on a class |
Control flow and functions
Traps
- Indices start at 0, and the second argument of
sliceis one past the end. Every loop bound and everysubcall must move by one. 0is false. Lua counts onlynilandfalseas false. In Quirrel0and0.0are false too, soif (count)is a different test."",[]and{}stay true.+does not read a number out of a string."10" + 1is"101", not11. Convert with.tointeger(). Every other arithmetic operator throws on a string.- A stored
nullis not a removed key.t.k = nullleaves the slot, and"k" in tis still true. Uset.$rawdelete("k"). - A missing key throws. There is no
nilto test afterwards, so test first within, or read witht?.k ?? dflt. %follows C.-7 % 3is-1here and2in Lua.//is a comment, so a Lua 5.3 floor division drops the rest of the line with no error.- One return value only. A function returns one value. Pack the rest into an array or a table and destructure it.
- A method accesses its own object through
this. There is no implicitselflookup. Inside a method, call another method asthis.other(). - Table order is undefined, and the hash seed changes between runs. Sort the keys when the order is visible.
// indices start at 0, and an array is its own type
let squad = ["alpha", "bravo"]
println(squad[0])
println(squad.len())
// + joins as soon as one side is a string; it never reads a number out of one
println("10" + 1)
println("10".tointeger() + 1)
// a stored null keeps the slot, so removing one needs rawdelete
let ammo = { rifle = 30 }
let key = "rifle"
ammo.rifle = null
println($"after null: len={ammo.len()} has {key}={key in ammo}")
ammo.$rawdelete(key)
println($"after rawdelete: len={ammo.len()} has {key}={key in ammo}")
// % takes its sign from the left side, as in C
println(-7 % 3)Output:
alpha
2
101
11
after null: len=1 has rifle=true
after rawdelete: len=0 has rifle=false
-1What is not there
- metatables on a plain table:
setmetatableandsetdelegateare both gone - multiple return values,
goto,repeat ... until, Lua string patterns - implicit globals, and the root table is deprecated
- integer keys and array parts in one type; an array is a separate type
#as an operator,..as an operator,elseif,then,end,do
See also
- Cheat sheet - the language on two printable pages
- Traps - traps for every user, not only Lua users
- Tables and arrays and Values and types
- Generators and threads - the Quirrel form of coroutines
- Coming from Python, JavaScript, Squirrel