Welcome to L
Homepage of the L programming language
Features highlight
- L is a compiled, efficient language, that is well suited for extensive optimisation
- L is multi-paradigm: it is an imperative language, but is based on expressions (which allows functional programming). Using definers, it could also be turned into a declarative language
- The L implementation allows interactive compiling, testing and debugging
- New basic language constructs can be added at compile- or
run-time, for instance
foreach
-like keywords, lazy evaluation, exception support, C++/Java-like object oriented programming, or garbage collection support - Multi-stage Lisp-like compilation, that allows pre-computations to be made by the compiler, saving programmer typing and enabling user-programmed compile-time optimisations
- The language's syntax is extensible at run time. L standard C-like syntax renders L easy to use for beginner programmer.
- Strongly-typed, with optional type annotation : the time
of
BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream)
is over! - Builtin notion of tuple enables efficient compilation, multiple return values...
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!