Slag Language Primerv0.22 / 2008.07.25 / Abe Pralle

Contents

1. Audience

2. Getting Started
2.1. Setup
2.1.1. Windows
2.2. Hello World Program
2.3. Compiling
2.4. Running
2.5. Source File Organization
2.5.1. Main Project File
2.5.2. [include "filename.slag"] directive
2.5.3. [includeDir "directory_path"] directive
2.5.4. [mainClass ClassName] directive
2.6. Odds & Ends
2.6.1. Comments
2.6.2. Ellipsis Line Continuation
2.6.3. Escape Sequences

3. Conventions
3.1. Terminology
3.2. Type Names
3.2.1. Type Name Capitalization
3.2.2. Meaning
3.3. Properties (Variables and Methods)
3.3.1. Member Capitalization
3.3.2. Meaning
3.3.2.1. State (nouns)
3.3.2.2. Queries (is_X, has_X)
3.3.2.3. Conversions (to_X)
3.3.2.4. Reinterpretations (as_x)
3.3.2.5. Class Factory Methods (create)
3.3.2.6. Nonstandard Parameters (by_X)
3.3.2.7. Primitive and Compound Properties (x_of)
3.3.2.8. Containers (set, get, add)

4. Primitives
4.1 Primitive Types
4.2. Primitive Type Conversions

5. Literal Values

6. Console Input and Output
6.1. Printing Output
6.2. Input

7. Simple Classes
7.1. Key Points
7.2. Example: Name Class
7.3. Example: Stoplight Class
7.4. Example: Die
7.5. Example: Temperature Class

8. Operators
8.1. Math Operators
8.2. Bitwise Operators
8.3. Relational and Logical Operators
8.4. Range Operators
8.5. Type-checking and Cast Operators
8.6. 'duplicate' Operator

9. Control Structures
9.1. Single-line and Multi-line Commands
9.2. Selection
9.2.1. 'if'
9.2.2. 'which'
9.2.3. 'contingent'
9.3. Iteration
9.3.1. 'while'
9.3.2. 'forEach'
9.3.3. 'loop'
9.3.4. 'repeat'
9.4. try-catch and throw
9.5. Labels and Breaks
9.5.1. Labels
9.5.2. 'escapeX'
9.5.3. 'nextIteration'

10. Extended Classes
10.1. Key Points
10.2. Example: Shape class

11. Advanced Class Topics
11.1. init_class method
11.2. init_object method
11.3. Operator Methods
11.3.1. Example: CaesarCypherMessage
11.4. 'promote' Method

12. Compounds

13. 'enum' definitions
13.1. BitFlags enum augment

14. Templates
14.1. Non-template / Template Comparison
14.2. Key Points
14.3. Example: Pair
14.4. Template Conditionals

15. Aspects

16. Augments

17. Primitive Methods

18. Arrays and Lists
18.1. Arrays
18.2. Lists
18.2.1. Operators and Lists
18.2.2. Technical Details

19. Standard Library & API Reference
19.1. Global
19.2. RequiresCleanup
19.3. File I/O
19.4. HashTable
19.5. Pattern

20. SlagDoc

1. Audience
This Slag language primer is written for the experienced Java programmer. Others may benefit, but Slag concepts are often defined using Java examples and terminology.

You will need some familiarity with using the system command prompt and a generic text editor of your choice.

2. Getting Started

2.1. Setup

2.1.1. Windows

Unzip the Slag installation to disk - for example, place the "Slag" root directory in "c:\Program Files\" or another location of your choice.

Add "c:\Program Files\Slag\" (or similar) to your system path.


2.2. Hello World Program

Type in this program and save it as "hello.slag":

hello.slag
#===========================================================
# hello.slag
#
# History: 2007.08.31 / Abe Pralle - Created
#===========================================================
class Hello
  # Prints 'Hello World' to the screen.
  METHODS
    method init:
      println( "Hello World" )
endClass


2.3. Compiling

To compile "hello.slag" into "hello.exe" (VM/bytecode bundle), type the following at the command prompt:

slagc hello.slag

(or just "slagc hello")

To compile "hello.slag" to a ETC (Execution-Tree Code) file, type this:

slagc -noexe hello.slag


2.4. Running

You can run your program by typing:

hello

or

slag hello

depending on how you compiled it.


2.5. Source File Organization

2.5.1. Main Project File

The single source file name you give you give to the compiler should directly or indirectly [include] all the other source files used in the project.

The [mainClass] directive specifies the starting class of the project. An object of that type is created and its init method is called.


2.5.2. [include "filename.slag"] directive

  • Queues the named file for compilation.
  • Order is unimportant.
  • Two files can mutually reference one another without causing an error - each file will only be included once.
  • The standard library file "standard.slag" is automatically included.


2.5.3. [includeDir "directory_path"] directive

  • Recursively includes all the ".slag" files in the given directory.

Example:

project.slag
[include "monsters.slag"]
[include "heroes.slag"]

class Simulation
  ...

monsters.slag
[include "heroes.slag"]

class Monster
  ...

heroes.slag
[include "monsters.slag"]

class Hero
  ...


2.5.4. [mainClass ClassName] directive

  • Specifies a program's main class.
  • When the program is started, an object of the main class is created and its init() method is called.
  • Once that method returns the program is over.
  • If the mainClass directive is omitted, the first class the compiler encounters in the original file is assumed to be the main class.

Example:

name.slag
[mainClass StartHere]

class DoNotStartHere
  METHODS
    method init: ...
endClass

class StartHere
  METHODS
    method init: ...
endClass


2.6. Odds & Ends

2.6.1. Comments

Examples:
# Single-line comment

#{ Multi-line
comment }#


2.6.2. Ellipsis Line Continuation

...

An ellipsis (...) at the end of a line means the next line should be considered a continuation of the previous line. This is only necessary when the syntax at the end of the previous line is incomplete.

local String st

# Ellipsis required here because end of first line can be
# considered complete.
st = "hello " ...
    + "world"

# Line continuation not required here because end of first line
# is obviously incomplete.
st = "hello " +
    "world"

# Line continuation required here because the string at the end
# of the first line could be considered complete - if the
# ellipsis were omitted, the compiler would be looking for a
# closing ')' instead of string formatting arguments.
println( "2^8=$" ...
    (2^8) )
  

2.6.3. Escape Sequences

Slag supports the following control character escape sequences in character and string literals:

\n - newline (Unicode 10)
\r - return (Unicode 13)
\t - tab (Unicode 9)
\' - one single-quote
\" - one double-quote
\\ - one backslash

Slag supports arbitrary Unicode characters anywhere in the source code:

\uXXXX - where XXXX is a Unicode hex value 0000..ffff

Unlike the control sequences above, Unicode sequences are processed before parsing. In general they are used in addition to but not instead of the control sequences. For example:

println( "hello\nworld" )

# displays:
#   hello
#   world

