L Language Reference

 



Table Of Contents

    Comments
        Line Comments
        Block Comments

    Atomic Objects
        Creating Atomic Objects

    Aggregate Objects
        Creating Aggregate Objects
        Accessing Members by Name
        Accessing Members by Index
        Adding Members
        Removing Members
        (Re)Naming Members
        Checking for Member Presence
        Using an Aggregate Like a Stack
        Using an Aggregate Like a Queue
        Sorting Aggregate Members
        Assigning Types to Aggregates
        Creating Methods for Aggregates 
            Object Class Initialization/Termination Methods
            Object Instance Initialization/Termination Methods
            User-Defined Methods
        Data Scoping  
            Method Local Data
            Method Arguments
            Method Instance Data
            Package Data
            Module Data
            Global Data
            Scope Export
        Calling Methods
        Spoofing  
            Calling an Object's Method on this Object's Instance
            Calling Another Object's Method on Yet Another Object's Instance
        Executors  
            Using an Executor for Delayed Execution
            Inline Executors
        Prototyping Classes  
            Prototyping Class Members
            Prototyping Aggregated Classes
        Member Virtualization  
            Virtualizing a Specific Member
            Virtualizing All Public Data Members
            Invoking Virtualizers
            Intrinsic Virtualizers
            Global Virtualizers

    Built-In Operators

    Operator Precedence

    Special Object Operators

    Built-In String Functions

    Object Type Checking
        Checking for Atomic Types
        Checking for Untyped Aggregates
        Checking for Typed Aggregates
        Checking for Multiple Aggregated Types

    Automatic Type Conversion

    Explicit Type Conversion

    String Conversion Formatting

    References

    Indirection

    Program Flow Control
        Global Methods for an L Program
        Conditional Execution
        Multi-Conditional Execution
        Unconditional Looping
        Conditional Looping
        List Looping
        List Looping with Removal
        Early Loop Continuation
        Early Loop Exit
        Early Nested Loop Continuation Using Loop Naming
        Early Nested Loop Exit Using Loop Naming
        Exception Handling
            Throwing Exceptions
            Catching Exceptions
            Retrying Exceptions
        Parallel Execution
            Explicit Parallelization
            Iterated Parallelization

    Inline C++ Code
        Writing Inline Code
        Accessing L Variables

    Wrapping a C++ Class

    Patterns
        Expression Notation
        Defining a Pattern
        Using Patterns
        Recursive Patterns

    File Inclusion

    Text Macros
        Basic Macro Symbols
        Macros with Arguments

   Conditional Compilation  
  
Comments Line Comments

// This is a whole line comment.

local x = 1;  // This comment occurs after a complete statement.

local x = [ member1 = 1,   // Here's line a comment...
            member2 = 2,   // and another within a single statement.
            member3 = 3 ];

Block Comments

/*
  This is a bunch of comment text.
  It can go on and on until the end of comment symbol
  is encountered below.
*/

/* But do not nest them:
   /* Nested block comments like this are illegal. */
*/
Atomic Objects

Creating Atomic Objects

local aVariable;             // Creates a local variable with no value.
local anotherVar = empty;    // Also creates a local variable with no value.

local aBool      = true;     // Creates a local variable with a boolean value.

local anInt      = 3;        // Creates a local variable with an integer value.

local aFloat     = 1.2;      // Creates a local variable with a floating-point value.

local aFloat2    = -2.7e-03; // Creates another floating pointer variable.

local aString    = "12345";  // Creates a local variable with a string value.

local aMultiLineString = "\
                          This is a string that looks exactly
                          as typed here, including how it breaks
                          across multiple lines.

                          All indentation up to the opening quote
                          mark for this kind of string is removed
                          by the L compiler, so this string as 
                          shown will have no leading spaces except
                             this line which will have two.

                          ";


Aggregate Objects

Creating Aggregate Objects

// Create an aggregate with four unnamed members. 
local aggregate1 = [ 1, 3.14, "hello there", true ];

// Create an aggregate with three members named member1, member2, and
// "this is valid too!"
//
local aggregate2 = [ member1 = 1, 
                     member2 = "a member", 
                     "this is valid too!" = 3 ];
            
// Create an aggregate with named and unnamed members.
local aggregate3 = [ member1 = 1, 3 ];

Accessing Members by Name

local anAggregate = [ member1 = 1, 
                      member2 = "a member", 
                      "this is valid too!" = 3 ];

local aValue = anAggregate.member1;        // aValue gets the value 1.

aValue = anAggregate."this is valid too!"; // aValue gets the value 3.

Accessing Members By Index

local anArray = [ aMember = 1, 42, "Wow!" ];

local aValue = anArray[0];  // aValue gets the value 1.
aValue = anArray[head];     // Same thing
aValue = anArray[1];        // aValue gets the value 42.
aValue = anArray[2];        // aValue gets the string "Wow!".
aValue = anArray[tail];     // Same thing.

anArray[0] = 3.14; // aMember gets assigned the value 3.14.

Adding Members

// Make an aggregate with three initial members.
local anArray = [ aMember = true, "Wow!", anotherMember = 3.14 ];

anArray.stillAnother = 42;   // Add a fourth member named stillAother to 
                             // the aggregate and assign it the value 42.

anArray[4] = 69;             // Add a fifth (unnamed member) with the
                             // initial value 69.

anArray[tail+1] = true;      // Add a sixth (unnamed member) with the
                             // initial boolean value true.

insert anArray[3] = 2.71;    // Insert an unnamed member between anotherMember
                             // and stillAnother with the value 2.71.

Removing Members

