S2 Language Overview The S2 language is an object-oriented and statically-typed language with syntax mostly borrowed from Perl, but with some borrowings from other languages including Java and Python. This language is domain-specific, which means that it has a reduced feature set compared to general-purpose programming languages you may be familiar with. In particular, the flow control constructs available are restricted to conditional branching (if statements) and iteration over finite lists (foreach loops). The language has several features which are designed to make life easier: Variables can be referenced from within string literals, in which case their values will be interpolated into the resulting string. This behavior is similar to perl. String literals can be given enclosed in three sets of quotes ("""), which allows quote characters to be included within the string unescaped. This is useful for producing HTML which features lots of quote symbols. String expressions can be used as statements, causing their value to be implicitly output to the client. Properties can be defined which are special global variables exposed from a layer to be set from other layers or from a friendly customization interface. This section serves as a reference guide for the S2 language. Anyone who is familiar with procedural programming should be right at home. If you have not done programming before, you may like to get an idea of the concepts behind programming before you begin.
Literals S2 supports integer, string, boolean, array and hash literals: 123 Integer literal representing the number one hundred and twenty three. "Blah blah blah" Simple string literal. Variables are interpolated and escape sequences are processed. """string with "quotes" in it""" Triple-quote string literal. Quotes can occur within with no escaping for one or two consecutive characters. Variables are interpolated and escape sequences are processed. truefalse The two boolean literals. [ item, item, item, item ] Array literal. All items must be of the same type, and this literal will return an array of that type. Trailing commas are fine, if you want to include them. { key => value, key => value, key => value } Array literal. All key items must be strings, and all value items must be of the same type. This literal will return a hash of that type. The following escape sequences are supported in strings: \n Newline \" Literal quote \\ Literal backslash \$ Literal dollarsign
Variables S2 supports several primitive types, as well as regular and associative arrays and objects based on classes.
Variable Types S2 has the following primitive types: int An integer bool A boolean (true/false) value string A string
Declaration var int somenumber; Declare a simple variable named somenumber as an integer. var string[] names Declare a regular array of strings called names. var bool{} has_stuff Declare an associative array of boolean values called has_stuff. var SomeClass[] stuffs Declare a regular array of the class SomeClass and name it stuffs. var SomeClass{} stuffs Declare an associative array of the class SomeClass and name it stuffs.
Access $variable Returns the value stored in the variable, or reference to the regular or associative array or instance of a class with this name. $this An instance of the class the currently-executing function was envoked from. $.member$this.member Equivalent ways to return the value stored in this member of the class which owns the function being executed. $var.member Returns the value stored in this member of the instance of a class known as var. $*propertyname Returns the value of the property specified. $variable[2] Returns the value stored in element 2 (3rd element) of the regular array. $variable{"fred"} A value stored in the associative array.
Operators S2 operators have the following associativity and precedence, listed from highest precedence to lowest. leftTerms (literals, expressions in parentheses, variables, function/method calls, named unary operators) nonassoc++ -- rightnot - left* / % left+ - nonassoc <= >=]]> nonassoc== != leftand leftor nonassoc.. nonassoc? : right=
Arithmetic Operators The S2 language has the usual set of arithmetic operators you'd expect to find in a programming language. These are the same as the arithmetic operators in perl. As you might expect, these operators can only be applied to integers, and will return integers. +binaryAddition -binarySubtraction *binaryMultiplication /binaryDivision %binaryModulus (remainder) ++unaryIncrement in-place --unaryDecrement in-place -unaryNegation
Comparison Operators These operators allow you to compare one value to another. Both operands must be of the same type, and all but simple equality/inequality can only be applied to integers. Only primitive types may be compared with these operators. All comparison operators return a boolean value. ==binaryEquals !=binaryDoes not equal <binaryLess than >binaryGreater than <=binaryLess than or equal to >=binaryGreater than or equal to
Logical Operators These operators perform logical operations on boolean values. All operands must be boolean, and a boolean value results. andbinaryLogical AND orbinaryLogical OR notunaryLogical complement In summary, AND returns true if and only if both of its operands are true. OR returns true if one or both of its operands are true. NOT returns true if its operand is false, and false otherwise. The two binary logical operators are usually used in an if statement to include two or more comparisons.
Other Operators These operators don't really fit into a category of their own, and have perhaps non-obvious functions.
The assignment operator: <literal>=</literal> The assignment operator (which should not be confused with the equality test operator, ==), is used to assign a value to a named variable. This means that unlike most other operators, its left operand must be a variable name. No other kind of expression will do. The assignment operator can take operands of any type, but the right-hand operand must be of the same type as the variable given on the left, or coercable into that type. The assignment operator also returns whatever value it has just assigned to the variable, meaning that you can chain several assignments together as follows: $foo = $bar = $baz = 2; In this case, $foo, $bar and $baz will all be assigned the value 2. As a special case, assignment can also be used when declaring local variables: var string $name = "John"; However, this is only true for local variables. Class variables cannot be initialized in this way. The symbol = is used for several other assignment-like things in the S2 language, including setting property values and layerinfo. These aren't really assignment, but act similarly and in the case of property setting follow many of the rules described above.
String Concatenation with <literal>+</literal> The + symbol actually has two purposes in the S2 language. When one of its operands is a string, it becomes the string concatenation operator. In this form, it will firstly attempt to coerce any non-string operand into a string, then stick the two strings together to form a resulting string which is returned. The simplest form of string concatenation involves two strings. In this case, both strings are just concatenated and that is it. However, as long as one operand remains a string, the other operand can be an integer, a boolean or an object with certain conditions. Objects may only be concatenated to strings if their class has an as_string() method which returns a string. The return value of this method will be used as the string value for concatenation. String literals with interpolated variables are really just concatenation with a more convenient syntax, which is why you are able to interpolate integers and certain objects directly into string literals.
The ternary conditional operator <literal>? :</literal> This is the only operator in the S2 language with three operands. Its function can be thought of as being like an if statement in the form of an operator. This operator is best explained with an example: $label = ($c == 1 ? "1 comment" : "$c comments"); In this example, the conditional operator and its operands are enclosed in parenthesis for clarity. These are optional, but can make things more readable. The first operand, preceding the question-mark symbol, is a boolean expression. In this case it is an equality test. If this expression evaluates to true, the second operand, which is after the question-mark and before the colon, is returned. If the expression is false, the third operand is returned. The first operand must be a boolean expression. The second and third operands can be of any type but their types must match. The return type of this operator is the same as that of the second and third operand. In the above example, then, the return type would be string. In all cases an if construct can be used in place of this operator, but the conditional operator is shorter and more readable in some cases.
The range operator <literal>..</literal> This rather quirky operator is borrowed from Perl. It takes two integer operands and returns an array of integers containing all integers between the first and second operand, inclusive. For example: var int[] list = 1 .. 10; The array $list will now contain ten elements, each numbered in order from 1 to 10. This has the same effect as: var int[] list = [1,2,3,4,5,6,7,8,9,10]; This operator is really only useful for creating a loop which will iterate a certain number of times: foreach var int i (1 .. 10) { println "Iteration number $i"; } This example will print ten lines, each containing a number from 1 to 10 in order. Some readers will probably balk at the use of an array of integers to create a loop such as this, as it would use more memory than a conventional for loop from a general programming language. However, you don't have to worry. The above idiom is optimised to an efficient form at compile time, so there is no memory wasted.
Named Unary Operators The following are the built-in named operators. You do not have to wrap the following term in parentheses. isnull $object returns a boolean: true if $object is null (has no value) defined $object opposite of isnull. might be prettier than negating isnull new ClassName returns an instance of ClassName with all members empty or zero newnull ClassName returns an undefined value of type ClassName reverse $string returns copy of $string with characters reversed reverse $array returns shallow copy of $array with elements reversed. size $string returns number of bytes in $string. This behavior is deprecated. Please see note below. size $array returns number of elements in $array The use of the size operator on strings is deprecated and will be removed or updated without notice. Instead, you should use the string class's length method to get the number of characters. Remember that some characters are represented by more than one byte.
Statements A statement is an expression terminated by a semicolon. Statements can contain code blocks delimited by curly braces which can then contain one or more statements themselves. Flow control constructs can also be statements. A statement consisting wholly of a string literal, or a string literal with other strings concatenated to it will be output to the client. A statement consisting wholly of a variable will behave similarly. The print instruction will cause the string or numeric expression supplied as its parameter to be output to the client. The print safe alternative does similarly, but forces the output to be checked for safety. In the case of LiveJournal and FotoBilder, this means running a HTML cleaner. All untrusted (non-system) layers always run through the checker, reglardless of which print instuction is used.
Flow Control A limited subset of the flow control constructs normally present in high-level programming languages is available in S2. Each includes at least one code block enclosed in curly braces. if ( expr ) block Simple conditional. If expr evaluates to true, block will be executed, otherwise it will be skipped. if ( expr ) block1 else block2 If expr evaluates to true, block1 will be executed, otherwise block2 will be executed. if ( expr1 ) block1 elseif ( expr2 ) block2 else block3 If expr evaluates to true, block1 will be executed. Otherwise, expr2 will be tested and if it evaluates to true, block2 will be executed. If both expr1 and expr2 evaluate to false, block3 is executed. foreach var ( expr ) block block will be executed once for each element in the regular or associative array given in expr. On each iteration, an element of the array (or key of an associative array element) will be placed into the variable declared in var. foreach can also be used on strings, in which case the iteration variable must be a string and this variable will contain a character from the string with each iteration.
Classes Base classes are defined using the following syntax: class Image { var string url; var int w; var int h; var string alt; var string extrahtml; var string title; var string longdesc; function output; } A subclass of any class can be created as follows: class Button extends Image { var string linkurl; var int bordersize; } All members of Image are now valid in Button, with the addition of our two new members linkurl and bordersize. The function Button::output will override Image::output if it exists, otherwise Image::output will be used as a fallback. See the variable and function reference for the syntax for accessing members of classes.
Functions Functions in S2 can be simple functions, methods of a class, or call functions written in the backend language from the builtin layer. Functions can return data in any simple internal datatype, or void if no return data is required.
Declaration Declaring a function within a class is done as follows: class SomeClass { function do_stuff(int para1, string para2) : string; } This declares a function called do_stuff in the SomeClass class with two parameters and which returns a string. The colon and type keyword may be omitted where a function will return void (no value). Functions outside classes are declared in the same way: function do_global_stuff(int para1, string para2); Functions outside classes do not need to be declared, but if they are not declared they must be defined before they are called. Built-in functions can be declared in the core layer using the builtin keyword in the function prototype: function builtin ehtml(string s) : string; class string { function builtin ends_with (string sub) : bool; } Functions can have some or no parameters, in the latter case the parentheses may be omitted in declaration. If several functions of the same name exist with different parameters, they can be defined as follows: function dostuff(int para1); function dostuff; Non-class (global) functions can be implemented when they are declared: function do_global_stuff() { print "I'm doing stuff!"; } Class functions (methods) must first be declared within the class they will apply to, and can then be implemented outside the class declaration as follows: function SomeClass::do_stuff() { print "I'm doing stuff!"; } It is not permitted to implement a builtin function. These will instead be mapped onto some code written in the host language in the S2 backend. Layouts are allowed to add new methods to a class without pre-declaration, but with a few special constraints. Firstly, the method must be declared and implemented before it is used. Secondly, the method name must begin with lay_, to avoid problems in the future when new methods may be added to the core layer with the same name.
Calling Functions outside classes can be called using the following syntax: dostuff(); dostuff(5); Functions in classes are envoked from an instance of the class as follows: $thingy->dostuff(45,"boink"); Note that the parentheses are required when envoking a function.
<varname>layerinfo</varname> declarations You can use the layerinfo declaration to set arbitrary meta-data for a layer. However, certain meta-data keys and values are required for each type of layer. The syntax of layerinfo is: "=" ";" ::= | | ]]> At minimum, all that's required to create a valid layer is one layerinfo declaration with name "type", stating the type of the layer. Depending on the layer type, more declarations may be required. Valid types are those listed in the Layers section. Example:
Keys
Required Keys typeThe layer type identifier majorversionRequired for core layers only langcodeLanguage being configured. I18n and i18nc layers only.
Recommended Keys nameThe name of the layer to be displayed in the interface.
Other Supported Keys desA longer description of the layer. author_nameThe name of the author. author_emailThe email address of the author. source_viewableSource of the layer is available to all users on the host site. is_publicAllow users on the host site to use layers from other users in their styles. The email address, if given, will likely be displayed on the host site in clear-text. You should probably leave it out if you are concerned about email-address collecting software and spam.
Properties Properties are single values which are exposed by the core and layout layers and set in all subsequent layers to configure aspects of a style. Properties can be of any primitive type as well as any class which provides both an as_string() function which returns a string representation of its data and a constructor function named after the class which accepts a string as a parameter and returns an object based on that string. The purpose of properties is to expose certain configuration settings of a style such that they can be set from a friendly wizard-like interface.
Declaration Properties are defined using the following syntax: property int page_recent_items { des = "Number of journal entries to show on recent entry page"; min = 5; max = 50; } The key and value pairs within the braces are known as attributes. These are used by the host application for various uses, including the presentation of a friendly customization interface.
Standard Properties The following attributes are available for all properties: des Required - A textual description of this property. values A string setting the acceptable values of this property, along with a natural language description of each, in the format "value1|Description1|value2|Description2" and so on. example An example of what might be put in this field. Can be used when it's not obvious what the value should look like. note A note to be displayed with the field to enter this value. noui Suppresses the display of this property in the editing GUI. There are also attributes only available for specific types of property, as described in the following sections.
Attributes for integer properties max The maximum value permitted. min The minimum value permitted.
Attributes for string properties maxlength The maximum amount of characters this property can contain. size The size (in characters) of text widget which should be used to set this property from an interface.
Setting The set command is used to set the values of properties from all layers: set text_read_comment = "Read 1 comment"; The value set by the highest layer will be used.
Use In order to use a property from the core layer in a layout, you should first tell the system that you are going to use it. This makes it appear in the customization UI if applicable. property use property_name; All layers can use properties as variables as described in the variables section above: print $*property_name;
DocStrings You can add short documentation strings to your classes, methods, data members and functions by placing them in quotes inside the declaration, like so: class Example "An example class" { var string some_string "An example data member"; function some_method() : int "An example method"; } function some_function() "An example global function"; These are made available to the S2 backend support code, and applications may provide some kind of documentation browsing facility or the ability to generate static files containing documentation.