The distinguishing features of Rascal and how they solve real problems.
Meta-programs are programs that analyze, transform or generate other programs. Ordinary programs work on data; meta-programs work on programs.
The range of programs to which meta-programming can be applied is large: from programs in standard languages like C and Java to domain-specific languages for describing high-level system models or applications in specialized areas like gaming or finance. In some cases, even test results or performance data are used as input for meta-programs.
The range of kinds of meta programs that can be applied is also large. There are simple meta programs that generate boilerplate code from a list of items. There are complex meta programs that reverse engineer and statically analyse a big software system before visualizing the results. When writing all these kinds of meta programs one needs similar operations and similar data-types.
The point of Rascal is to provide an easy-to-use and easy-to-combine set of primitives to build and manipulate program representations. The point is not to be or provide a unified representation of programs to let generic algorithms operate on. In meta programming the devil is often in the details. Rascal makes sure to not a priori abstract from the important details programming language syntax and semantics. Instead we make sure it is easy to address every important detail.
The unique selling point of Rascal is linguistic integration of all the tools you need to make meta-programming effective:
- higher-order type-parameterized functions and algebraic data-types
- immutable (builtin) data collections: lists, sets, maps, relations, trees.
- extensive pattern matching and subsitution primitives for every data-type.
- source locations for provenance of code artefacts and other data sources.
- structured programming with exceptions:
- structured fixed-point programming:
- lexically scoped backtracking for data generators and non-unitary pattern matching.
- syntax definition and parser generation
- abstract syntax definition and reading in ASTs from external sources
- generic recursive traversal primitives
- relational calculus, list, set and map comprehensions, and reducers, including transitive closures and comprehensions for lists, sets, relations, list relations and maps.
- modular extensibility of syntax, data and functions (!) (no expression problem)
Typical applications of Rascal are:
- Empirical methods in Software Engineering research: metrics, statistics, impact analysis
- Design and Implementing of domain-specific languages (DSLs);
- Design and Implementing of Programming Languages;
- Writing compilers.
- Construction of Software Refactorings, ad-hoc or reusable;
- Analyzing Legacy Software: Reverse-engineering
- Renovating Legacy Software: Re-engineering
- Software Verification
- Model Driven Engineering: model-to-code, code-to-model, model verification, test code generation
All these cases involve a form of meta-programming: software programs (in a wide sense) are the objects-of-interest that are being analyzed, transformed or generated. More often than not, other sources of information (other than source code) are applied in metaprogramming. Rascal is very generic: it can be applied in domains ranging from compiler construction and implementing domain-specific languages to constraint solving and software renovation.
Since representation of information is central to the approach, Rascal provides a rich set of built-in data types. To support extraction and analysis, parsing and advanced pattern matching are provided. High-level control structures make analysis and synthesis of complex data structures simple.
- Familiar syntax in a what-you-see is-what-you-get style is used even for sophisticated concepts and this makes the language easy to learn and easy to use.
- Sophisticated built-in data types provide standard solutions for many meta-programming problems.
- Safety is achieved by finding most errors before the program is executed and by making common errors like missing initializations or invalid pointers impossible.
- Local type inference makes local variable declarations redundant.
- Pattern matching can be used to analyze all complex data structures.
- Syntax definitions make it possible to define new and existing languages and to write tools for them.
- Visiting makes it easy to traverse data structures and to extract information from them or to synthesize results.
- Templates enable easy code generation.
- Functions as values permit programming styles with high re-use.
- Generic types allow writing functions that are applicable for many different types.
- Eclipse integration makes Rascal programming a breeze. All familiar tools are at your fingertips.