Tuesday, 10 May 2011

XPL: A Functional Language for Language Oriented Programming

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.

No comments:

Post a Comment