| L Language Reference |
![]() |
![]() |
|
|
|
|
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
// 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 ];
/* 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. */ */ 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.
";
// 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 ];
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.
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.
// 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.
// 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.
// 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.
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.
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.
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 ]
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.
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.)
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
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
class DooHickey
{
aFunc( x, y )
{
local a;
a = x; // Copy value of argument x into a.
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
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 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.
}
}
}
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.
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 . . .
}
}
}
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.
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.
}
}
class gadget
{
doSomething
{
. . . code . . .
}
}
class thing {
foo( x, y )
{
x = [is doohickey];
gadget.doSomething(this=x); // Gadget Method uses x's instance
// variables.
}
}
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.
// Call sort function on given list with compare
// function defined inline.
//
sort list with executor( aName, aVal, bName, bVal ) {
if ( aVal < bVal ) return -1;
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.
};
class Widget
{
x; // Prototype a data member...
foo(); // and a method.
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
Note:
Virtualization is not yet supported in L, so the following material
should be 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
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
<nameof> <typeof> <countof> <has> <is> <.> <[]> <@> <float>, <int>, etc. <->> <call> // Default virtualizer for all method, executor, and function calls. <intrinsic> // Default virtualizer for all intrinsics.
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).
Special Object Operators
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"!
Object Type Checking
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.
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'. 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'. 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'. 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.
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.
When strings are used in non-string operations, they are converted to non-string values according to the following tables:
When converting non-string types to their equivalent string values, the following conversions are used:
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.
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.
make( ... )
{
initialization code
}
go( ... )
{
main body of code
}
unmake( ... )
{
termination code
}
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
}
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
}
$case( baseExpression ) {
of( subExpression0 )
do this block of statements
of( subExpression1 )
do this block of statements
. . .
default
do this block statements
}
^$case( baseExpression ) {
of( subExpression0 )
do this block of statements
of( subExpression1 )
do this block of statements
. . .
default
do this block statements
}
loop {
do this block of statements
if( boolean true expression ) break;
}
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 ] );
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
}
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
}
The following
two variations remove each member of aggregate in turn and
set
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
}
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
}
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
}
while( boolean true expression ) {
do some stuff
// Jumps to first statement after loop.
if( boolean true expression ) break;
do more stuff
}
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 );
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;
}
}
Note:
Exceptions are not yet supported in L, so the following material exception execptionAaggregate;
statement;
onException( exceptionAggregate )
exception handling statement;
or
[ if(. . .) | else | loop | do | while(...) ] {
block of statements
}
onException( exceptionAggregate )
exception handling statement;
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.
}
Note:
Parallel execution is not yet supported in L, so the following material
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.
parallel iterValue = startValue to endValue {
x[iterValue] = sqrt( y[iterValue] );
)
// Execution resumes at first statement after block when all parallel paths
// have completed.
C++: C++ Statement;
C++: {
Block of C++ Statements
}
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.
. . .
}
Here is an extended example demonstrating how an L class can wrap a C++ class (i.e. provide an L interface for it).
// 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']+;
// 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;
This string contains words
// 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" ]
]
// 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" ] ] 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 ) Note:
Macros are not yet supported in L, so the following material
macro symbolname { return "text to substitute"; }
macro symbolname( args ) {
L code to calculate substitution text
return resultText;
}
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
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|
|
|
|
|