S2 Guide: Language Tutorial
S2 is a programming language designed to be a style system. It has properties in common with Perl, Python, and Java.
Comments
Comments are a way to leave notes in the program. If the first nonwhite space character on a line is a #, it makes that line into a comment:
# This variable holds the width of the image var int width = 500;
Statements
A statement is an instruction in the program. Statements end with the ; character. They are usually formed by a combination of expressions, which are a combination of values, variables, operators, and functions that return a value.
Statements can also contain code blocks, which are surrounded by the { and } characters. Code blocks can contain one or more statements.
Variables
Variables are like containers that hold values. You can put values into them, use their values in your programming, or change their value to something else.
Variables are one of the basic concepts of programming. If you're not familiar with them, you may be able to pick up what they are and do from context here, or you can also look for some more in-depth explanations. Every programming language uses variables slightly differently, and many examples will use a specific language to demonstrate, but the underlying principles are the same.
More about variables (programming) on wikipedia
How to Think Like a Computer Scientist (Python version): Variables, expressions and statements (via intro_to_cs)
Declaring a variable
In S2, variables must be declared ahead of time in order to be used. The basic format goes:
var TYPE variablename;
- var indicates you're creating a new variable
- TYPE describes what type of variable you're making. The type can be one of the basic types, described below, or a previously defined class (to be explained later). This type doesn't change.
- variablename is the name you give the variable in your code. This name must start with a letter and can contain letters, digits, and the underscore character ("_").
You can assign a variable a value when declaring it, or wait until later--but make sure to assign a variable a value before you use it! An example of declaring a variable while assigning it is:
var TYPE variablename = VALUE;
Or, in real code:
var string greetings = "hello";
Note: you can assign the value of a variable to another variable, as well, if they are the same type. Here's an example that gives variable1 the value of variable2:
var TYPE variable1 = $variable2;
Or, in real code:
var string goodbye = $greetings;
Basic Types
Strings
String variables, which are lists of characters, are declared as string in S2. You can define strings in one of two ways.
The first way comes in between double quotes:
var string test = "Testing";
The second way comes in between sets of three double quotes--this is very useful when writing HTML, which can have a lot of single double quotes in it. Here's an example:
"""I am also a string, but I can have "quotes" inside me.""";
The above example demonstrates another property of string values, in that if you don't assign them to a variable name, they'll print to output.
Some characters need to be escaped to show up properly in strings, with a backslash (\):
- If you need a newline, use: \n
- If you need a double quote inside single double quotes (or need to make three or more double quotes inside a triple double quotes), use: \"
- If you need to make a backslash, use: \\
- If you want to make a dollar sign (you'll learn why later in the accessing variables section), use: \$
Integers
Integers (whole numbers) are another value like strings that the S2 language understands, and are declared with int. This is an example of a variable being assigned a literal integer value:
var int width = 500;
Booleans
A boolean is a variable that is either true or false, and is declared with bool. Here is an example that expresses having candy but no cake as boolean variables:
var bool has_candy = true; var bool has_cake = false;
Arrays
An array is a list of items. They must all be the same type! Declaring an array variable is a little different from the usual variable declaration:
var TYPE[] variablename;
The [] after the type lets the program know this variable is an array.
Here is an example with strings:
var string[] counting = ["one", "two", "three"];
Associative arrays
Instead of an ordered list of items, an associative array gives each item in it a "key" that serves as an identifying label. All keys must be strings, and all the times must be the same type. Keys are unique--you can't have two items with the same key. Like regular arrays, declaring associative arrays is a little different:
var TYPE{} variablename;
The {} after the type lets the program know this variable is an associative array.
Here's an example with strings:
var string{} fruits = {"apple" => "red", "lemon" => "yellow", "grape" => "purple"};
Accessing variables
Putting a $ in front of the variable name lets you access it: $variablename. Remember how in strings, the dollar sign needed to be escaped with \$ to make $ show up properly? That's because when you reference a variable in a string, it's replaced with the value of the variable! For instance, this code:
# declare our greeting variable, assign it the value of "hi" var string greeting = "hi"; # print out a sting containing our greeting print "I greet you with: $greeting\n"; # change the value of our greeting to "hello" $greeting = "hello"; # print out a string containing the new value of the greeting print "I greet you with: $greeting\n"; # print out a string containing $greeting print "I greet you with: \$greeting\n";
Will print out:
I greet you with hi I greet you with hello I greet you with $greeting
For regular arrays, you'll need to refer to the position of the value you want in the array; the count starts at 0. Example:
var string[] counting = ["one", "two", "three"] print "I have $counting[2] bananas and $counting[1] apples and $counting[0] orange.\n";
Will print out:
I have three bananas and two apples and one orange.
For associative arrays, you'll want to use the key of the value you want. Example:
var string{} fruits = {"apple" => "red", "lemon" => "yellow", "grape" => "purple"}; print "I have $fruits{"grape"}-colored pants.\n";
Will print out:
I have purple-colored pants.
If you need to access a variable inside of a string but have trouble because it's right next to other characters instead of a space, put the variable name into ${}:
var string verb = "run"; print "I am ${verb}ing.";
Will print out:
I am running.
Properties
Properties are special global variables that are available between layers. EXPAND
Declaring properties
The basic format of a property declation goes:
property TYPE NAME { DECLARATION }
Here is an integer property:
property int corner_radius { # the description for this property des = "Radius on corners, between 0-20px."; doc_flags = "[construct]"; # the minimum allowed for this property min = 0; # the maximum allowed for this property max = 20; }
Flags include:
-
des
-- For describing the property. -
noui
-- This keeps a property from being included in the customization wizard. -
label
-
grouped
-
grouptype
-
values
-
doc
-
doc_flags
-
example
-
note
-
size
-- For string properties, the size of the text box. -
maxlength
-- For string properties, the maximum length of the string. -
cols
-- Number of columns for the text area. -
rows
-- Number of rows for the text area. -
string_mode
-- For string properties, uses values "css" and "html". -
min
-- For integer properties, the minimum. -
max
-- For integer properties, the maximum.
Setting properties
The basic format for setting a property is:
set NAME = VALUE;
Examples:
set entry_metadata_position = "bottom"; set use_shared_pic = false; set comment_datetime_format_group = ["comment_date_format", "comment_time_format"]; set num_items_recent = 20;
Property groups
In the customization wizard, different properties can be grouped together like this:
propgroup images { property use image_background_page_group; property use image_background_header_group; property use image_background_header_height; property use image_background_entry_group; property use image_background_module_group; }
Accessing properties
When you are trying to access a property, instead of $name
, use $*name
.
Operators
An operator takes in a certain number of arguments called operands and (usually) returns another value. A binary operator has two items, and a unary operator only uses one. They are often described by what position they inhabit on the line--left and right.
Assignment
Because the assignment operator returns the value it is assigning, it's possible to chain multiple assignments together:
var string a; var string b; $a = $b = "text";
As previously mentioned, local variables can be assigned when they are declared.
var string a = "apple";
Arithmetic operators
Arithmetic operators are all made to do math on integers; they only take integers as arguments and return integers.
# + Addition, binary var int total = $x + $y; # - Subtraction, binary var int difference = $x - $y; # * Multiplication, binary var int total = $x * $y; # / Division, binary # Note: since S2 only has integer numbers, the result is also an integer # even if there's no even division var int ratio = $height / $width; # % Modulus (remainder), binary var int leftover = $x % $y; # ++ Increment in-place, unary # -- Decrement in-place, unary # - Negation, unary var int negative_margin = -$x;
Comparison operators
Comparison operators compare two arguments of the same type, and return a boolean (true or false). Equals (==) or does not equal (!=) can accept integers or strings as arguments; the rest only accept integers.
# == Equals, binary if ( $var1 == $var2 ) { print "They are equal."; } # != Does not equal, binary if ( $var1 != $var2 ) { print "They are not equal."; } # < Less than, binary if ( $var1 < $var2 ) { print "var1 is less than var2"; } # > Greater than, binary if ( $var1 > $var2 ) { print "var1 is greater than var2"; } # <= Less than or equal to, binary if ( $var1 <= $var2 ) { print "var1 is less than or equal to var2"; } # >= Greater than or equal to, unary if ( $var1 >= $var2 ) { print "var1 is greater than or equal to var2"; }
Logical operators
Logical operators work with boolean (true or false) values, and also return a boolean value.
# and, Logical AND, binary # returns true only if both arguments are true if ( $var1 and $var2 ) { print "Both var1 and var2 are true"; } # or, Logical OR, binary # returns true if either argument is true if ( $var1 or $var2 ) { print "At least one of var1 and var2 are true." } # not, Logical complement, unary # returns false if the argument is true, true if it is false if ( ! $var1 ) { print "var1 is not true" }
Type operators
Type operators only work on object values. The left operand is the object in question, and the right operand is a class name.
# isa -- Returns true if the object is a given class or one of its children # instanceof -- Returns true if the object is a given class only # as -- Returns an object as if it was the given class if that's possible, # returns a null object if the object is incompatible with the given class
String concatenation (+)
Besides being used for addition, the + operand also concatenates (puts together) strings.
var string a = "one"; var string b = "two"; var string c = $a + " " + $b;
Range operator (..)
The range operator takes two integers and returns an array of integers inclusively listing all integers between the two numbers.
The conditional operator
The conditional operator is the only ternary operator, meaning that it uses three items! It is very similar to if/else; in fact, you can use if/else instead of the conditional operator in all cases.
Named unary operators
A unary operator takes one argument after the operator, and returns one value.
- isnull returns a true value if the argument is null (has no value) and false otherwise
- defined is the opposite of isnull--it returns true when the argument is defined and false if it hasn't been
- new creates a new instance of the given classname; read more about this in the Classes section.
- null creates an undefined variable of the given classname; chances are, you won't use this
- reverse is used on strings and will return a copy of the given string with all the characters reversed
- size takes an array and returns the number of elements in it
- reverse takes an array and returns an array with all of the same items in reverse order
Operator precedence
Logic Flow
If/else
The if/else control statement is a good way to only run a bit of code if certain conditions apply.
The following pseudocode will execute the code inside the BLOCK if the EXPRESSION evaluates to true:
if ( EXPRESSION ) BLOCK
The following pseudocode example will execute the code inside BLOCK1 if the EXPRESSION evaluates to true, otherwise it will execute the code inside BLOCK2:
if ( EXPRESSION ) BLOCK1 else BLOCK2
The following pseudocode example will execute the code inside BLOCK1 if EXPRESSION1 is true. Next, it tries to evaluate EXPRESSION2 and if that is true, it executes the code inside BLOCK2. (There can be more than one elseif statement in a row here.) Finally, if none of the expressions evaluate to true, the code inside BLOCK3 runs:
if ( EXPRESSION1 ) BLOCK1 elseif ( EXPRESSION2 ) BLOCK2 else BLOCK3
You can use a plain variable instead of an expression. Strings that have length with evaluate to try, and numbers that are not 0 will evaluate to true. Arrays that have objects in them evaluate to true, empty arrays evaluate to false.
Foreach
The foreach control statement is a good way to apply the same code to each item in an array. The basic outline of the statement is this:
foreach VARIABLE ( EXPRESSION ) BLOCK
Each item takes a turn at becoming VARIABLE and running through the statements in BLOCK.
If the array is an associative array, the key of the array item becomes VARIABLE. If the expression being used is a string, each character will become VARIABLE.
Functions
In general, a function is given a list of variables (called arguments) runs a set of statements, and returns a variable. Some functions won't have any arguments, and some don't return a value.
Variables declared inside of a function are only available inside of that function. (This is called scope.)
You can use existing functions by calling them in a statement, or you can define your own functions.
Calling functions
You can call functions in statements. Examples:
# This statement calls a function with one argument and doesn't use # any return value it may give back. The arguments go # inside of parentheses. my_function(2); # This statement calls a function with no arguments. Even though there # aren't any arguments, we still need the parentheses to call the function. my_function(); # This statement calls a function with three arguments; multiple # arguments are separated with commas. my_function(2, "cows"); # You can call a function using variables as arguments, too! my_function($greeting, $farewell); # This statement calls a function with an argument and gets back a # value that it assigns to a variable var int height = proportion(500);
Declaring and implementing functions
In order to declare a function, you'll need this information:
- The name you want to give the function
- The arguments the function takes, as well as the type of each argument
- If the function returns a value, the type of that value
When you declare a function, you can also include a docstring after the rest of the declaration but before the ending semicolon of the statement. Doing this allows your function to be automatically documented when viewing information about a layer.
# This function doesn't return a value function print_hello(string greeting); # This function returns an integer function get_height(int width) : int; # This function is documented with a docstring function print_image(string url, string alt, int width, int height) "This function prints out an image tag.";
However, you probably don't want to just declare a function, but also implement it! You can do that like this:
# This function returns a value function get_height(int width) : int "This function returns a height that is three times the given width." { return 3 * $width; } # This function doesn't return a value function greet(string greeting) "This function prints out a greeting." { print "Here is my greeting: $greeting"; }
If you're not dealing with a class function or built-in function (more on those later), you'll also want to implement your function when you declare it.
Built-in functions
Built-in functions aren't written in S2 but in Perl in the backend. They are then declared in a core layer using the builtin keyword. Example:
function builtin ehtml (string s) : string "Escapes all HTML tags and entities from the text";
Classes
Declaring classes
class Mammal { var string voice; var int legs; }
Using classes
Accessing class variables
If you're working inside of a class function and want to access the value stored in a variable belonging to the function, you can use $.classvar or $this.classvar.
If you have a variable whose type is of a certain class, and want to access a variable belonging to it, you can user $var.classvar.
Calling class functions
Extending classes
A class can be extended.
class Dog extends Mammal { var bool likes_cats; }