// Make an aggregate with three initial members.
local anArray = [ aMember1 = 69, 
                  aMember2 = 42, 
                  aMember3 = "Wow!", 
                  aMember4 = 4 ];

remove anArray.aMember3 ; // Remove aMember3 from the aggregate.

local anInt = remove anArray.aMember4;  // Remove aMember4 from 
                                        // the aggregate, placing it's 
                                        // value in anInt.

remove anArray[1]; // Remove the second member (aMember2).

anInt = remove anArray[0];    // Remove first member.   
anInt = remove anArray[head]; // Remove first member.
anInt = remove anArray[tail]; // Remove last member.
(Re)Naming Members

// Make an aggregate with three initial members.
local anArray = [ aMember1 = 69, 
                  aMember2 = 42, 
                  aMember5 = "Wow!", 
                  4 ];

nameof anArray[3] = "aMember4";         // Name the unnamed member aMember4.

nameof anArray.aMember5 = "aMember3";   // Rename aMember5 to aMember3.


Checking For Member Presence

local anAggregate = [ memberA = 1, memberC = 2 ];

local there = anAggregate has memberA; // there is set to true, because memberA
                                       // is present in aggregate.

there = anAggregate has memberB;       // there is set to false, because memberB
                                       // doesn't exist in the aggregate.

local able = anAggregate can func1;    // able is set to false, because the 
                                       // aggregate has no method named func1.

local notThere = anAggregate !has memberX; // notThere is set to true, because
                                           //  memberX is NOT in aggregate.

local notAble = anAggregate !can func1;    // notAble is set to true, because
                                           // aggregate has no method func1.
Using an Aggregate Like a Stack

local aStack;                    // Create an empty object.

insert aStack[head] = 3;         // Adds 3 to top of stack.
insert aStack[head] = 4;         // Adds 4 to top of stack.

aValue = aStack[head];           // aValue gets 4 without removing it.
aValue = remove aStack[head];    // 4 is removed from the stack and assigned
                                 // to aValue.  

Using an Aggregate Like a Queue
local aQueue;                   // Create an empty object.

aQueue[tail+1] = 1;             // Adds 1 to tail of queue.
aQueue[tail+1] = 2;             // Adds 2 to tail of queue.

local aValue = aQueue[head];    // aValue gets 1 without removing it.

aValue = remove aQueue[head];   // 1 is removed from the queue and assigned
                                // to aValue. 

Sorting Aggregate Members

local agg = [ 10, 5, 8, 1 ];    // Construct an aggregate.
sort agg;                       // Now agg contains [ 1, 5, 8, 10 ]

local agg = [ b=1, c=2, a=3 ];  // Aggregate with named members.
sort agg;                       // Now agg contains [ a=3, b=1, c=2 ]

local agg = [ 10, 5, 8, 1 ];    // Construct an aggregate.

// Sort agg with a custom comparison function.
sort agg with executor( aName, aVal, bName, bVal ) {
                  if( aVal > bVal ) return -1;
                  else              return  1;
              };
// Now agg contains [ 10, 8, 5, 1 ]
  
Assigning Types to Aggregates

local anObject = [ is Thingy, aMember = 3, 4 ];  // Creates an aggregate of type
                                                 // Thingy with two data members.

local anObject = [ is Gadget, is Doohikey, 3.14 ]; // Creates an aggregate that is
                                                   // both a Gadget and a Doohickey,
                                                   // and has one unnamed data member.

Creating Methods

 

Object Class Initialization/Termination Methods

class Thingy 
{

    // Define class initialization method (happens once at program start.)
    // Called in alphabetical order by class name.
    //
    common make 
    {
      . . . code . . .
    }

    // Define class termination method (happens once at program end.)
// Called in reverse alphabetical order by class name.
//
common unmake {
. . . code . . . } } // class Thingy
Object Instance Initialization/Termination Methods

class Thingy 
{

    // Define instance initialization method (happens once each time
    // a Thingy is created.) Note that parameters for make are allowed
    // but not required.
    //
    make( x, y ) 
    {
      . . . code . . .
    }

    // Define instance termination method (happens once each time a 
    // Thingy is no longer referenced and gets destroyed) 
    //
    unmake
    {
      . . . code . . .
    }

} // class Thingy


User-Defined Methods


class DooHickey 
{

    // Instance variables
a, b; // This method takes no arguments. // method1 {
. . . code . . . } // This method requires the caller to pass two arguments. // method2( x, y ) { . . . code . . . } // This method takes two arguments, which will set the // instance variables this.x and this.y. // method3( this.x, this.y ) { . . . code . . . } // This method requires one or two arguments; if the caller // doesn't pass a value for y, the default value of 1 is // used. // method4( x, y = 1 ) { . . . code . . . } // This method takes any number of arguments, which will // be iterated through and processed. // method5( ... ) { . . . code . . . } } // class DooHickey
Data Scoping

 

Method Local Data

class WhatsIt 
{

    aMethod 
    {
        local x = 3;       // Create a local variable x. Creating a local 
                           // variable in this way adds the variable to the 
                           // compiler's lexical tree, so...

        x = x + 1;         // We can access the variable with no additional
                           // scoping required.
                                   
        local.y = 3;       // Also can create a local variable like this, but
                           // variables created in this way must always be 
                           // preceeded by 'local.', since they are not added
                           // to the compiler's lexical tree.

        x = locals.y + 1;  // Can use 'locals.' or 'local.' interchangeably.
    }

} // class WhatsIt

Method Arguments

class DooHickey 
{

