String Literals

Through the use of string literals, programmers can embed text strings as values inside Sling programs. Sling supports two types of string literals, the double-quoted version and the double-square-bracketed version:

// A double-quoted string literal
var myString = "This is a string literal"

// A double-square-bracketed string literal
var myString2 = [[ This is also a string literal ]]

Notably, the double-square-bracketed version is intended to be used for strings that span multiple lines:

var sampleString = [[
	This is a sample string literal that
	spans multiple lines and that forms a
	part of the program.

	Obviously, this string may also contain
	"double quoted" portions.
]]

The data type of all string literals is string.

Boolean Literals

A boolean literal is either the literal string true or false, representing true and false boolean values, respectively. The data type of a boolean literal is bool. See examples:

// Through type inference, both variables will
// receive the data type 'bool'
var booleanVariable1 = true
var booleanVariable2 = false

Null Literals

The null literal is the literal string null, and respects a pointer or value whose value is not set.

The null literal does not have a specific real data type in itself, but can rather be used as a value for several different data types, namely the following: buffer, date, function, map, object, pointer, array, string, vector and all custom data type nodes (user-defined classes, interfaces and other entities). These mentioned data types can loosely be referred to as nullable types.

The following, therefore, is not legal, since the data type of the variable cannot be inferred from the right-side initializer:

// This is not correct
var myVariable = null

The following, however, is allows:

var myVariable as string = null

Failure Literals

The failure literal is a special literal represented by the string fail inside code, which represents a "failure" value that can be returned by the current function. The exact meaning of "failure", then, depends on the actual return type of the function. Particularly, the following rules are observed:

  • For boolean methods, fail means false

  • For numeric (integer types, float and double), fail means 0 or 0.0

  • For nullable types (see above), fail means null

The following are, then, legal Sling fuctions:

func doSomethingWithIntegerValue as int
{
	// This will return '0'
	return fail
}

func doSomethingWithBooleanValue as bool
{
	// This will return 'false'
	return fail
}

func doSomethingWithStringValue as string
{
	// This will return 'null'
	return fail
}

The failure literal, therefore, can be used as an easy-to-use common "failure value" without requiring the programmer to consider what is the return type of the current function. Furthermore, failure literals are commonly used in macros so that the same macro can be used in many kind of functions, and the return value adapts accordingly.

Failure literals do not have a real data type in themselves, and cannot be used outside of return statements.

Character Literals

Character literals are used to encode character values, and are expressed as single-character values inside single quotes, as follows:

var myCharacter1 = 'a'
var myCharacter2 = 'B'
var myCharacter3 = '4'

The data type of all these declarations (and character literals in general) is char. The following special character values are supported in Sling (compatible with C-based languages):

var audibleBellCharacter = '\a'
var backspaceCharacter = '\b'
var formFeedCharacter = '\f'
var newLineCharacter = '\n'
var carriageReturnCharacter = '\r'
var tabCharacter = '\t'
var verticalTabCharacter = '\v'
var literalBackSlashCharacter = '\\'
var literalSingleQuoteCharacter = '\''
var questionMarkCharacter = '\?'
var nullCharacter = '\0'

Furthermore, escape sequences may also be used to encode characters based on their ASCII or UNICODE values, using the following:

\xXX   (where XX represents the ASCII value of a character)
\uXXXX (where XXXX represents the UNICODE value of a character)

Floating Point Number Literals

Floating point numbers are identified as numbers with a decimal component, which is identifier by the presence of the dot '.' character in the literal. The number may be negative (start with the '-' sign). Particularly, a token in source code is identifier as a floating point literal if it starts with either '-', a digit or a dot '.' that is immediately followed by a digit, AND contains a dot '.' somewhere in the token string. The value may also contain an exponent component delimited with the 'e' character.

The data type of a floating point literal is by default double. However, if you suffix the literal with the character 'f' or 'F', the literal will be treated as float.

var myDoubleVariable = 5.364
var myFloatVariable = 7.2366f
var anotherDoubleVariable = .566 // means 0.566

Integer Literals

Integer literals are composed of digits / numbers only (characters 0 .. 9). The number will be parsed as is, and treated as data type int. If you suffix an integer literal with the letter 'l' or 'L', the literal is treated as long.

var myIntVariable = 1003

6.2.8. Array Literals

Array literal offer a convenient way to construct array data structures directly in code by simply typing in the values to place inside the array. An array literal is enclosed inside square brackets '[' and ']' and is composed of a sequence of values delimited by commas (','), as follows:

var myArrayLiteral = [
	"first value",
	"second value",
	"third value"
]

An array literal may also be declared on a single line:

var myArrayLiteral = [ "first", "second", "third" ]

Any data type may be inserted in the array literal. The compiler will then automatically detect the data type of the array based on the contents of the variables inside. For example, in the sample above, the data type of myArrayLiteral would have been array<string> (since all elements of the array are strings). The following is also possible, however:

var anotherArrayLiteral = [
	new MyObject(1),
	new MyObject(2)
]

In this case, the data type of anotherArrayLiteral would have been array<MyObject>.

If you mix different data types in the literal, the most specific common data type will be used as the data type. If no common data type is found, then object is used, eg.:

var validArrayLiteral = [ new MyObject(1), "superman" ]

The data type of validArrayLiteral, in this case, would be array<object>.

To manually specify the data type of an array literal, you may use the more complex array creation expression, as follows:

var mySpecificArrayLiteral = new MyObjectBaseClass[] {
	new MyObject(1),
	new MyObject(2)
}

The example above would have declared the variable mySpecificArrayLiteral as data type array<MyObjectBaseClass>, holding two values in the array, both of them instances of MyObject. For this to work, MyObject would need to be derived from MyObjectBaseClass.

Map Literals

Map literals are used to declare objects of the map data type together with the containing values. Map objects are composed of key/value pairs, and are declared similarly to arrays but using the curly braces '{' and '}' instead of square brackets. The syntax is as follows:

var myMapVariable = {
	"key1" : "value for key1",
	"key2" : "value for key2"
}

Similarly to an array, the data types of both keys and values can be any valid data types, and the compiler will automatically determine the data type of the resulting literal based on the combination of the contained data types. The data type of the literal in the above example would have been map<string,string>.

Sub-Expressions

Syntax: ( <expression> )

Any part of an expression may be contained inside parenthesis '(' and ')' to contain it as a "sub-expression". This is usually done in order to enforce a particular order of operator evaluation.

VALUE

Syntax: VALUE "<variable name>"

The VALUE keyword can be used to inject values of preprocessor variables within the code as string literals. Indeed, the resulting value of a VALUE keyword is always a string literal, the content of which is the value of the given preprocessor variable. For example, consider the following:

var myStringVariable = VALUE "superman"

If you compile this code as is, the compiler will warn you that the preprocessor variable "superman" does not have a value. However, if you compile it with eg. the following kind of command:

samce slingc MyCode.sling -Dsuperman=HelloWorld

The code would, then, be equivalent to this:

var myStringVariable = "HelloWorld"

TEXTFILE

Syntax: TEXTFILE "<filename>"

The TEXTFILE keyword can be used to include the entire contents of an external text file as part of the code as a string literal: A TEXTFILE is always expanded to a string literal by the compiler, with the contents of the string being the complete contents of the file, unmodified (with newlines preserved, etc.). Consider the following file named "sample.txt":

This file is a sample file.
It contains text.

A lot of text.

Then consider this code:

var myString = TEXTFILE "sample.txt"
PRINT myString

The output of this code would be the complete contents of "sample.txt". Note that the inclusion of the file is done by the compiler at compile-time. This means that the contents of the file will be included in the code and in the resulting executable, and the sample.txt file does not need to be present when running the program.

new

Syntax: new <datatype> [(parameters)]

The "new" operator is used to create new object instances, either from user-defined classes or from language internals. The syntax is as follows:

// Create an instance of user-defined class 'MyClass'
var myObject = new MyClass()

// Create an instance of a vector object holding strings
var myVectorObject = new vector<string>

Note the presence of parenthesis when constructing the user-defined class and their absence when constructing a language internal object.

The object can be given parameters upon creation, eg.:

var myObject = new MyClass(10)

The parameter given inside the parenthesis will be passed to the constructor of MyClass as is. If the class does not declare a constructor that accepts an integer (or is compatible with integer), the compilation will fail.

MALLOC

Syntax: MALLOC <integer>

The MALLOC keyword allocates a specific-size buffer of memory and returns an object with data type buffer as the return value. The returned buffer will have the specified size of bytes available:

var oneKilobyteOfMemory = MALLOC 1024

Buffer objects, just like all other objects in Sling, are memory-managed automatically, and the allocated buffer will be automatically unallocated when there are no references left to the buffer object returned. There is now "free" keyword for unallocating the buffer.

SIZE

Syntax: SIZE <expression>

The SIZE keyword can be used to get the size of an object. The SIZE keyword can be applied to objects of type array, vector and buffer, in particular. In each case, SIZE return the size (as an integer) of the object passed to it as a parameter. For example:

var memory = MALLOC 102
var size = SIZE memory
PRINT String.forInteger(size) // will print 102

DEFAULT

Syntax: DEFAULT <datatype>

The DEFAULT keyword returns a value (that can roughly be treated as a literal value) that represents the "default value" of the given data type. For example, the "default value" for the integer data type is 0, and null for nullable types.

lang

Syntax: lang "<language name>" <datatype> {{{ <code in another language> }}}

The lang keyword in an expression context can be used to call functionality of another programming language, and to capture and utilize the return value of that given piece of code in Sling. For example:

var thisHoldsTheValue = lang "java" string {{{ java.lang.System.getProperty("java.vm.name") }}}

Obviously this would make no sense if the code is not compiled to run on a Java virtual machine. See below the sections for conditional compilation (IFDEF and IFNDEF) for how to address this.

this

The this keyword refers to the current object instance. Has no meaning and cannot be used in a static context.

thisObject

The thisObject keyword is similar and same as the this keyword, but which one notable difference: When used inside macro declarations, the "this" keyword is copied from the macro to the calling context as is, and becomes a reference to the "this" object of the calling context rather than the object where the macro was declared. However, when using "thisObject" inside a macro, the reference to "thisObject" will be translated to refer to the object where the macro was declared. Consider the following:

class FirstClass
{
	prop stringVariable as string

	macro doSomething
	{
		PRINT this.stringVariable
	}
}

class SecondClass
{
	func myMethod
	{
		var v = new FirstClass()
		v.setStringVariable("value")
		// This will not work, since "this" object
		// does not contain "stringVariable"
		v.doSomething()
	}
}

Changing "this.stringVariable" in FirstClass to "thisObject.stringVariable" will cause the reference to remain with the instance of "FirstClass", and resolves the problem.

base

Refers to the current object (same object as "this"), but the reference will be treated as an instance of the base class rather than the current.

Function Declarations

A function declaration expression is done using the following syntax:

func[(<parameters)] [as <return data type>] {
	// Function contents
}

For example:

var myFunction = func {
	PRINT "Hello"
}

OR

var myOtherFunction = func(text as string) as bool {
	PRINT text
	return true
}

Identifiers

Any identifier (see definition in the beginning of the document) is a valid primary expression. These are used to refer to symbols within the program, which may be variable names, class names, functions, etc.


Twitter Facebook LinkedIn Youtube Slideshare Github