Welcome to L

Homepage of the L programming language

L is a new programming language, developped from scratch by Matthieu Lemerre. It is still in active development, but the following features are already present:

Features highlight

Example unique features of L

First example: while definition

In L, while is not a base construct. It can be defined from other base constructs, while retaining the same syntax. Example of while use:
//Iterative factorial with the while macro.
int fact (int n)
{
  let i = 0;
  let result = 1;
  while (i < n)
   { i = i + 1;
     result = result * i;
   }
  result
}
Notice that the syntax for while (and for the rest of the code snippet) is similar to the one used in C. The way while is defined is by using a macro, that is to say, a code that will transform your while into lower-level constructs. For instance:
  while (i < n)
   { i = i + 1;
     result = result * i;
   }
is simply transformed into:
  loop
   {
      if(!(i < n)))
        break;
      { i = i + 1;
        result = result * i; }
   }
 

This mecanism is much more powerful than (say) C++ templates or C macros, because with it you can really add new language constructs with the syntax you want. The macro definition looks like this:

parse_macro while '(' @(Expression(Bool) condition) ')' @(Statement body)
{ 
  Statement { 
        loop { if(!($condition))
                 break;
               $body }}
}

Note that the macros are typed : you can check type during macro expansion, and expand code depending on the type of your expression. For instance, the foreach macro (that helps iterating over a collection) produces code that depends of the type of the collection (whether it is an Array, a List...). L does not have iterators; the foreach macro is a cleaner and much more efficient abstraction.

Note: As L is incomplete, the parse language is not yet implemented yet, nor is the macro definition language; But you can already manually write the while parser and the while construct transformation code in L; so while is really defined as a library.

Second example : a regexp library

In L, regular expressions, despite being defined in a library, are well integrated in the language. You can write:
import std.regexp;

let (base_dir,file_name, extension) 
      = match(/^(.*)\/(.*)\.(.*)$/, "/home/lemerre/a.out");
//base_dir is "/home/lemerre", file_name is "a", extension is "out"

let result_string = replace(/^([^ ]*) *([^ ]*)/$2 $1/,"foo baz");
//result_string is "baz foo"

Compiles this into a deterministic finite automata, and then into a function. My tests indicated that it is around 10 times faster than the libpcre (that must construct the automata at run-time, and then "interpret" the automata, instead of compiling it). This is one example of the complex computations you can do at compile-time to execute efficiently at run-time.

Note: As this was the first thing I wrote, the code currently directly outputs assembly code at run-time. Some changes are needed so that it can really output L code at compile time.

Third example : integrate L and XML

The third example shows how to write a library that would integrate XML in L.
Step 1: write a XML parser
This simple step consists of taking a XML input and to transform that into a L-specific tree structure. The code isn't very interesting and not shown here. Basically, what it does is to transform:
XML (<foo>bar baz</foo>)
into:
XML(foo("bar baz")).
Step2: write an XML expander
The XML expander creates the data structure from the tree; that is to say, it transforms:
XML(foo("bar baz"))
that comes from Step 1, into:
create_xml_interior_node('foo', create_xml_string_node("bar baz"));
(In fact, unlike step 1, this step depends on the structure you choose for your XML representation. This has some advantages, see later.) If create_xml_interior_node and create_xml_string_node are also macros or inline functions, the code continues to be expanded, and so on, until all constructs are base constructs.
Step 3: write a XML printer
This is just a function that takes a XML data structure and prints its contents. Nothing really complicated.
Step 4: integrate it into L
Now you can already do some funny stuff by mixing L and XML. You just have to type:
define_printer(XMLNode, xml_print_function);
define_parser('XML', xml_parser_function);
define_expander('XML', xml_expander_function);
Where xml_parser_function is the function in Step 1, xml_expander_function is the function in Step 2, xml_print_function the function in Step 3, and XMLNode the type of the XML data structure chosen in Step 2. From here you can write things like:
let XMLNode node = XML(<foo>bar baz</foo>);
print(node);
This code snippet takes the XML chunk given, and print it ... Except that the XML is really parsed: if you had typed, for instance
let XMLNode node = XML(<foo>bar baz</br>);
you would have get an invalid XML: 'foo' and 'br' don't match error (or something like that, depending on your parser.)

Now, by extending a bit the XML parser (by adding an "escape" element, $), you get a complete PHP-Like templating language for L!

This function

void print_web_page(char *title, int number)
{
  print (XML (<html>
                <head>$title</head>
                  <body><h1>$title</h1>
       	            <ul>
            	      $@{make_list_for(let i=0; i < number; i++)
       	    		              {
                                        XML(<li>$i</li>)
                                      }
                        }
                    </ul>
                  </body></html>))
}
can be compiled, takes a string and a number as arguments, and outputs (when given parameters "Foo" and 3):
<html>
  <head>Foo</head>
  <body>
    <h1>Foo</h1>
    <ul>
      <li>0</li>
      <li>1</li>
      <li>2</li>
      <li>3</li>
    </ul>
  </body>
</html>   

Note that the XML output is perfectly indented, while the L code need not to be. You can also always know at compile time that you will generate valid XML. In fact, if you extend this even more, you can know at compile time that you will generate valid XML that conforms to a DTD or a XML Schema.

In the last example, make_list_for is another macro, that incrementally constructs a list based on the result of each iteration of the for loop.

Here, at each iteration the loop produces a XML node equivalent to what would be constructed (in C) with this:

int i;
struct XMLNode xml = malloc(sizeof(struct XMLNode));
xml->type = COMPOUND_NODE;
xml->compound_node.tag = "li";
xml->compound_node.attributes_list = NULL;
xml->compound_node.sub_nodes = malloc(sizeof(struct XMLNode));
xml->next = NULL;
xml->compound_node.sub_nodes->type = INT_NODE;
xml->compound_node.sub_nodes->int_node.value = i;
xml->compound_node.sub_nodes->next = NULL;

The whole make_list_for thus constructs a list of these XML nodes. So, the previous expanded code is expanded once again; and this continues until there is nothing but base constructs.

Note that you can easily optimize the translator in the future to produce more optimized code. For instance, you could use a different XMLNode data structure more suited to your application; see how your code is completly independent of this data structure. L way of programming encourages code reutilisation, and helps separating algorithm implementation to optimisation.

Note: a proof-of-concept implementation of all this exists until this point.

Step 5: compile your L code into ANSI C

Finally, using the C output backend (being currently written), you can output the print_web_page function into C, and directly incorporate it into your C program. Without external dependencies, or anything. You have written a compiled, very efficient XML transformation program.

That means that L can also be used as a code generator; this is useful in many applications.

This example was shown only for XML, but you can extend it to any output language. You can use it, for instance, to output L code itself: this is the way we will define macros in L. In general, this facility makes L well suited for program transformation, code-generating code, and compiler writing.

Interested? Please help contributing to L now!