    aFunc( x, y ) 
    {
        local a;

        a = x;       // Copy value of argument x into a.

a = args.y; // Copy value of argument y into a. args.z = 4; // Can even add arguments! As with locals, // arguments added explicitly must be scoped // explicitly, since this form doesn't add the // variable to the compiler's lexical tree. arg.z = 5; // Can use 'arg.' and 'args.' interchangeably. } } // class DooHickey
Method Instance Data

class Thingy 
{
    i; // Indicate that this class has an instance variable i.

    aFunc 
    {
        local a;
        local b = i; // Since i was prototyped above, we can get its 
                     // value without using 'this.'
                        
        a = this.i;  // Explicitly copy the value of instance variable
                     // i into local variable a.

        z = b;       // Compiler warning will result, since z is not 
                     // defined as local, instance, package or global.

        this.z = 4;  // However, this will add z to the instance 
                     // space for later use. Like the explicit local
                     // creation shown above, doing this doesn't add 
                     // the variable to the compiler's lexical tree.
                     // So variables created this way must always be
                     // explicitly scoped with 'this.'

        b = inst.z;  // Can use 'inst.' or  'this.' interchangeably.
    }

} // class Thingy

Global Data

package global {
    g;             // Explicitly prototype a variable m as member 
                   // of the global package.

};

package global g;  // Can leave braces off if only on statement,
                   // like this.

g;                 // Or you can simply prototype a global by 
                   // placing outside of all module/package/class
                   // declarations in a file. 
 
class Thingy 
{

    aFunc 
    {
        local a;
        local b = g;   // Since m was prototyped above, we can get its
                       // value without using 'global.'

        a = global.g;  // Explicitly copy the value of global variable
                       // m into local variable a.

        s = b;         // Compiler error will result, since s is not 
                       // defined as local, instance, package, or global.

        global.s = 4;  // However, this will add s to the global 
                       // package, just like for args, locals, 
                       // and instance variables. Again, this form
                       // doesn't add the variable to the lexical 
                       // tree, so variables added this way must 
                       // always be preceeded by 'global.'

        b = glabals.s; // Can use 'global.' and 'globals.' interchangeably.
    }

} // class Thingy

Package Data

package aGroup 
{
    x, y;

    class Thingy
    {
    
        func
        {
            aGroup.x = 1;  // Can access package members explicitly...
            y = 3;         // or implicitly, because x & y are declared above.

            p = x;         // Compiler warning will result, since p is not 
                           // defined as local, instance, package, or global.
            aGroup.p = 4;  // However, this will add p to the aGroup 
                           // package, just like for arg, local, global,
                           // and instance variables. Again, this form
                           // doesn't add the variable to the lexical 
                           // tree, so variables added this way must 
                           // always be scoped by the package name...

            x = aGroup.p;  // Like this.
        }
    }
}

Module Data
A module is an unnamed package valid only for the current source file. It behaves pretty much like a package:

module

{
    x, y; 

    class WhatsIt
    {
    
        func2
        {
            aGroup.y = 1; // Can access module members explicitly...
            x = 3;        // or implicitly, because x & y are declared above.
        }
    }
}

go
{
    x = 10;               // 'module' members are automatically searched...
    y = 11;               // just as if they were global.
}
Scope Export

package aGroup
{
    a;         // Causes a to be defined only in this package.

    export b;  // Causes b to be defined in this package, but 
               // also to appear as a member of the global space.

    package innerGroup
    {
        export x, y; // Causes x and y to be defined in 
                     // innerGroup, but also to appear as  
                     // members of the containing package 
                     // (aGroup).
               
    
        class WhatsIt       // Valid only within innerGroup.
        {
           export common z; // Defines a common variable for 
                            // class WhatsIt, but also to appear
                            // as a member of package innerGroup.
        }

        export class Thingy // Defined here, but also valid in
                            // the containing package (aGroup).
        {
          . . . code . . .
        }
    }
}
Calling Methods

class Widget
{

    make( a, b )
    {
      . . . code . . .
    }

    aFunc( x, y )
    {
      . . . code . . .
    }
}


// Create a widget, passing two values to its make function.
local aWidget = [ is Widget(1,2) ];
 
aFunc( 1, 2 )       // Make a call with unnamed parameters. In this case, parameters
                    // are assigned left to right.

aFunc( x=1, y=2 );  // Make call with named parameters. Named parameters are order 
                    // independent, so...

aFunc( y=2, x=1 );  // This is valid too!  And it does the same thing.

aFunc( y=2, 1 );    // Can also mix named and unnamed. When mixed, all named 
                    // parameters are assigned as expected, and the remaining
                    // unnamed arguments are assigned to the rest of the 
                    // parameters left to right.

Spoofing

 

Calling an Object's Method on this Object's Instance

class gadget
{
    doSomething
    {
      . . . code . . .
    }
}

class thing {

    foo( x, y )
    {
        gadget.doSomething();  // Gadget Method uses calling thing's 
                               // instance variables.
    }
}

Calling Another Object's Method on Yet Another Object's Instance

class gadget
{
    doSomething
    {
      . . . code . . .
    }
}

class thing {

    foo( x, y )
    {
        x = [is doohickey];
        gadget.doSomething(this=x);  // Gadget Method uses x's instance 
                                     // variables.
    }
}

Executors

 

Using an Executor for Delayed Execution

func( a, b )
{
  . . . code . . .
}

// Make variable x a function for later execution.  Code
// inside an executor can access any variables from the
// enclosing code.
//
local x = executor { func( a = 1, b = z+3 ) };

. . . later . . .

x();  // Actually call the executor with the preset 
      // arguments defined above.

Inline Executors

    // Call sort function on given list with compare
    // function defined inline.
    //
    sort list with executor( aName, aVal, bName, bVal ) { 
                       if     ( aVal < bVal ) return -1;
else if( aVal > bVal ) return 1;
else return 0; };
or // Predefine compare function inline. x = executor( a, b ) { if ( aVal < bVal ) return -1;
else if( aVal > bVal ) return 1;
else return 0; } // Call sort function on given list // with predefined compare function. // sort list with x;
Prototyping Classes

 

Prototyping Class Members

class DooHickey 
{
    x, y, z;             // Defines Instance Data Members.
    common x, y, z;      // Defines Class Common Data Members.
    f1(), f2();          // Defines Instance Member Functions.
    common f1(), f2();   // Defines Class Common Member Functions.
}; 

Prototyping Aggregated Classes

class Widget
{
    x;                  // Prototype a data member...
    foo();              // and a method.
}; class Thingy is Widget, DooHickey // Pick up all of Widget's and DooHickey's members. { y, z; // Add some data members of our very own. foo2(), foo3(); // Not to mention methods of our own. };
Resulting member name shortcutting when class Thingy is actually implemented later:
class Thingy
{

    make
    {
        x = 1;  //
        y = 2;  // Don't need explicit "this." thanks to prototypes above.
        z = 3;  //
    }

} // class Thingy
Member Virtualization

Note: Virtualization is not yet supported in L, so the following material should be
viewed as provisional and subject to change.

Virtualizing A Specific Member
class thing
{

    <x>
    {
        // If x is being set to a new value...
        if( countof args > 0 ) {


            // If the passed value is negative or not an integer, force the 
            // value of the instance x to be zero. Otherwise, set the value
            // as is.
            //
            if( !(args[0] is int) || args[0] < 0 ) this.x = 0;
            else                                   this.x = args[0];
        }
  
        // Return resulting value to caller.
        return x;

    } // <x>

} // class thing 

Virtualizing All Public Data Members

class Thingy 
{

    <default>( memberName )
    {
        // If x is being set to a new value...
        if( countof args > 1 ) {

            // For this example, force non-positive integers to zero.
            if( args[1] !is int || args[1] < 0 ) this.@memberName = 0;
            else                                 this.@memberName = args[1];
        }

        // Return resulting value to caller.
        return( this.@memberName );

    } // <default>

} // class Thingy

Intrinsic Virtualizers

<nameof>
<typeof>
<countof>
<has>
<is>
<.>
<[]>
<@>
<float>, <int>, etc.
<->>

Global Virtualizers

<call>       // Default virtualizer for  all method, executor, and function calls.

<intrinsic>  // Default virtualizer for all intrinsics. 

Invoking Virtualizers

class Thingy { <x> { . . . } } // class Thingy Thingy aThing; aThing.x = -3; // The value -3 is passed to the virtualizer routine for member // x rather than x being set directly by this statement. aValue = aThing.x; // The virtualizer routine for member x is called rather than // the value of x being returned directly. class Doohickey { <z> { . . . } aMethod { this.z = 3; // Within a class method, doing this Assigns value 3 directly // to z. this.<z> = 3; // You have to be explicit if you want the virtualizer. } } // DooHickey
Built-In Operators
Operators
Function
+ - * / ** ++ -- Standard Mathematical Operators. These operators treat their operands as numeric values and produce numeric results. Division result is always floating-point.
div mod Integer division and modulo Operators. These operators treat their operands as numeric values and produce integer results.
+= -= *= /= **=
div= mod= <<= >>=
Self-Modifying Math Operators. The left operand is set to the value of the (left op right).
and or not && || ! Boolean Operators. These operators treat their operands as boolean values and produce boolean results.
bitand bitor bitxor bitnot
& | ^ ~
Bitwise Operators. These operators treat their operands as integer values and produce integer results.
== != < > <= >= Numeric Comparison Operators. These operators treat their operands as numeric values and produce boolean results..
$== $!= $> $< $>= $<= Case-Insensitive String Comparison Operators. These operators treat their operands as strings and produce boolean results.
^$== ^$!= ^$> $^< ^$>= ^$<= Case-Sensitive String Comparison Operators. These operators treat their operands as strings and produce boolean results.
$+ $+= String Concatenation Operators. These operators treat their operands as strings and produce boolean results.
= Asssignment Operator. This operator sets the value and type for its left-hand operand to the value and type for the expression to the right.
-> ->> Reference Creation Operators. These operators assign the left-hand operand a reference to the right hand operand.
->== ->!= Reference Comparison Operators. These operators treat their operands as references, and produce boolean results.
.+ .+= .| .|= .- .-= Aggregate Operators. These operators treat their operands as aggregates, and produce aggregate results.
insert remove sort
has can is in $in ^$in
Aggregate Member Operators. These operators perform various member related functions on aggregates.
!has !can !is !in !$in !^$in Inverted Aggregate Member Operators. Note that user-defined operators can be inverted this way also.
int float string bool Explicit Type-Casting Operators.These operators cast their operands to specific atomic types.
typeof countof nameof indexof Special Object Operators. These operators access special properties of atomic and aggregate objects.
Operator Precedence

If not explicitly grouped using parentheses, the L compiler groups operators according to the following precedence table. Operators are listed from highest precedence (evaluated first) to lowest precedence (evaluated last).

Operators
Associativity
[] () . @
Left-to-right
user-defined prefix operators
Left-to-right
insert remove nameof indexof countof typeof
Left-to-right
unary + - ! ~ ++ --
Right-to-left
int float string bool
Right-to-left
user-defined infix operators
Left-to-right
**
Left-to-right
* / mod div
Left-to-right
+ -
Left-to-right
$+ .+ .-
Left-to-right
is has can in $in ^$in
Left-to-right
<< >>
Left-to-right
sort
None
< > <= >= == != $< $> $<= $>= ^$< ^$> ^$<= ^$>=
Left-to-right
== != $== $!= ^$== ^$!=
Left-to-right
&
Left-to-right
^
Left-to-right
|
Left-to-right
.|
Left-to-right
&& and
Left-to-right
|| or
Left-to-right
= -> ->> += -= *= /= **= div= mod= <<= >>= $+= .+= .|= .-=
Right-to-left

 

Special Object Operators
countof
Count of number of members in an object.

nameof
Identifier name for specified aggregate member

indexof
Numeric index for specified aggregate member

typeof
 
 
empty
An empty object.
 
bool
An atomic object containing a boolean value.
 
string
An atomic object containing a string value.
 
int
An atomic object containing an integer value.
 
float
An atomic object containing a floating point value.
 
method
An aggregate member function that operates on the owning aggregate's instance data. Discussed in greater detail in a following section.
 
executor
An aggregate member function that operates on some other aggregate's instance data.
 
aggregate
An untyped aggregate object.
 
pattern
An object containing a pattern.
             

Examples:

local  anAggregate = [ member1 = 1, 
                       member2 = "a member", 
                       member3 -> member1 ];

local aType  = typeof anAggregate;        // aType gets the string "aggregate"
                                          // since this is a generic untyped
                                          // aggregate.
    
aType = typeof anAggregate.member2;       // aType gets the string "string".

aType = typeof anAggregate.member3;       // aType gets the string "reference".

local aCount = countof anAggregate;       // aCount gets the numeric value 3.

local aName  = nameof anAggregate[0];     // aName gets the string "member1".

local aNum = indexof anAggregate.member3; // aNumber gets the numeric value 2.

local  aClass = [ is Widget, 
                  is WhatsIt, 
                  member1 = 1 ]; 

aType = typeof aClass;               // aType gets an untyped aggregate 
                                     // containing the strings "Widget"
                                     // and "WhatsIt". Note that type is 
                                     // NOT the string "aggregate"!


Built-In String Functions

Function
Behavior
length Returns the length in characters for a string.

find( what, startPos )

Returns the zero-based position of the first occurence of what in a string. This argument may be a simple string or an L Pattern. If no match is found, empty is returned. The startPos parameter is an optional starting position within the string.
reverseFind( what )
Returns the zero-based position of the last occurence of what in a string. At this time, this argument is always interpreted as a simple string, and not an L Pattern. If no match is found, empty is returned.
replace( what, with, all ) If all is true , this function replaces all occurences of what with the simple string with. Otherwise, this function replaces only the first occurence. The what argument may be a simple string or an L Pattern. This function returns the number of replacements actually made.
matches( pattern ) Returns true if the given string matches the specified L Pattern, false otherwise.
contains( what ) Returns true if the given string contains the simple string what. Otherwise this function returns false.
tokenize() Returns an aggregate containing zero or more unnamed string members that identify white-space separated words in the given string. Note that words/tokens in the resulting list will not contain any white-space characters. If the string contains only one token (no white-space characters anywhere in the string), this function will return a list with only one entry that contains the entire string. If the source string is empty or zero-length, this function will return an empty token list.
left( n ) Returns a string containing the first n characters from the source string. If the entire string has fewer than n characters, a copy of the entire source string is returned.
mid( startPos, n ) Returns a string containing n characters starting at the zero-based offset startPos. If the specified starting position is beyond the end of the source string, a zero-length string is returned. If fewer than n characters exist after the specified starting position, everything from startPos to the end of the original string is returned.
right( n ) Returns a string containing the last n characters from the source string. If the entire string has fewer than n characters, a copy of the entire source string is returned.
leftStrip( strToStrip ) Removes the simple string strToStrip from the beginning of the given string. If strToStrip is empty or a zero-length string, all the leading white-space characters are stripped instead. This function returns a reference to the resulting string.
rightStrip( strToStrip ) Removes the simple string strToStrip from the end of the given string. If strToStrip is empty or a zero-length string, all the trailing white-space characters are stripped instead. This function returns a reference to the resulting string.
toupper() Converts all the alphabetic characters in the string to upperase. This function returns a reference to the resulting string.
tolower() Converts all the alphabetic characters in the string to lowercase. This function returns a reference to the resulting string.
hash() Returns the 32-bit case-insensitive hash value for a string.

 

 

Object Type Checking Checking for Atomic Types

local anInteger = 3;

local isAnInteger = anIteger is int;       // 'isAnInteger' gets the value 'True'.
local isAString   = anInteger is string;   // 'isAString' gets the value 'False'.
local isAtomic    = anInteger is atomic;   // 'isAtomic' gets the value 'True'.
local isNumber    = anInteger is number;   // 'isNumber' gets the value 'True'.
local isCallable  = anInteger is callable; // 'isCallable" gets the value 'False'
                                           //  since anInteger is not a method or
                                           //  an executor.

Checking for Untyped Aggregates

local aThing = [ x = 3, y = 4 ];

local isThing      = aThing is aThing;     // 'isThing' gets the value 'False'.
local isAtomic     = aThing is atomic;     // 'isAtomic' gets the value 'False'.
local isAggregate  = aThing is aggregate;  // 'isAggregate' gets the value 'True'.
local isUntyped    = aThing is untyped;    // 'isUntyped' gets the value 'True'.

Checking for Typed Aggregates

local aThing = [is thing];

local isDoohickey  = aThing is doohickey;  // 'isDoohickey' gets the value 'False'.
local isThing      = aThing is thing;      // 'isThing' gets the value 'True'.
local isAtomic     = aThing is atomic;     // 'isAtomic' gets the value 'False'.
local isAggregate  = aThing is aggregate;  // 'isAggregate' gets the value 'True'.
local isUntyped    = aThing is untyped;    // 'isUntyped' gets the value 'False'.

Checking for Multiple Aggregated Types

local aThing = [is thing, is doohickey];

local isDoohickey = aThing is doohickey;  // 'isDoohickey' gets the value 'True'.
local isThing     = aThing is thing;      // 'isThing' also gets the value 'True'.

Automatic Type Conversion

Operands used in L expressions are automatically re-cast by the operator that appears between them. The following table summarizes how various L operators re-cast their associated operands.

For the operators
If one operand is
And the other is
They are both converted to
And the resulting expression is
+ - * / += -= *= /= **=
++ --
int or bool
int or bool
int
int if result is an integer value,
float otherwise
float or string
any type
float
div mod div= mod=
any type
any type
int
int
and or not
&& || !
any type
any type
bool
bool
bitand bitor bitxor bitnot
& | ^ ~
any type
any type
int
int
== != < > <= >=
int or bool
int or bool
int
bool
float or string
any type
float
$== $!= $> $< $>= $<=
any type
any type
string
bool
$+ $+=
any type
any type
string
string
.+ .| .+= .|=
any type
any type
aggregate
aggregate

 

 

Explicit Type Conversion

The following keywords explicitly force objects to be cast to particular atomic types:
int    - Convert the following operand to Integer.
float  - Convert the following operand to Floating-Point.
string - Convert the following operand to a String.
bool   - Convert the following operand to a Boolean.    
local anInt  = 1;
local aFloat = 2.14;

local theAnswer = aFloat + anInt;   // Due to automatic promotion,
                                    // theAnswer gets the value 3.14.

theAnswer = int aFloat + anInt;     // Due to explicit cast, theAnswer2
                                    // gets the value 3.

String Conversion Formatting

When strings are used in non-string operations, they are converted to non-string values according to the following tables:

Given the string
In a Boolean Operation Converts To
"false" (in any combination of upper/lower case)
false
Empty String
false
Any Other String
true

 

Given the string
In a Numeric Operation Converts To
"true" (in any combination of upper/lower case)
1
-#.####e-##

Floating point value. # may be 0-9.
Minus signs and leading/trailing zeros are optional.

-#.####
Floating point value. # may be 0-9.
Minus signs and leading/trailing zeros are optional.
-######

Integer if the value can be stored in 32 bits or less.
Otherwise converted
to a floating-point value.
# may be 0-9, minus sign is optional.

-0x#####
Equivalent hex value if it can be stored in 32 bits or less.
Otherwise converted to a floating-point value.
#
May be 0-9, A-F, or a-f, minus sign is optional.
-0b#####
Equivalent binary value if it can be stored in 32 bits or less. Otherwise converted to a floating-point value.
# May be 0 or 1, minus sign is optional.
Any Other String
Integer or floating-point 0.

 

String
In an Aggregate Operation Converts To
Any String Value
An untyped aggregate with its first member set to the given string.

 

When converting non-string types to their equivalent string values, the following conversions are used:

Type for non-String Value
Converts To
Integer
"-#####"
No leading zeros, leading minus sign if
required.
# is a character in the range 0-9.
Floating-Point
"-#.######" or "-#.#####e-##"
The resulting string will have either
fixed-point
or normalized scientific notation, whichever i
s shorter.
Boolean
"true" or "false"
Aggregate

Equivalent string of first member.

Note: If the first member is an aggregate, the conversion recurses until a non-aggregate value is reached.

 

References
local aVariable = "12345";
local aReference -> aVariable;

local aValue = aReference;  // 'aValue' gets the value of what aReference
                            // refers to, or the string "12345". 

local anotherVar = "abc";
local anotherRef ->> aReference; // Make a "deep" reference, i.e. refer to
                                 // aReference instead of to what 'aReference'
                                 // refers to.

aValue = anotherRef;        // 'aValue' gets the value of what 'anotherRef'
                            // refers to ('aReference') and in turn what
                            // that refers to, or the string "12345".

aReference -> anotherVar;

aValue = anotherRef;        // Since 'anotherRef' is a "deep" reference, 
                            // 'aValue' now gets the value "abc".

local refSame = aReference ->== anotherRef; // 'refSame' gets the value
                                            // 'True', because 'aReference'
                                            // and 'anotherRef' ultimately
                                            // refer to the same object.
Indirection
local aVariable = 3;          
local anAggregate = [ member1 = 1, 
                      member2 = "a member", 
                      member3 -> anotherVariable ];

