@R:Programming @SW:Squirrel @D:Introduction to the Squirrel scripting language @T:Speedy Squirrel @C:http://www.123rf.com/photo_3618236_bored-with-technology.html @C: or a (red) squirrel @V:If you are looking for a scripting language with a lean interpreter that is easy to integrate with your applications, your search will probably take you to Lua. But a tried-and-trusted competitor by the name of Squirrel has been hiding out in Lua's shadow for many years. @A:By Tim Schürmann @L:More than eight years ago, Alberto Demichelis was working for the popular German games developers Crytek and tackling his first game, Far Cry, the program logic of which was to be formulated in the Lua scripting language. When he tried to integrate the results, Alberto Demichelis discovered an issue with automated memory management (garbage collection). After trying some workarounds he finally pulled the rip-cord and promptly wrote his own scripting language. He wanted its interpreter to be like Lua's: small, lean, and easy to integrate with any application. The results were Squirrel [1], whose interpreter currently weighs in at 442KB. @ZT:Old Friends @L:Squirrel is an imperative, object-oriented scripting language whose syntax looks like a mix of C, C++, Java, Javascript, and Python. Squirrel copied tables from Lua, a flexible data structure for nearly everything. And it also has a garbage collection feature that autonomously handles memory management. The bottom line is that Squirrel combines an easy and familiar syntax with the benefits of Lua. The reference implementation of the interpreters was placed under the MIT license in version 3.0. The source code is thus not just open source, the interpreter can be used free of charge for commercial projects. @L:Squirrel is currently mainly used for computer games, the best known commercial games being Left 4 Dead 2, Final Fantasy Crystal Chronicles: My Life as a King und Portal 2 [2]. The developers of the open source Transport Tycoon Deluxe clone, OpenTTD, wrote the artificial intelligence of the computerized players in Squirrel (Figure 1). If you use the integrated developer environment Code::Blocks, you will also have encountered Squirrel: Squirrel scripts enhance the functional scope [3] (Figure 2). Squirrel is thus also recommended for applications beyond gaming, as the Squirrel Shell goes to prove -- you can even use it as a Bash replacement [4]. @Bi:F01-openttd.png @B:Figure 1: Computerized opponents in the OpenTTD use AI courtesy of Squirrel scripts. @Bi:F02-codeblocks1.png @B:Figure 2: The Code::Blocks developer environment can be extended using Squirrel scripts. @ZT:Manual Work @L:Unfortunately, Squirrel is not included in the repositories of most popular distributions. This typically gives the user no alternative but to grab the reference implementation from the Squirrel website, and build the interpreter themselves [1]. The source code archive is available in two flavors: a standalone variant that runs Squirrel scripts directly at the command line, and a static library, which you can tack onto your C++ programs. As a bonus, users get the Squirrel Standard Library with some useful Squirrel functions, a handful of sample programs and a PDF language reference. @L:All you need to create the interpreter is a C++ compiler, and to run make in the directory created by unpacking the archive. This puts the standalone interpreter sq in the bin directory. You can then pass the Squirrel script you want to run to the interpreter: @LI: sq myscript.nut @L:To accelerate execution of the script, the interpreter first converts it to bytecode. You can do this explicitly up front: @LI: sq -c myscript.nut -o myscript.cnut @L:Then pass in the resulting myscript.cnut to sq: sq myscript.cnut. If you call sq without any parameters, this launches the interpreter in an interactive operating mode where you can type Squirrel commands directly (Figure 3). However, to quit the prompt you will have to press [Ctrl]+[C]. @Bi:squirrel-term1.png @B:Figure 3: The @ in front of the string keeps the text between the quotes character for character. @ZT:Hello World @L:The obligatory Hello World in Squirrel is a one-liner: @LI: print("Hello World!") @L:The dynamically typized language makes it easy to use variables which you can populate with arbitrary content: @LI: local a = 3.14; local a = "Tux loves Agnes\n"; local a = null; @L:Strings can be dressed with the Escape characters that you probably know from C. The null keyword empties the variable and is thus similar to, say, the NULL pointer in C, or nil in Lua. Semicolons complete an expression. If an expression is the only thing in a line, like in our example, you can leave out the semicolon. Squirrel is case sensitive; comments are enclosed between /* and */, or you can use // for a single-line comment. @ZT:Keeping Control @L:The control structures, if, while and for, work like in C or C++. Listing 1 uses them to compute the largest common denominator of two numbers. @#:Indent four spaces @KT:Listing 1: Largest Common Denominator @LI: // largest common denominator of a and b function lcD(a, b=10) { if (b == 0) return a; else { if (a > b) return lcD(a-b, b); else return lcD(a, b-a); } } // compute a couple of lcDs local x=1; while(x < 10) { print (lcD(x, 27)+"\n"); x = x+1; } @KE: @L:On top of this, Squirrel also has a practical foreach: @LI: local a=[1,2,3,4,5,6] foreach(number in a) print("number="+number+"\n"); @L:Listing 1 also shows how Squirrel programmers define a function: the function keyword is followed by the function name and then the parameter is brackets; return returns the results of the computation. In Listing 1, b is set to a value of 10. The interpreter will use this if the second argument for a call is missing. Squirrel even allows a variable number of parameters: @LI: function foo(x, ...) { local a = x+1; local b = vargv[0]; /* ... */ } @L:You can pass in any number of arguments to this function and use the vargv array to access them in the main function. Functions are first class objects in Squirrel: you can put them in variables, or pass them in as arguments to other functions. @L:To save typing, Squirrel version 3.0 and later use lambda expressions, that is functions without names. They are really useful for sorting and searcing: @LI: local numbers = [9,3,2,4,7]; numbers.sort(@(a,b) a <=> b); @L:Alternatively you could do this: @LI: numbers.sort(function(a,b)§§ { return a <=> b; } ); @L:The comparative operator, <=>, is also new in Squirrel version 3.0. It returns 0 if the two values are equal. sort() is a function of any array, which is really neat. @ZT:Tables @L:Besides arrays in C or C++ style: @LI: local colors = ["red", "green", "blue"]; @L:Squirrel also supports tables. Other languages will call this data structure a dictionary or an associative data field. Like a phonebook it stores any information you pass to it under a unique key. For example, you can store the value Tim under the Name keyword. In Squirrel, programmers can save numbers and complete functions as payload data -- not just text -- and you can mix things as needed: @LI: local test= { color="red" b=function(x) { return x*x; } } @L: Squirrel refers to each key/value pair as a slot. Access to stored information is possible using dot notation: @LI: print(test.color); print(test.b(2)); @L:You can change an existing slot with a simple assignment: @LI: test.b = 20; @L:To add a new slot you use the <- operator: @LI: test.c <- 20; @L:As of Squirrel 3.0 tables can also use JSON notation: @LI: local color = { "name": "blue", "colornumber": 173, "variants": ["metallic","matt"] } @L:Squirrel stores global variables, functions and tables in a global, or root, table. At the end of Listing 1, slots for the lcD() function and the variable x are added to the table. The preceding colon lets you access one of these slots in the root table: @LI: ::lcD(1,2); @L:In real life, programmers often need to browse lists. In Squirrel, you can use generators to help you do this. These special functions work just like a bubble gum machine that spits out the next ball of bubble gum whenever you kick it. @ZT:Give It To Me! @L:To create a generator, you simply need to tag the return value in a normal function with yield: @LI: function vendor(n) { for(local i=0; iresume command triggers the generator: @LI: local x=resume intake; @L:The vendor() now runs precisely until the yield command occurs. And yield returns the results of the expression that follows to it -- in our example, the content of i and thus a 1. After this, the vendor() function stops again. If you wake it up with resume, it will run until the next yield, and this would return 2 in our example. Using this approach, the following loop @LI: while((x=resume intake)!=null) print(x+"\n"); @L:would pick up all the numbers from the generator from 1 to n. @ZT:Stop and Go @L:In addition to generators Squirrel has threads. This is what Squirrel calls functions that stop in midstream and can be woken up later. In contrast to generators, thread functions in the interpreter have their own stack, their own root table, and their own error handler. @L:To convert a function into a thread, Squirrel programmers use suspend() to define the points where it should go to sleep: @LI: function saytwo() { ::print("First\n"); ::suspend(); ::print("Second\n"); } @L:newthread() creates a thread object from the function @LI: local onethread = ::newthread(saytwo); @L:and starts the thread using call(): @LI: onethread.call(); @L:It keeps on running up to the first suspend(). To wake it up again, you need wakeup(): @LI: onethread.wakeup(); @L:You can assign a parent table to any table. If you then query a non-existent key in the table, the Squirrel interpreter automatically delegates the request to the parent table. Listing 2 shows a simple example of this: because _colorname doesn't exist in the Mixer table, the interpreter automatically asks the parent table, Color, which kindly returns the value of _colorname. @KT:Listing 2: Delegation @LI: Mixer <- { } Mixer.Content <- function() { ::print(_colorname); } local color = { _colorname="red" } Color.setdelegate(Mixer) Color.Content(); @KE: @ZT:Classic @L:In contrast to Lua, Squirrel has some object-orientation, as you can see in Listing 3. It starts by defining the Dot class, from which the Rectangle class is then derived. Rectangle overwrites the constructor() function in this process. The base keyword lets it access the basic class function. this ensures that the variables in this class are used, rather than the interpreter creating new, local variables. @KT:Listing 3: Inheritance @LI: class Dot { x = 0; y = 0; function constructor(x1,y1) { this.x=x1; this.y=y1; } } class Rectangle extends Dot { height = 0; width = 0; function constructor (x,y, h,b) { this.height=h; this.width=b; base.constructor(x,y); } function Output() { ::print(x+","+y+","+height+","+width+" \n"); } } @KE: @L:You can now create an object in the finished class: @LI: local onedot = Dot(1,2); @L:Squirrel automatically passes the tow arguments to the Dot class's constructor() function, and then returns a finished object. You can use dot notation to access its variables and functions: @LI: onedot.output(); @L:As you might guess from this notation, Squirrel handles classes like tables internally. The Dot class could also be written as shown in Listing 4. @KT:Listing 4: Alternative Class Definition @LI: Dot <- class { x = 0; y = 0; function constructor(x1,y1) { this.x=x1; this.y=y1; } } @KE: @L:There is a funny side effect here that you can retrospectively use arrow notation to add functions or variables to a class: @LI: Dot.add <- function(c,d) { this.x=this.x+c; this.y=this.y+d; } @L:There is a more common abbreviation for this that C++ programmers will be more familiar with: @LI: function Punkt::add(c,d) { this.x=this.x+c; this.y=this.y+d; } @L:The use of tables comes at the price of all functions and variables being public. But you can store classes in variables, just like functions, or pass them into a function as parameters. @L:Like Java 5, Squirrel supports annotations; programmers can thus append metadata to classes and functions. This information, which Squirrel refers to as attributes, can then be read and used by a documentation system or a developer environment. @C:Two-columns please @KT:Listing 5: Attributes @LI: class Test { function PrintTesty() { ... } } @KE: @ZT:Totally Overloaded @L:The following notation would be an elegant approach to quickly adding two Dots: @LI: local p1 = Dot(1,2); local p2 = Dot(4,7); local p3 = p1 + p2; @L:To be able to use this, Squirrel coders simply need to implement the _add() function in the Dot class (Listing 6) @KT:Listing 6: Overloading @LI: class Dot { ... function _add(otherdot) { return ::Dot(x+otherdot.x, y+otherdot.y); } } @KE: @L:The _add() function belongs to the group of meta methods. If you overwrite them, or implement them yourself, you change the behavior of one of Squirrel's built in functions, or in this case of an operator. In our example, Squirrel knows how to apply the plus sign to Dots. @ZT:Conclusions @L:Squirrel doesn't deserve to lead the life of a wallflower in the shadow of Lua. It is a tried-and-trusted language, and much easier to learn for object-oriented developers. The fact that the scripting language is mainly used in computer games should not put you off. On the contrary: these programs typically need a very lean and fast interpreter. @L:On the downside, the documentation is pretty terse. It mainly comprise a language reference, although it does include many small examples, is easy to read, and very comprehensive. For more information you can check out the somewhat chaotic looking Wiki [5]. And there is a forum if you have any open issues [6]. (mhu) @IT:Info @IL: [1] Squirrel website: http://squirrel-lang.org/ [2] Programs that use Squirrel: http://wiki.squirrel-lang.org/default.aspx/SquirrelWiki/ProjectsUsingSquirrel.html [3] Squirrel in Code::Blocks: http://wiki.codeblocks.org/index.php?title=Scripting_Code::Blocks [4] Squirrel Shell: http://squirrelsh.sourceforge.net/ [5] Squirrel Wiki: http://wiki.squirrel-lang.org/default.aspx/SquirrelWiki/SquirrelWiki.html [6] Squirrel Forum: http://forum.squirrel-lang.org/ [7] Listings for this article: http://www.linux-magazin.de/static/listings/magazin/2011/10/squirrel @IE: