Skip to the main content of this page.
The sable mammal, Martes zibellina
SABLE Programming Language

Literals

SABLE defines four kinds of literals: numbers, characters, strings, and symbols.

Numbers

Number literals have the form shown below, where each word represents a sequence of digits, the brackets enclose optional elements, and the slash means "OR". Other characters represent themselves.

  [+/-] [ BASE # ] VALUE [ . FRACTION ] [ ^ [+/-] EXPONENT ] [ $ [+/-] WIDTH ]  

The meaning of each element is:

BASEThe number base in range 2-36 for the digits in VALUE and FRACTION. If not provided, the base is 10. E.g. 16#B and 2#1011 both equal decimal 11.
VALUEThe integer part of the number. For number bases over 10, A-Z represent digits 10-35. (Upper case is recommended.)
FRACTIONThe fractional part, also using BASE digits. E.g. 16#0.C equals (12 / 16), or 0.75.
EXPONENTIf present, VALUE.FRACTION is multiplied by BASE to the EXPONENT power, which is the same as shifting the decimal point to the right by EXPONENT number of BASE digits, or left if EXPONENT's sign is negative. E.g. 16#0.4^2 equals 16#40, or 64.
WIDTHIf present, it explicitly specifies the literal number's type. (See below.)

BASE, EXPONENT, and WIDTH are always expressed in base 10.

If either FRACTION or EXPONENT is present, then the literal represents a floating-point number, otherwise it's an integer. For a float with no WIDTH, the type defaults to {FLOAT32}. For an integer, then the type is either {INT32}, {INT64}, or {UINT64}, the first of which can represent the integer value.

Use WIDTH to specify a literal number's type when declaring a variable or when it's a message receiver. WIDTH gives the number of bits. Types default to signed except for WIDTH 08; the WIDTH sign can alter the type, where "-" means a signed type and "+" makes it unsigned.

|byteval|  := 253$08.    "{BYTE}"
|ulongOne| := 1$+64.     "{UINT64}"
0$+16 until: 256 do: [:chval "{UINT16}" | ...].

Characters

A character literal begins with a dollar sign and has one of these forms:

FormExamplesDescription
Character$( $) $. $$ $` $9 $XAny single printable character can define a character literal as itself. For whitespace and control characters, use either of the following two forms.
Integer$07 $32 $8#60 $16#431A multi-character number, which must be an integer 0..16#FFFF without a WIDTH specifier, defines a character by numeric value. Note: $7 = $55.
Word$NUL $LF $ESCA multi-character Word names a character from the list below.

The set of character literals currently defined are:

CharValueDescription
$NUL0Often ends a platform string
$ALERT7Usually rings a bell when output
$BACK8Backspace
$TAB9Horizontal tab
$LF10Line feed
$VTAB11Vertical tab
$FF12Form feed
$CR13Carriage return
$ESC27Escape character
$SPACE32Simple space
$DEL127Delete
$BOM16#FFFEByte Order Marker
$END16#FFFFEnd of stream

Strings

String literals are delimited by single-quote ($') characters. Contained quotes are doubled, as in ['Isn''t this fun?']. Strings may span lines, but this is not recommended. The reasons are that it doesn't format well, and the embedded linefeeds depend on the platform where the code was compiled, when they should vary according to the runtime platform. Use instead string chains, character escapes, and line escapes, described next.

This example demonstrates how we represent strings that are large, contain linefeeds, and/or contain arbitrary characters:

CONSOLE writeLine:
    'Usage:   copy [-n] [-e] {source-dir} {target-dir}$LINE'
    '$TAB`Copy {source-dir} directory and all contents to {target-dir}.$LINE'
    '$TAB`Create directory {target-dir} if it does not already exist.$LINE`'
    'Options:$LINE`'
    '$TAB`-n$TAB`Copy (n)ested subdirectories also, unless they are empty.$LINE'
    '$09`-e$09`Copy (e)mpty subdirectories also.$LINE$LINE' escaped lineEscaped.

One string can appear immediately after another forming a String Chain. These are simply concatenated by the compiler into a single literal. Long strings can therefore be broken up to appear on multiple lines.

SABLE has no syntax that supports arbitrary characters within a string literal. Instead, class STRING defines these two instance methods:

escaped  ^{STRING}.
    {~ primitive: #escaped}
    "The receiver string literal with SABLE-style escape sequences replaced."
lineEscaped  ^{STRING}.
    {~ primitive: #lineEscaped; nonfunctional}
    "The receiver string with '$LINE`' replaced by the platform newline character(s)."

>#escaped requires a string literal as receiver. The compiler converts character literals within the string into their values. The only Character-Form char recognized is '$$'; there is no reason to use any other. An Integer and Word-Form character may end with a grave accent character to separate it from the following text. Additionally, '$LINE' (plus the optional grave accent) is converted to '$LINE`', preparing the string to receive >#lineEscaped. Unrecognized sequences result in a compiler error.

>#lineEscaped requires a literal string, including an escaped string. During type initialization, it replaces '$LINE`' with the platform newline sequence. (The BeerSong example placed this message in a constant block; it wasn't needed because this message already includes such behavior, but I wanted to demonstrate constant block syntax.)

Symbols

The Symbol is our most versatile literal, having several different uses.

|bigint| {INT32} := #MaxValue.
|adderBldr| :=
    myTypeBuilder
        defineMethodNamed: 'DoAdd'
        attributes: #Public | #Static       "<----"
        returnType: INT32
        argumentTypes: #(INT32, INT32).
|colors| := ##{CONSOLE_COLOR}(#Red, #Green, #Blue).

For many expressions, the expected type is known. This is the case when assigning to a typed declaration or a variable, passing message arguments, populating the elements of an array structure, and elsewhere. The compiler looks in the class of the expected type for a literal, constant field, or static property with the symbol's name and a compatible type. This is most commonly used to retrieve enumeration values, but as the first example above shows, that usage is not exclusive.

One class can be defined as an extension to another, in which case its literals, constants, and static properties are effectively added into the extended class. E.g. in #System.Drawing, class PENS is defined as an extension of PEN; thus, [|blk| {PEN} := #Black] retrieves the PENS>~>#Black property value.

SymbolExample.exe
    {~ console
        entryClass: #Examples.SYMBOLS method: #main;
         reference: 'mscorlib.dll';
        use: #System;
        use: #System.Collections.Generic.DICTIONARY`2 as: #MAPPING`2;
         import: 'mymod.netmodule';
        use: #'' as: #MyMod withNested: True}

Various pragma and primitive messages accept the SABLE-added type {System.SYMBOL} to represent a namespace or qualified global class name; therefore a symbol can use a dotted word. To represent a generic class, it can end with a grave accent and the number of parameters. To represent the root namespace, #'' is a symbol with no characters.

+: other {MyType}  ^{MyType}.
    {~ primitive: #+:}
escaped  ^{STRING}.
    {~ primitive: #escaped}
compareTo_object: other {OBJECT?}  ^{INT32}.
    {~ override: COMPARABLE >~> #compareTo:}

Various pragma and primitive messages accept the type {System.SYMBOL} to represent a method name; therefore a symbol can be any of the four message name patterns, preceded by a hash character.

Class SYMBOL contains a few methods of its own. One such usage is to have compile-time settable defined values. An assembly can initialize "defines" using a pragma.

NetworkManager.dll
    {~ library
        define: #SupportXINS;
        define: #MaxNodeCount as: '512';
        reference: 'mscorlib.dll';
        use: #System}

The development environment will provide some way to set defined values as well. In the simplest case, a command line compiler can accept switch values to set them:

sable -DSupportCorba -DSupportXINS=0 -DMaxNodeCount=2048 NetworkManager.dll.sab

Finally, the code will test and retrieve the defined values:

|allNodes| := {ARRAY[NETWORK_NODE]} new: #MaxNodeCount definedInteger.
#SupportCorba isDefined then: ["..."].
#SupportXINS definedBoolean ifTrue: ["..."].