Coming from Squirrel
Quirrel is Squirrel with the ambiguous parts removed. The language grew out of Gaijin's Squirrel codebase. Nearly every change has one goal: a mistake fails at compile time, not at run time. A Squirrel file rarely compiles unchanged, but the compiler names what it rejects and usually names the replacement too.
Read what changed quietly first. The compiler reports everything else.
What the compiler rejects
| Squirrel | Quirrel |
|---|---|
class B extends A { } | class B(A) { } |
# a comment | //; # now starts a compiler directive |
0123 | 0x7B or 123: leading 0 is not allowed, octal numbers are not supported |
delete t.k | t.$rawdelete("k"); the delete operator is off by default |
a.push(v) | a.append(v), and it takes several values |
a.find(v), s.find(sub) | a.indexof(v), s.indexof(sub) |
f.getinfos() | getfuncinfos, which also works on a callable table or instance |
t.setdelegate(d), t.getdelegate() | gone; use a class |
function A::m() { } | declare m in the class body, or A.m <- function() { } before the class is used |
class A { </ x = 1 /> } | class and member attributes are gone |
local a = (1, 2) | the comma operator is gone |
A() { x = 1 } post-initializer | set the slots in the constructor |
setroottable, setconsttable | gone |
seterrorhandler(f) | debug.seterrorhandler |
rawcall | gone |
switch (x) { } | switch is deprecated and off by default; #allow-switch-statement at the top of the file turns it back on |
format("%d", n) | from "string" import format first, or interpolate it as $"{n}" |
f() inside a method, meaning this.f() | write this.f(): there is no implicit this lookup |
What changed quietly
These compile and run. Test a ported file; a build alone does not find them.
filterswapped its callback arguments. Squirrel passes(index, value); Quirrel passes(value, index). This agrees withmapandreduce, and withforeach, where the index is optional. Code that filtered on the index still compiles and now reads the value.- A function or class at file scope is local. It is no longer a slot in the root table, so nothing outside the file can find it by name. Return it from a module; see modules.
constandenumare local too, unless they are declaredglobal constorglobal enum.switchis off by default. Without#allow-switch-statementthe wordsswitch,caseanddefaultare ordinary identifiers, so the block fails to parse.deleteis off by default in the same way, and the compiler reports the replacement.- A callback can take the container as a last argument.
map,filterandreducepass it, sothisinside the callback is no longer the array. array.appendandarray.extendtake several values, so an accidental second argument is no longer ignored.- The static analyzer runs on request,
sq -sa file.nut, and has about a hundred separate checks. It finds unused bindings, unreachable code, suspicious null handling and more. Treat its output as part of the port.
What is new
letfor a binding that cannot be assigned again; it is the default choice.localstill works where a variable changes.- freeze for a table, array or instance that must not be edited, and
is_frozento ask. - The null-safe operators
?.,?[,?()and??. - String interpolation:
$"got {n} of {total}", which nests. - Destructuring:
let [a, b] = pair,let { x, y } = point, the same in a parameter list, and the shorthand table{ x, y }. - A module system with two forms:
require("m")at run time andimport "m"at compile time. Both are on the modules page. .$methodcalls a type method even when a slot has the same name, sodata.$len()is the length no matter whatdata.lenholds.not in,1_000_000, docstrings written@@"text",staticfor a value computed once, type annotations, and compiler directives.printlnanderrorln, and every print function takes several values.- String methods moved onto the type:
strip,split,startswith,endswith,indexof,subst,replace,join,escape,hash. The string module keeps its own copies. - Tables and arrays gained
each,map,filter,reduce,findvalue,findindex,contains,hasindex,hasvalue,swap,replace_with,totable,topairs,__mergeand__update.
How to port a file
- Compile it, and fix what the compiler names, top to bottom. Most of it is
extends,#,push,findand the implicitthis. - Search for
filter(and check the argument order in every callback. - Search for the names that other files read through the root table, and export them from a module.
- Run
sq -saover the file and read the diagnostics. - Change
localtoletwhere nothing assigns the name again. The analyzer points these out.
// filter passes (value, index), which is the other way round
let heavy = [10, 20, 30].filter(@(value, index) value > 15)
println(heavy.len())
// find is indexof, and a miss answers null instead of -1
println([10, 20].indexof(20))
println([10, 20].indexof(99))
// a method reaches its own class through this
class Squad {
size = 2
function report() { return this.describe() }
function describe() { return $"size {this.size}" }
}
println(Squad().report())
// delete is off; rawdelete removes a slot
let ammo = { rifle = 30 }
ammo.$rawdelete("rifle")
println(ammo.len())Output:
2
1
null
size 2
0See also
- Cheat sheet - the language on two printable pages
- Traps - traps for every user, not only Squirrel users
- Bindings and constants -
let,local,const,global - Compiler directives - the
#lines that turn features on and off in a file - Coming from Python, JavaScript, Lua