                                         // Use indirection to...
local aName = "aVariable";    
local aValue = @aName;                   // ...access an atomic.

local aMemberName = "member1";         
local anAggregate.@aMemberName = 123;    // ...access an aggregate member.

local aName = "anAggregate";
local aReference -> @aName;              // ...refer to a variable.

local aName = "member1";
local aBoolVal = anAggregate has @aName; // ...check for a member.

local aName = "member1";
local aBoolVal = anAggregate can @aName; // ...check for a method.

local aName = "aggregate";
local aBoolVal = anAggregate is @aName;  // ...check an aggregate's type.

Program Flow Control Global Methods for an L Program

make( ... )
{
    initialization code
}


go( ... )
{
    main body of code
}


unmake( ... )
{
    termination code
}

Conditional Execution
  
  if( boolean true expression )
      do this statement;       

or

  if( boolean true expression ) {
      do this block of statements
  }

or

  if( boolean true expression )
      do this statement;
  else
      otherwise do this statement;

or

  if( boolean true expression ) {
      do this block of statements
  }
  else {
      otherwise do this block of statements
  }

Multi-Conditional Execution

 

For repetetive comparisons, the following case evaluator is offered where each sub-expression is evaluated until a true expression is found, the default case is reached, or the end of the case statement is reached:

case {
      of( boolean true expression0 )    
          do this block of statements

      of( boolean true expression1 ) 
          do this block of statements
      . . .

      default
          do this block statements
  }

For repetetive numeric comparisons, the following variant is offered where the base expression is compared to the sub-expressions using the == operator:

