More notes. This article contains a longer more comprehensive list of differences, but I want a more concise list for both my own reference, and for when I’m teaching.
Tables are Objects
In JavaScript and Squirrel, you have two types: Arrays which are indexed with an integer number, and Objects/Tables that are indexed with a string. In JavaScript the string one is called Objects, and in Squirrel one is called Tables. The name (and some of the original code) is borrowed from Lua.
Members of Arrays and Tables in Squirrel are called Slots.
The “this” member of function scope is called the Context or the Environment.
.length is .len()
The meaning of “this” can be changed in a function call
The “in” operator (hasOwnProperty)
To check if a Table has a member, use “in”.
var and local are not the same
In JavaScript, you use the var keyword to declare a variable. Depending on the scope of that variable (in a function, in global scope), the variable is declared there.
In JavaScript, to add a member variable to class, you don’t use var but instead use dot syntax and directly set it. i.e. MyObject.MyNewMember = value;
In Squirrel, you have a keyword local, which is like var, but only applies to function level scope. In Squirrel, Global Scope is actually a table called the Root Table. To add a member to a Table, you use the arrow syntax.
That said, local is allowed in the Global Scope, but it doesn’t add objects to the root table. Instead, like all other locals, it adds them to the local scope (i.e. the stack).
A related topic is Free Variables.
FYI: getstackinfos() returns a Table, see the docs for more info. The locals member is a Table of all local variables. Also the argument to getstackinfos is the call stack level. 0=Current Function, 1=Caller, 2=Caller’s Caller, etc.
Closures
Closures are Functions. Both terms are used interchangeably.
The value of “this” can be changed using the “.call” method of a function. This is called the Environment Object.
An explicit Environment Object can also be bound to a function, forcing all calls to use that environment and ignore the typical value of “this”. This may seem strange, but this whole mechanism will be useful later.
Here’s a discussion on JavaScript closures that may be of interest: https://developer.mozilla.org/en-US/docs/JavaScript/Guide/Closures
JSON Syntax Caveats
One of the main reasons I’m so insistent of Squirrel is because it’s extremely close syntactically with JavaScript. So far, I’ve found a way to do everything I like in JavaScript with Squirrel, but sometimes it’s just works a little differently.
As of Squirrel 3, you’ve been able to use a JSON-like syntax for building objects. Here is a snippet ported over from TOOM:
Unlike JavaScript though, all member names have to be wrapped in strings to use the colon syntax (i.e. in JS, “name” could be just name).
Alternatively, the Squirrel way is as follows:
At the moment of this writing, I’m undecided, but leaning towards the JSON syntax because the hardcoded data will be portable to JavaScript. Function syntax for JavaScript and Squirrel is identical, so porting code between the two may simply be a matter of fixing global scoped things, converting var’s to local’s, and any in’s to hasOwnProperty calls… well, constants/enums/operators aside.
Generated String Indexing
This works the same as JavaScript.
Pseudo Inheritance with Delegates
Squirrel does support classes and inheritance, but it also supports an interesting alternative called Delegates.
Tables can be assigned a Delegate, i.e. a parent Table. Delegates are used to assign Metamethods (i.e. overloaded operators) and default values.
That’s not a very useful output. Using a delegate:
That’s better.
Metamethods (operators) only work using Delegates. So while you are able to add a function called _tostring directly to the Player Object, Metamethods will only fire if they are inside a Delegate.
Default values using Delegates
If we add a member variable to the delegate, the child implicitly gets it.
Finally, adding a value to the child (Player) uses its value instead.
Delegates are like inheritance without classes.
All Basic Types have Delegates
Yes. So says the documentation. That’s how they get their member functions.
Member Function Syntax
typeof operator
Using Delegates, we can change the returned type string.
Even in C code, the value returned by the _typeof metamethod will be a string. Type checking is therefor:
NOTE: Built-in type names returned are lower case strings.
callee() and .getinfos()
callee() returns the currently running function object.
To get information about the current function, you can use the .getinfos() method of any function.
But as you can see above, it returns a table of information. And unlike JavaScript, Table Printing is somewhat uninteresting.
name: The function name.
defparams: The list of default parameter values (if any)
native: Is it a native C function or Squirrel function?
varargs: How many varadic arguments (printf style).
src: What file it comes from.
parameters: Names of the parameters (NOT values, just names).
NOTE: Native C functions return a different set of results. name, native, paramscheck and typecheck.
Missing _tostring pointers
The returned string “(table : 0x00112340)” is actually a sort of error code. It implies that no _tostring member of the delegate is available.
AFAIK, there is no way to get the pointer value of a table from inside Squirrel (on the stack?). I should look in to this some day, but I’m not interested right now.
String Syntax is "” only, not '’
When it comes to strings, Squirrel has roots in C, and opts for the C syntax for string creation. This means “this string” is valid, but ‘this other string’ is not. Squirrel treats '' strings as ASCII values, like ‘A’, which returns the an integer value equal to the character code.
Standalone Contexts
Okay! This is the important part for engine designers that want to scope things to game objects.
Key things to know:
- A Squirrel script file can be thought of as one big function
- The default value of “this” is the root table, but that’s only the default
On that note, I’m going to change the definition of one of the earlier examples.
If this is called as is, the current Environment is the root table, so it will find Scooter in both this and the Root table.
To change the environment, wrap this code in a function (or do the equivalent), bind a different environment, then call the wrapper function. The reason we need to bind first before calling is so that all our references to the new environment correctly resolve to the environment, and not the root table.
In the example above I’m calling Hello() inside the function, but it can now be safely called outside using:
And it will correctly set the Environment to MyEnv
Things to remember:
- local variables inside the MyStandaloneEnv function disappear at the end.
- ::MyVar syntax is for accessing the root table (globals), not the environment.
- Variable search order is local first, then environment, then globals (root/const).
- When I said earlier that bindenv forces the “this” to be what you bind no matter what, what I neglected to say is that it only applies to the wrapping function itself. So any functions defined inside your environment, your this’es will work as expected (member, argument to call, or the current environment). If this makes no sense, just ignore it.