Questions and issues to Walter Cazzola.
A brief introduction to Neverlang
DSLs are used to solve several problems, such as typesetting documents and code (TEX/LATEX, lout, ...), to express and verify constraints in several domains (OCL, iLOG CP, C4J, ...) and to coordinate the computation and/or to data query (Linda, SQL, ...). In some cases, these are simply a bunch of programming features useless standalone embedded in a general purpose programming language or provided as external libraries (e.g., Linda and SQL). In these case performances and flexibility are often compromised especially when the DSL is realized as program transformation towards another high-level programming language.
A DSL integrating features from different programming languages and paradigms would be the best choice to express a concise and clean solution to a problem avoiding a cumbersome one due to the absence of a specific feature or to a farraginous general-purpose programming language. Unfortunately, to develop an ad hoc programming language implies a considerable effort.
To simplify and speed up the development of problem-tailored programming languages we developed Neverlang. The Neverlang tool basically reflects the fact that programming languages have a sectional definition and each language feature could be easily plugged and unplugged. A complete compiler/interpreter built up with Neverlang is the result of a compositional process involving several building blocks.
In this scenario, to design a domain specific language consists of implementing a set of slices (each of them coding a single programming feature and the necessary support code, such as type checking and code generation) and composing them together. The whole structure of the compiler/interpreter is the result of the composition of such a slices, in particular of the code necessary to compile/interpret each single feature.
How does it work?
The framework basically provides: a language for writing the building blocks and a mechanism for composing the blocks together and for generating the compiler/interpreter.
The basic units composing a programming language developed by using Neverlang are modules. Each module encapsulates a specific feature of the language, e.g., a module can encapsulate the syntactically aspect of a loop, the type checking code of a comparison, or the code generation for a method call. Roles define how modules composing together forming the compiler/interpreter. syntax, type-checking and evaluation are examples of roles. Finally, modules regarding the same language structure but with different roles are grouped together in slices.
The example below shows how Neverlang can be used to define a if-else conditional construct. Three roles (syntax, typechecking and evaluation) are involved; each role is defined in a separate module and combined together in the if slice.