  case( baseExpression ) {

     of( subExpression0 )
        do this block of statements

     of( subExpression1 )
        do this block of statements
     . . .
     default
        do this block statements
  }


For repetetive case-insensitive string comparisons, the following variant is offered where the base expression is compared to the sub-expressions using the $== operator:

  $case( baseExpression ) {

     of( subExpression0 )
        do this block of statements

     of( subExpression1 )
        do this block of statements
     . . .
     default
        do this block statements
  }


For repetetive case sensitive ecomparisons, the following variant is offered where the base
expression is compared to the sub-expressions using the ^$== operator:

  ^$case( baseExpression ) {

     of( subExpression0 )
        do this block of statements

     of( subExpression1 )
        do this block of statements
     . . .
     default
        do this block statements
  }

Unconditional Looping

loop {
   do this block of statements
   if( boolean true expression ) break;
}

Conditional Looping
    
    while( boolean true expression [ ; endloop expression list ] )
        do this statement;

or

    while( boolean true expression [ ; endloop expression list ] ) {
        do this block of statements
    }


  do
      do this statement;
  while( boolean true expression [ ; endloop expression list ] );

or

  do {
      do this block of statements
  } while( boolean true expression [ ; endloop expression list ] );

List Looping

 

The following two variations set object equal to the value of each member of aggregate:

