Differences from Squirrel language
Squirrel language was used a lot in all Gaijin projects since 2006. Gaijin used Lua before but found Lua too unsafe and hard to debug, and also hard sometimes to bind with native code due to lack of types. It is well known and there are lot info on this in the Internet. Squirrel has not only C++ like syntax (which is helpful for C++ programmers sometimes), but also has type system much closer to CC++ code. It also has safer syntax and semantic. However during heavy usage we found that some features of language are not used in our real codebase of millions of LoC in 20+ projects on 10+ platforms. Some language features were also not very safe, or inconsistent. We needed stricter language with tools for easier refactor and support. And of course in game-development any extra performance is always welcome. Basically in any doubt we used ‘Zen of Python’.
We renamed language from Squirrel to Quirrel to avoid ambiguity.
New features
Language syntax
Template strings:
$"Hello {name}"for string interpolationDocumentation strings:
@@"docstring"for inline documentationCompiler directives:
#pragmafor configuring compiler optionsType method access:
obj.$methodandobj?.$methodfor explicit access to type default delegatesNumeric literal separators:
1_000_000for better readabilityDestructuring assignment for unpacking values from arrays and tables (
let {a, b} = table)ES2015-style shorthand table initialization:
{x, y}instead of{x=x, y=y}Named functions in expressions:
let foo = function foo() {}Null-safety operators:
?.- Null-conditional member access:obj?.property?[- Null-conditional indexing:arr?[index]?(- Null-conditional function call:func?(args)??- Null-coalescing operator:value ?? default
New keywords:
let- Immutable binding declaration (alternative tolocal)global- Explicit global variable declarationnot in- For membership testing, alternative to!(x in y)static- For evaluating expressions once on first access and memoizing the resultconstcan also be used for inline declaration inside expressions
Static analysis
Comprehensive static analyzer 90+ diagnostic checks (including reporting unused variables, functions, imports, parameters, unreachable code detection, type mismatches and null safety violations, data flow analysis, scope and visibility checks and more)
Compiler architecture
AST-based multi-pass compilation (vs. Squirrel’s single-pass direct compilation)
VM architecture
Class method hinting for performance optimization (approaching LuaJIT non-JIT performance)
Added tracking of changing values of container fields (saving the stack of code that modifies the values)
Type system & immutability
Immutability support:
freeze()function for protecting references to instances, tables, arrays from modificationletkeyword for non-assignable bindings
Pure function marking for optimization hints and use in constants
Standard library
Base library additions:
println(),errorln()- print/error with newline variantsprint(),println(),error(),errorln()accept arbitrary number of parameters to printfreeze(obj)- return an immutable reference to an object (alsogetobjflags(obj)to get object flags (e.g., SQOBJ_FLAG_IMMUTABLE) andis_frozen()default delegate for container types.deduplicate_object(obj)- deduplicate table/array contents for memory efficiencyEnhanced
assert()- accepts closures for lazy message evaluationSupport for
call()/acall()delegates for classesSupport negative indexes in
array.slice()andstring.slice()
String default delegates:
Added:
.hash(),.contains(),.hasindex(),.subst(),.replace(),.join(),.concat(),.clone(), plus moved from stdlib to default delegate:.strip(),.lstrip(),.rstrip(),.split(),.split_by_chars(),.escape(),.startswith(),.endswith()(some are shared with the string stardard library)Changed:
.find()renamed to.indexof()to reduce ambiguity
Table default delegates:
Added:**
.each(),.reduce(),.findindex(),.findvalue(),.topairs(),.swap(),.__merge(),.__update(),.replace_with(),.hasindex(),.hasvalue(),.clone(),.is_frozen(),.getfuncinfos()Removed:
.setdelegate(),.getdelegate()
Array default delegates:
Added:
.each(),.findindex(),.findvalue(),.contains(),.hasindex(),.hasvalue(),.totable(),.replace_with(),.swap(),.clone(),.is_frozen()Changed: *
.find()renamed to.indexof()*.append()and.extend()now accept multiple argumentsRemoved:
.push()(use.append()instead)
Standard library modules:
Added: datetime, debug, and serialization modules
String module: Functions moved to string default delegates for convenience (still available as module functions)
Module system
Support for modules. Two APIs - runtime (require() function) and compile-time (import statement)
Removed features
Language features
Octal number literals - was error-prone,
0123syntax now produces an errorComma operator - removed from expressions (was never used, but error-prone)
Class and member attributes -
</and/>XML-style attribute syntax removed (were never used in real code)#single line comments - only//comments supported now (consistency - “one obvious way”)rawcallkeywordPost-initializer syntax - removed due to ambiguity (especially with optional commas)
class::methodsyntax sugar - adding table/class methods this way disallowed (as There should be one – and preferably only one – obvious way to do it). Declare methods in-place or explicitly add slots with<-operator.
Base library functions
The following functions were removed for security, simplicity, or architectural reasons:
setroottable(), setconsttable(), seterrorhandler()
Changes
Scoping and declarations
Functions and classes are locally scoped
Constants and enums are locally scoped by default - use
globalkeyword for global declarationsglobalkeyword required for declaring global constants and enums explicitlyReferencing the closure environment object (
this) is explicit - no implicitthislookupUsage of the root table is deprecated
No fallback to global root table for undeclared variables - must use
::operator to access globals (if root table is enabled at all)
Compilation Model
AST-based compiler (vs. Squirrel’s single-pass direct bytecode generation):
Multi-pass compilation with intermediate AST representation
AST optimizations (closure hoisting, constant folding)
Static analyzer operates on AST
C/C++ Binding API
Human-readable function type declarations:
Replaces cryptic type masks (
"ac.") with readable syntaxExample:
split_by_chars(str: string, separators: string, [skip_empty: bool]): array
Non-stack-based C APIs for better performance
Class declaration syntax
Python-style class inheritance:
class Baz(Bar)instead ofclass Baz extends BarClass
extendskeyword removed in favor of parentheses-based syntax
Operators
deleteis deprecated: * Now configurable via pragmas:#forbid-delete-operator/#allow-delete-operator* Recommended to usetable.$rawdelete()ortable.rawdelete()instead
Default delegates
.find()renamed to.indexof()for arrays and stringsarray.append()andarray.extend()now accept multiple argumentsarray.filter()argument order changed to match map/reduce conventions (and in certain degree the one offoreachby making key argument optional)array.filter(),array.map()andarray.reduce()callbacks optionally accept the reference to the container being processed (instead of passing it viathis)getinfos()renamed togetfuncinfos()and now works on callable objects (instances, tables)
Error handling
Structured diagnostics with severity levels
Error callback with source location, diagnostic ID, and extra information
Configurable static analyzer warnings
Possible future changes
See RFCs page