but:

println( "hello\u000aworld" )

# Invalid - the above is as if you typed:
println( "hello
world" )

3. Conventions

3.1. Terminology

  • Primitive
    One of the fundamental types of data that more complex types are composed of - the "Lego blocks" that are used to build the castles and spaceships of our programs.

  • Class
    The type of a thing; the blueprint of an object.

  • Object
    An instance of a class; the thing itself. if "Wizard" is a class, then "Gandalf" and "Harry Potter" are two different objects of that class.

  • Reference
    A variable that points to (references) an object. Objects are designed so that they can be referenced by many different variables simultaneously. A reference is to an object like a URL is to a web site.

  • Object Property (in Java: field or instance variable)
    Object properties (or just: "properties") are the primitives and object references that are used to model each object. If we give the Name class two properties called first and last, then every name object we create will have its own first and last variable.

  • Object Method (in Java: instance method)
    Object methods are the behaviors ("functions") that each object of a class is capable of. Different objects may respond differently to the same method call. Two different things will happen if the say_name method is called on two different People objects.

  • Class Property (in Java: static field or static variable)
    Class properties are variables that are shared among all objects of a class - in other words, they pertain to the class itself rather than any specific object of the class. For example, a "Day" class might have a list of day names as a class property since each Day object wouldn't need its own separate copy of day names.

  • Class Method (in Java: static method)
    Class methods are behaviors that are related to a class but are independent of any particular object. For example, factory methods that create an appropriate type of object are class methods.

  • Property-Set and Property-Get Methods
    A property-set or property-get method is a method that has the same name as a property and is used to regulate and validate access to that variable. See "Transparent Access Methods" under "Key Points" of "Simple Classes", below.


    3.2. Type Names

    ArrayList
    Boolean
    String
    Char
    RANK
    SUIT

    3.2.1. Type Name Capitalization

    Both primitives and regular classes use the "CamelCase" convention - "ArrayList", "Boolean", etc.

    Enumerated types use "WIDE_NAMES_ALL_CAPS" naming convention for the type itself and the "wide_names" convention for the categories (i.e. enumerated values). Where in Java you might have "SortOrder.ASCENDING", in Slag you have "SORT_ORDER.ascending". The rationale is that it's more important to quickly comprehend that a type itself is an emumeration (as in method sort(SORT_ORDER c)) than to recognize that any given value comes from an enumeration.


    3.2.2. Meaning

    Classes, compounds, and enums are named for types of things - SORT_ORDER, List, etc.

    Aspects are either named for types (Reader, Writer) when incorporating them into classes will provide that functionality or as adjectives (Readable, Writable) when incorporating classes can be used for or turned to that purpose.

    For example, an ArrayList is not a Reader because it does not provide has_another and read methods, but it is Readable because it can provide a separate Reader object that can read from it.


    3.3. Properties (Variables and Methods)

    start_x
    start_y
    compare_to
    to_String
    by_ordinal
    is_digit
    has_another
    add
    get
    set

    3.3.1. Member Capitalization

    Methods, member variables, and enumeration values all use the "wide_names" convention with type names always capitalized. In Slag the role of an object "member" can be filled by a variable or a method with no perceptible difference between the two. For example, when we see "list.count", count can be an object variable access, an object method call, or a method call with a backing variable of the same name.


    3.3.2. Meaning

    3.3.2.1. State (nouns)

    Properties (variables or methods) should be nouns (things) when they specify the state of an object. "visible" would specify whether or not an object is visible.

    3.3.2.2. Queries (is_X, has_X)

    Properties is_X, has_X, and the like are used to reflect the state of an object without modeling it. Setting either opacity to zero or visible to false might set the is_visible member to false - yet setting the is_visible member to true - were it allowed - wouldn't affect either the opacity or the visibility.

    3.3.2.3. Conversions (to_X)

    Methods to_X (to_String, to_list) are used to convert an existing object into another type of object. They must be defined on a case-by-case basis.

    3.3.2.4. Reinterpretations (as_x)

    Methods as_X (as_Int32, as_Real64) are used to reassign the type of data without converting the data. For example, to_String( as_Int64(3.14), 2 ) shows the 64 bits that define the double-precision floating point value "3.14".

    3.3.2.5. Class Factory Methods (create)

    create methods are class methods used to assist the programmer in creating an object when it's not immediately clear what subclass is necessary or what parameters need to be passed in. As a hypothetical example, say you wanted to create a VideoCard object - but you weren't sure if you should instantiate a ATIVideoCard or an NVidiaVideoCard subclass. The original class designer might include a method VideoCard.create() that creates and returns the correct object for the current computer.

    3.3.2.6. Nonstandard Parameters (by_X)

    by_X methods are used to detail operations using a non-standard parameters. For example, RANK.by_name(0) would return the enumerated value that has ordinal position "0" within the enumeration.

    3.3.2.7. Primitive and Compound Properties (x_of)

    Class methods that retrieve properties from primitives or compounds generally have an "of" suffix. For example, in Slag Plasmacore, magnitude_of(Vector2) is used to return the magnitude of a given vector.

    3.3.2.8. Containers (set, get, add)

    Methods named set, get, and add, (among others) are typically used with containers - objects whose clear purpose is to contain a set of other objects.

4. Primitives

4.1 Primitive Types

The following primitive data types are built-in.

Int64, Int32, Int16, Int8
Char, Byte
Real64, Real32
Boolean, Ternary

  • IntXX are signed integers.
  • Char and Byte are unsigned integers; Char prints as a symbol.
  • In general use Int32, Char, Real64, Boolean, and Ternary.
  • Boolean: true, false.
  • Ternary: yes, no, void or gt, eq, lt (yes == gt, etc.)
  • Boolean and Ternary are logical types. The others are numerical types.
  • A Ternary value may be used almost anywhere where a "Boolean" is expected. In this case Ternary "yes" counts as "true" while "no" and "void" both count as "false".


    4.2. Primitive Type Conversions

    • Any numerical type may be assigned to a variable of any other numerical type without explicitly casting or converting the type.

    • The Boolean and Ternary logical types are never implicitly converted. They may be cast to or from any numerical type with an explict cast:

      boolean_value = Int32_value.(Boolean)

      or

      boolean_value = Boolean( Int32_value )

      When casting Boolean -> numerical:

      true  -> 1
      false -> 0

      When casting numerical -> Boolean:

      non-zero -> true
      zero     -> false

      When casting Ternary -> numerical:

      yes/gt  ->  1
      no/eq   ->  0
      void/ne -> -1

      When casting numerical -> Ternary:

      positive -> yes/gt
      zero     -> no/eq
      negative -> void/lt

  • 5. Literal Values
    Literals of various types may be expressed in the following ways.

    • Int64 - Int64(34), Int64(0), Int64(-5)
    • Int32 - 34, 0, -5
    • Int16 - Int16(34), Int16(0), Int16(-5)
    • Int8 - Int8(34), Int8(0), Int8(-5)
    • Char - 'A', '!', Char(97)
    • Byte - Byte(0), Byte(255)
    • Real64 - 3.14, 0.0, -5.2
    • Real32 - Real32(3.14), Real32(0.0), Real32(-5.2)
    • Boolean - true, false
    • Ternary - yes, no, void (or gt, eq, lt)
    • String - "Quotes are \"like this\"\n" or //Quotes are "like this"\n//
    • Range - 1..10, 9..1 step -2, 0..<count, count..>0
    • List Literal (explicit type): Real64{ 2.3, 6.7 }
    • List Literal (implicit type): { 2.3, 6.7 }, {1..5}.to_list

    See the section on Arrays & Lists for more details on list literals, readers, and readable things.

    6. Console Input and Output

    6.1. Printing Output

    print( value )
    println( value )
    println( "$ + $ = $" (a,b,a+b) )

    • The style of "string literal" (args) is called a formatted string and may be used outside of println().
    • The parenthesized arguments are inserted in place of the dollar-sign format markers in the string.
    • The following format markers are allowed:

      • $, $() - insert with default formatting
      • $(5) - right-justify in at least 5 spaces
      • $(05) - at least 5 digits, pad with 0's in front instead of spaces.
      • $(4.2) - real number with at least 4 digits before '.' and 2 digits after.
      • $(04.2) - as above but padded with 0's in front.
      • $$ - insert a single $ - not technically a format marker.


    6.2. Input

    Int32 n = input_Int32( "Enter a whole number: " )
    println( "You typed in the number $." (n) )

    String text = input_String( "Enter some text: " )
    println( "You typed in \"$\"" (text) )
    println( "You typed in $()$()$." ('"', "abc", '"') )
    println( "I need about $$3.50." )

    7. Simple Classes

    7.1. Key Points

    • Member categories are used to group class members appropriately.
      Object variables and methods are defined under PROPERTIES and METHODS headings. Class variables and methods are defined under CLASS_PROPERTIES and CLASS_METHODS.

    • Constructors are named 'init' and are usually inherited.
      All constructors are named 'init'. They are called automatically when an object is created; they can also be called by one another or at any time from outside code.

    • When there are multiple method signatures that match a call - such as an instantiation Circle() where Circle has constructors init() and *init(radius=1.0)*, preference is given to non-inherited methods defined in the extended class.

    • Class members are generally 'public'.
      The primary reasons to forbid direct access to variables (as in C++ and Java) are to promote implementation independence and control access to the class members via get/set access methods. Slag sidesteps this problem entirely (see next item) and so members are encouraged to be 'public' by default. The compiler accepts other qualifiers: private, readOnly, writeOnly. Methods can be public or private; private is like protected in Java. Variables can be public, private, readOnly, or writeOnly. A readOnly variable is one that has public read access but private write access; writeOnly is vice versa.

      Example:

      a : Int32           # public by default
      b : readOnly Int32  # public read, private write
      c : writeOnly Int32 # public write, private read
      d : private Int32   # private read/write

    • Transparent access methods eliminate get/set methods.
      For a given member variable, say:

      PROPERTIES
        x : Int32

      Transparent property-get/property-set (accessor/mutator) methods may be defined:

      METHODS
        method x.Int32: return &x
        method x( Int32 new_x ): &x = new_x

      These methods, if defined, intercept accesses to x and allow it to be validated or converted. A pair of such access methods may also be used to simulate the presence of a non-existent member variable, yielding implementation independence.

    • Parameters can auto-initialize member vars.
      Leaving the type off of a method parameter indicates that the parameter should be automatically stored into the member variable of the same name. Parameter and local variable names cannot otherwise "shadow" member variables.

    • Default parameters can be specified.
      A method parameter can be of the form "Int32 n = 3", meaning that if the argument is omitted by the caller, the value "3" will be inserted automatically. Note that the default value has to make sense from the caller's context, not the context of the class in which the method is defined.

    • Local variables declarations are preceded with 'local'.

    • Local variables may be declared as type 'var'.
      Their true type is then inferred from their initial assignment. For example, the following two lines are equivalent:

      local Range<<Int32>> range = 1..10
      local var range = 1..10

    • Object creation requires () but not "new".
      To create an object of a type, write *ClassName([params])*. No "new" keyword is required, but you must at least have empty parentheses.

      local Circle c1    # c1 references "null"
      local Circle c2()  # same as: local Circle c2 = Circle()

    • Slag's enums operate similarly to Java's.


    7.2. Example: Name Class

    Java:
    public class NameTest
    {
      static public void main( String[] args )
      {
        Name my_name = new Name( "Abe", "Pralle" );
        System.out.println( "My name is " + my_name
            + "\n(that's \"" + my_name.toRosterString()
            + "\" on the roster)" );

        // prints:
        //   My name is Abe Pralle
        //   (that's "Pralle, Abe" on the roster)
      }
    }

    public class Name
    {
      private String first, last;

      public Name( String first_name, String last_name )
      {
        first = first_name;
        last = last_name;
      }

      public String toString() { return first + " " + last; }

      public String toRosterString()
      {
        return last + ", " + first;
      }
    }

    Slag:

    name.slag
    [mainClass NameTest]

    class NameTest
      METHODS
        method init:
          local Name my_name( "Abe", "Pralle" )
          println( "My name is $\n(that's \"$\" on the roster)" ...
              (my_name, my_name.to_roster_String) )

          # prints:
          #   My name is Abe Pralle
          #   (that's "Pralle, Abe" on the roster)
    endClass

    class Name
      PROPERTIES
        first, last : String

      METHODS
        method init( first, last );
        method to_String.String: return "$ $" (first,last)
        method to_roster_String.String: return "$, $" (last,first)
    endClass


    7.3. Example: Stoplight Class

    Java:
    public class Test
    {
      static public void main( String[] args )
      {
        Stoplight light = new Stoplight();
        for (int i=0; i<4; i++)
        {
          System.out.println( light );
          light.cycle();
        }

        # prints:
        #   a RED light
        #   a GREEN light
        #   a YELLOW light
        #   a RED light

      }
    }

    public class Stoplight
    {
      enum StoplightColor
      {
        GREEN, YELLOW, RED;
      };

      private StoplightColor state;

      public Stoplight() { state = StoplightColor.RED; }
      public Stoplight( StoplightColor state )
      {
        this.state = state;
      }

      public void cycle()
      {
        switch (state)
        {
          case GREEN:
            state = StoplightColor.YELLOW; break;

          case YELLOW:
            state = StoplightColor.RED; break;

          case RED:
            state = StoplightColor.GREEN; break;
        }
      }

      public String toString()
      {
        return "a " + state + " light";
      }
    }

    Slag:

    stoplight.slag
    [mainClass StoplightTest]

    class StoplightTest
      METHODS
        method init:
          local Stoplight light()
          forEach (1..4)
            println( light )
            light.cycle
          endForEach

          # prints:
          #   a red light
          #   a green light
          #   a yellow light
          #   a red light

    endClass

    enum STOPLIGHT_COLOR
      CATEGORIES
        green, yellow, red
    endEnum

    class Stoplight
      PROPERTIES
        state : STOPLIGHT_COLOR

      METHODS
        method init( state = STOPLIGHT_COLOR.red );

        method cycle:
          state = state.next_in_cycle

        method to_String.String:
          return "a $ light" (state)

    endClass


    7.4. Example: Die

    die.slag
    [mainClass DieTest]

    class DieTest
      METHODS
        method init:
          local Die d6(6), d8(8)
          println( "D6: $" (d6.roll) )
          println( "D6: $" (d6.roll) )
          println( "D6: $" (d6) )
          println( "D8: $" (d8.roll) )
          println( "D8: $" (d8.roll) )
    endClass

    class Die
      PROPERTIES
        sides, value : Int32

      METHODS
        method init( sides ): roll

        method roll.Int32:
          value = random_Int32( 1, sides )
          return value

        method to_String.String: return "$" (value)
    endClass


    7.5. Example: Temperature Class

    temperature.slag
    [mainClass TemperatureTest]

    class TemperatureTest
      METHODS
        method init:
          local Temperature abs_zero( 0.0, TEMP_SCALE.kelvin )
          println( "Abs zero is $(4.2)K / $(4.2)C / $(4.2)F" ...
              (abs_zero.k, abs_zero.c, abs_zero.f) )

          local Temperature boiling( 212.0, TEMP_SCALE.fahrenheit )
          println( "Boiling is  $(4.2)K / $(4.2)C / $(4.2)F" ...
              (boiling.k, boiling.c, boiling.f) )

          local Temperature freezing( 0.0, TEMP_SCALE.celsius )
          println( "Freezing is $(4.2)K / $(4.2)C / $(4.2)F" ...
              (freezing.k, freezing.c, freezing.f) )

    endClass

    enum TEMP_SCALE
      CATEGORIES
        fahrenheit, celsius, kelvin
    endEnum

    class Temperature
      PROPERTIES
        f : Real64

      METHODS
        method init( Real64 t=0.0,
            TEMP_SCALE scale=TEMP_SCALE.fahrenheit ):
          which (scale)
            case TEMP_SCALE.fahrenheit: f = t
            case TEMP_SCALE.celsius:    c = t
            case TEMP_SCALE.kelvin:     k = t
            others: throw InvalidOperandError( "scale" )
          endWhich

        method c( Real64 cels ): f = (9.0/5.0) * cels + 32.0
        method c.Real64:         return (5.0/9.0) * (f - 32.0)

        method k( Real64 kelvin ): c = kelvin - 273.15
        method k.Real64:           return c + 273.15
    endClass

    8. Operators

    8.1. Math Operators

    + - / *
    %   (modulo)
    ^   (exponentiation)
    i++ (increment)
    i-- (decrement)

    Modulo operates differently than in Java and C++ when used with negative values. Any N % M yields 0..(M-1), preserving the pattern when N is negative. For example, -2 % 10 == 8. In addition, modulo behaves usefully with real numbers - (3.1 % 1.4 == 0.3).

    Increment and decrement must be used as stand-alone commands - they can't be part of a larger expression as with Java and C++. They simply add one or subtract one from the given variable.

    When used with lists of primitive values, arithmetic operators automatically operate on each element of the list.


    8.2. Bitwise Operators

    & (bitwise and)
    | (bitwise or)
    ~ (bitwise xor)
    ! (bitwise complement)

    a.left_shifted(b)
    a.right_shifted(b)
    a.right_xshifted(b)
    a.left_rotated(b)
    a.right_rotated(b)

    The bit rotate and shift commands are actual operators even though they use method call syntax.


    8.3. Relational and Logical Operators

    • Relational Operators
      == != <= < > >=
      obj1 is obj2
      obj1 isNot obj2

      The is operator compares two references to see if they're referencing the same object.

      Using "==" between two references calls "obj1.equals(obj2)".

    • Logical Operators
      and
      or
      xor
      not

      Logical operators only work on Boolean values.


    8.4. Range Operators

    low upTo high [step step_size]
    low upToLessThan high [step step_size]
    high downToGreaterThan low [step step_size]
    low .. high [step step_size]
    low ..< high [step step_size]
    high ..> low [step step_size]

    The range operators create a sequence (technically a readable Range object) that can be iterated over or turned into a list with the to_list method.

    upTo (..) counts from the first up to and including the last. A negative step size must be explicitly stated if you wish to count backwards.

    upToLessThan (..<) counts from the first up to but not including the last.

    downTo counts from the first down to and including the last. The step size is assumed to be (-1) if no other step size is given. downTo is the only range operator that doesn't have a shorthand equivalent.

    downToGreaterThan (..>) counts from the first down to but not including the last. The step size is assumed to be (-1) if no other step size is given.

    # print numbers 1 through 10
    forEach (n in 1..10) println( n )

    # print numbers 10 down to 1
    forEach (n in 10..1 step -1) println( n )


    8.5. Type-checking and Cast Operators

    obj instanceOf ClassOrAspect
    obj notInstanceOf ClassOrAspect
    obj.(BaseOrExtendedType)
    num.(OtherPrimitiveType)
    OtherPrimitiveType(num)

    The instanceOf operator returns true if the class or aspect on the right-hand side is the type or is a base type of the object on the left-hand side.

    A base Shape reference can be cast to an extended Circle reference with the syntax "shape.(Circle)".

    For example:

    forEach (shape in shape_list)
      if (shape instanceOf Circle) record_radius(shape.(Circle).radius)
    endforEach

    An extended reference can also be cast to a base type for purposes of selecting which overloaded method you wish to call (assuming there are several). Casting references like this never converts one type of object to another; it merely causes the compiler to treat an object as a more specialized or more generalized base class or sub class.

    Primitives may be cast in the same style: "intval = realval.(Int32)". They may also be cast using "constructor-style" notation: "intval = Int32(realval)".


    8.6. 'duplicate' Operator

    value = duplicate( value )

    Any object or primitive may be cloned with the operator duplicate(existing).

    duplicate can be called on any value-type (such as a primitive or a compound) with no other preparation.

    To be able to duplicate an object, you must define the create_duplicate.ClassType method. The decision of whether to make a deep or shallow copy can be made on a case-by-case basis.

    [mainClass Test]

    class Name
      PROPERTIES
        first, last : String

      METHODS
        method init( first, last );

        method create_duplicate.Name:
          return Name( first, last )

        method to_String.String: return "$ $" (first,last)
    endClass

    class Test
      METHODS
        method init:
          local Name name1( "Abe", "Pralle" )
          local Name name2 = duplicate( name1 )
          println( "name1: $" (name1) )
          println( "name2: $" (name2) )
          println( "name1 is name2: $" (name1 is name2) )
    endClass

    9. Control Structures

    9.1. Single-line and Multi-line Commands

    • If a control structure has commands on the same line after the condition, it is taken to be a single-line statement - all commands on that line (separated by semi-colons) are part of the body and no terminal end marker is required.

    • If a control structure doesn't have body commands following on the same line it's considered a multi-line command and an appropriate "end" command is looked for.

    • A multi-part if/elseIf/else cannot mix and match single and multi-line blocks. They must all be single or all be multi (otherwise nested if statements could be ambiguous).

    • A single-line statement may not have any nested control structures.

    • Not all control structures have a single-line variant.

    • As a rule, any command that evaluates a condition for its trueness or falseness surrounds that condition with parentheses. Thus "if (condition)" requires parentheses but "return expression" doesn't.


    9.2. Selection

    9.2.1. 'if'

    if (condition)
      ...
    elseIf (condition)
      ...
    else
      ...
    endIf

    if (condition) single; line; cmds
    elseIf (condition) single; line; cmds
    else single; line; cmds


    9.2.2. 'which'

    which (expr)
      case 1: ...
      case a+b:  ...
      others:  ...
    endWhich

    • The which structure replaces the switch or select of other languages.
    • Its cases may be any expressions, not just constants.
    • Internally which statements are converted into if statements. The expression is saved in a local variable and subsequently compared to each case value.
    • No "break" commands are necessary.
    • "others:" replaces "default:"


    9.2.3. 'contingent'

    contingent
      # body
      ...
      necessary( condition )
      ...
      sufficient( condition )
      ...
    satisfied
      # satisfied clause
      ...
    unsatisfied
      # unsatisfied clause
      ...
    endContingent

    Slag introduces contingents as a new kind of conditional. They are similar in flow to a try/catch block but they deal with positive and negative logic tests rather than error generation.

    • Any commands may be placed in the body. These commands can include necessary() and sufficient() logic tests.

    • If a necessary condition is true, execution proceeds to the next command. If false, execution jumps to the unsatisfied clause.

    • If a sufficient condition is false, execution proceeds. If true, execution jumps to the satisfied clause.

    • If all the body code is executed with no necessary conditions being false and no sufficient conditions being true, execution proceeds with the satisfied clause.

    • If the satisfied clause executes, the unsatisfied clause is skipped.


    9.3. Iteration

    9.3.1. 'while'

    while (condition)
      ...
    endWhile

    while (condition) single; line; cmds


    9.3.2. 'forEach'

    forEach (expr)
      ...
    endForEach


    forEach (var_name in expr)
      ...
    endForEach


    forEach (var_name of expr)
      ...
    endForEach


    forEach (expr) single; line; cmds
    forEach (var_name in expr) single; line; cmds
    forEach (var_name of expr) single; line; cmds
     
    The forEach command iterates over each element of a Reader or Readable expression. These include lists ({3,4,5}), ranges (3..5), and files.

    The simple forEach "forEach (1..10)" just repeats its body the given number of times.

    The *forEach-in" creates a local variable with the specified name and sets it to each element in the expression.

    The *forEach-of" creates a local Int32 variable with the specified name and sets it to the index of each element in the expression.


    9.3.3. 'loop'

    loop
      ...
    endLoop


    loop single; line; cmds

    The loop command defines an infinite loop. This is conceptually preferable over a "while (true)" loop.


    9.3.4. 'repeat'

    repeat
      ...
    until (condition)

    The body is repeated until the condition becomes true.


    9.4. try-catch and throw

    try
      ...
      if (xyz) throw SomeError()
      ...
    catch (ErrorType1 err)
      ...
    catch (ErrorType2 err)
      ...
    endTry

    Slag's Error class is analogous to Java's Exception class.

    There are no checked exceptions in Slag - errors may be thrown at any desired point and enclosing try-catches are entirely optional.

    Error objects that are instanceOf the first catch type are handled by that catch, and so on.


    9.5. Labels and Breaks

    9.5.1. Labels

    if::label_name (condition)
    while::label_name (condition)
    repeat::label_name
    ...

    Any structure can be labeled by following the initial keyword with a double-colon and then an arbitrary label name.


    9.5.2. 'escapeX'

    escapeIf
    escapeWhich
    escapeContingent
    escapeWhile
    escapeLoop
    escapeRepeat
    escapeForEach
    escapeTry
    escape label_name

    The 'escape' command is analogous to Java's "break". escapeWhile, for instance, breaks out of the innermost while command that encloses the escapeWhile.


    9.5.3. 'nextIteration'

    nextIteration
    nextIteration label_name

    The 'nextIteration' command is analogous to Java's "continue". Used by itself it skips the remaining body of the current forEach, while, repeat, or loop but continues the loop. It can be used with a label to target an outer loop.

    forEach::next_number (n in low..high)
      forEach (d in {2,3..sqrt(n)} )
        if (n % d == 0) nextIteration next_number
      endWhile
      println( "$ is a prime number" (d) )
    endWhile

    10. Extended Classes

    10.1. Key Points

    • Each class extends exactly one other base class.

    • If no base class is specified, a class extends class Object by default.

    • 'init' constructor methods are inherited by extended classes unless they are "limited".

      Example:

      limited method init:
        # non-inherited constructor

      method init( Int32 n ):
        # inherited constructor

      private limited init( String st ):
        # private, non-inherited constructor

    • Use the syntax prior.method_name instead of super.method_name to call the overridden version of a method instead of the current version. This applies to constructors as well (prior.init).

    • As in Java, the type of the reference variable determines what methods can be called. The type of the object itself determines the version of the method that is called (single dynamic dispatch).

    • Abstract methods are defined by putting the keyword "abstract" in place of any other method body.

    • Use the operators instanceOf and notInstanceOf to determine if an object is at least a certain class (and possibly more).


    10.2. Example: Shape class

    shape.slag
    [mainClass ShapeTest]

    class ShapeTest
      METHODS
        method init:
          local Shape s1 = Circle( 4 )
          local Shape s2 = Rectangle( 3, 4 )
          local Shape s3 = Square( 5 )
          display_info( s1 )
          display_info( s2 )
          display_info( s3 )

        method display_info( Shape s ):
          println( s )
          println( "  is a shape:     $" (s instanceOf Shape) )
          println( "  is a circle:    $" (s instanceOf Circle) )
          println( "  is a rectangle: $" (s instanceOf Rectangle) )
          println( "  is a square:    $" (s instanceOf Square) )
          println
    endClass

    class Shape
      METHODS
        method name.String:      abstract
        method area.Real64:      abstract
        method perimeter.Real64: abstract

        method to_String.String:
          return "A $ with area $ and perimeter $" ...
              (name,area,perimeter)
    endClass

    class Circle : Shape
      PROPERTIES
        radius : Real64

      METHODS
        method init( radius );

        method name.String:  return "circle"
        method area.Real64:  return pi * radius^2
        method perimeter.Real64: return pi * radius
    endClass

    class Rectangle : Shape
      PROPERTIES
        width, height : Real64

      METHODS
        method init( width, height );

        method name.String:  return "rectangle"
        method area.Real64:  return width * height
        method perimeter.Real64: return 2*width + 2*height
    endClass

    class Square : Rectangle
      METHODS
        method init( Real64 size ): prior.init( size, size )
        method init( width, height ): throw UnsupportedMethodError()

        method name.String:  return "square"
    endClass

    11. Advanced Class Topics

    11.1. init_class method

    class MathConstants
      CLASS_PROPERTIES
        x=3 : Int32
        y   : Int32

      CLASS_METHODS
        method init_class:
          # implicit "x = 3" statement here
          y = 4
    endClass

    Every class has a hidden method named init_class() that is called once at the beginning of a Slag program. Its purpose is to perform all the class property initializations. If you define a class method called init_class(), any statements you write will be performed after class property initializations.


    11.2. init_object method

    class Alpha
      PROPERTIES
        a=3 : Int32

      METHODS
        method init_object:
          # implicit "a = 3" here
          println( "a = $" (a) )
    endClass

    class Beta : Alpha
      PROPERTIES
        b=4 : Int32

      METHODS
        method init_object:
          # implicit "prior.init_object; b = 4" here
          println( "b = $" (b) )

        method init:
          println( "Beta init" )
    endClass

    class TestInitObject
      METHODS
        method init:
          Beta()
            # prints:
            #   a = 3
            #   b = 4
            #   Beta init
    endClass

    Every class has a hidden object method called init_object() that is called when an object is first created, before a regular init() method is called. Every init_object() method begins with a call to prior.init_object(), then performs property initialization. If you define an init_object() method, any statements you write will be performed after the automatic initialization.


    11.3. Operator Methods

    method op+( Type rhs ).Type:
    method op-( Type rhs ).Type:
    method op*( Type rhs ).Type:
    method op/( Type rhs ).Type:
    method op%( Type rhs ).Type:
    method op^( Type rhs ).Type:
    method op&( Type rhs ).Type:
    method op|( Type rhs ).Type:
    method op~( Type rhs ).Type:
    method op!.Type:

    All of the basic arithmetic, relational, and bitwise operators can be defined on a per-class basis.

    When a is a reference, expressions of the form "a + b" are transformed into "a.op+(b)". The op+ must be defined in a's class, but the parameter and return type can be any desired value. Multiple overloaded methods are fine under the normal rules (signatures must vary).

    11.3.1. Example: CaesarCypherMessage

    caesar.slag
    [mainClass Test]

    class Test
      METHODS
        method init:
          local CaesarCypherMessage mesg( "Slag is awesome!" )
          println( mesg - 1 )
          println( mesg + 1 )
    endClass

    class CaesarCypherMessage
      PROPERTIES
        message : String

      METHODS
        method init( message );

        method op+( Int32 shift ).CaesarCypherMessage:
          local StringBuilder result()
          forEach (ch in message)
            if (ch.is_uppercase())
              ch = Char( (ch+shift).wrap('A'.Int32,'Z'.Int32) )
            elseIf (ch.is_lowercase())
              ch = Char( (ch+shift).wrap('a'.Int32,'z'.Int32) )
            endIf
            result.add( ch )
          endForEach
          return CaesarCypherMessage( result.to_String )

        method op-( Int32 shift ).CaesarCypherMessage:
          return this + (-shift)

        method to_String.String: return message
    endClass


    11.4. 'promote' Method

    If "a + b" is encountered and b is an object but a is not, the expression is converted to:

    BType.promote(a) + b

    For example, here's a fragment of some "BigInteger" class that would handle "5 + BigInteger(7)" (which would become "BigInteger.promote(5).op+(BigInteger(7))":

    class BigInteger
      ...
      CLASS_METHODS
        method promote( Int32 n ).BitInteger: return BigInteger(n)

      METHODS
        method init( Int32 n ): ...
        method op+( BigInteger other ).BigInteger: ...
    endClass

    12. Compounds
    Syntax:

    compound CompoundName( Type1 name1, Type2 name2, ... )

    Compounds are composite by-value types that are composed of one or more primitives or other compounds.

    • Compounds are by-value types
    • Compounds are a composite of other, existing primitives and compounds (no references allowed).
    • Compound members can be individually read but not individually written.
    • To alter a compound, it must be reassigned as a compound with different values.
    • More efficient than objects when:
      • There are a small number of members.
      • Multiple references to the same data are unnecessary.
      • There are no codependent elements (if A contains a B, B can't contain an A).

    Example:

    compound.slag
    compound MonthDayYear( Int32 month, Int32 day, Int32 year )
    ...
    local MonthDayYear today( 1, 18, 2008 )  # okay
    local MonthDayYear payday = today      # okay
    payday.year = 2009  # ILLEGAL - can't write to individual members!
    payday = MonthDayYear( today.month, today.day, 2009 )   # okay

    13. 'enum' definitions
    Enums (rhymes with Tums) offer the same power and flexibility as they do in Java. By convention the type-name is in ALL_CAPS to make enum parameters easy to spot; the categories (enum values) are in lowercase.

    A simple enum:

    enum AI_STATE
      CATEGORIES
        idle, wander, seek
    endEnum
    ...
    class Monster
      PROPERTIES
        plan = AI_STATE.idle : AI_STATE
      ...
      METHODS
        method decide_move:
          which (plan)
            case AI_STATE.idle: ...
            case AI_STATE.wander: ...
            case AI_STATE.seek: ...
            ...

    A more complex enum that adds an additional property to each category:

    enum TERRAIN_FLAGS
      CATEGORIES
        obstructed(1), mire(2), special(4)
      
      PROPERTIES
        mask : Int32

      METHODS
        method init( mask );
    endEnum

    ...
    method has_flag( TERRAIN_FLAGS f ):
      return (flags & f.mask) != 0

    method is_obstructed.Boolean:
      return has_flag(TERRAIN_FLAGS.obstructed)

    13.1. BitFlags enum augment

    An enumeration augment called BitFlags is provided in the standard library to meet a common need for enumerated values: having a set of bit flags that can be manipulated with bitwise OR (setting/merging), AND (clearing), and NOT (toggling). See type BitFlags in the API for an example.

    14. Templates

    14.1. Non-template / Template Comparison

    Non-templated example:

    class Test
      METHODS
        method init:
          local IntWrapper integer(25)
          local RealWrapper real(3.14)
    endClass

    class IntWrapper
      PROPERTIES
        value : Int32

      METHODS
        method init( value );
    endClass

    class RealWrapper
      PROPERTIES
        value : Real64

      METHODS
        method init( value );
    endClass

    Templated example:

    class Test
      METHODS
        method init:
          local Wrapper<<Int32>> integer(25)
          local Wrapper<<Real>> real(3.14)
    endClass

    class Wrapper<<DataType>>
      PROPERTIES
        value : DataType

      METHODS
        method init( value );
    endClass


    14.2. Key Points

    • Templates are a C++-style mechanism for instantiating specialized classes; they are not like Java-style generics.

    • Templates are to classes what classes are to objects. You can define a single template and generate several classes from it as necessary.

    • You write a template class like you would a normal class except that certain key types are defined as placeholder types rather than being a specific type.

    • When you refer to the template in code you must supply exact types to substitute for the placeholder types. Each unique template reference causes the compiler to generate an entirely new class that includes the unique substitution types in lieu of the placeholder types. At run-time, instanced template classes are just as fast and efficient as non-template-based classes (because at run-time they are just another class).

    • Template placeholder types can have default substitution types in the same way that methods can have default parameter values. Default substitution types may specify earlier placeholder types, in which case both placeholder types are assigned the same substitution type.

    • Compounds can be defined as templates.


    14.3. Example: Pair

    class Test
      METHODS
        method init:
          local Pair<<String>> name( "Abe", "Pralle" )
          local Pair<<Int32,Real64>> half_circle( 180, pi )
          println( "name: $" (name) )
          println( "half_circle: $" (half_circle) )
    endClass

    class Pair<<FirstType,SecondType=FirstType>>
      PROPERTIES
        first  : FirstType
        second : SecondType

      METHODS
        method init( first, second );
        method to_String.String: return "[$,$]" (first,second)
    endClass


    14.4. Template Conditionals

    Template conditionals allow template instances to be customized for particular substitution types by including or omitting certain members.

    Examples:

    class Vector<<DataType>>
    [if DataType is reference]
      BASE_TYPES
        BaseVector<<Object>>
    [else]
      BASE_TYPES
        BaseVector<<DataType>>
    [endIf]
    ...
        
    class MyArray<<DataType>>
      METHODS
        method init( Int32 size ):
          ensure_capacity( size )
          forEach (i in 1..size) set( i, default_value )

    [if DataType is reference]
      METHODS
        method default_value.DataType: return null

    [elseIf DataType is numerical]
      METHODS
        method default_value.DataType: return 0

    [elseIf DataType == Boolean]
      METHODS
        method default_value.DataType: return false

    [else]
      ...
    [endIf]
    ...

    ...
    [if DataType == Real64 or DataType == Real32]
      METHODS
        method divide( DataType a, DataType b ).DataType:
          return a / b
    [else]
      METHODS
        method divide( DataType a, DataType b ).DataType:
          if (b == 0) throw DivideByZeroError()
          return a / b
    [endIf]

    • Use '==', '!=', 'instanceOf', and 'notInstanceOf' to compare two specific types.

    • Use 'is' and 'isNot' to determine whether or not a specific type is of a certain category. Categories include:

      • reference (any class, aspect, or enum)
      • compound
      • enum (also counts as a reference)
      • primitive
      • numerical (any of the integer or real primitives)
      • logical (a Boolean or Ternary primitive)
      • integer (Int64, Int32, Int16, Int8, Char, or Byte)
      • real (Real64 or Real32)

    • Use 'or' operators to allow multiple type comparisons to satisfy an 'if' or 'elseIf'.

    • Template directives don't support more complex logic (most notably there is no 'and').

    • Template directives may be nested.

    15. Aspects
    Slag aspects are a new approach to aspect-oriented programming and a powerful replacement for Java interfaces.

    In simplest terms, an aspect is like a Java interface that can include method bodies and additional instance variables in its definition. Where Java implements interfaces, Slag incorporates aspects. During aspect incorporation, aspect method bodies are "cut & pasted" into existing methods of the same name. The syntax for controlling the order of this aspect layering is similar to that of making a prior (super-class) call.

    Example:

    class Test : IncreaseFormatValue, AddFormatBrackets
      METHODS
        method init:
          println( format(5) )  #prints out: <6>

        method format( Int32 n ).String:
          return "$" (n)
    endClass

    overlaying aspect IncreaseFormatValue
      METHODS
        method format( Int32 n ).String:
          return underlying( n + 1 )
    endAspect

    overlaying aspect AddFormatBrackets
      METHODS
        method format( Int32 n ).String:
          return "<$>" (underlying(n))
    endAspect

    Note that after the above example is compiled, there is a single format method that is called once.

    • Each aspect must be declared as overlaying or underlying as a whole, which indicates the default order methods should be merged in. Individual methods can then be specified as being underlying or overlaying contrary to the default.

    • In the class declaration:

      class ExtendedClass : BaseClass, AspectAlpha, AspectBeta

      methods are layered in the following order (the top one listed here is the bottom-most layer):

      • All methods from BaseClass.
      • Underlying methods from AspectAlpha.
      • Underlying methods from AspectBeta.
      • All methods from ExtendedClass.
      • Overlaying methods from AspectAlpha.
      • Overlaying methods from AspectBeta.

        (Note that all "underlying" code references from underlying AspectAlpha methods would be adjusted to be "prior" calls instead)

    • Methods may reference "underlying" code even if there is no underlying code as long as no attempt is made to use an underlying return value. Such calls to non-existent code are silently removed.

    • Aspects can be used just like Java interfaces (where all methods are abstract) if desired.

    • Object references can be of aspect types rather than class types. The same method-calling rules and restrictions apply as are found with Java interfaces.

    16. Augments
    Augments are a way to add additional members and base types to a class separate from its primary definition.

    Examples:

    # Operators methods (such as op+) are only automatically added
    # to primitive-type lists.  Add them the "Complex" ArrayList:
    augment ArrayList
    [if DataType == Complex]
      BASE_TYPES
        ListArithmetic<<Complex>>
    [endIf]
    endAugment

    The following example is equivalent to the example above:

    # Operators methods (such as op+) are only automatically added
    # to primitive-type lists.  Add them the "Complex" ArrayList:
    augment ArrayList<<Complex>>
      BASE_TYPES
        ListArithmetic<<Complex>>
    endAugment

    # Add a debugging aspect to class MyGame
    augment MyGame : DebugMyGame;

    # Add a new class method to class Global
    augment Global
      CLASS_METHODS
        method num_digits( Int32 num, Int32 base=10 ).Int32:
          local Int32 count = 0
          while (num != 0) count++; num /= 10
          return count
    endAugment

    17. Primitive Methods
    Source code:

    Int32 n = 3
    n = n.plus( 5 )

    Compiler converts to:

    Int32 n = 3
    n = Global.plus( n, 5 )

    To define the plus() class method in class Global:

    augment Global
      CLASS_METHODS
        method plus( Int32 num, Int32 delta ).Int32: return num + delta
    endAugment

    18. Arrays and Lists

    18.1. Arrays

    local Array<<Int32>> nums(10)
    nums[0] = 5
    println( nums[1] )

    • Arrays are built-in to Slag, but for most applications the programmer should use Lists instead.

    • Arrays are objects and can be stored in an Object reference.

    • Arrays are not co-variant; an array of Circle objects cannot be treated as an array of Shape objects.


    18.2. Lists

    local Int32[] nums()
    local Int32[] nums(8)
    local Int32[] nums = Int32[8]
    local Int32[] nums = { 3, 4, 5 }
    local Int32[] nums = { -5..-1, 1..5 }
    local ArrayList<<Int32>> nums()
    nums.add( x )
    nums.add( y )
    println( nums.count )
    forEach (n in nums) println( n )
    forEach (index of nums) println( n[i] )
    nums[ {0,n.count-1} ] = 1
    nums[1..n.count-2] = 0
    nums.clear

    • "Array-style" notation creates and manipulates Lists (aka vectors) in Slag.

    • List references are created as type List<<ElementType>> and literal lists
      are created as type ArrayList<<ElementType>> (which is compatible with List).

    • Multi-dimensional lists are "lists of lists" and are not required to be rectangular.

    18.2.1. Operators and Lists

    The standard numerical and bitwise operators are defined for lists of the appropriate primitive types. They provide list functionality that's similar to Matlab's matrix functionality:

    local Int32[] nums = {1..10}

    # {1,2,3,4,5,6,7,8,9,10}
    println( nums )

    # {1,0,1,0,1,0,1,0,1,0}
    println( nums % 2 )

    # {false,true,false,true,false,true,false,true,false,true}
    println( nums % 2 == 0 )

    # {2,4,6,8,10}
    println( nums[nums % 2 == 0] )


    18.2.2. Technical Details

    • The following:

      local String[] ids = { "Some", "work", "of", "noble", "note" }
      local Int32[][] map = { {1,2,3}, {4,5,6} }

      is equivalent to:

      local List<<String>> ids = ...
          ArrayList<<String>>(5).add("Some") ...
          .add("work").add("of").add("noble").add("note")

      local List<<List<<Int32>>>> map = ...
          ArrayList<<List<<Int32>>>>(2) ...
          .add( ArrayList<<Int32>>(3).add(1).add(2).add(3) ) ...
          .add( ArrayList<<Int32>>(3).add(4).add(5).add(6) )

    • Every List<<DataType>> incorporates aspect Readable<<DataType>>. This
      means that create_reader may be called to obtain a Reader<<DataType>> object.

    • Something that's Readable can, by definition, have create_reader called
      upon it over and over successfully, for the same result each time. Hence Readers are not themselves Readable because they are not suited to recycling (nor is a Readable thing itself a Reader).

    19. Standard Library & API Reference
    Refer to a Slag language reference (such as slag_reference.html) for an description of all the classes and methods available to Slag programs. Here's a brief overview of some of them:

    19.1. Global

    Many of the "built-in" methods and methods that operate on primitives and compounds are actually Global class methods.


    19.2. RequiresCleanup

    class OpenNetworkConnection : RequiresCleanup
    ...
    method cleanup:
      ...

    Objects of classes that incorporate RequiresCleanup have their cleanup method called when they have no more references to them and their memory is about to be freed. Object allocation and deletion involving objects that require cleanup is a little slower than for other objects.


    19.3. File I/O

    # Reverse a text file
    class Reverser
      METHODS
        method init:
          if (command_line_args.count == 0)
            println( "Usage: slag progname filename" )
            return
          endIf
          local Char[] chars()
          forEach (ch in File(command_line_args[0])) chars.add( ch )
          chars.reverse
          Pump<<Char>>( chars, File("reversed.txt") ).pump_all
    endClass


    19.4. HashTable

    class HashTableExample
      METHODS
        method init:
          local HashTable<<String,Int32>> ages()
          ages["Abe"] = 34
          ages["Murphy"] = 30
          ages["Leah"] = 20
          println( "Leah is $ years old!" (ages["Leah"]) )
          println( "Murphy is $ years old!" (ages["Murphy"]) )
          println( "Abe is $ years old!" (ages["Abe"]) )
    endClass


    19.5. Pattern

    Slag patterns use a syntax that's closer to EBNF than to regular expressions.

    patterns.slag
    class PatternTest
      METHODS
        method init:
          local Pattern name_pattern( "{id} ws {id}" )
          local Pattern phone_number_pattern(
              "(not digit)* {digit*3} (not digit)* "...
              "{digit*3} (not digit)* {digit*4}" )

          local String name = input_String( "Enter your name: " )
          while (not name.matches(name_pattern))
            name = input_String(
                //That should be "first last".  Try again: // )
          endWhile

          println( name.replace( name_pattern,
              //Hi $[0] $[1] - or should I say "$[1], $[0]"?// ) )

          local String number = input_String(
              "Now enter your phone number: " )

          while (not number.matches(phone_number_pattern))
            number = input_String( "Try again (10 digits): " )
          endWhile

          println( "The digit groups are: $"
              (number.extract_groups(phone_number_pattern)) )

          println( "Your number is $" (number.replace( name_pattern,
              "($[0]) $[1]-$[2]" )) )

    endClass

    Patterns are composed of the following symbols:

    '...'      - literal text term, e.g. 'simulation_speed'
    ()         - group of terms - counts as a term itself
    {}         - tagged group of terms - when matched, saved in results
                 list
    ?          - zero or one of the preceding term
    +          - one or more of the preceding term
    *          - zero or more of the preceding term
    *3         - exactly 3 of the preceding term
    *4..6      - 4, 5, or 6 of the preceding term
    'c1'..'c2' - matches any single character in the range c1 through c2
    not        - matches any single character so long as the next term
                 is not satisfied.
    or         - pattern can be satisfied by previous term or next term

    The following identifiers are also defined as the specified patterns:

          space - ' '
            tab - [tab character]
     whitespace - (space or tab) *
             ws - whitespace
          digit - ('0'..'9')
      lowercase - ('a'..'z')
      uppercase - ('A'..'Z')
         letter - (lowercase or uppercase)
             id - (letter or '_')
             lf - [Char(10)]
             cr - [Char(13)]
            eol - cr? lf
      backslash - '\'
          quote - [']
            any - [any character]

    20. SlagDoc
    Slag has an autodocumentation tool called "slagdoc", available in the root directory of the Slag distribution. Run it without arguments - "slagdoc" - to see how to use it.

    SlagDoc recognizes comments immediately following the first line of a type definition, a member variable declaration, or a method definition and generates documentation containing those comments. No special tags are recognized.

    Any comment that begins more than one space after the initial "#" is considered indented and is displayed in a monospace font.

    See "standard.slag" for examples of writing SlagDoc comments.