     foreach( object in aggregate [ where  boolean true expression ] )
        do this statement;

or
    foreach( object in aggregate [ where boolean true expression ] ) {
       do this block of statements
    } 


The remaining two variations make refObject refer in turn to the value of
each member of aggregate:


    foreach( -> refObject in aggregate [ where boolean true expression ] )
        do this statement;

or
    foreach( -> refObject in aggregate [ where boolean true expression ] ) {
        do this block of statements
    } 

List Looping With Removal

 

The following two variations remove each member of aggregate in turn and set
object
equal to that member:


  pulleach( object in aggregate [ where  boolean true expression ] )
      do this statement;

or
  
  pulleach( object in aggregate [ where boolean true expression ] ) {
      do this block of statements
  } 


The remaining two variations remove each member of aggregate in turn and
makes robject refer to that member:


  pulleach( -> robject in aggregate [ where boolean true expression ] )
      do this statement;

or
  
  pulleach( -> robject in aggregate [ where boolean true expression ] ) {
      do this block of statements
  } 

Early Loop Continuation

while( boolean true expression ) {

    do some stuff

    // Jumps to 'while' and reevaluates.
    if( boolean true expression ) next; 

    do more stuff
}

do {

    do some stuff

    // Jumps to 'while' and reevaluates.
    if( boolean true expression ) next; 

    do more stuff

} while( boolean true expression );



loop {

    do some stuff

    // Jumps to top of loop.
    if( boolean true expression ) next; 

    do more stuff
}

Early Loop Exit

while( boolean true expression ) {

    do some stuff

    // Jumps to first statement after loop.
    if( boolean true expression ) break;

    do more stuff
}

Early Nested Loop Continuation using Loop Naming


Note: Loops without braces cannot be labeled.

while( boolean true expression ) loop1 {

   do some stuff

    while( boolean true expression ) loop2 {

        do some stuff

        // Jumps to 'while' of loop1 and reevaluates.
        if( boolean true expression ) next loop1; 
    }
}



do loop1 {

   do some stuff

    do loop2 {

        do some stuff

        // Jumps to 'while' of loop1 and reevaluates.
        if( boolean true expression ) next loop1; 

    } while( boolean true expression ); 

} while( boolean true expression ); 


Early Nested Loop Exit using Loop Naming


Note: Loops without braces cannot be labeled.


while( boolean  true expression ) loop1 {
    
    do some stuff

    while( boolean true expression ) loop2 {

        do some stuff

        // Jumps to first statement after loop1.
        if( boolean true expression ) break loop1; 
    }
}

Exception Handling

Note: Exceptions are not yet supported in L, so the following material
should be viewed as provisional and subject to change.

Throwing Exceptions

exception execptionAaggregate;

Catching Exceptions
    
    statement;

    onException( exceptionAggregate )
        exception handling statement;

or

    [ if(. . .) | else | loop | do | while(...) ] {
        block of statements
    }
    onException( exceptionAggregate )
        exception handling statement;

Retrying Exceptions

    statement;

    onException( exceptionAggregate ) {

        do corrective work here
        
        retry; // Retries statement.
    }


or

    [ if(. . .) | else | loop | do | while(...) ] {

        block of statements

    }

    onException( exceptionAggregate ) {

        do corrective work here

        retry; // retries previous block.
    }
Parallel Execution

 

Note: Parallel execution is not yet supported in L, so the following material
should be viewed as provisional and subject to change.

Explicit Parallelization

parallel {

     // If parallel capable system, each statement within this block is executed
     // in parallel. Otherwise, they are executed in the order encountered.
     //
     x[0] = sqrt( y[0] );
     x[1] = sqrt( y[1] );
     x[2] = sqrt( y[2] );
     x[3] = sqrt( y[3] );
};

// Execution resumes at first statement after block when all parallel paths
// have completed.

Iterated Parallelization

parallel iterValue = startValue to endValue {

    x[iterValue] = sqrt( y[iterValue] );
)

// Execution resumes at first statement after block when all parallel paths
// have completed.

Inline C++ Code Writing Inline Code


C++ code can be embedded inline from within or outside of an L function.

