XPL is a functional language that has been developed to experiment with Domain Specific Languages and Language Oriented Programming. It is written in Java. The source code for XPL v 0.1 can be downloaded
here. The language has first-class grammars that can be combined and has access to its own abstract syntax. Grammars use quasi-quotes to build new syntax structures that can be inserted into the XPL execution stream. This is like macros in Scheme (in that language features can be defined with a limited scope). However unlike Scheme XPL can define the syntax of each new language feature.
Here is a simple language feature inspired by an example from Martin Fowler. Suppose that we get a stream of character codes as input. The stream contains information about customers and we need to chop up the input to produce data records. If there are a large number of different types of input data and they change regularly then it makes sense to define a declarative language construct that defines each type. The following XPL code defines a language construct for recovering structure from an input stream:
export test1,test2
// We need syntax constructors for Record and Field:
import 'src/xpl/exp.xpl'
// We need list operations:
// take([1,2,3],2) = [1,2]
// drop([1,2,3],2) = [3]
// foldr(f,g,b,[1,2,3]) = g(f(1),g(f(2),g(f(3),b)))
import 'src/xpl/lists.xpl'
// Define some functions to be used as args to foldr:
combine(left,right) = [| fun(l) ${left}(l,fun(r,l) r + ${right}(l)) |]
id(x) = x
empty = [| fun(l) {} |]
// Define a function that constructs *the syntax* of a field extractor:
// extractor('name',5) = [| fun(l,k) k({name=asString(take(l,5))},drop(l,5)) |]
extractor(n,i) =
let record = Record([Field(n,[| asString(take(l,${i})) |])])
in [| fun(l,k) k(${record},drop(l,${i})) |]
// Define the grammar to consists of a sequence of fields.
// Each field builds an extractor. All extractors are combined
// into a mapping from a sequence of character codes to a record:
grammar = {
fields -> fs=field* { foldr(id,combine,empty,fs) };
field -> n=name whitespace ':' i=int { extractor(n,i) };
int -> whitespace n=numeric+ { Int(asInt(n)) };
whitespace -> (32 | 10 | 9 | 13)*;
name -> whitespace l=alpha ls=alpha* { asString(l:ls) };
alpha -> ['a','z'];
numeric -> ['0','9']
}
// Here is a use of the language: a customer is a name (5 chars) followed
// by an address (15 chars), followed by an account number (3 chars):
customer =
intern grammar {
customer:5
address:15
account:3
}
// The customer map is used by applying it to a stream of char codes:
input = [102,114,101,100,32,49,48,32,77,97,105,110,32,82,111,97,100,32,32,32,53,48,49]
test1() = customer(input)
// Just to show everything is first-class:
test2() = map(customer,repeat(input,10))
To use XPL you download the ZIP file (link above). It is developed as an Eclipse project, but can also be run stand-alone. The interpreter is in the xpl package in the source folder. If you run the Interpreter as a Java application in Eclipse then the console becomes an XPL top-level loop that you can type XPL commands to. Here is a transcript of the example given above (user input after a '>' followed by XPL output):
[src/xpl/xpl.xpl 2353 ms,136]
> import 'src/xpl/split.xpl';
[src/xpl/split.xpl 179 ms,262]
[src/xpl/exp.xpl 26 ms,404]
[src/xpl/lists.xpl 108 ms,164]
> test1();
{customer=fred ;address=10 Main Road ;account=501}
> test2();
[{customer=fred ;address=10 Main Road ;account=501},
{customer=fred ;address=10 Main Road ;account=501},
{customer=fred ;address=10 Main Road ;account=501},
{customer=fred ;address=10 Main Road ;account=501},
{customer=fred ;address=10 Main Road ;account=501},
{customer=fred ;address=10 Main Road ;account=501},
{customer=fred ;address=10 Main Road ;account=501},
{customer=fred ;address=10 Main Road ;account=501},
{customer=fred ;address=10 Main Road ;account=501},
{customer=fred ;address=10 Main Road ;account=501}]
>
There is currently no user documentation (contact me if you are interested in this). But there are some technical articles:
language modules in XPL,
modular interpreters in XPL, and
parsing infix operators using XPL. In addition the source code contains a number of examples in the xpl folder.