  C++: C++ Statement;

  C++: {
      Block of C++ Statements
  }

Accessing L Variables

local anLVar = 3;

C++: {
    int x = `anLVar`;     // Assigns the value 3 to the C++ variable x.
    int y = `anLVar + 5`; // Assigns the value 8 to the C++ variable y.
    `anLVar` = "abc";     // Assigns the value "abc" to the L variable anLVar.
    . . .
}

Wrapping a C++ Class

Here is an extended example demonstrating how an L class can wrap a C++ class (i.e. provide an L interface for it).

C++: {

    // Derive your C++ class from LNativeObj. If you don't
    // have access to the source, derive a wrapper class 
    // from both LNativeObj and the desired class.
    //
    class MyFile : public LNativeObj
{ public: MyFile( const char* fileName ); ~MyFile; // Define makeCopy() to return NULL if this // object should be reference counted, or a // pointer to a heap object if it should be // copied. // LNativeObj* makeCopy() { return NULL; } void write( const char* str ); void flush(); }; } // Here is the L class that wraps MyFile. class FileWrapper
{ // Declare a member variable as 'native' that will hold // a pointer to a C++ MyFile instance. // native MyFile pFile; // Construct a new wrapper, with a new MyFile inside. make( fileName ) { C++: `pFile` = new MyFile( `fileName` ); } // Destroy a wrapper, which destroys the MyFile inside. // automatically. // unmake() { } // Write two strings, just to be different. write( str1, str2 )
{ C++: { `pFile`->write( `str1` ); `pFile`->write( `str2` ); } } // Flush the previous writes to disk. // flush()
{ `pFile`->flush(); } }
// class FileWrapper
Patterns Expression Notation

 

Notation
Meaning
'string'
Literal string or literal character.
* or AnyChar
Match any single character.
^ or StartOfLine
Match Start of Line.
$ or EndOfLine
Match End of Line.
pat* or ZeroOrMore(pat)
Match zero or more occurences.
pat+ or OneOrMore(pat)
Match one or more occurences.
pat? or ZeroOrOne(pat)
Match zero or one occurrence.
pat1 | pat2 | . . .
pat1 or pat2 or . . .
Match any one of the listed expressions.
[ 'string' ]
Match any of the characters in the string.
['lowchar' - 'hichar']
Match a character in the given range.
[~ 'string' ]
Match none of the characters in the string.
[~ 'lowchar' - 'hichar']
Match none of the characters in the given range.
( expr1 ) ( expr2 ) . . .
Match expr1 followed by expr2, etc.
( name = expr )
Match the specified expression, and name the result name.
varName
Match the pattern contained in variable varName.

 

Defining a Pattern

// Define a regular expression for a 'word' (i.e., one or more non-blank 
// characters).
//
local wordPat = pattern OneOrMore( [~' \t'] ); 

// A more cryptic way to say the same thing.
local wordPat2 = pattern [~' \t']+;
Using Patterns


For the following code:

             
    // Define a string to process.
    local aString = "This string contains words";

    // Define a pattern for a bunch of words separated by spaces. Note
    // that we can reference a previously defined pattern (wordPat)
    // simply by using its name.
    //
    local sentencePat = pattern OneOrMore( wordPat (' ' | EndOfLine) );

    // Parse the string with the given regular expression.
    local result = parse aString with sentencePat;


the contents of result will be:

    This string contains words
             


For the following code:

    // Define a sentence as a series of named words separated by spaces.
    local sentencePat2 = pattern OneOrMore( (word = wordPat)
                                            (' ' or EndOfLine) );

    // Parse the string with the named regular expression.
    local result2 = parse aString with sentencePat2;
the contents of result2 will be an aggregate:
    [ "This string contains words",
      word = [ "This", "string", "contains", "words" ]
    ]
             
Recursive Patterns

// Define mutually recursive patterns to parse a simple math expression.
local term, expr;

term = pattern (['0'-'9']+) or ('(' expr ')');
expr = pattern term | (left=term) '+' (right=term);

local aString = "1+(2+3)";
local result  = parse aString with expr;
Then result will contain:
        [ "1+(2+3)", left="1", right=[ "(2+3)", left="2", right="3" ] ]

File Inclusion
include "path/filename";    // Only performed once.

include "filename";        // Only performed once. Search include directories.

reinclude "path/filename";  // May be done multiple times
reinclude "path/filename";  // (e.g., for macros.)

myFunc( x, y )
{ include "stuff.t"; // Include/reinclude can be used anywhere, // including within a function. }
Text Macros

Note: Macros are not yet supported in L, so the following material
should be treated as provisional and subject to change.

Basic Macro Symbols

macro symbolname { return "text to substitute"; }
Macros with Arguments

macro symbolname( args ) {

    L code to calculate substitution text

    return resultText;
}

Conditional Compilation The L programmer can instruct the compiler to observe or ignore code based on
whether compile-time symbols are defined. For example:
    compileif( DEBUG ) {  
        code compiled only if DEBUG symbol is set

    }
    elseCompileIf( CHECK_RELEASE ) {

        code compiled only if CHECK_RELEASE symbol is set

    }
    elseCompile {

        code compiled if neither of the above was compiled

    }
This can also be useful to temporarily disable a section of code, like this:
    compileIf( false ) {

        this code won't be compiled
}